diff options
Diffstat (limited to 'kernel/taskstats.c')
| -rw-r--r-- | kernel/taskstats.c | 172 |
1 files changed, 104 insertions, 68 deletions
diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 11281d5792bd..c8231fb15708 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; |
| @@ -360,6 +354,12 @@ static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid) | |||
| 360 | struct nlattr *na, *ret; | 354 | struct nlattr *na, *ret; |
| 361 | int aggr; | 355 | int aggr; |
| 362 | 356 | ||
| 357 | /* If we don't pad, we end up with alignment on a 4 byte boundary. | ||
| 358 | * This causes lots of runtime warnings on systems requiring 8 byte | ||
| 359 | * alignment */ | ||
| 360 | u32 pids[2] = { pid, 0 }; | ||
| 361 | int pid_size = ALIGN(sizeof(pid), sizeof(long)); | ||
| 362 | |||
| 363 | aggr = (type == TASKSTATS_TYPE_PID) | 363 | aggr = (type == TASKSTATS_TYPE_PID) |
| 364 | ? TASKSTATS_TYPE_AGGR_PID | 364 | ? TASKSTATS_TYPE_AGGR_PID |
| 365 | : TASKSTATS_TYPE_AGGR_TGID; | 365 | : TASKSTATS_TYPE_AGGR_TGID; |
| @@ -367,7 +367,7 @@ static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid) | |||
| 367 | na = nla_nest_start(skb, aggr); | 367 | na = nla_nest_start(skb, aggr); |
| 368 | if (!na) | 368 | if (!na) |
| 369 | goto err; | 369 | goto err; |
| 370 | if (nla_put(skb, type, sizeof(pid), &pid) < 0) | 370 | if (nla_put(skb, type, pid_size, pids) < 0) |
| 371 | goto err; | 371 | goto err; |
| 372 | ret = nla_reserve(skb, TASKSTATS_TYPE_STATS, sizeof(struct taskstats)); | 372 | ret = nla_reserve(skb, TASKSTATS_TYPE_STATS, sizeof(struct taskstats)); |
| 373 | if (!ret) | 373 | if (!ret) |
| @@ -424,39 +424,46 @@ err: | |||
| 424 | return rc; | 424 | return rc; |
| 425 | } | 425 | } |
| 426 | 426 | ||
| 427 | static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) | 427 | static int cmd_attr_register_cpumask(struct genl_info *info) |
| 428 | { | 428 | { |
| 429 | int rc; | ||
| 430 | struct sk_buff *rep_skb; | ||
| 431 | struct taskstats *stats; | ||
| 432 | size_t size; | ||
| 433 | cpumask_var_t mask; | 429 | cpumask_var_t mask; |
| 430 | int rc; | ||
| 434 | 431 | ||
| 435 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) | 432 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) |
| 436 | return -ENOMEM; | 433 | return -ENOMEM; |
| 437 | |||
| 438 | rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask); | 434 | rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask); |
| 439 | if (rc < 0) | 435 | if (rc < 0) |
| 440 | goto free_return_rc; | 436 | goto out; |
| 441 | if (rc == 0) { | 437 | rc = add_del_listener(info->snd_pid, mask, REGISTER); |
| 442 | rc = add_del_listener(info->snd_pid, mask, REGISTER); | 438 | out: |
| 443 | goto free_return_rc; | 439 | free_cpumask_var(mask); |
| 444 | } | 440 | return rc; |
| 441 | } | ||
| 442 | |||
| 443 | static int cmd_attr_deregister_cpumask(struct genl_info *info) | ||
| 444 | { | ||
| 445 | cpumask_var_t mask; | ||
| 446 | int rc; | ||
| 445 | 447 | ||
| 448 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) | ||
| 449 | return -ENOMEM; | ||
| 446 | rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask); | 450 | rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask); |
| 447 | if (rc < 0) | 451 | if (rc < 0) |
| 448 | goto free_return_rc; | 452 | goto out; |
| 449 | if (rc == 0) { | 453 | rc = add_del_listener(info->snd_pid, mask, DEREGISTER); |
| 450 | rc = add_del_listener(info->snd_pid, mask, DEREGISTER); | 454 | out: |
| 451 | free_return_rc: | ||
| 452 | free_cpumask_var(mask); | ||
| 453 | return rc; | ||
| 454 | } | ||
| 455 | free_cpumask_var(mask); | 455 | free_cpumask_var(mask); |
| 456 | return rc; | ||
| 457 | } | ||
| 458 | |||
| 459 | static int cmd_attr_pid(struct genl_info *info) | ||
| 460 | { | ||
| 461 | struct taskstats *stats; | ||
| 462 | struct sk_buff *rep_skb; | ||
| 463 | size_t size; | ||
| 464 | u32 pid; | ||
| 465 | int rc; | ||
| 456 | 466 | ||
| 457 | /* | ||
| 458 | * Size includes space for nested attributes | ||
| 459 | */ | ||
| 460 | size = nla_total_size(sizeof(u32)) + | 467 | size = nla_total_size(sizeof(u32)) + |
| 461 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); | 468 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); |
| 462 | 469 | ||
| @@ -465,33 +472,64 @@ free_return_rc: | |||
| 465 | return rc; | 472 | return rc; |
| 466 | 473 | ||
| 467 | rc = -EINVAL; | 474 | rc = -EINVAL; |
| 468 | if (info->attrs[TASKSTATS_CMD_ATTR_PID]) { | 475 | pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); |
| 469 | u32 pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); | 476 | stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); |
| 470 | stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); | 477 | if (!stats) |
| 471 | if (!stats) | 478 | goto err; |
| 472 | goto err; | 479 | |
| 473 | 480 | rc = fill_stats_for_pid(pid, stats); | |
| 474 | rc = fill_pid(pid, NULL, stats); | 481 | if (rc < 0) |
| 475 | if (rc < 0) | 482 | goto err; |
| 476 | goto err; | 483 | return send_reply(rep_skb, info); |
| 477 | } else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) { | 484 | err: |
| 478 | u32 tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); | 485 | nlmsg_free(rep_skb); |
| 479 | stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); | 486 | return rc; |
| 480 | if (!stats) | 487 | } |
| 481 | goto err; | 488 | |
| 482 | 489 | static int cmd_attr_tgid(struct genl_info *info) | |
| 483 | rc = fill_tgid(tgid, NULL, stats); | 490 | { |
| 484 | if (rc < 0) | 491 | struct taskstats *stats; |
| 485 | goto err; | 492 | struct sk_buff *rep_skb; |
| 486 | } else | 493 | size_t size; |
| 494 | u32 tgid; | ||
| 495 | int rc; | ||
| 496 | |||
| 497 | size = nla_total_size(sizeof(u32)) + | ||
| 498 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); | ||
| 499 | |||
| 500 | rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); | ||
| 501 | if (rc < 0) | ||
| 502 | return rc; | ||
| 503 | |||
| 504 | rc = -EINVAL; | ||
| 505 | tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); | ||
| 506 | stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); | ||
| 507 | if (!stats) | ||
| 487 | goto err; | 508 | goto err; |
| 488 | 509 | ||
| 510 | rc = fill_stats_for_tgid(tgid, stats); | ||
| 511 | if (rc < 0) | ||
| 512 | goto err; | ||
| 489 | return send_reply(rep_skb, info); | 513 | return send_reply(rep_skb, info); |
| 490 | err: | 514 | err: |
| 491 | nlmsg_free(rep_skb); | 515 | nlmsg_free(rep_skb); |
| 492 | return rc; | 516 | return rc; |
| 493 | } | 517 | } |
| 494 | 518 | ||
| 519 | static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) | ||
| 520 | { | ||
| 521 | if (info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK]) | ||
| 522 | return cmd_attr_register_cpumask(info); | ||
| 523 | else if (info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK]) | ||
| 524 | return cmd_attr_deregister_cpumask(info); | ||
| 525 | else if (info->attrs[TASKSTATS_CMD_ATTR_PID]) | ||
| 526 | return cmd_attr_pid(info); | ||
| 527 | else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) | ||
| 528 | return cmd_attr_tgid(info); | ||
| 529 | else | ||
| 530 | return -EINVAL; | ||
| 531 | } | ||
| 532 | |||
| 495 | static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk) | 533 | static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk) |
| 496 | { | 534 | { |
| 497 | struct signal_struct *sig = tsk->signal; | 535 | struct signal_struct *sig = tsk->signal; |
| @@ -555,9 +593,7 @@ void taskstats_exit(struct task_struct *tsk, int group_dead) | |||
| 555 | if (!stats) | 593 | if (!stats) |
| 556 | goto err; | 594 | goto err; |
| 557 | 595 | ||
| 558 | rc = fill_pid(-1, tsk, stats); | 596 | fill_stats(tsk, stats); |
| 559 | if (rc < 0) | ||
| 560 | goto err; | ||
| 561 | 597 | ||
| 562 | /* | 598 | /* |
| 563 | * Doesn't matter if tsk is the leader or the last group member leaving | 599 | * Doesn't matter if tsk is the leader or the last group member leaving |
