openvswitch: Reduce locking requirements.
Reduce and clarify locking requirements for ovs_flow_cmd_alloc_info(), ovs_flow_cmd_fill_info() and ovs_flow_cmd_build_info(). A datapath pointer is available only when holding a lock. Change ovs_flow_cmd_fill_info() and ovs_flow_cmd_build_info() to take a dp_ifindex directly, rather than a datapath pointer that is then (only) used to get the dp_ifindex. This is useful, since the dp_ifindex is available even when the datapath pointer is not, both before and after taking a lock, which makes further critical section reduction possible. Make ovs_flow_cmd_alloc_info() take an 'acts' argument instead a 'flow' pointer. This allows some future patches to do the allocation before acquiring the flow pointer. The locking requirements after this patch are: ovs_flow_cmd_alloc_info(): May be called without locking, must not be called while holding the RCU read lock (due to memory allocation). If 'acts' belong to a flow in the flow table, however, then the caller must hold ovs_mutex. ovs_flow_cmd_fill_info(): Either ovs_mutex or RCU read lock must be held. ovs_flow_cmd_build_info(): This calls both of the above, so the caller must hold ovs_mutex. Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
This commit is contained in:
parent
86ec8dbae2
commit
0e9796b4af
@ -663,7 +663,7 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Called with ovs_mutex or RCU read lock. */
|
/* Called with ovs_mutex or RCU read lock. */
|
||||||
static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
|
static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
|
||||||
struct sk_buff *skb, u32 portid,
|
struct sk_buff *skb, u32 portid,
|
||||||
u32 seq, u32 flags, u8 cmd)
|
u32 seq, u32 flags, u8 cmd)
|
||||||
{
|
{
|
||||||
@ -680,7 +680,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
|
|||||||
if (!ovs_header)
|
if (!ovs_header)
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
|
|
||||||
ovs_header->dp_ifindex = get_dpifindex(dp);
|
ovs_header->dp_ifindex = dp_ifindex;
|
||||||
|
|
||||||
/* Fill flow key. */
|
/* Fill flow key. */
|
||||||
nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
|
nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
|
||||||
@ -703,6 +703,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
|
|||||||
nla_nest_end(skb, nla);
|
nla_nest_end(skb, nla);
|
||||||
|
|
||||||
ovs_flow_stats_get(flow, &stats, &used, &tcp_flags);
|
ovs_flow_stats_get(flow, &stats, &used, &tcp_flags);
|
||||||
|
|
||||||
if (used &&
|
if (used &&
|
||||||
nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used)))
|
nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used)))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
@ -730,9 +731,9 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
|
|||||||
const struct sw_flow_actions *sf_acts;
|
const struct sw_flow_actions *sf_acts;
|
||||||
|
|
||||||
sf_acts = rcu_dereference_ovsl(flow->sf_acts);
|
sf_acts = rcu_dereference_ovsl(flow->sf_acts);
|
||||||
|
|
||||||
err = ovs_nla_put_actions(sf_acts->actions,
|
err = ovs_nla_put_actions(sf_acts->actions,
|
||||||
sf_acts->actions_len, skb);
|
sf_acts->actions_len, skb);
|
||||||
|
|
||||||
if (!err)
|
if (!err)
|
||||||
nla_nest_end(skb, start);
|
nla_nest_end(skb, start);
|
||||||
else {
|
else {
|
||||||
@ -753,41 +754,40 @@ error:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must be called with ovs_mutex. */
|
/* May not be called with RCU read lock. */
|
||||||
static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow,
|
static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *acts,
|
||||||
struct genl_info *info,
|
struct genl_info *info,
|
||||||
bool always)
|
bool always)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
size_t len;
|
|
||||||
|
|
||||||
if (!always && !ovs_must_notify(info, &ovs_dp_flow_multicast_group))
|
if (!always && !ovs_must_notify(info, &ovs_dp_flow_multicast_group))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
len = ovs_flow_cmd_msg_size(ovsl_dereference(flow->sf_acts));
|
skb = genlmsg_new_unicast(ovs_flow_cmd_msg_size(acts), info, GFP_KERNEL);
|
||||||
|
|
||||||
skb = genlmsg_new_unicast(len, info, GFP_KERNEL);
|
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must be called with ovs_mutex. */
|
/* Called with ovs_mutex. */
|
||||||
static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
|
static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow,
|
||||||
struct datapath *dp,
|
int dp_ifindex,
|
||||||
struct genl_info *info,
|
struct genl_info *info, u8 cmd,
|
||||||
u8 cmd, bool always)
|
bool always)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
skb = ovs_flow_cmd_alloc_info(flow, info, always);
|
skb = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), info,
|
||||||
|
always);
|
||||||
if (!skb || IS_ERR(skb))
|
if (!skb || IS_ERR(skb))
|
||||||
return skb;
|
return skb;
|
||||||
|
|
||||||
retval = ovs_flow_cmd_fill_info(flow, dp, skb, info->snd_portid,
|
retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb,
|
||||||
info->snd_seq, 0, cmd);
|
info->snd_portid, info->snd_seq, 0,
|
||||||
|
cmd);
|
||||||
BUG_ON(retval < 0);
|
BUG_ON(retval < 0);
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
@ -868,8 +868,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
|
|||||||
goto err_flow_free;
|
goto err_flow_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = ovs_flow_cmd_build_info(flow, dp, info,
|
reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
|
||||||
OVS_FLOW_CMD_NEW, false);
|
info, OVS_FLOW_CMD_NEW, false);
|
||||||
} else {
|
} else {
|
||||||
/* We found a matching flow. */
|
/* We found a matching flow. */
|
||||||
/* Bail out if we're not allowed to modify an existing flow.
|
/* Bail out if we're not allowed to modify an existing flow.
|
||||||
@ -895,8 +895,9 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
|
|||||||
rcu_assign_pointer(flow->sf_acts, acts);
|
rcu_assign_pointer(flow->sf_acts, acts);
|
||||||
ovs_nla_free_flow_actions(old_acts);
|
ovs_nla_free_flow_actions(old_acts);
|
||||||
}
|
}
|
||||||
reply = ovs_flow_cmd_build_info(flow, dp, info,
|
|
||||||
OVS_FLOW_CMD_NEW, false);
|
reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
|
||||||
|
info, OVS_FLOW_CMD_NEW, false);
|
||||||
|
|
||||||
/* Clear stats. */
|
/* Clear stats. */
|
||||||
if (a[OVS_FLOW_ATTR_CLEAR])
|
if (a[OVS_FLOW_ATTR_CLEAR])
|
||||||
@ -958,7 +959,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
|
|||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW, true);
|
reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info,
|
||||||
|
OVS_FLOW_CMD_NEW, true);
|
||||||
if (IS_ERR(reply)) {
|
if (IS_ERR(reply)) {
|
||||||
err = PTR_ERR(reply);
|
err = PTR_ERR(reply);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
@ -1005,7 +1007,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
|
|||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = ovs_flow_cmd_alloc_info(flow, info, false);
|
reply = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), info,
|
||||||
|
false);
|
||||||
if (IS_ERR(reply)) {
|
if (IS_ERR(reply)) {
|
||||||
err = PTR_ERR(reply);
|
err = PTR_ERR(reply);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
@ -1014,7 +1017,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
|
|||||||
ovs_flow_tbl_remove(&dp->table, flow);
|
ovs_flow_tbl_remove(&dp->table, flow);
|
||||||
|
|
||||||
if (reply) {
|
if (reply) {
|
||||||
err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid,
|
err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex,
|
||||||
|
reply, info->snd_portid,
|
||||||
info->snd_seq, 0,
|
info->snd_seq, 0,
|
||||||
OVS_FLOW_CMD_DEL);
|
OVS_FLOW_CMD_DEL);
|
||||||
BUG_ON(err < 0);
|
BUG_ON(err < 0);
|
||||||
@ -1054,7 +1058,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
|||||||
if (!flow)
|
if (!flow)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ovs_flow_cmd_fill_info(flow, dp, skb,
|
if (ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, skb,
|
||||||
NETLINK_CB(cb->skb).portid,
|
NETLINK_CB(cb->skb).portid,
|
||||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||||
OVS_FLOW_CMD_NEW) < 0)
|
OVS_FLOW_CMD_NEW) < 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user