{
if (dp_idx < 0 || dp_idx >= ODP_MAX)
return NULL;
- return rcu_dereference(dps[dp_idx]);
+ return rcu_dereference_check(dps[dp_idx], rcu_read_lock_held() ||
+ lockdep_is_held(&dp_mutex));
}
EXPORT_SYMBOL_GPL(get_dp);
return dp;
}
+static struct tbl *get_table_protected(struct datapath *dp)
+{
+ return rcu_dereference_protected(dp->table, lockdep_is_held(&dp->mutex));
+}
+
/* Must be called with rcu_read_lock or RTNL lock. */
const char *dp_name(const struct datapath *dp)
{
- return vport_get_name(dp->ports[ODPP_LOCAL]);
+ return vport_get_name(rcu_dereference_rtnl(dp->ports[ODPP_LOCAL]));
}
static inline size_t br_nlmsg_size(void)
hdr->ifi_change = 0;
NLA_PUT_STRING(skb, IFLA_IFNAME, vport_get_name(port));
- NLA_PUT_U32(skb, IFLA_MASTER, vport_get_ifindex(dp->ports[ODPP_LOCAL]));
+ NLA_PUT_U32(skb, IFLA_MASTER,
+ vport_get_ifindex(rtnl_dereference(dp->ports[ODPP_LOCAL])));
NLA_PUT_U32(skb, IFLA_MTU, vport_get_mtu(port));
#ifdef IFLA_OPERSTATE
NLA_PUT_U8(skb, IFLA_OPERSTATE,
dp->drop_frags = 0;
dp->stats_percpu = alloc_percpu(struct dp_stats_percpu);
- if (!dp->stats_percpu)
+ if (!dp->stats_percpu) {
+ err = -ENOMEM;
goto err_destroy_local_port;
+ }
rcu_assign_pointer(dps[dp_idx], dp);
dp_sysfs_add_dp(dp);
flow_node = tbl_lookup(rcu_dereference(dp->table), &key,
flow_hash(&key), flow_cmp);
if (unlikely(!flow_node)) {
- dp_output_control(dp, skb, _ODPL_MISS_NR, OVS_CB(skb)->tun_id);
+ dp_output_control(dp, skb, _ODPL_MISS_NR,
+ (__force u64)OVS_CB(skb)->tun_id);
stats_counter_off = offsetof(struct dp_stats_percpu, n_missed);
goto out;
}
static int flush_flows(struct datapath *dp)
{
- struct tbl *old_table = rcu_dereference(dp->table);
+ struct tbl *old_table = get_table_protected(dp);
struct tbl *new_table;
new_table = tbl_create(0);
static int expand_table(struct datapath *dp)
{
- struct tbl *old_table = rcu_dereference(dp->table);
+ struct tbl *old_table = get_table_protected(dp);
struct tbl *new_table;
new_table = tbl_expand(old_table);
struct sw_flow *flow;
struct tbl *table;
int error;
+ u32 hash;
- table = rcu_dereference(dp->table);
- flow_node = tbl_lookup(table, &uf->flow.key, flow_hash(&uf->flow.key), flow_cmp);
+ 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. */
struct sw_flow_actions *acts;
error = expand_table(dp);
if (error)
goto error;
- table = rcu_dereference(dp->table);
+ table = get_table_protected(dp);
}
/* Allocate flow. */
rcu_assign_pointer(flow->sf_acts, acts);
/* Put flow in bucket. */
- error = tbl_insert(table, &flow->tbl_node, flow_hash(&flow->key));
+ error = tbl_insert(table, &flow->tbl_node, hash);
if (error)
goto error_free_flow_acts;
error = PTR_ERR(new_acts);
if (IS_ERR(new_acts))
goto error;
- old_acts = rcu_dereference(flow->sf_acts);
+
+ 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)) {
return 0;
}
-static int do_answer_query(struct sw_flow *flow, u32 query_flags,
+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)
if (!actions_len)
return 0;
- sf_acts = rcu_dereference(flow->sf_acts);
+ 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 0;
}
-static int answer_query(struct sw_flow *flow, u32 query_flags,
- struct odp_flow __user *ufp)
+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(flow, query_flags,
+ 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 = rcu_dereference(dp->table);
+ struct tbl *table = get_table_protected(dp);
struct tbl_node *flow_node;
int error;
if (IS_ERR(flow))
return PTR_ERR(flow);
- error = answer_query(flow, 0, ufp);
+ error = answer_query(dp, 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);
+ struct tbl *table = get_table_protected(dp);
u32 i;
for (i = 0; i < flowvec->n_flows; i++) {
if (!flow_node)
error = put_user(ENOENT, &ufp->stats.error);
else
- error = answer_query(flow_cast(flow_node), uf.flags, ufp);
+ error = answer_query(dp, flow_cast(flow_node), uf.flags, ufp);
if (error)
return -EFAULT;
}
}
struct list_flows_cbdata {
+ struct datapath *dp;
struct odp_flow __user *uflows;
u32 n_flows;
u32 listed_flows;
if (copy_to_user(&ufp->key, &flow->key, sizeof flow->key))
return -EFAULT;
- error = answer_query(flow, 0, ufp);
+ error = answer_query(cbdata->dp, flow, 0, ufp);
if (error)
return error;
if (!flowvec->n_flows)
return 0;
+ cbdata.dp = dp;
cbdata.uflows = (struct odp_flow __user *)flowvec->flows;
cbdata.n_flows = flowvec->n_flows;
cbdata.listed_flows = 0;
- error = tbl_foreach(rcu_dereference(dp->table), list_flow, &cbdata);
+ error = tbl_foreach(get_table_protected(dp), list_flow, &cbdata);
return error ? error : cbdata.listed_flows;
}
static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
{
- struct tbl *table = rcu_dereference(dp->table);
+ struct tbl *table = get_table_protected(dp);
struct odp_stats stats;
int i;
return 0;
}
-static int compat_answer_query(struct sw_flow *flow, u32 query_flags,
+static int compat_answer_query(struct datapath *dp, struct sw_flow *flow,
+ u32 query_flags,
struct compat_odp_flow __user *ufp)
{
compat_uptr_t actions;
if (get_user(actions, &ufp->actions))
return -EFAULT;
- return do_answer_query(flow, query_flags, &ufp->stats,
+ return do_answer_query(dp, flow, query_flags, &ufp->stats,
compat_ptr(actions), &ufp->actions_len);
}
if (IS_ERR(flow))
return PTR_ERR(flow);
- error = compat_answer_query(flow, 0, ufp);
+ error = compat_answer_query(dp, flow, 0, ufp);
flow_deferred_free(flow);
return error;
}
struct compat_odp_flow __user *flows,
u32 n_flows)
{
- struct tbl *table = rcu_dereference(dp->table);
+ struct tbl *table = get_table_protected(dp);
u32 i;
for (i = 0; i < n_flows; i++) {
if (!flow_node)
error = put_user(ENOENT, &ufp->stats.error);
else
- error = compat_answer_query(flow_cast(flow_node), uf.flags, ufp);
+ error = compat_answer_query(dp, flow_cast(flow_node),
+ uf.flags, ufp);
if (error)
return -EFAULT;
}
}
struct compat_list_flows_cbdata {
+ struct datapath *dp;
struct compat_odp_flow __user *uflows;
u32 n_flows;
u32 listed_flows;
if (copy_to_user(&ufp->key, &flow->key, sizeof flow->key))
return -EFAULT;
- error = compat_answer_query(flow, 0, ufp);
+ error = compat_answer_query(cbdata->dp, flow, 0, ufp);
if (error)
return error;
if (!n_flows)
return 0;
+ cbdata.dp = dp;
cbdata.uflows = flows;
cbdata.n_flows = n_flows;
cbdata.listed_flows = 0;
- error = tbl_foreach(rcu_dereference(dp->table), compat_list_flow, &cbdata);
+ error = tbl_foreach(get_table_protected(dp), compat_list_flow, &cbdata);
return error ? error : cbdata.listed_flows;
}
u16 csum_start, csum_offset;
get_skb_csum_pointers(skb, &csum_start, &csum_offset);
+ csum_start -= skb_headroom(skb);
+
BUG_ON(csum_start >= skb_headlen(skb));
retval = skb_copy_and_csum_datagram(skb, csum_start, buf + csum_start,
copy_bytes - csum_start, &csum);