diff options
Diffstat (limited to 'kernel/taskstats.c')
-rw-r--r-- | kernel/taskstats.c | 205 |
1 files changed, 136 insertions, 69 deletions
diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 11281d5792bd..3308fd7f1b52 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c | |||
@@ -175,22 +175,8 @@ static void send_cpu_listeners(struct sk_buff *skb, | |||
175 | up_write(&listeners->sem); | 175 | up_write(&listeners->sem); |
176 | } | 176 | } |
177 | 177 | ||
178 | static int fill_pid(pid_t pid, struct task_struct *tsk, | 178 | static void fill_stats(struct task_struct *tsk, struct taskstats *stats) |
179 | struct taskstats *stats) | ||
180 | { | 179 | { |
181 | int rc = 0; | ||
182 | |||
183 | if (!tsk) { | ||
184 | rcu_read_lock(); | ||
185 | tsk = find_task_by_vpid(pid); | ||
186 | if (tsk) | ||
187 | get_task_struct(tsk); | ||
188 | rcu_read_unlock(); | ||
189 | if (!tsk) | ||
190 | return -ESRCH; | ||
191 | } else | ||
192 | get_task_struct(tsk); | ||
193 | |||
194 | memset(stats, 0, sizeof(*stats)); | 180 | memset(stats, 0, sizeof(*stats)); |
195 | /* | 181 | /* |
196 | * Each accounting subsystem adds calls to its functions to | 182 | * Each accounting subsystem adds calls to its functions to |
@@ -209,17 +195,27 @@ static int fill_pid(pid_t pid, struct task_struct *tsk, | |||
209 | 195 | ||
210 | /* fill in extended acct fields */ | 196 | /* fill in extended acct fields */ |
211 | xacct_add_tsk(stats, tsk); | 197 | xacct_add_tsk(stats, tsk); |
198 | } | ||
212 | 199 | ||
213 | /* Define err: label here if needed */ | 200 | static int fill_stats_for_pid(pid_t pid, struct taskstats *stats) |
214 | put_task_struct(tsk); | 201 | { |
215 | return rc; | 202 | struct task_struct *tsk; |
216 | 203 | ||
204 | rcu_read_lock(); | ||
205 | tsk = find_task_by_vpid(pid); | ||
206 | if (tsk) | ||
207 | get_task_struct(tsk); | ||
208 | rcu_read_unlock(); | ||
209 | if (!tsk) | ||
210 | return -ESRCH; | ||
211 | fill_stats(tsk, stats); | ||
212 | put_task_struct(tsk); | ||
213 | return 0; | ||
217 | } | 214 | } |
218 | 215 | ||
219 | static int fill_tgid(pid_t tgid, struct task_struct *first, | 216 | static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) |
220 | struct taskstats *stats) | ||
221 | { | 217 | { |
222 | struct task_struct *tsk; | 218 | struct task_struct *tsk, *first; |
223 | unsigned long flags; | 219 | unsigned long flags; |
224 | int rc = -ESRCH; | 220 | int rc = -ESRCH; |
225 | 221 | ||
@@ -228,8 +224,7 @@ static int fill_tgid(pid_t tgid, struct task_struct *first, | |||
228 | * leaders who are already counted with the dead tasks | 224 | * leaders who are already counted with the dead tasks |
229 | */ | 225 | */ |
230 | rcu_read_lock(); | 226 | rcu_read_lock(); |
231 | if (!first) | 227 | first = find_task_by_vpid(tgid); |
232 | first = find_task_by_vpid(tgid); | ||
233 | 228 | ||
234 | if (!first || !lock_task_sighand(first, &flags)) | 229 | if (!first || !lock_task_sighand(first, &flags)) |
235 | goto out; | 230 | goto out; |
@@ -268,7 +263,6 @@ out: | |||
268 | return rc; | 263 | return rc; |
269 | } | 264 | } |
270 | 265 | ||
271 | |||
272 | static void fill_tgid_exit(struct task_struct *tsk) | 266 | static void fill_tgid_exit(struct task_struct *tsk) |
273 | { | 267 | { |
274 | unsigned long flags; | 268 | unsigned long flags; |
@@ -355,6 +349,10 @@ static int parse(struct nlattr *na, struct cpumask *mask) | |||
355 | return ret; | 349 | return ret; |
356 | } | 350 | } |
357 | 351 | ||
352 | #ifdef CONFIG_IA64 | ||
353 | #define TASKSTATS_NEEDS_PADDING 1 | ||
354 | #endif | ||
355 | |||
358 | static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid) | 356 | static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid) |
359 | { | 357 | { |
360 | struct nlattr *na, *ret; | 358 | struct nlattr *na, *ret; |
@@ -364,9 +362,33 @@ static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid) | |||
364 | ? TASKSTATS_TYPE_AGGR_PID | 362 | ? TASKSTATS_TYPE_AGGR_PID |
365 | : TASKSTATS_TYPE_AGGR_TGID; | 363 | : TASKSTATS_TYPE_AGGR_TGID; |
366 | 364 | ||
365 | /* | ||
366 | * The taskstats structure is internally aligned on 8 byte | ||
367 | * boundaries but the layout of the aggregrate reply, with | ||
368 | * two NLA headers and the pid (each 4 bytes), actually | ||
369 | * force the entire structure to be unaligned. This causes | ||
370 | * the kernel to issue unaligned access warnings on some | ||
371 | * architectures like ia64. Unfortunately, some software out there | ||
372 | * doesn't properly unroll the NLA packet and assumes that the start | ||
373 | * of the taskstats structure will always be 20 bytes from the start | ||
374 | * of the netlink payload. Aligning the start of the taskstats | ||
375 | * structure breaks this software, which we don't want. So, for now | ||
376 | * the alignment only happens on architectures that require it | ||
377 | * and those users will have to update to fixed versions of those | ||
378 | * packages. Space is reserved in the packet only when needed. | ||
379 | * This ifdef should be removed in several years e.g. 2012 once | ||
380 | * we can be confident that fixed versions are installed on most | ||
381 | * systems. We add the padding before the aggregate since the | ||
382 | * aggregate is already a defined type. | ||
383 | */ | ||
384 | #ifdef TASKSTATS_NEEDS_PADDING | ||
385 | if (nla_put(skb, TASKSTATS_TYPE_NULL, 0, NULL) < 0) | ||
386 | goto err; | ||
387 | #endif | ||
367 | na = nla_nest_start(skb, aggr); | 388 | na = nla_nest_start(skb, aggr); |
368 | if (!na) | 389 | if (!na) |
369 | goto err; | 390 | goto err; |
391 | |||
370 | if (nla_put(skb, type, sizeof(pid), &pid) < 0) | 392 | if (nla_put(skb, type, sizeof(pid), &pid) < 0) |
371 | goto err; | 393 | goto err; |
372 | ret = nla_reserve(skb, TASKSTATS_TYPE_STATS, sizeof(struct taskstats)); | 394 | ret = nla_reserve(skb, TASKSTATS_TYPE_STATS, sizeof(struct taskstats)); |
@@ -424,74 +446,122 @@ err: | |||
424 | return rc; | 446 | return rc; |
425 | } | 447 | } |
426 | 448 | ||
427 | static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) | 449 | static int cmd_attr_register_cpumask(struct genl_info *info) |
428 | { | 450 | { |
429 | int rc; | ||
430 | struct sk_buff *rep_skb; | ||
431 | struct taskstats *stats; | ||
432 | size_t size; | ||
433 | cpumask_var_t mask; | 451 | cpumask_var_t mask; |
452 | int rc; | ||
434 | 453 | ||
435 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) | 454 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) |
436 | return -ENOMEM; | 455 | return -ENOMEM; |
437 | |||
438 | rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask); | 456 | rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask); |
439 | if (rc < 0) | 457 | if (rc < 0) |
440 | goto free_return_rc; | 458 | goto out; |
441 | if (rc == 0) { | 459 | rc = add_del_listener(info->snd_pid, mask, REGISTER); |
442 | rc = add_del_listener(info->snd_pid, mask, REGISTER); | 460 | out: |
443 | goto free_return_rc; | 461 | free_cpumask_var(mask); |
444 | } | 462 | return rc; |
463 | } | ||
464 | |||
465 | static int cmd_attr_deregister_cpumask(struct genl_info *info) | ||
466 | { | ||
467 | cpumask_var_t mask; | ||
468 | int rc; | ||
445 | 469 | ||
470 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) | ||
471 | return -ENOMEM; | ||
446 | rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask); | 472 | rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask); |
447 | if (rc < 0) | 473 | if (rc < 0) |
448 | goto free_return_rc; | 474 | goto out; |
449 | if (rc == 0) { | 475 | rc = add_del_listener(info->snd_pid, mask, DEREGISTER); |
450 | rc = add_del_listener(info->snd_pid, mask, DEREGISTER); | 476 | out: |
451 | free_return_rc: | ||
452 | free_cpumask_var(mask); | ||
453 | return rc; | ||
454 | } | ||
455 | free_cpumask_var(mask); | 477 | free_cpumask_var(mask); |
478 | return rc; | ||
479 | } | ||
480 | |||
481 | static size_t taskstats_packet_size(void) | ||
482 | { | ||
483 | size_t size; | ||
456 | 484 | ||
457 | /* | ||
458 | * Size includes space for nested attributes | ||
459 | */ | ||
460 | size = nla_total_size(sizeof(u32)) + | 485 | size = nla_total_size(sizeof(u32)) + |
461 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); | 486 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); |
487 | #ifdef TASKSTATS_NEEDS_PADDING | ||
488 | size += nla_total_size(0); /* Padding for alignment */ | ||
489 | #endif | ||
490 | return size; | ||
491 | } | ||
492 | |||
493 | static int cmd_attr_pid(struct genl_info *info) | ||
494 | { | ||
495 | struct taskstats *stats; | ||
496 | struct sk_buff *rep_skb; | ||
497 | size_t size; | ||
498 | u32 pid; | ||
499 | int rc; | ||
500 | |||
501 | size = taskstats_packet_size(); | ||
462 | 502 | ||
463 | rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); | 503 | rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); |
464 | if (rc < 0) | 504 | if (rc < 0) |
465 | return rc; | 505 | return rc; |
466 | 506 | ||
467 | rc = -EINVAL; | 507 | rc = -EINVAL; |
468 | if (info->attrs[TASKSTATS_CMD_ATTR_PID]) { | 508 | pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); |
469 | u32 pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); | 509 | stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); |
470 | stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); | 510 | if (!stats) |
471 | if (!stats) | ||
472 | goto err; | ||
473 | |||
474 | rc = fill_pid(pid, NULL, stats); | ||
475 | if (rc < 0) | ||
476 | goto err; | ||
477 | } else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) { | ||
478 | u32 tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); | ||
479 | stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); | ||
480 | if (!stats) | ||
481 | goto err; | ||
482 | |||
483 | rc = fill_tgid(tgid, NULL, stats); | ||
484 | if (rc < 0) | ||
485 | goto err; | ||
486 | } else | ||
487 | goto err; | 511 | goto err; |
488 | 512 | ||
513 | rc = fill_stats_for_pid(pid, stats); | ||
514 | if (rc < 0) | ||
515 | goto err; | ||
489 | return send_reply(rep_skb, info); | 516 | return send_reply(rep_skb, info); |
490 | err: | 517 | err: |
491 | nlmsg_free(rep_skb); | 518 | nlmsg_free(rep_skb); |
492 | return rc; | 519 | return rc; |
493 | } | 520 | } |
494 | 521 | ||
522 | static int cmd_attr_tgid(struct genl_info *info) | ||
523 | { | ||
524 | struct taskstats *stats; | ||
525 | struct sk_buff *rep_skb; | ||
526 | size_t size; | ||
527 | u32 tgid; | ||
528 | int rc; | ||
529 | |||
530 | size = taskstats_packet_size(); | ||
531 | |||
532 | rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); | ||
533 | if (rc < 0) | ||
534 | return rc; | ||
535 | |||
536 | rc = -EINVAL; | ||
537 | tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); | ||
538 | stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); | ||
539 | if (!stats) | ||
540 | goto err; | ||
541 | |||
542 | rc = fill_stats_for_tgid(tgid, stats); | ||
543 | if (rc < 0) | ||
544 | goto err; | ||
545 | return send_reply(rep_skb, info); | ||
546 | err: | ||
547 | nlmsg_free(rep_skb); | ||
548 | return rc; | ||
549 | } | ||
550 | |||
551 | static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) | ||
552 | { | ||
553 | if (info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK]) | ||
554 | return cmd_attr_register_cpumask(info); | ||
555 | else if (info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK]) | ||
556 | return cmd_attr_deregister_cpumask(info); | ||
557 | else if (info->attrs[TASKSTATS_CMD_ATTR_PID]) | ||
558 | return cmd_attr_pid(info); | ||
559 | else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) | ||
560 | return cmd_attr_tgid(info); | ||
561 | else | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | |||
495 | static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk) | 565 | static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk) |
496 | { | 566 | { |
497 | struct signal_struct *sig = tsk->signal; | 567 | struct signal_struct *sig = tsk->signal; |
@@ -532,8 +602,7 @@ void taskstats_exit(struct task_struct *tsk, int group_dead) | |||
532 | /* | 602 | /* |
533 | * Size includes space for nested attributes | 603 | * Size includes space for nested attributes |
534 | */ | 604 | */ |
535 | size = nla_total_size(sizeof(u32)) + | 605 | size = taskstats_packet_size(); |
536 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); | ||
537 | 606 | ||
538 | is_thread_group = !!taskstats_tgid_alloc(tsk); | 607 | is_thread_group = !!taskstats_tgid_alloc(tsk); |
539 | if (is_thread_group) { | 608 | if (is_thread_group) { |
@@ -555,9 +624,7 @@ void taskstats_exit(struct task_struct *tsk, int group_dead) | |||
555 | if (!stats) | 624 | if (!stats) |
556 | goto err; | 625 | goto err; |
557 | 626 | ||
558 | rc = fill_pid(-1, tsk, stats); | 627 | fill_stats(tsk, stats); |
559 | if (rc < 0) | ||
560 | goto err; | ||
561 | 628 | ||
562 | /* | 629 | /* |
563 | * Doesn't matter if tsk is the leader or the last group member leaving | 630 | * Doesn't matter if tsk is the leader or the last group member leaving |