From: "David S. Miller" 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); _