- return 0;
-}
-
-static int do_put_flow(struct datapath *dp, struct odp_flow_put *uf,
- struct odp_flow_stats *stats)
-{
- struct tbl_node *flow_node;
- struct sw_flow *flow;
- struct tbl *table;
- struct sw_flow_actions *acts = NULL;
- int error;
- u32 hash;
-
- hash = flow_hash(&uf->flow.key);
- table = get_table_protected(dp);
- flow_node = tbl_lookup(table, &uf->flow.key, hash, flow_cmp);
- if (!flow_node) {
- /* No such flow. */
- error = -ENOENT;
- if (!(uf->flags & ODPPF_CREATE))
- goto error;
-
- /* Expand table, if necessary, to make room. */
- if (tbl_count(table) >= tbl_n_buckets(table)) {
- error = expand_table(dp);
- if (error)
- goto error;
- table = get_table_protected(dp);
- }
-
- /* Allocate flow. */
- flow = flow_alloc();
- if (IS_ERR(flow)) {
- error = PTR_ERR(flow);
- goto error;
- }
- flow->key = uf->flow.key;
- clear_stats(flow);
-
- /* Obtain actions. */
- acts = get_actions(&uf->flow);
- error = PTR_ERR(acts);
- if (IS_ERR(acts))
- goto error_free_flow;
- rcu_assign_pointer(flow->sf_acts, acts);
-
- /* Put flow in bucket. */
- error = tbl_insert(table, &flow->tbl_node, hash);
- if (error)
- goto error_free_flow_acts;
-
- memset(stats, 0, sizeof(struct odp_flow_stats));
- } else {
- /* We found a matching flow. */
- struct sw_flow_actions *old_acts, *new_acts;
-
- flow = flow_cast(flow_node);
-
- /* Bail out if we're not allowed to modify an existing flow. */
- error = -EEXIST;
- if (!(uf->flags & ODPPF_MODIFY))
- goto error;
-
- /* Swap actions. */
- new_acts = get_actions(&uf->flow);
- error = PTR_ERR(new_acts);
- if (IS_ERR(new_acts))
- goto error;
-
- old_acts = rcu_dereference_protected(flow->sf_acts,
- lockdep_is_held(&dp->mutex));
- if (old_acts->actions_len != new_acts->actions_len ||
- memcmp(old_acts->actions, new_acts->actions,
- old_acts->actions_len)) {
- 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_bh(&flow->lock);
- get_stats(flow, stats);
- if (uf->flags & ODPPF_ZERO_STATS)
- clear_stats(flow);
- spin_unlock_bh(&flow->lock);
- }
-
- return 0;
-
-error_free_flow_acts:
- kfree(acts);
-error_free_flow:
- flow->sf_acts = NULL;
- flow_put(flow);
-error:
- return error;
-}
-
-static int put_flow(struct datapath *dp, struct odp_flow_put __user *ufp)
-{
- struct odp_flow_stats stats;
- struct odp_flow_put uf;
- int error;
-
- if (copy_from_user(&uf, ufp, sizeof(struct odp_flow_put)))
- return -EFAULT;
-
- error = do_put_flow(dp, &uf, &stats);
- if (error)
- return error;
-
- if (copy_to_user(&ufp->flow.stats, &stats,
- sizeof(struct odp_flow_stats)))
- return -EFAULT;
-
- return 0;
-}
-
-static int do_answer_query(struct datapath *dp, struct sw_flow *flow,
- u32 query_flags,
- struct odp_flow_stats __user *ustats,
- struct nlattr __user *actions,
- u32 __user *actions_lenp)
-{
- struct sw_flow_actions *sf_acts;
- struct odp_flow_stats stats;
- u32 actions_len;
-
- spin_lock_bh(&flow->lock);
- get_stats(flow, &stats);
- if (query_flags & ODPFF_ZERO_TCP_FLAGS)
- flow->tcp_flags = 0;
-
- spin_unlock_bh(&flow->lock);
-
- if (copy_to_user(ustats, &stats, sizeof(struct odp_flow_stats)) ||
- get_user(actions_len, actions_lenp))
- return -EFAULT;
-
- if (!actions_len)
- return 0;
-
- sf_acts = rcu_dereference_protected(flow->sf_acts,
- lockdep_is_held(&dp->mutex));
- if (put_user(sf_acts->actions_len, actions_lenp) ||
- (actions && copy_to_user(actions, sf_acts->actions,
- min(sf_acts->actions_len, actions_len))))
- return -EFAULT;
-
- return 0;
-}
-
-static int answer_query(struct datapath *dp, struct sw_flow *flow,
- u32 query_flags, struct odp_flow __user *ufp)
-{
- struct nlattr __user *actions;
-
- if (get_user(actions, (struct nlattr __user * __user *)&ufp->actions))
- return -EFAULT;
-
- return do_answer_query(dp, flow, query_flags,
- &ufp->stats, actions, &ufp->actions_len);
-}
-
-static struct sw_flow *do_del_flow(struct datapath *dp, struct odp_flow_key *key)
-{
- struct tbl *table = get_table_protected(dp);
- struct tbl_node *flow_node;
- int error;
-
- flow_node = tbl_lookup(table, key, flow_hash(key), flow_cmp);
- if (!flow_node)
- return ERR_PTR(-ENOENT);
-
- error = tbl_remove(table, flow_node);
- if (error)
- return ERR_PTR(error);
-
- /* XXX Returned flow_node's 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. */
- return flow_cast(flow_node);
-}
-
-static int del_flow(struct datapath *dp, struct odp_flow __user *ufp)
-{
- struct sw_flow *flow;
- struct odp_flow uf;
- int error;
-
- if (copy_from_user(&uf, ufp, sizeof(uf)))
- return -EFAULT;
-
- flow = do_del_flow(dp, &uf.key);
- if (IS_ERR(flow))
- return PTR_ERR(flow);
-
- error = answer_query(dp, flow, 0, ufp);
- flow_deferred_free(flow);
- return error;