datapath: Use call_rcu() when deleting a datapath.
authorJesse Gross <jesse@nicira.com>
Wed, 5 Jan 2011 20:39:57 +0000 (12:39 -0800)
committerJesse Gross <jesse@nicira.com>
Wed, 5 Jan 2011 22:05:03 +0000 (14:05 -0800)
When deleting a datapath, we remove all of the vports and then immediately
free the datapath data structures.  Since the vports are allowed to use
call_rcu() to free their data, it's possible for them to return immediately
while packet processing is still taking place.  This breaks apart the dropping
of references and the freeing of the data using call_rcu() for protection.

This race cannot actually occur in practice since the last port to be
deleted is an internal device, which uses synchronize_rcu() itself
(implicitly through unregister_netdevice()).  However, there is no
requirement that it must do this nor should there be.

Reported-by: Ben Pfaff <blp@nicira.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
datapath/datapath.c
datapath/datapath.h

index 01b3025d9d5d1297322f74ed562962af24bf1546..4117ba9d8c7d7ea9ad8d3266f7644f034809b915 100644 (file)
@@ -308,12 +308,24 @@ err:
        return err;
 }
 
+static void destroy_dp_rcu(struct rcu_head *rcu)
+{
+       struct datapath *dp = container_of(rcu, struct datapath, rcu);
+       int i;
+
+       for (i = 0; i < DP_N_QUEUES; i++)
+               skb_queue_purge(&dp->queues[i]);
+
+       tbl_destroy((struct tbl __force *)dp->table, flow_free_tbl);
+       free_percpu(dp->stats_percpu);
+       kobject_put(&dp->ifobj);
+}
+
 static int destroy_dp(int dp_idx)
 {
        struct datapath *dp;
        int err = 0;
        struct vport *p, *n;
-       int i;
 
        rtnl_lock();
        mutex_lock(&dp_mutex);
@@ -330,18 +342,11 @@ static int destroy_dp(int dp_idx)
                        dp_detach_port(p);
 
        dp_sysfs_del_dp(dp);
-
        rcu_assign_pointer(dps[dp->dp_idx], NULL);
-
        dp_detach_port(get_vport_protected(dp, ODPP_LOCAL));
-       tbl_destroy(get_table_protected(dp), flow_free_tbl);
-
-       for (i = 0; i < DP_N_QUEUES; i++)
-               skb_queue_purge(&dp->queues[i]);
-       free_percpu(dp->stats_percpu);
 
        mutex_unlock(&dp->mutex);
-       kobject_put(&dp->ifobj);
+       call_rcu(&dp->rcu, destroy_dp_rcu);
        module_put(THIS_MODULE);
 
 out:
index e4c6534f7632eed589c3105d28cdf2c30d956a1a..28ce0dae4ded0d212cbc3a80e36c640f1c66d2c1 100644 (file)
@@ -59,6 +59,7 @@ struct dp_stats_percpu {
 
 /**
  * struct datapath - datapath for flow-based packet switching
+ * @rcu: RCU callback head for deferred destruction.
  * @mutex: Mutual exclusion for ioctls.
  * @dp_idx: Datapath number (index into the dps[] array in datapath.c).
  * @ifobj: Represents /sys/class/net/<devname>/brif.
@@ -77,6 +78,7 @@ struct dp_stats_percpu {
  * sampling a given packet.
  */
 struct datapath {
+       struct rcu_head rcu;
        struct mutex mutex;
        int dp_idx;
        struct kobject ifobj;