Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: Fix soft lockups/OOM issues w/ unix garbage collector

This is an implementation of David Miller's suggested fix in:
https://bugzilla.redhat.com/show_bug.cgi?id=470201

It has been updated to use wait_event() instead of
wait_event_interruptible().

Paraphrasing the description from the above report, it makes sendmsg()
block while UNIX garbage collection is in progress. This avoids a
situation where child processes continue to queue new FDs over a
AF_UNIX socket to a parent which is in the exit path and running
garbage collection on these FDs. This contention can result in soft
lockups and oom-killing of unrelated processes.

Signed-off-by: dann frazier <dannf@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

dann frazier and committed by
David S. Miller
5f23b734 efbbced3

+13 -3
+1
include/net/af_unix.h
··· 9 9 extern void unix_inflight(struct file *fp); 10 10 extern void unix_notinflight(struct file *fp); 11 11 extern void unix_gc(void); 12 + extern void wait_for_unix_gc(void); 12 13 13 14 #define UNIX_HASH_SIZE 256 14 15
+2
net/unix/af_unix.c
··· 1343 1343 1344 1344 if (NULL == siocb->scm) 1345 1345 siocb->scm = &tmp_scm; 1346 + wait_for_unix_gc(); 1346 1347 err = scm_send(sock, msg, siocb->scm); 1347 1348 if (err < 0) 1348 1349 return err; ··· 1494 1493 1495 1494 if (NULL == siocb->scm) 1496 1495 siocb->scm = &tmp_scm; 1496 + wait_for_unix_gc(); 1497 1497 err = scm_send(sock, msg, siocb->scm); 1498 1498 if (err < 0) 1499 1499 return err;
+10 -3
net/unix/garbage.c
··· 80 80 #include <linux/file.h> 81 81 #include <linux/proc_fs.h> 82 82 #include <linux/mutex.h> 83 + #include <linux/wait.h> 83 84 84 85 #include <net/sock.h> 85 86 #include <net/af_unix.h> ··· 92 91 static LIST_HEAD(gc_inflight_list); 93 92 static LIST_HEAD(gc_candidates); 94 93 static DEFINE_SPINLOCK(unix_gc_lock); 94 + static DECLARE_WAIT_QUEUE_HEAD(unix_gc_wait); 95 95 96 96 unsigned int unix_tot_inflight; 97 97 ··· 268 266 list_move_tail(&u->link, &gc_candidates); 269 267 } 270 268 271 - /* The external entry point: unix_gc() */ 269 + static bool gc_in_progress = false; 272 270 271 + void wait_for_unix_gc(void) 272 + { 273 + wait_event(unix_gc_wait, gc_in_progress == false); 274 + } 275 + 276 + /* The external entry point: unix_gc() */ 273 277 void unix_gc(void) 274 278 { 275 - static bool gc_in_progress = false; 276 - 277 279 struct unix_sock *u; 278 280 struct unix_sock *next; 279 281 struct sk_buff_head hitlist; ··· 382 376 /* All candidates should have been detached by now. */ 383 377 BUG_ON(!list_empty(&gc_candidates)); 384 378 gc_in_progress = false; 379 + wake_up(&unix_gc_wait); 385 380 386 381 out: 387 382 spin_unlock(&unix_gc_lock);