return 0;
}
-static int set_flow_actions(struct datapath *dp, struct odp_flow __user *ufp)
+static struct sw_flow_actions *get_actions(const struct odp_flow *flow)
{
- struct sw_flow_actions *new_acts, *old_acts;
- struct sw_flow *flow;
- struct odp_flow uf;
+ struct sw_flow_actions *actions;
int error;
- error = -EFAULT;
- if (copy_from_user(&uf, ufp, sizeof uf))
+ actions = flow_actions_alloc(flow->n_actions);
+ error = PTR_ERR(actions);
+ if (IS_ERR(actions))
goto error;
- /* Get actions. */
- new_acts = flow_actions_alloc(uf.n_actions);
- error = -ENOMEM;
- if (!new_acts)
- goto error;
error = -EFAULT;
- if (copy_from_user(new_acts->actions, uf.actions,
- uf.n_actions * sizeof *uf.actions))
+ if (copy_from_user(actions->actions, flow->actions,
+ flow->n_actions * sizeof(union odp_action)))
goto error_free_actions;
- error = validate_actions(new_acts);
+ error = validate_actions(actions);
if (error)
goto error_free_actions;
- /* Replace actions. */
- flow = dp_table_lookup(dp->table, &uf.key);
- error = -ENOENT;
- if (!flow)
- goto error_free_actions;
- old_acts = rcu_dereference(flow->sf_acts);
- rcu_assign_pointer(flow->sf_acts, new_acts);
- flow_deferred_free_acts(old_acts);
-
- return 0;
+ return actions;
error_free_actions:
- kfree(new_acts);
+ kfree(actions);
error:
- return error;
+ return ERR_PTR(error);
}
-static int put_stats(struct sw_flow *flow, struct __user odp_flow *ufp)
+static void get_stats(struct sw_flow *flow, struct odp_flow_stats *stats)
{
- struct odp_flow_stats stats;
- unsigned long flags;
-
if (flow->used.tv_sec) {
- stats.used_sec = flow->used.tv_sec;
- stats.used_nsec = flow->used.tv_nsec;
+ stats->used_sec = flow->used.tv_sec;
+ stats->used_nsec = flow->used.tv_nsec;
} else {
- stats.used_sec = 0;
- stats.used_nsec = 0;
+ stats->used_sec = 0;
+ stats->used_nsec = 0;
}
+ stats->n_packets = flow->packet_count;
+ stats->n_bytes = flow->byte_count;
+ stats->ip_tos = flow->ip_tos;
+ stats->tcp_flags = flow->tcp_flags;
+}
- spin_lock_irqsave(&flow->lock, flags);
- stats.n_packets = flow->packet_count;
- stats.n_bytes = flow->byte_count;
- stats.ip_tos = flow->ip_tos;
- stats.tcp_flags = flow->tcp_flags;
- spin_unlock_irqrestore(&flow->lock, flags);
-
- return __copy_to_user(&ufp->stats, &stats, sizeof ufp->stats);
+static void clear_stats(struct sw_flow *flow)
+{
+ flow->used.tv_sec = flow->used.tv_nsec = 0;
+ flow->tcp_flags = 0;
+ flow->ip_tos = 0;
+ flow->packet_count = 0;
+ flow->byte_count = 0;
}
-static int add_flow(struct datapath *dp, struct odp_flow __user *ufp)
+static int put_flow(struct datapath *dp, struct odp_flow_put __user *ufp)
{
- struct odp_flow uf;
+ struct odp_flow_put uf;
struct sw_flow *flow, **bucket;
struct dp_table *table;
- struct sw_flow_actions *sf_acts;
+ struct odp_flow_stats stats;
int error;
error = -EFAULT;
- if (copy_from_user(&uf, ufp, sizeof uf))
+ if (copy_from_user(&uf, ufp, sizeof(struct odp_flow_put)))
goto error;
- flow = flow_alloc(uf.n_actions);
- if (flow == NULL)
+retry:
+ table = rcu_dereference(dp->table);
+ bucket = dp_table_lookup_for_insert(table, &uf.flow.key);
+ if (!bucket) {
+ /* No such flow, and the slots where it could go are full. */
+ error = uf.flags & ODPPF_CREATE ? -EXFULL : -ENOENT;
goto error;
- sf_acts = rcu_dereference(flow->sf_acts);
+ } else if (!*bucket) {
+ /* No such flow, but we found an available slot for it. */
+ struct sw_flow_actions *acts;
- /* Initialize flow. */
- flow->key = uf.key;
- if (copy_from_user(sf_acts->actions, uf.actions,
- uf.n_actions * sizeof *uf.actions))
- goto error_free_flow;
- error = validate_actions(sf_acts);
- if (error)
- goto error_free_flow;
+ error = -ENOENT;
+ if (!(uf.flags & ODPPF_CREATE))
+ goto error;
- flow->used.tv_sec = flow->used.tv_nsec = 0;
- flow->tcp_flags = 0;
- flow->ip_tos = 0;
- spin_lock_init(&flow->lock);
- flow->packet_count = 0;
- flow->byte_count = 0;
+ /* Expand table, if necessary, to make room. */
+ if (dp->n_flows * 4 >= table->n_buckets &&
+ table->n_buckets < DP_MAX_BUCKETS) {
+ error = dp_table_expand(dp);
+ if (error)
+ goto error;
- /* Add to table. */
- table = rcu_dereference(dp->table);
- if (dp->n_flows * 4 >= table->n_buckets &&
- table->n_buckets < DP_MAX_BUCKETS) {
- error = dp_table_expand(dp);
- if (error)
+ /* The bucket's location has changed. Try again. */
+ goto retry;
+ }
+
+ /* Allocate flow. */
+ flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
+ if (flow == NULL)
+ goto error;
+ flow->key = uf.flow.key;
+ spin_lock_init(&flow->lock);
+ clear_stats(flow);
+
+ /* Obtain actions. */
+ acts = get_actions(&uf.flow);
+ error = PTR_ERR(acts);
+ if (IS_ERR(acts))
goto error_free_flow;
- table = dp->table;
- }
+ rcu_assign_pointer(flow->sf_acts, acts);
- bucket = dp_table_lookup_for_insert(table, flow);
- error = -EXFULL;
- if (!bucket)
- goto error_free_flow;
- else if (!*bucket) {
- error = 0;
+ /* Put flow in bucket. */
rcu_assign_pointer(*bucket, flow);
dp->n_flows++;
+ memset(&stats, 0, sizeof(struct odp_flow_stats));
} else {
- /* Replace 'old_flow' by 'flow'. */
- struct sw_flow *old_flow = *rcu_dereference(bucket);
- rcu_assign_pointer(*bucket, flow);
+ /* We found a matching flow. */
+ struct sw_flow *flow = *rcu_dereference(bucket);
+ struct sw_flow_actions *old_acts, *new_acts;
+ unsigned long int flags;
+
+ /* Bail out if we're not allowed to modify an existing flow. */
+ error = -EEXIST;
+ if (!(uf.flags & ODPPF_MODIFY))
+ goto error;
- /* XXX These statistics might lose a few packets, since other
- * CPUs can be using this flow. We used to synchronize_rcu()
- * to make sure that we get completely accurate stats, but that
- * blows our performance, badly. */
- error = put_stats(old_flow, ufp) ? -EFAULT : 0;
- flow_deferred_free(old_flow);
+ /* Swap actions. */
+ new_acts = get_actions(&uf.flow);
+ error = PTR_ERR(new_acts);
+ if (IS_ERR(new_acts))
+ goto error;
+ old_acts = rcu_dereference(flow->sf_acts);
+ if (old_acts->n_actions != new_acts->n_actions ||
+ memcmp(old_acts->actions, new_acts->actions,
+ sizeof(union odp_action) * old_acts->n_actions)) {
+ rcu_assign_pointer(flow->sf_acts, new_acts);
+ flow_deferred_free_acts(old_acts);
+ } else {
+ kfree(new_acts);
+ }
+
+ /* Fetch stats, then clear them if necessary. */
+ spin_lock_irqsave(&flow->lock, flags);
+ get_stats(flow, &stats);
+ if (uf.flags & ODPPF_ZERO_STATS)
+ clear_stats(flow);
+ spin_unlock_irqrestore(&flow->lock, flags);
}
- return error;
+ /* Copy stats to userspace. */
+ if (__copy_to_user(&ufp->flow.stats, &stats,
+ sizeof(struct odp_flow_stats)))
+ return -EFAULT;
+ return 0;
error_free_flow:
flow_free(flow);
static int answer_query(struct sw_flow *flow, struct odp_flow __user *ufp)
{
- if (put_stats(flow, ufp))
+ struct odp_flow_stats stats;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&flow->lock, flags);
+ get_stats(flow, &stats);
+ spin_unlock_irqrestore(&flow->lock, flags);
+
+ if (__copy_to_user(&ufp->stats, &stats, sizeof(struct odp_flow_stats)))
return -EFAULT;
return put_actions(flow, ufp);
}
err = flush_flows(dp);
break;
- case ODP_FLOW_ADD:
- err = add_flow(dp, (struct odp_flow __user *)argp);
- break;
-
- case ODP_FLOW_SET_ACTS:
- err = set_flow_actions(dp, (struct odp_flow __user *)argp);
+ case ODP_FLOW_PUT:
+ err = put_flow(dp, (struct odp_flow_put __user *)argp);
break;
case ODP_FLOW_DEL:
- case ODP_FLOW_QUERY:
+ case ODP_FLOW_GET:
err = del_or_query_flow(dp, (struct odp_flow __user *)argp,
cmd);
break;
- case ODP_FLOW_QUERY_MULTIPLE:
+ case ODP_FLOW_GET_MULTIPLE:
err = do_flowvec_ioctl(dp, argp, query_multiple_flows);
break;
struct dp_table *dp_table_create(unsigned int n_buckets);
void dp_table_destroy(struct dp_table *, int free_flows);
struct sw_flow *dp_table_lookup(struct dp_table *, const struct odp_flow_key *);
-struct sw_flow **dp_table_lookup_for_insert(struct dp_table *table,
- struct sw_flow *target);
+struct sw_flow **dp_table_lookup_for_insert(struct dp_table *, const struct odp_flow_key *);
int dp_table_delete(struct dp_table *, struct sw_flow *);
int dp_table_expand(struct datapath *);
int dp_table_flush(struct datapath *);
struct sw_flow_actions *sfa;
if (n_actions > (PAGE_SIZE - sizeof *sfa) / sizeof(union odp_action))
- return NULL;
+ return ERR_PTR(-EINVAL);
sfa = kmalloc(sizeof *sfa + n_actions * sizeof(union odp_action),
GFP_KERNEL);
- if (sfa)
- sfa->n_actions = n_actions;
- return sfa;
-}
-
-
-/* Allocates and returns a new flow with room for 'n_actions' actions. Returns
- * the new flow or a null pointer on failure. */
-struct sw_flow *flow_alloc(size_t n_actions)
-{
- struct sw_flow_actions *sfa;
- struct sw_flow *flow;
-
- sfa = flow_actions_alloc(n_actions);
if (!sfa)
- return NULL;
-
- flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
- if (flow)
- rcu_assign_pointer(flow->sf_acts, sfa);
- else
- kfree(sfa);
+ return ERR_PTR(-ENOMEM);
- return flow;
+ sfa->n_actions = n_actions;
+ return sfa;
}
+
/* Frees 'flow' immediately. */
void flow_free(struct sw_flow *flow)
{
u8 tcp_flags; /* Union of seen TCP flags. */
};
+extern struct kmem_cache *flow_cache;
+
struct sw_flow_actions *flow_actions_alloc(size_t n_actions);
-struct sw_flow *flow_alloc(size_t n_actions);
void flow_free(struct sw_flow *);
void flow_deferred_free(struct sw_flow *);
void flow_deferred_free_acts(struct sw_flow_actions *);
return jhash2((u32*)key, sizeof *key / sizeof(u32), 0x55555555);
}
-static void find_buckets(struct dp_table *table, struct odp_flow_key *key,
+static void find_buckets(struct dp_table *table,
+ const struct odp_flow_key *key,
struct sw_flow **buckets[2])
{
buckets[0] = find_bucket(table, table->flows[0], flow_hash0(key));
}
struct sw_flow **
-dp_table_lookup_for_insert(struct dp_table *table, struct sw_flow *target)
+dp_table_lookup_for_insert(struct dp_table *table,
+ const struct odp_flow_key *target)
{
struct sw_flow **buckets[2];
struct sw_flow **empty_bucket = NULL;
int i;
- find_buckets(table, &target->key, buckets);
+ find_buckets(table, target, buckets);
for (i = 0; i < 2; i++) {
struct sw_flow *f = rcu_dereference(*buckets[i]);
if (f) {
- if (!memcmp(&f->key, &target->key, sizeof f->key))
+ if (!memcmp(&f->key, target, sizeof f->key))
return buckets[i];
} else if (!empty_bucket)
empty_bucket = buckets[i];
#define ODP_PORT_GROUP_SET _IOR('O', 11, struct odp_port_group)
#define ODP_PORT_GROUP_GET _IOWR('O', 12, struct odp_port_group)
-#define ODP_FLOW_FLUSH _IO('O', 13)
-#define ODP_FLOW_ADD _IOR('O', 14, struct odp_flow)
-#define ODP_FLOW_SET_ACTS _IOR('O', 15, struct odp_flow)
-#define ODP_FLOW_DEL _IOWR('O', 16, struct odp_flow)
-#define ODP_FLOW_QUERY _IOWR('O', 17, struct odp_flow)
-#define ODP_FLOW_QUERY_MULTIPLE _IOWR('O', 18, struct odp_flowvec)
-#define ODP_FLOW_LIST _IOWR('O', 19, struct odp_flowvec)
+#define ODP_FLOW_GET _IOWR('O', 13, struct odp_flow)
+#define ODP_FLOW_GET_MULTIPLE _IOWR('O', 14, struct odp_flowvec)
+#define ODP_FLOW_LIST _IOWR('O', 15, struct odp_flowvec)
-#define ODP_EXECUTE _IOR('O', 20, struct odp_execute)
+#define ODP_FLOW_FLUSH _IO('O', 16)
+#define ODP_FLOW_PUT _IOWR('O', 17, struct odp_flow)
+#define ODP_FLOW_DEL _IOWR('O', 18, struct odp_flow)
-#define ODP_SNAT_ADD_PORT _IOR('O', 21, struct odp_snat_config)
-#define ODP_SNAT_DEL_PORT _IOR('O', 22, int)
+#define ODP_EXECUTE _IOR('O', 19, struct odp_execute)
+
+#define ODP_SNAT_ADD_PORT _IOR('O', 20, struct odp_snat_config)
+#define ODP_SNAT_DEL_PORT _IOR('O', 21, int)
struct odp_stats {
/* Flows. */
__u32 n_actions;
};
+/* Flags for ODP_FLOW_PUT. */
+#define ODPPF_CREATE (1 << 0) /* Allow creating a new flow. */
+#define ODPPF_MODIFY (1 << 1) /* Allow modifying an existing flow. */
+#define ODPPF_ZERO_STATS (1 << 2) /* Zero the stats of an existing flow. */
+
+/* ODP_FLOW_PUT argument. */
+struct odp_flow_put {
+ struct odp_flow flow;
+ __u32 flags;
+};
+
struct odp_flowvec {
struct odp_flow *flows;
int n_flows;
if (cmd_name) {
if (error) {
VLOG_WARN_RL(&error_rl, "dp%u: ioctl(%s) failed (%s)",
- dpif->minor, cmd_name, strerror(errno));
+ dpif->minor, cmd_name, strerror(error));
} else {
VLOG_DBG_RL(&dpmsg_rl, "dp%u: ioctl(%s): success",
dpif->minor, cmd_name);
return do_ioctl(dpif, ODP_FLOW_FLUSH, "ODP_FLOW_FLUSH", NULL);
}
+static enum vlog_level
+flow_message_log_level(int error)
+{
+ return error ? VLL_WARN : VLL_DBG;
+}
+
+static bool
+should_log_flow_message(int error)
+{
+ return !vlog_should_drop(THIS_MODULE, flow_message_log_level(error),
+ error ? &error_rl : &dpmsg_rl);
+}
+
static void
-log_flow_message(const struct dpif *dpif, enum vlog_level level, int error,
+log_flow_message(const struct dpif *dpif, int error,
const char *operation,
const flow_t *flow, const struct odp_flow_stats *stats,
const union odp_action *actions, size_t n_actions)
ds_put_cstr(&ds, ", actions:");
format_odp_actions(&ds, actions, n_actions);
}
- vlog(THIS_MODULE, level, "%s", ds_cstr(&ds));
+ vlog(THIS_MODULE, flow_message_log_level(error), "%s", ds_cstr(&ds));
ds_destroy(&ds);
}
if (error && show_stats) {
flow->n_actions = 0;
}
- if (!(error ? VLOG_DROP_WARN(&error_rl) : VLOG_DROP_DBG(&dpmsg_rl))) {
- log_flow_message(dpif, error ? VLL_WARN : VLL_DBG, error, operation,
- &flow->key,
+ if (should_log_flow_message(error)) {
+ log_flow_message(dpif, error, operation, &flow->key,
show_stats && !error ? &flow->stats : NULL,
flow->actions, flow->n_actions);
}
}
int
-dpif_flow_add(struct dpif *dpif, struct odp_flow *flow)
-{
- return do_flow_ioctl(dpif, ODP_FLOW_ADD, flow, "add flow", false);
-}
-
-int
-dpif_flow_set_actions(struct dpif *dpif, const struct odp_flow_key *key,
- const union odp_action *actions, size_t n_actions)
+dpif_flow_put(struct dpif *dpif, struct odp_flow_put *put)
{
- struct odp_flow flow;
-
- flow.key = *key;
- flow.actions = (union odp_action *) actions;
- flow.n_actions = n_actions;
- return do_flow_ioctl(dpif, ODP_FLOW_SET_ACTS, &flow, "set flow actions",
- false);
+ int error = do_ioctl(dpif, ODP_FLOW_PUT, NULL, put);
+ if (should_log_flow_message(error)) {
+ struct ds operation = DS_EMPTY_INITIALIZER;
+ ds_put_cstr(&operation, "put");
+ if (put->flags & ODPPF_CREATE) {
+ ds_put_cstr(&operation, "[create]");
+ }
+ if (put->flags & ODPPF_MODIFY) {
+ ds_put_cstr(&operation, "[modify]");
+ }
+ if (put->flags & ODPPF_ZERO_STATS) {
+ ds_put_cstr(&operation, "[zero]");
+ }
+#define ODPPF_ALL (ODPPF_CREATE | ODPPF_MODIFY | ODPPF_ZERO_STATS)
+ if (put->flags & ~ODPPF_ALL) {
+ ds_put_format(&operation, "[%x]", put->flags & ~ODPPF_ALL);
+ }
+ log_flow_message(dpif, error, ds_cstr(&operation), &put->flow.key,
+ !error ? &put->flow.stats : NULL,
+ put->flow.actions, put->flow.n_actions);
+ ds_destroy(&operation);
+ }
+ return error;
}
int
}
int
-dpif_flow_query(const struct dpif *dpif, struct odp_flow *flow)
+dpif_flow_get(const struct dpif *dpif, struct odp_flow *flow)
{
check_rw_odp_flow(flow);
memset(&flow->stats, 0, sizeof flow->stats);
- return do_flow_ioctl(dpif, ODP_FLOW_QUERY, flow, "query flow", true);
+ return do_flow_ioctl(dpif, ODP_FLOW_GET, flow, "get flow", true);
}
int
-dpif_flow_query_multiple(const struct dpif *dpif,
- struct odp_flow flows[], size_t n)
+dpif_flow_get_multiple(const struct dpif *dpif,
+ struct odp_flow flows[], size_t n)
{
struct odp_flowvec fv;
size_t i;
for (i = 0; i < n; i++) {
check_rw_odp_flow(&flows[i]);
}
- return do_ioctl(dpif, ODP_FLOW_QUERY_MULTIPLE, "ODP_FLOW_QUERY_MULTIPLE",
+ return do_ioctl(dpif, ODP_FLOW_GET_MULTIPLE, "ODP_FLOW_GET_MULTIPLE",
&fv);
}
flows[i].n_actions = 0;
}
}
- error = do_ioctl(dpif, ODP_FLOW_LIST, "ODP_FLOW_LIST", &fv);
- *n_out = error ? 0 : fv.n_flows;
+ error = do_ioctl(dpif, ODP_FLOW_LIST, NULL, &fv);
+ if (error) {
+ *n_out = 0;
+ VLOG_WARN_RL(&error_rl, "dp%u: flow list failed (%s)",
+ dpif->minor, strerror(error));
+ } else {
+ *n_out = fv.n_flows;
+ VLOG_DBG_RL(&dpmsg_rl, "dp%u: listed %zu flows", dpif->minor, *n_out);
+ }
return error;
}
uint16_t ports[], size_t n_ports, size_t *n_out);
int dpif_flow_flush(struct dpif *);
-int dpif_flow_add(struct dpif *, struct odp_flow *);
-int dpif_flow_set_actions(struct dpif *, const struct odp_flow_key *,
- const union odp_action *actions, size_t n_actions);
+int dpif_flow_put(struct dpif *, struct odp_flow_put *);
int dpif_flow_del(struct dpif *, struct odp_flow *);
-int dpif_flow_query(const struct dpif *, struct odp_flow *);
-int dpif_flow_query_multiple(const struct dpif *, struct odp_flow[], size_t n);
+int dpif_flow_get(const struct dpif *, struct odp_flow *);
+int dpif_flow_get_multiple(const struct dpif *, struct odp_flow[], size_t n);
int dpif_flow_list(const struct dpif *, struct odp_flow[], size_t n,
size_t *n_out);
int dpif_flow_list_all(const struct dpif *,
}
}
+static int
+do_put_flow(struct ofproto *ofproto, struct rule *rule, int flags,
+ struct odp_flow_put *put)
+{
+ memset(&put->flow.stats, 0, sizeof put->flow.stats);
+ put->flow.key = rule->cr.flow;
+ put->flow.actions = rule->odp_actions;
+ put->flow.n_actions = rule->n_odp_actions;
+ put->flags = flags;
+ return dpif_flow_put(&ofproto->dpif, put);
+}
+
static void
-rule_install(struct ofproto *p, struct rule *rule,
- struct rule *displaced_rule)
+rule_install(struct ofproto *p, struct rule *rule, struct rule *displaced_rule)
{
assert(!rule->cr.wc.wildcards);
if (rule->may_install) {
- struct odp_flow odp_flow;
-
- odp_flow.key = rule->cr.flow;
- odp_flow.actions = rule->odp_actions;
- odp_flow.n_actions = rule->n_odp_actions;
- if (!dpif_flow_add(&p->dpif, &odp_flow)) {
+ struct odp_flow_put put;
+ if (!do_put_flow(p, rule,
+ ODPPF_CREATE | ODPPF_MODIFY | ODPPF_ZERO_STATS,
+ &put)) {
rule->installed = true;
if (displaced_rule) {
- update_stats(rule, &odp_flow.stats);
+ update_stats(rule, &put.flow.stats);
rule_post_uninstall(p, displaced_rule);
}
}
if (actions_changed) {
/* XXX should really do rule_post_uninstall() for the *old* set
* of actions, and distinguish the old stats from the new. */
- dpif_flow_set_actions(&ofproto->dpif, &rule->cr.flow,
- rule->odp_actions, rule->n_odp_actions);
+ struct odp_flow_put put;
+ do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY, &put);
}
} else {
rule_install(ofproto, rule, NULL);
packet_count = rule->packet_count;
byte_count = rule->byte_count;
- if (!dpif_flow_query_multiple(&p->dpif, odp_flows, n_odp_flows)) {
+ if (!dpif_flow_get_multiple(&p->dpif, odp_flows, n_odp_flows)) {
size_t i;
for (i = 0; i < n_odp_flows; i++) {
struct odp_flow *odp_flow = &odp_flows[i];
f->actions = actions;
f->n_actions = MAX_ACTIONS;
- dpif_flow_query(&dpif, f);
+ dpif_flow_get(&dpif, f);
ds_clear(&ds);
format_odp_flow(&ds, f);