diff options
author | dann frazier <dannf@hp.com> | 2008-11-26 18:32:27 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-11-26 18:32:27 -0500 |
commit | 5f23b734963ec7eaa3ebcd9050da0c9b7d143dd3 (patch) | |
tree | f82c2265540dd5d32d64f08976cd9b6216842544 | |
parent | efbbced361f3ff4ff9e85310ccff894185c4d904 (diff) |
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>
-rw-r--r-- | include/net/af_unix.h | 1 | ||||
-rw-r--r-- | net/unix/af_unix.c | 2 | ||||
-rw-r--r-- | net/unix/garbage.c | 13 |
3 files changed, 13 insertions, 3 deletions
diff --git a/include/net/af_unix.h b/include/net/af_unix.h index c29ff1da8a18..1614d78c60ed 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h | |||
@@ -9,6 +9,7 @@ | |||
9 | extern void unix_inflight(struct file *fp); | 9 | extern void unix_inflight(struct file *fp); |
10 | extern void unix_notinflight(struct file *fp); | 10 | extern void unix_notinflight(struct file *fp); |
11 | extern void unix_gc(void); | 11 | extern void unix_gc(void); |
12 | extern void wait_for_unix_gc(void); | ||
12 | 13 | ||
13 | #define UNIX_HASH_SIZE 256 | 14 | #define UNIX_HASH_SIZE 256 |
14 | 15 | ||
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index eb90f77bb0e2..66d5ac4773ab 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -1343,6 +1343,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
1343 | 1343 | ||
1344 | if (NULL == siocb->scm) | 1344 | if (NULL == siocb->scm) |
1345 | siocb->scm = &tmp_scm; | 1345 | siocb->scm = &tmp_scm; |
1346 | wait_for_unix_gc(); | ||
1346 | err = scm_send(sock, msg, siocb->scm); | 1347 | err = scm_send(sock, msg, siocb->scm); |
1347 | if (err < 0) | 1348 | if (err < 0) |
1348 | return err; | 1349 | return err; |
@@ -1493,6 +1494,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
1493 | 1494 | ||
1494 | if (NULL == siocb->scm) | 1495 | if (NULL == siocb->scm) |
1495 | siocb->scm = &tmp_scm; | 1496 | siocb->scm = &tmp_scm; |
1497 | wait_for_unix_gc(); | ||
1496 | err = scm_send(sock, msg, siocb->scm); | 1498 | err = scm_send(sock, msg, siocb->scm); |
1497 | if (err < 0) | 1499 | if (err < 0) |
1498 | return err; | 1500 | return err; |
diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 6d4a9a8de5ef..abb3ab34cb1e 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c | |||
@@ -80,6 +80,7 @@ | |||
80 | #include <linux/file.h> | 80 | #include <linux/file.h> |
81 | #include <linux/proc_fs.h> | 81 | #include <linux/proc_fs.h> |
82 | #include <linux/mutex.h> | 82 | #include <linux/mutex.h> |
83 | #include <linux/wait.h> | ||
83 | 84 | ||
84 | #include <net/sock.h> | 85 | #include <net/sock.h> |
85 | #include <net/af_unix.h> | 86 | #include <net/af_unix.h> |
@@ -91,6 +92,7 @@ | |||
91 | static LIST_HEAD(gc_inflight_list); | 92 | static LIST_HEAD(gc_inflight_list); |
92 | static LIST_HEAD(gc_candidates); | 93 | static LIST_HEAD(gc_candidates); |
93 | static DEFINE_SPINLOCK(unix_gc_lock); | 94 | static DEFINE_SPINLOCK(unix_gc_lock); |
95 | static DECLARE_WAIT_QUEUE_HEAD(unix_gc_wait); | ||
94 | 96 | ||
95 | unsigned int unix_tot_inflight; | 97 | unsigned int unix_tot_inflight; |
96 | 98 | ||
@@ -266,12 +268,16 @@ static void inc_inflight_move_tail(struct unix_sock *u) | |||
266 | list_move_tail(&u->link, &gc_candidates); | 268 | list_move_tail(&u->link, &gc_candidates); |
267 | } | 269 | } |
268 | 270 | ||
269 | /* The external entry point: unix_gc() */ | 271 | static bool gc_in_progress = false; |
270 | 272 | ||
271 | void unix_gc(void) | 273 | void wait_for_unix_gc(void) |
272 | { | 274 | { |
273 | static bool gc_in_progress = false; | 275 | wait_event(unix_gc_wait, gc_in_progress == false); |
276 | } | ||
274 | 277 | ||
278 | /* The external entry point: unix_gc() */ | ||
279 | void unix_gc(void) | ||
280 | { | ||
275 | struct unix_sock *u; | 281 | struct unix_sock *u; |
276 | struct unix_sock *next; | 282 | struct unix_sock *next; |
277 | struct sk_buff_head hitlist; | 283 | struct sk_buff_head hitlist; |
@@ -376,6 +382,7 @@ void unix_gc(void) | |||
376 | /* All candidates should have been detached by now. */ | 382 | /* All candidates should have been detached by now. */ |
377 | BUG_ON(!list_empty(&gc_candidates)); | 383 | BUG_ON(!list_empty(&gc_candidates)); |
378 | gc_in_progress = false; | 384 | gc_in_progress = false; |
385 | wake_up(&unix_gc_wait); | ||
379 | 386 | ||
380 | out: | 387 | out: |
381 | spin_unlock(&unix_gc_lock); | 388 | spin_unlock(&unix_gc_lock); |