[XFRM] Handle device down/unregister events

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi:

Currently the xfrm dst cache does not handle device unregister events.
This could cause device unregisteration to hang indefinitely.

This patch makes prunes all bundles containing devices being shut down
or removed.  It also merges two existing functions that walks bundles
looking for things to delete.

Cheers,
-- 
Debian GNU/Linux 3.0 is out! ( http://www.debian.org/ )
Email:  Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Index: kernel-source-2.5/net/xfrm/xfrm_policy.c
===================================================================
RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/xfrm/xfrm_policy.c,v
retrieving revision 1.30
diff -u -r1.30 xfrm_policy.c
--- kernel-source-2.5/net/xfrm/xfrm_policy.c	28 Oct 2003 09:58:40 -0000	1.30
+++ kernel-source-2.5/net/xfrm/xfrm_policy.c	26 Nov 2003 04:33:58 -0000
@@ -19,6 +19,8 @@
 #include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
 
@@ -1027,7 +1029,8 @@
 	return dst;
 }
 
-static void __xfrm_garbage_collect(void)
+static void xfrm_prune_bundles(int (*func)(struct dst_entry *, void *),
+			      void *data)
 {
 	int i;
 	struct xfrm_policy *pol;
@@ -1039,7 +1042,7 @@
 			write_lock(&pol->lock);
 			dstp = &pol->bundles;
 			while ((dst=*dstp) != NULL) {
-				if (atomic_read(&dst->__refcnt) == 0) {
+				if (func(dst, data)) {
 					*dstp = dst->next;
 					dst->next = gc_list;
 					gc_list = dst;
@@ -1059,8 +1062,20 @@
 	}
 }
 
-static int bundle_depends_on(struct dst_entry *dst, struct xfrm_state *x)
+static int unused_dst(struct dst_entry *dst, void *data)
 {
+	return !atomic_read(&dst->__refcnt);
+}
+
+static void __xfrm_garbage_collect(void)
+{
+	xfrm_prune_bundles(unused_dst, NULL);
+}
+
+static int bundle_depends_on(struct dst_entry *dst, void *data)
+{
+	struct xfrm_state *x = data;
+
 	do {
 		if (dst->xfrm == x)
 			return 1;
@@ -1070,35 +1085,7 @@
 
 int xfrm_flush_bundles(struct xfrm_state *x)
 {
-	int i;
-	struct xfrm_policy *pol;
-	struct dst_entry *dst, **dstp, *gc_list = NULL;
-
-	read_lock_bh(&xfrm_policy_lock);
-	for (i=0; i<2*XFRM_POLICY_MAX; i++) {
-		for (pol = xfrm_policy_list[i]; pol; pol = pol->next) {
-			write_lock(&pol->lock);
-			dstp = &pol->bundles;
-			while ((dst=*dstp) != NULL) {
-				if (bundle_depends_on(dst, x)) {
-					*dstp = dst->next;
-					dst->next = gc_list;
-					gc_list = dst;
-				} else {
-					dstp = &dst->next;
-				}
-			}
-			write_unlock(&pol->lock);
-		}
-	}
-	read_unlock_bh(&xfrm_policy_lock);
-
-	while (gc_list) {
-		dst = gc_list;
-		gc_list = dst->next;
-		dst_free(dst);
-	}
-
+	xfrm_prune_bundles(bundle_depends_on, x);
 	return 0;
 }
 
@@ -1221,6 +1208,35 @@
 	read_unlock(&afinfo->lock);
 }
 
+static int bundle_has_dev(struct dst_entry *dst, void *data)
+{
+	struct net_device *dev = data;
+
+	do {
+		if (dst->dev == dev)
+			return 1;
+	} while ((dst = dst->child) != NULL);
+	return 0;
+}
+
+static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct net_device *dev = ptr;
+
+	switch (event) {
+	case NETDEV_UNREGISTER:
+	case NETDEV_DOWN:
+		xfrm_prune_bundles(bundle_has_dev, dev);
+	}
+	return NOTIFY_DONE;
+}
+
+struct notifier_block xfrm_dev_notifier = {
+	xfrm_dev_event,
+	NULL,
+	0
+};
+
 void __init xfrm_policy_init(void)
 {
 	xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
@@ -1231,6 +1247,7 @@
 		panic("XFRM: failed to allocate xfrm_dst_cache\n");
 
 	INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task, NULL);
+	register_netdevice_notifier(&xfrm_dev_notifier);
 }
 
 void __init xfrm_init(void)

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux