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

Configure Feed

Select the types of activity you want to include in your feed.

[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>

authored by

Shailabh Nagar and committed by
Linus Torvalds
bb129994 f9fd8914

+19 -5
+19 -5
kernel/taskstats.c
··· 51 51 struct listener { 52 52 struct list_head list; 53 53 pid_t pid; 54 + char valid; 54 55 }; 55 56 56 57 struct listener_list { ··· 128 127 struct listener *s, *tmp; 129 128 struct sk_buff *skb_next, *skb_cur = skb; 130 129 void *reply = genlmsg_data(genlhdr); 131 - int rc, ret; 130 + int rc, ret, delcount = 0; 132 131 133 132 rc = genlmsg_end(skb, reply); 134 133 if (rc < 0) { ··· 138 137 139 138 rc = 0; 140 139 listeners = &per_cpu(listener_array, cpu); 141 - down_write(&listeners->sem); 140 + down_read(&listeners->sem); 142 141 list_for_each_entry_safe(s, tmp, &listeners->list, list) { 143 142 skb_next = NULL; 144 143 if (!list_is_last(&s->list, &listeners->list)) { ··· 151 150 } 152 151 ret = genlmsg_unicast(skb_cur, s->pid); 153 152 if (ret == -ECONNREFUSED) { 154 - list_del(&s->list); 155 - kfree(s); 153 + s->valid = 0; 154 + delcount++; 156 155 rc = ret; 157 156 } 158 157 skb_cur = skb_next; 159 158 } 160 - up_write(&listeners->sem); 159 + up_read(&listeners->sem); 161 160 161 + if (!delcount) 162 + return rc; 163 + 164 + /* Delete invalidated entries */ 165 + down_write(&listeners->sem); 166 + list_for_each_entry_safe(s, tmp, &listeners->list, list) { 167 + if (!s->valid) { 168 + list_del(&s->list); 169 + kfree(s); 170 + } 171 + } 172 + up_write(&listeners->sem); 162 173 return rc; 163 174 } 164 175 ··· 303 290 goto cleanup; 304 291 s->pid = pid; 305 292 INIT_LIST_HEAD(&s->list); 293 + s->valid = 1; 306 294 307 295 listeners = &per_cpu(listener_array, cpu); 308 296 down_write(&listeners->sem);