diff options
author | Shailabh Nagar <nagar@watson.ibm.com> | 2006-07-14 03:24:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-15 00:53:57 -0400 |
commit | bb129994c3bff9c5e8df91f05d7e9b6402fbd83f (patch) | |
tree | 8d8c4e27af727c9de42ac6aeae04f70537e6bd01 /kernel | |
parent | f9fd8914c1acca0d98b69d831b128d5b52f03c51 (diff) |
[PATCH] Remove down_write() from taskstats code invoked on the exit() path
In send_cpu_listeners(), which is called on the exit path, a down_write()
was protecting operations like skb_clone() and genlmsg_unicast() that do
GFP_KERNEL allocations. If the oom-killer decides to kill tasks to satisfy
the allocations,the exit of those tasks could block on the same semphore.
The down_write() was only needed to allow removal of invalid listeners from
the listener list. The patch converts the down_write to a down_read and
defers the removal to a separate critical region. This ensures that even
if the oom-killer is called, no other task's exit is blocked as it can
still acquire another down_read.
Thanks to Andrew Morton & Herbert Xu for pointing out the oom related
pitfalls, and to Chandra Seetharaman for suggesting this fix instead of
using something more complex like RCU.
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Shailabh Nagar <nagar@watson.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/taskstats.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/kernel/taskstats.c b/kernel/taskstats.c index abb59e323544..f45179ce028e 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c | |||
@@ -51,6 +51,7 @@ __read_mostly = { | |||
51 | struct listener { | 51 | struct listener { |
52 | struct list_head list; | 52 | struct list_head list; |
53 | pid_t pid; | 53 | pid_t pid; |
54 | char valid; | ||
54 | }; | 55 | }; |
55 | 56 | ||
56 | struct listener_list { | 57 | struct listener_list { |
@@ -127,7 +128,7 @@ static int send_cpu_listeners(struct sk_buff *skb, unsigned int cpu) | |||
127 | struct listener *s, *tmp; | 128 | struct listener *s, *tmp; |
128 | struct sk_buff *skb_next, *skb_cur = skb; | 129 | struct sk_buff *skb_next, *skb_cur = skb; |
129 | void *reply = genlmsg_data(genlhdr); | 130 | void *reply = genlmsg_data(genlhdr); |
130 | int rc, ret; | 131 | int rc, ret, delcount = 0; |
131 | 132 | ||
132 | rc = genlmsg_end(skb, reply); | 133 | rc = genlmsg_end(skb, reply); |
133 | if (rc < 0) { | 134 | if (rc < 0) { |
@@ -137,7 +138,7 @@ static int send_cpu_listeners(struct sk_buff *skb, unsigned int cpu) | |||
137 | 138 | ||
138 | rc = 0; | 139 | rc = 0; |
139 | listeners = &per_cpu(listener_array, cpu); | 140 | listeners = &per_cpu(listener_array, cpu); |
140 | down_write(&listeners->sem); | 141 | down_read(&listeners->sem); |
141 | list_for_each_entry_safe(s, tmp, &listeners->list, list) { | 142 | list_for_each_entry_safe(s, tmp, &listeners->list, list) { |
142 | skb_next = NULL; | 143 | skb_next = NULL; |
143 | if (!list_is_last(&s->list, &listeners->list)) { | 144 | if (!list_is_last(&s->list, &listeners->list)) { |
@@ -150,14 +151,26 @@ static int send_cpu_listeners(struct sk_buff *skb, unsigned int cpu) | |||
150 | } | 151 | } |
151 | ret = genlmsg_unicast(skb_cur, s->pid); | 152 | ret = genlmsg_unicast(skb_cur, s->pid); |
152 | if (ret == -ECONNREFUSED) { | 153 | if (ret == -ECONNREFUSED) { |
153 | list_del(&s->list); | 154 | s->valid = 0; |
154 | kfree(s); | 155 | delcount++; |
155 | rc = ret; | 156 | rc = ret; |
156 | } | 157 | } |
157 | skb_cur = skb_next; | 158 | skb_cur = skb_next; |
158 | } | 159 | } |
159 | up_write(&listeners->sem); | 160 | up_read(&listeners->sem); |
161 | |||
162 | if (!delcount) | ||
163 | return rc; | ||
160 | 164 | ||
165 | /* Delete invalidated entries */ | ||
166 | down_write(&listeners->sem); | ||
167 | list_for_each_entry_safe(s, tmp, &listeners->list, list) { | ||
168 | if (!s->valid) { | ||
169 | list_del(&s->list); | ||
170 | kfree(s); | ||
171 | } | ||
172 | } | ||
173 | up_write(&listeners->sem); | ||
161 | return rc; | 174 | return rc; |
162 | } | 175 | } |
163 | 176 | ||
@@ -290,6 +303,7 @@ static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd) | |||
290 | goto cleanup; | 303 | goto cleanup; |
291 | s->pid = pid; | 304 | s->pid = pid; |
292 | INIT_LIST_HEAD(&s->list); | 305 | INIT_LIST_HEAD(&s->list); |
306 | s->valid = 1; | ||
293 | 307 | ||
294 | listeners = &per_cpu(listener_array, cpu); | 308 | listeners = &per_cpu(listener_array, cpu); |
295 | down_write(&listeners->sem); | 309 | down_write(&listeners->sem); |