datapath: Use RCU lock for flow dump operation.
[sliver-openvswitch.git] / datapath / datapath.c
index ee3d5e4..744a9a4 100644 (file)
@@ -260,6 +260,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
        }
 
        OVS_CB(skb)->flow = flow;
+       OVS_CB(skb)->pkt_key = &key;
 
        stats_counter = &stats->n_hit;
        ovs_flow_used(OVS_CB(skb)->flow, skb);
@@ -923,6 +924,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
                goto err_flow_free;
 
        OVS_CB(packet)->flow = flow;
+       OVS_CB(packet)->pkt_key = &flow->key;
        packet->priority = flow->key.phy.priority;
        skb_set_mark(packet, flow->key.phy.skb_mark);
 
@@ -1124,7 +1126,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
                                  u32 seq, u32 flags, u8 cmd)
 {
        const int skb_orig_len = skb->len;
-       const struct sw_flow_actions *sf_acts;
+       struct sw_flow_mask *mask;
        struct nlattr *start;
        struct ovs_flow_stats stats;
        struct ovs_header *ovs_header;
@@ -1133,8 +1135,6 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        u8 tcp_flags;
        int err;
 
-       sf_acts = ovsl_dereference(flow->sf_acts);
-
        ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd);
        if (!ovs_header)
                return -EMSGSIZE;
@@ -1156,8 +1156,8 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        if (!nla)
                goto nla_put_failure;
 
-       err = ovs_flow_to_nlattrs(&flow->key,
-                       &ovsl_dereference(flow->mask)->key, skb);
+       mask = rcu_dereference_check(flow->mask, lockdep_ovsl_is_held());
+       err = ovs_flow_to_nlattrs(&flow->key, &mask->key, skb);
        if (err)
                goto error;
 
@@ -1195,6 +1195,11 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
         */
        start = nla_nest_start(skb, OVS_FLOW_ATTR_ACTIONS);
        if (start) {
+               const struct sw_flow_actions *sf_acts;
+
+               sf_acts = rcu_dereference_check(flow->sf_acts,
+                                               lockdep_ovsl_is_held());
+
                err = actions_to_attr(sf_acts->actions, sf_acts->actions_len, skb);
                if (!err)
                        nla_nest_end(skb, start);
@@ -1343,12 +1348,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                /* We found a matching flow. */
                struct sw_flow_actions *old_acts;
 
-               /* Make sure the it has the same unmasked key. */
-               if (!ovs_flow_cmp_unmasked_key(flow, &key, match.range.end)) {
-                       error = -EINVAL;
-                       goto err_unlock_ovs;
-               }
-
                /* Bail out if we're not allowed to modify an existing flow.
                 * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL
                 * because Generic Netlink treats the latter as a dump
@@ -1360,6 +1359,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                    info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
                        goto err_unlock_ovs;
 
+               /* The unmasked key has to be the same for flow updates. */
+               error = -EINVAL;
+               if (!ovs_flow_cmp_unmasked_key(flow, &key, match.range.end))
+                       goto err_unlock_ovs;
+
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
                rcu_assign_pointer(flow->sf_acts, acts);
@@ -1506,15 +1510,14 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        struct datapath *dp;
        struct flow_table *table;
 
-       ovs_lock();
+       rcu_read_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        if (!dp) {
-               ovs_unlock();
+               rcu_read_unlock();
                return -ENODEV;
        }
 
-       table = ovsl_dereference(dp->table);
-
+       table = rcu_dereference(dp->table);
        for (;;) {
                struct sw_flow *flow;
                u32 bucket, obj;
@@ -1534,7 +1537,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                cb->args[0] = bucket;
                cb->args[1] = obj;
        }
-       ovs_unlock();
+       rcu_read_unlock();
        return skb->len;
 }