diff options
| author | Vasiliy Kulikov <segoon@openwall.com> | 2011-06-27 19:18:11 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-06-27 21:00:13 -0400 |
| commit | 26c4caea9d697043cc5a458b96411b86d7f6babd (patch) | |
| tree | cf4de2553fbfcafc41eb4f84bc9ee1eb69adf725 /kernel | |
| parent | 08142579b6ca35883c1ed066a2681de6f6917062 (diff) | |
taskstats: don't allow duplicate entries in listener mode
Currently a single process may register exit handlers unlimited times.
It may lead to a bloated listeners chain and very slow process
terminations.
Eg after 10KK sent TASKSTATS_CMD_ATTR_REGISTER_CPUMASKs ~300 Mb of
kernel memory is stolen for the handlers chain and "time id" shows 2-7
seconds instead of normal 0.003. It makes it possible to exhaust all
kernel memory and to eat much of CPU time by triggerring numerous exits
on a single CPU.
The patch limits the number of times a single process may register
itself on a single CPU to one.
One little issue is kept unfixed - as taskstats_exit() is called before
exit_files() in do_exit(), the orphaned listener entry (if it was not
explicitly deregistered) is kept until the next someone's exit() and
implicit deregistration in send_cpu_listeners(). So, if a process
registered itself as a listener exits and the next spawned process gets
the same pid, it would inherit taskstats attributes.
Signed-off-by: Vasiliy Kulikov <segooon@gmail.com>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/taskstats.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 9ffea360a778..fc0f22005417 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c | |||
| @@ -285,16 +285,18 @@ ret: | |||
| 285 | static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd) | 285 | static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd) |
| 286 | { | 286 | { |
| 287 | struct listener_list *listeners; | 287 | struct listener_list *listeners; |
| 288 | struct listener *s, *tmp; | 288 | struct listener *s, *tmp, *s2; |
| 289 | unsigned int cpu; | 289 | unsigned int cpu; |
| 290 | 290 | ||
| 291 | if (!cpumask_subset(mask, cpu_possible_mask)) | 291 | if (!cpumask_subset(mask, cpu_possible_mask)) |
| 292 | return -EINVAL; | 292 | return -EINVAL; |
| 293 | 293 | ||
| 294 | s = NULL; | ||
| 294 | if (isadd == REGISTER) { | 295 | if (isadd == REGISTER) { |
| 295 | for_each_cpu(cpu, mask) { | 296 | for_each_cpu(cpu, mask) { |
| 296 | s = kmalloc_node(sizeof(struct listener), GFP_KERNEL, | 297 | if (!s) |
| 297 | cpu_to_node(cpu)); | 298 | s = kmalloc_node(sizeof(struct listener), |
| 299 | GFP_KERNEL, cpu_to_node(cpu)); | ||
| 298 | if (!s) | 300 | if (!s) |
| 299 | goto cleanup; | 301 | goto cleanup; |
| 300 | s->pid = pid; | 302 | s->pid = pid; |
| @@ -303,9 +305,16 @@ static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd) | |||
| 303 | 305 | ||
| 304 | listeners = &per_cpu(listener_array, cpu); | 306 | listeners = &per_cpu(listener_array, cpu); |
| 305 | down_write(&listeners->sem); | 307 | down_write(&listeners->sem); |
| 308 | list_for_each_entry_safe(s2, tmp, &listeners->list, list) { | ||
| 309 | if (s2->pid == pid) | ||
| 310 | goto next_cpu; | ||
| 311 | } | ||
| 306 | list_add(&s->list, &listeners->list); | 312 | list_add(&s->list, &listeners->list); |
| 313 | s = NULL; | ||
| 314 | next_cpu: | ||
| 307 | up_write(&listeners->sem); | 315 | up_write(&listeners->sem); |
| 308 | } | 316 | } |
| 317 | kfree(s); | ||
| 309 | return 0; | 318 | return 0; |
| 310 | } | 319 | } |
| 311 | 320 | ||
