[ovs-dev] [PATCH v6 6/6] datapath: Minimize ovs_flow_cmd_new|set critical sections.

Pravin Shelar pshelar at nicira.com
Tue Apr 1 22:20:57 UTC 2014


On Mon, Mar 31, 2014 at 11:18 AM, Jarno Rajahalme <jrajahalme at nicira.com> wrote:
> Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>

> ---
> v6: Use key fields of the newly allocated flow directly in ovs_flow_cmd_new().
>     Correctly handle the case of no acts in ovs_flow_cmd_set().
>
>  datapath/datapath.c |  193 ++++++++++++++++++++++++++++++---------------------
>  1 file changed, 115 insertions(+), 78 deletions(-)
>
> diff --git a/datapath/datapath.c b/datapath/datapath.c
> index f88147e..52c76b4 100644
> --- a/datapath/datapath.c
> +++ b/datapath/datapath.c
> @@ -798,8 +798,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
>  {
>         struct nlattr **a = info->attrs;
>         struct ovs_header *ovs_header = info->userhdr;
> -       struct sw_flow_key key, masked_key;
> -       struct sw_flow *flow;
> +       struct sw_flow *flow, *new_flow;
>         struct sw_flow_mask mask;
>         struct sk_buff *reply;
>         struct datapath *dp;
> @@ -807,61 +806,76 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
>         struct sw_flow_match match;
>         int error;
>
> -       /* Extract key. */
> +       /* Must have key and actions. */
>         error = -EINVAL;
>         if (!a[OVS_FLOW_ATTR_KEY])
>                 goto error;
> +       if (!a[OVS_FLOW_ATTR_ACTIONS])
> +               goto error;
>
> -       ovs_match_init(&match, &key, &mask);
> +       /* Most of the time we need to allocate a new flow, do it before
> +        * locking. */
> +       new_flow = ovs_flow_alloc();
> +       if (IS_ERR(new_flow)) {
> +               error = PTR_ERR(new_flow);
> +               goto error;
> +       }
> +
> +       /* Extract key. */
> +       ovs_match_init(&match, &new_flow->unmasked_key, &mask);
>         error = ovs_nla_get_match(&match,
>                                   a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
>         if (error)
> -               goto error;
> +               goto err_kfree_flow;
>
> -       /* Validate actions. */
> -       error = -EINVAL;
> -       if (!a[OVS_FLOW_ATTR_ACTIONS])
> -               goto error;
> +       ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask);
>
> +       /* Validate actions. */
>         acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
>         error = PTR_ERR(acts);
>         if (IS_ERR(acts))
> -               goto error;
> +               goto err_kfree_flow;
>
> -       ovs_flow_mask_key(&masked_key, &key, &mask);
> -       error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
> -                                    &masked_key, 0, &acts);
> +       error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key,
> +                                    0, &acts);
>         if (error) {
>                 OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
> -               goto err_kfree;
> +               goto err_kfree_acts;
> +       }
> +
> +       reply = ovs_flow_cmd_alloc_info(acts, info, false);
> +       if (IS_ERR(reply)) {
> +               error = PTR_ERR(reply);
> +               goto err_kfree_acts;
>         }
>
>         ovs_lock();
>         dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
> -       error = -ENODEV;
> -       if (!dp)
> +       if (unlikely(!dp)) {
> +               error = -ENODEV;
>                 goto err_unlock_ovs;
> -
> +       }
>         /* Check if this is a duplicate flow */
> -       flow = ovs_flow_tbl_lookup(&dp->table, &key);
> -       if (!flow) {
> -               /* Allocate flow. */
> -               flow = ovs_flow_alloc();
> -               if (IS_ERR(flow)) {
> -                       error = PTR_ERR(flow);
> -                       goto err_unlock_ovs;
> -               }
> -
> -               flow->key = masked_key;
> -               flow->unmasked_key = key;
> -               rcu_assign_pointer(flow->sf_acts, acts);
> +       flow = ovs_flow_tbl_lookup(&dp->table, &new_flow->unmasked_key);
> +       if (likely(!flow)) {
> +               rcu_assign_pointer(new_flow->sf_acts, acts);
>
>                 /* Put flow in bucket. */
> -               error = ovs_flow_tbl_insert(&dp->table, flow, &mask);
> -               if (error) {
> +               error = ovs_flow_tbl_insert(&dp->table, new_flow, &mask);
> +               if (unlikely(error)) {
>                         acts = NULL;
> -                       goto err_flow_free;
> +                       goto err_unlock_ovs;
>                 }
> +
> +               if (unlikely(reply)) {
> +                       error = ovs_flow_cmd_fill_info(new_flow,
> +                                                      ovs_header->dp_ifindex,
> +                                                      reply, info->snd_portid,
> +                                                      info->snd_seq, 0,
> +                                                      OVS_FLOW_CMD_NEW);
> +                       BUG_ON(error < 0);
> +               }
> +               ovs_unlock();
>         } else {
>                 struct sw_flow_actions *old_acts;
>
> @@ -871,40 +885,45 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
>                  * request.  We also accept NLM_F_EXCL in case that bug ever
>                  * gets fixed.
>                  */
> -               error = -EEXIST;
> -               if (info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
> +               if (unlikely(info->nlhdr->nlmsg_flags & (NLM_F_CREATE
> +                                                        | NLM_F_EXCL))) {
> +                       error = -EEXIST;
>                         goto err_unlock_ovs;
> -
> +               }
>                 /* The unmasked key has to be the same for flow updates. */
> -               if (!ovs_flow_cmp_unmasked_key(flow, &match))
> +               if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
> +                       error = -EEXIST;
>                         goto err_unlock_ovs;
> -
> +               }
>                 /* Update actions. */
>                 old_acts = ovsl_dereference(flow->sf_acts);
>                 rcu_assign_pointer(flow->sf_acts, acts);
> -               ovs_nla_free_flow_actions(old_acts);
> -       }
>
> -       reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
> -                                       info, OVS_FLOW_CMD_NEW, false);
> -       ovs_unlock();
> +               if (unlikely(reply)) {
> +                       error = ovs_flow_cmd_fill_info(flow,
> +                                                      ovs_header->dp_ifindex,
> +                                                      reply, info->snd_portid,
> +                                                      info->snd_seq, 0,
> +                                                      OVS_FLOW_CMD_NEW);
> +                       BUG_ON(error < 0);
> +               }
> +               ovs_unlock();
>
> -       if (reply) {
> -               if (!IS_ERR(reply))
> -                       ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
> -               else
> -                       netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
> -                                       ovs_dp_flow_multicast_group.id,
> -                                       PTR_ERR(reply));
> +               ovs_nla_free_flow_actions(old_acts);
> +               ovs_flow_free(new_flow, false);
>         }
How much do we save by unlocking ovs-lock before free, I think this is
rare case. If so then we can move ovs-unlock out of if and else block.
It is just easy to read.

