diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/taskstats.c | 57 |
1 files changed, 44 insertions, 13 deletions
diff --git a/kernel/taskstats.c b/kernel/taskstats.c index c8231fb15708..3308fd7f1b52 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c | |||
| @@ -349,25 +349,47 @@ static int parse(struct nlattr *na, struct cpumask *mask) | |||
| 349 | return ret; | 349 | return ret; |
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | #ifdef CONFIG_IA64 | ||
| 353 | #define TASKSTATS_NEEDS_PADDING 1 | ||
| 354 | #endif | ||
| 355 | |||
| 352 | 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) |
| 353 | { | 357 | { |
| 354 | struct nlattr *na, *ret; | 358 | struct nlattr *na, *ret; |
| 355 | int aggr; | 359 | int aggr; |
| 356 | 360 | ||
| 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) | 361 | aggr = (type == TASKSTATS_TYPE_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; |
| 370 | if (nla_put(skb, type, pid_size, pids) < 0) | 391 | |
| 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)); |
| 373 | if (!ret) | 395 | if (!ret) |
| @@ -456,6 +478,18 @@ out: | |||
| 456 | return rc; | 478 | return rc; |
| 457 | } | 479 | } |
| 458 | 480 | ||
| 481 | static size_t taskstats_packet_size(void) | ||
| 482 | { | ||
| 483 | size_t size; | ||
| 484 | |||
| 485 | size = nla_total_size(sizeof(u32)) + | ||
| 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 | |||
| 459 | static int cmd_attr_pid(struct genl_info *info) | 493 | static int cmd_attr_pid(struct genl_info *info) |
| 460 | { | 494 | { |
| 461 | struct taskstats *stats; | 495 | struct taskstats *stats; |
| @@ -464,8 +498,7 @@ static int cmd_attr_pid(struct genl_info *info) | |||
| 464 | u32 pid; | 498 | u32 pid; |
| 465 | int rc; | 499 | int rc; |
| 466 | 500 | ||
| 467 | size = nla_total_size(sizeof(u32)) + | 501 | size = taskstats_packet_size(); |
| 468 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); | ||
| 469 | 502 | ||
| 470 | rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); | 503 | rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); |
| 471 | if (rc < 0) | 504 | if (rc < 0) |
| @@ -494,8 +527,7 @@ static int cmd_attr_tgid(struct genl_info *info) | |||
| 494 | u32 tgid; | 527 | u32 tgid; |
| 495 | int rc; | 528 | int rc; |
| 496 | 529 | ||
| 497 | size = nla_total_size(sizeof(u32)) + | 530 | size = taskstats_packet_size(); |
| 498 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); | ||
| 499 | 531 | ||
| 500 | rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); | 532 | rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); |
| 501 | if (rc < 0) | 533 | if (rc < 0) |
| @@ -570,8 +602,7 @@ void taskstats_exit(struct task_struct *tsk, int group_dead) | |||
| 570 | /* | 602 | /* |
| 571 | * Size includes space for nested attributes | 603 | * Size includes space for nested attributes |
| 572 | */ | 604 | */ |
| 573 | size = nla_total_size(sizeof(u32)) + | 605 | size = taskstats_packet_size(); |
| 574 | nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); | ||
| 575 | 606 | ||
| 576 | is_thread_group = !!taskstats_tgid_alloc(tsk); | 607 | is_thread_group = !!taskstats_tgid_alloc(tsk); |
| 577 | if (is_thread_group) { | 608 | if (is_thread_group) { |
