|
@@ -2194,14 +2194,55 @@ static int __net_init ovs_init_net(struct net *net)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void __net_exit ovs_exit_net(struct net *net)
|
|
|
+static void __net_exit list_vports_from_net(struct net *net, struct net *dnet,
|
|
|
+ struct list_head *head)
|
|
|
{
|
|
|
- struct datapath *dp, *dp_next;
|
|
|
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
|
|
|
+ struct datapath *dp;
|
|
|
+
|
|
|
+ list_for_each_entry(dp, &ovs_net->dps, list_node) {
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {
|
|
|
+ struct vport *vport;
|
|
|
+
|
|
|
+ hlist_for_each_entry(vport, &dp->ports[i], dp_hash_node) {
|
|
|
+ struct netdev_vport *netdev_vport;
|
|
|
+
|
|
|
+ if (vport->ops->type != OVS_VPORT_TYPE_INTERNAL)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ netdev_vport = netdev_vport_priv(vport);
|
|
|
+ if (dev_net(netdev_vport->dev) == dnet)
|
|
|
+ list_add(&vport->detach_list, head);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void __net_exit ovs_exit_net(struct net *dnet)
|
|
|
+{
|
|
|
+ struct datapath *dp, *dp_next;
|
|
|
+ struct ovs_net *ovs_net = net_generic(dnet, ovs_net_id);
|
|
|
+ struct vport *vport, *vport_next;
|
|
|
+ struct net *net;
|
|
|
+ LIST_HEAD(head);
|
|
|
|
|
|
ovs_lock();
|
|
|
list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node)
|
|
|
__dp_destroy(dp);
|
|
|
+
|
|
|
+ rtnl_lock();
|
|
|
+ for_each_net(net)
|
|
|
+ list_vports_from_net(net, dnet, &head);
|
|
|
+ rtnl_unlock();
|
|
|
+
|
|
|
+ /* Detach all vports from given namespace. */
|
|
|
+ list_for_each_entry_safe(vport, vport_next, &head, detach_list) {
|
|
|
+ list_del(&vport->detach_list);
|
|
|
+ ovs_dp_detach_port(vport);
|
|
|
+ }
|
|
|
+
|
|
|
ovs_unlock();
|
|
|
|
|
|
cancel_work_sync(&ovs_net->dp_notify_work);
|