Re: [Bug #14301] WARNING: at net/ipv4/af_inet.c:154
From: Eric Dumazet <hidden>
Date: 2009-10-03 08:37:24
Rafael J. Wysocki a écrit :
This message has been generated automatically as a part of a report of regressions introduced between 2.6.30 and 2.6.31. The following bug entry is on the current list of known regressions introduced between 2.6.30 and 2.6.31. Please verify if it still should be listed and let me know (either way). Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=14301 Subject : WARNING: at net/ipv4/af_inet.c:154 Submitter : Ralf Hildebrandt [off-list ref] Date : 2009-09-30 12:24 (2 days old) References : http://marc.info/?l=linux-kernel&m=125431350218137&w=4
If commit d99927f4d93f36553699573b279e0ff98ad7dea6 (net: Fix sock_wfree() race) doesnt fix this problem, then maybe we should take a look at an old patch. < data mining... running... output results to lkml/netdev > Random guesses 1) : commit d55d87fdff8252d0e2f7c28c2d443aee17e9d70f (net: Move rx skb_orphan call to where needed) A similar problem on SCTP was fixed by commit 1bc4ee4088c9a502db0e9c87f675e61e57fa1734 (sctp: fix warning at inet_sock_destruct() while release sctp socket) 2) CORK and UDP sockets It seems we can leave an UDP socket with a frame in sk_write_queue Purge of this queue is done by udp_flush_pending_frames() This calls ip_flush_pending_frames() But this function only calls kfree_skb(), not sk_wmem_free_skb()... Could you try following patch ? Thanks [PATCH] net: UDP should not use ip_flush_pending_frames() Now xmit UDP messages are charged, we must take care of calling right skb freeing function. In case a close() is performed on a socket where CORKED frame is still queued in sk_write_queue, calling ip_flush_pending_frames() leads to sk_forward_alloc leak. Reported-by: Ralf Hildebrandt <redacted> Signed-off-by: Eric Dumazet <redacted> --- include/net/sock.h | 10 ++++++++++ include/net/tcp.h | 10 ---------- net/ipv4/tcp.c | 2 +- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/udp.c | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/include/net/sock.h b/include/net/sock.h
index 1621935..7c80fec 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h@@ -882,6 +882,16 @@ static inline void sk_wmem_free_skb(struct sock *sk, struct sk_buff *skb) __kfree_skb(skb); } +/* write queue abstraction */ +static inline void sk_write_queue_purge(struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) + sk_wmem_free_skb(sk, skb); + sk_mem_reclaim(sk); +} + /* Used by processes to "lock" a socket state, so that * interrupts and bottom half handlers won't change it * from under us. It essentially blocks any incoming
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 03a49c7..4c7036a 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h@@ -1220,16 +1220,6 @@ static inline void tcp_put_md5sig_pool(void) put_cpu(); } -/* write queue abstraction */ -static inline void tcp_write_queue_purge(struct sock *sk) -{ - struct sk_buff *skb; - - while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) - sk_wmem_free_skb(sk, skb); - sk_mem_reclaim(sk); -} - static inline struct sk_buff *tcp_write_queue_head(struct sock *sk) { return skb_peek(&sk->sk_write_queue);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 64d0af6..0124f5b 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c@@ -1992,7 +1992,7 @@ int tcp_disconnect(struct sock *sk, int flags) tcp_clear_xmit_timers(sk); __skb_queue_purge(&sk->sk_receive_queue); - tcp_write_queue_purge(sk); + sk_write_queue_purge(sk); __skb_queue_purge(&tp->out_of_order_queue); #ifdef CONFIG_NET_DMA __skb_queue_purge(&sk->sk_async_wait_queue);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 7cda24b..76e59df 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c@@ -1845,7 +1845,7 @@ void tcp_v4_destroy_sock(struct sock *sk) tcp_cleanup_congestion_control(sk); /* Cleanup up the write buffer. */ - tcp_write_queue_purge(sk); + sk_write_queue_purge(sk); /* Cleans up our, hopefully empty, out_of_order_queue. */ __skb_queue_purge(&tp->out_of_order_queue);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 6ec6a8a..58007d1 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c@@ -464,7 +464,7 @@ void udp_flush_pending_frames(struct sock *sk) if (up->pending) { up->len = 0; up->pending = 0; - ip_flush_pending_frames(sk); + sk_write_queue_purge(sk); } } EXPORT_SYMBOL(udp_flush_pending_frames);