Following patch allows unregister_netdevice() destroy dp.

Signed-off-by: Pravin B Shelar <pshe...@nicira.com>
---
 datapath/datapath.c           |   72 +++++++++++++++++++++++++++++-----------
 datapath/datapath.h           |    6 +++-
 datapath/dp_notify.c          |    7 +++-
 datapath/vport-internal_dev.c |    6 +++-
 4 files changed, 67 insertions(+), 24 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index 2875862..ab28a86 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -264,7 +264,7 @@ static struct vport *new_vport(const struct vport_parms 
*parms)
 }
 
 /* Called with RTNL lock. */
-void ovs_dp_detach_port(struct vport *p)
+void ovs_dp_detach_port(struct vport *p, bool delete_vport)
 {
        ASSERT_RTNL();
 
@@ -277,7 +277,8 @@ void ovs_dp_detach_port(struct vport *p)
        rcu_assign_pointer(p->dp->ports[p->port_no], NULL);
 
        /* Then destroy it. */
-       ovs_vport_del(p);
+       if (delete_vport)
+               ovs_vport_del(p);
 }
 
 /* Must be called with rcu_read_lock. */
@@ -1343,6 +1344,13 @@ static struct datapath *lookup_datapath(struct 
ovs_header *ovs_header,
        return dp ? dp : ERR_PTR(-ENODEV);
 }
 
+/* Must be called with genl and rtnl lock. */
+static void dp_del(struct datapath *dp)
+{
+       dp->user_destruct = true;
+       ovs_vport_del(rtnl_dereference(dp->ports[OVSP_LOCAL]));
+}
+
 static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
@@ -1388,6 +1396,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
                goto err_destroy_table;
        }
 
+       dp->user_destruct = false;
        /* Set up our datapath device. */
        parms.name = nla_data(a[OVS_DP_ATTR_NAME]);
        parms.type = OVS_VPORT_TYPE_INTERNAL;
@@ -1405,15 +1414,15 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
                goto err_destroy_percpu;
        }
 
+       list_add_tail(&dp->list_node, &dps);
+       ovs_dp_sysfs_add_dp(dp);
+
        reply = ovs_dp_cmd_build_info(dp, info->snd_pid,
                                      info->snd_seq, OVS_DP_CMD_NEW);
        err = PTR_ERR(reply);
        if (IS_ERR(reply))
                goto err_destroy_local_port;
 
-       list_add_tail(&dp->list_node, &dps);
-       ovs_dp_sysfs_add_dp(dp);
-
        rtnl_unlock();
 
        genl_notify(reply, genl_info_net(info), info->snd_pid,
@@ -1422,7 +1431,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
        return 0;
 
 err_destroy_local_port:
-       ovs_dp_detach_port(rtnl_dereference(dp->ports[OVSP_LOCAL]));
+       dp_del(dp);
+       rtnl_unlock();
+       goto err;
+
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
 err_destroy_table:
@@ -1439,7 +1451,6 @@ err:
 
 static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
 {
-       struct vport *vport, *next_vport;
        struct sk_buff *reply;
        struct datapath *dp;
        int err;
@@ -1460,14 +1471,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct 
genl_info *info)
        if (IS_ERR(reply))
                goto exit_unlock;
 
-       list_for_each_entry_safe(vport, next_vport, &dp->port_list, node)
-               if (vport->port_no != OVSP_LOCAL)
-                       ovs_dp_detach_port(vport);
-
-       ovs_dp_sysfs_del_dp(dp);
-       list_del(&dp->list_node);
-       ovs_dp_detach_port(rtnl_dereference(dp->ports[OVSP_LOCAL]));
-
+       dp_del(dp);
        /* rtnl_unlock() will wait until all the references to devices that
         * are pending unregistration have been dropped.  We do it here to
         * ensure that any internal devices (which contain DP pointers) are
@@ -1475,9 +1479,6 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct 
genl_info *info)
         */
        rtnl_unlock();
 
-       call_rcu(&dp->rcu, destroy_dp_rcu);
-       module_put(THIS_MODULE);
-
        genl_notify(reply, genl_info_net(info), info->snd_pid,
                    ovs_dp_datapath_multicast_group.id, info->nlhdr,
                    GFP_KERNEL);
@@ -1490,6 +1491,37 @@ exit:
        return err;
 }
 
+static int destroy_dp(void *data)
+{
+       struct datapath *dp = (struct datapath *) data;
+       struct vport *vport, *next_vport;
+
+       rtnl_lock();
+       list_for_each_entry_safe(vport, next_vport, &dp->port_list, node)
+               if (vport->port_no != OVSP_LOCAL)
+                       ovs_dp_detach_port(vport, true);
+
+       list_del(&dp->list_node);
+
+       /* User-space initiated dp destroy has already deleted dp-local-port. */
+       ovs_dp_detach_port(rtnl_dereference(dp->ports[OVSP_LOCAL]),
+                          !dp->user_destruct);
+
+       rtnl_unlock();
+
+       call_rcu(&dp->rcu, destroy_dp_rcu);
+       module_put(THIS_MODULE);
+       return 0;
+}
+
+void ovs_destroy_dp(struct datapath *dp)
+{
+       if (!dp->user_destruct)
+               genl_exec(destroy_dp, dp);
+       else
+               destroy_dp(dp);
+}
+
 static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *reply;
@@ -1795,7 +1827,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
                        err = PTR_ERR(reply);
        }
        if (err) {
-               ovs_dp_detach_port(vport);
+               ovs_dp_detach_port(vport, true);
                goto exit_unlock;
        }
        genl_notify(reply, genl_info_net(info), info->snd_pid,
@@ -1882,7 +1914,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct 
genl_info *info)
        if (IS_ERR(reply))
                goto exit_unlock;
 
-       ovs_dp_detach_port(vport);
+       ovs_dp_detach_port(vport, true);
 
        genl_notify(reply, genl_info_net(info), info->snd_pid,
                    ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
diff --git a/datapath/datapath.h b/datapath/datapath.h
index 27151b9..3887608 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -87,6 +87,9 @@ struct datapath {
 
        /* Stats. */
        struct dp_stats_percpu __percpu *stats_percpu;
+
+       /* Used only while destroying dp. */
+       bool user_destruct;
 };
 
 /**
@@ -136,13 +139,14 @@ extern struct genl_multicast_group 
ovs_dp_vport_multicast_group;
 extern int (*ovs_dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int 
cmd);
 
 void ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
-void ovs_dp_detach_port(struct vport *);
+void ovs_dp_detach_port(struct vport *, bool);
 int ovs_dp_upcall(struct datapath *, struct sk_buff *,
                  const struct dp_upcall_info *);
 
 const char *ovs_dp_name(const struct datapath *dp);
 struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
                                         u8 cmd);
+void ovs_destroy_dp(struct datapath *dp);
 
 int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
 #endif /* datapath.h */
diff --git a/datapath/dp_notify.c b/datapath/dp_notify.c
index d040d46..c0f8d65 100644
--- a/datapath/dp_notify.c
+++ b/datapath/dp_notify.c
@@ -39,12 +39,15 @@ static int dp_device_event(struct notifier_block *unused, 
unsigned long event,
 
        switch (event) {
        case NETDEV_UNREGISTER:
-               if (!ovs_is_internal_dev(dev)) {
+               if (ovs_is_internal_dev(dev)) {
+                       if (vport->port_no == OVSP_LOCAL)
+                               ovs_dp_sysfs_del_dp(vport->dp);
+               } else {
                        struct sk_buff *notify;
 
                        notify = ovs_vport_cmd_build_info(vport, 0, 0,
                                                          OVS_VPORT_CMD_DEL);
-                       ovs_dp_detach_port(vport);
+                       ovs_dp_detach_port(vport, true);
                        if (IS_ERR(notify)) {
                                netlink_set_err(INIT_NET_GENL_SOCK, 0,
                                                ovs_dp_vport_multicast_group.id,
diff --git a/datapath/vport-internal_dev.c b/datapath/vport-internal_dev.c
index c56f3b2..005b35e 100644
--- a/datapath/vport-internal_dev.c
+++ b/datapath/vport-internal_dev.c
@@ -161,6 +161,9 @@ static void internal_dev_destructor(struct net_device *dev)
 {
        struct vport *vport = ovs_internal_dev_get_vport(dev);
 
+       if (vport->port_no == OVSP_LOCAL)
+               ovs_destroy_dp(vport->dp);
+
        ovs_vport_free(vport);
        free_netdev(dev);
 }
@@ -267,7 +270,8 @@ static void internal_dev_destroy(struct vport *vport)
        dev_set_promiscuity(netdev_vport->dev, -1);
 
        /* unregister_netdevice() waits for an RCU grace period. */
-       unregister_netdevice(netdev_vport->dev);
+       if (netdev_vport->dev->reg_state == NETREG_REGISTERED)
+               unregister_netdevice(netdev_vport->dev);
 }
 
 static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
-- 
1.7.1

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to