-
- /* 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(flow->sf_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 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(flow->sf_acts);
- 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 sw_flow *flow, u32 query_flags,
- struct odp_flow __user *ufp)
-{
- struct nlattr *actions;
-
- if (get_user(actions, &ufp->actions))
- return -EFAULT;
-
- return do_answer_query(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 = rcu_dereference(dp->table);
- 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(flow, 0, ufp);
- flow_deferred_free(flow);
- return error;
-}
-
-static int do_query_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
-{
- struct tbl *table = rcu_dereference(dp->table);
- u32 i;
-
- for (i = 0; i < flowvec->n_flows; i++) {
- struct odp_flow __user *ufp = &flowvec->flows[i];
- struct odp_flow uf;
- struct tbl_node *flow_node;
- int error;
-
- if (copy_from_user(&uf, ufp, sizeof uf))
- return -EFAULT;
-
- flow_node = tbl_lookup(table, &uf.key, flow_hash(&uf.key), flow_cmp);
- if (!flow_node)
- error = put_user(ENOENT, &ufp->stats.error);
- else
- error = answer_query(flow_cast(flow_node), uf.flags, ufp);
- if (error)
- return -EFAULT;