From: "David S. Miller" <davem@redhat.com>

Basically, we walk a list, drop the lock to zap the time-wait bucket and
assume the list linkage stays sane, which is of course bogus as another cpu
could easily unlink and free up the entry we're pointing to.


---

 include/net/tcp.h        |    5 ++++-
 net/ipv4/tcp_minisocks.c |   16 ++++++++++++----
 2 files changed, 16 insertions(+), 5 deletions(-)

diff -puN include/net/tcp.h~tcp-oops-fix include/net/tcp.h
--- 25/include/net/tcp.h~tcp-oops-fix	2004-02-28 16:50:03.000000000 -0800
+++ 25-akpm/include/net/tcp.h	2004-02-28 16:50:03.000000000 -0800
@@ -263,7 +263,10 @@ static __inline__ int tw_del_dead_node(s
 #define tw_for_each(tw, node, head) \
 	hlist_for_each_entry(tw, node, head, tw_node)
 
-#define tw_for_each_inmate(tw, node, safe, jail) \
+#define tw_for_each_inmate(tw, node, jail) \
+	hlist_for_each_entry(tw, node, jail, tw_death_node)
+
+#define tw_for_each_inmate_safe(tw, node, safe, jail) \
 	hlist_for_each_entry_safe(tw, node, safe, jail, tw_death_node)
 
 #define tcptw_sk(__sk)	((struct tcp_tw_bucket *)(__sk))
diff -puN net/ipv4/tcp_minisocks.c~tcp-oops-fix net/ipv4/tcp_minisocks.c
--- 25/net/ipv4/tcp_minisocks.c~tcp-oops-fix	2004-02-28 16:50:03.000000000 -0800
+++ 25-akpm/net/ipv4/tcp_minisocks.c	2004-02-28 16:50:03.000000000 -0800
@@ -427,7 +427,7 @@ static u32 twkill_thread_slots;
 static int tcp_do_twkill_work(int slot, unsigned int quota)
 {
 	struct tcp_tw_bucket *tw;
-	struct hlist_node *node, *safe;
+	struct hlist_node *node;
 	unsigned int killed;
 	int ret;
 
@@ -439,8 +439,8 @@ static int tcp_do_twkill_work(int slot, 
 	 */
 	killed = 0;
 	ret = 0;
-	tw_for_each_inmate(tw, node, safe,
-			   &tcp_tw_death_row[slot]) {
+rescan:
+	tw_for_each_inmate(tw, node, &tcp_tw_death_row[slot]) {
 		__tw_del_dead_node(tw);
 		spin_unlock(&tw_death_lock);
 		tcp_timewait_kill(tw);
@@ -451,6 +451,14 @@ static int tcp_do_twkill_work(int slot, 
 			ret = 1;
 			break;
 		}
+
+		/* While we dropped tw_death_lock, another cpu may have
+		 * killed off the next TW bucket in the list, therefore
+		 * do a fresh re-read of the hlist head node with the
+		 * lock reacquired.  We still use the hlist traversal
+		 * macro in order to get the prefetches.
+		 */
+		goto rescan;
 	}
 
 	tcp_tw_count -= killed;
@@ -637,7 +645,7 @@ void tcp_twcal_tick(unsigned long dummy)
 			struct hlist_node *node, *safe;
 			struct tcp_tw_bucket *tw;
 
-			tw_for_each_inmate(tw, node, safe,
+			tw_for_each_inmate_safe(tw, node, safe,
 					   &tcp_twcal_row[slot]) {
 				__tw_del_dead_node(tw);
 				tcp_timewait_kill(tw);

_