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