> +
> +       if (reply)
> +               ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
>         return 0;
>
> -err_flow_free:
> -       ovs_flow_free(flow, false);
>  err_unlock_ovs:
>         ovs_unlock();
> -err_kfree:
> +       kfree_skb(reply);
> +err_kfree_acts:
>         kfree(acts);
> +err_kfree_flow:
> +       ovs_flow_free(new_flow, false);
>  error:
>         return error;
>  }
> @@ -918,7 +937,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
>         struct sw_flow_mask mask;
>         struct sk_buff *reply = NULL;
>         struct datapath *dp;
> -       struct sw_flow_actions *acts = NULL;
> +       struct sw_flow_actions *old_acts = NULL, *acts = NULL;
>         struct sw_flow_match match;
>         int error;
>
> @@ -945,56 +964,74 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
>                                              &masked_key, 0, &acts);
>                 if (error) {
>                         OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
> -                       goto err_kfree;
> +                       goto err_kfree_acts;
> +               }
> +       }
> +
> +       /* Can allocate before locking if have acts. */
> +       if (acts) {
> +               reply = ovs_flow_cmd_alloc_info(acts, info, false);
> +               if (IS_ERR(reply)) {
> +                       error = PTR_ERR(reply);
> +                       goto err_kfree_acts;
>                 }
>         }
>
>         ovs_lock();
>         dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
> -       error = -ENODEV;
> -       if (!dp)
> +       if (unlikely(!dp)) {
> +               error = -ENODEV;
>                 goto err_unlock_ovs;
> -
> +       }
>         /* Check that the flow exists. */
>         flow = ovs_flow_tbl_lookup(&dp->table, &key);
> -       error = -ENOENT;
> -       if (!flow)
> +       if (unlikely(!flow)) {
> +               error = -ENOENT;
>                 goto err_unlock_ovs;
> -
> +       }
>         /* The unmasked key has to be the same for flow updates. */
> -       error = -EEXIST;
> -       if (!ovs_flow_cmp_unmasked_key(flow, &match))
> +       if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
> +               error = -EEXIST;
>                 goto err_unlock_ovs;
> -
> +       }
>         /* Update actions, if present. */
> -       if (acts) {
> -               struct sw_flow_actions *old_acts;
> -
> +       if (likely(acts)) {
>                 old_acts = ovsl_dereference(flow->sf_acts);
>                 rcu_assign_pointer(flow->sf_acts, acts);
> -               ovs_nla_free_flow_actions(old_acts);
> +
> +               if (unlikely(reply)) {
> +                       error = ovs_flow_cmd_fill_info(flow,
> +                                                      ovs_header->dp_ifindex,
> +                                                      reply, info->snd_portid,
> +                                                      info->snd_seq, 0,
> +                                                      OVS_FLOW_CMD_NEW);
> +                       BUG_ON(error < 0);
> +               }
> +       } else {
> +               /* Could not alloc without acts before locking. */
> +               reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
> +                                               info, OVS_FLOW_CMD_NEW, false);
> +               if (unlikely(IS_ERR(reply))) {
> +                       error = PTR_ERR(reply);
> +                       goto err_unlock_ovs;
> +               }
>         }
>
> -       reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
> -                                       info, OVS_FLOW_CMD_NEW, false);
>         /* Clear stats. */
>         if (a[OVS_FLOW_ATTR_CLEAR])
>                 ovs_flow_stats_clear(flow);
>         ovs_unlock();
>
> -       if (reply) {
> -               if (!IS_ERR(reply))
> -                       ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
> -               else
> -                       netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
> -                                       ovs_dp_flow_multicast_group.id,
> -                                       PTR_ERR(reply));
> -       }
> +       if (reply)
> +               ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
> +       if (old_acts)
> +               ovs_nla_free_flow_actions(old_acts);
>         return 0;
>
>  err_unlock_ovs:
>         ovs_unlock();
> -err_kfree:
> +       kfree_skb(reply);
> +err_kfree_acts:
>         kfree(acts);
>  error:
>         return error;


Thanks for simplifying and improving datapath locking

Acked-by: Pravin B Shelar <pshelar at nicira.com>


> --
> 1.7.10.4
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list