diff options
author | KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> | 2010-05-18 20:37:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-19 14:48:18 -0400 |
commit | fa9dc265ace9774e62f0e31108e5f47911124bda (patch) | |
tree | f5230efca0ccf0cfec7690a6c45209cff938d740 /kernel/compat.c | |
parent | cf77e988ddfc7f047ac1ddc72cadb5eee7e09293 (diff) |
cpumask: fix compat getaffinity
Commit a45185d2d "cpumask: convert kernel/compat.c" broke libnuma, which
abuses sched_getaffinity to find out NR_CPUS in order to parse
/sys/devices/system/node/node*/cpumap.
On NUMA systems with less than 32 possibly CPUs, the current
compat_sys_sched_getaffinity now returns '4' instead of the actual
NR_CPUS/8, which makes libnuma bail out when parsing the cpumap.
The libnuma call sched_getaffinity(0, bitmap, 4096) at first. It mean
the libnuma expect the return value of sched_getaffinity() is either len
argument or NR_CPUS. But it doesn't expect to return nr_cpu_ids.
Strictly speaking, userland requirement are
1) Glibc assume the return value mean the lengh of initialized
of mask argument. E.g. if sched_getaffinity(1024) return 128,
glibc make zero fill rest 896 byte.
2) Libnuma assume the return value can be used to guess NR_CPUS
in kernel. It assume len-arg<NR_CPUS makes -EINVAL. But
it try len=4096 at first and 4096 is always bigger than
NR_CPUS. Then, if we remove strange min_length normalization,
we never hit -EINVAL case.
sched_getaffinity() already solved this issue. This patch adapts
compat_sys_sched_getaffinity() to match the non-compat case.
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Acked-by: Rusty Russell <rusty@rustcorp.com.au>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Reported-by: Ken Werner <ken.werner@web.de>
Cc: stable@kernel.org
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/compat.c')
-rw-r--r-- | kernel/compat.c | 25 |
1 files changed, 11 insertions, 14 deletions
diff --git a/kernel/compat.c b/kernel/compat.c index 7f40e9275fd9..5adab05a3172 100644 --- a/kernel/compat.c +++ b/kernel/compat.c | |||
@@ -495,29 +495,26 @@ asmlinkage long compat_sys_sched_getaffinity(compat_pid_t pid, unsigned int len, | |||
495 | { | 495 | { |
496 | int ret; | 496 | int ret; |
497 | cpumask_var_t mask; | 497 | cpumask_var_t mask; |
498 | unsigned long *k; | ||
499 | unsigned int min_length = cpumask_size(); | ||
500 | |||
501 | if (nr_cpu_ids <= BITS_PER_COMPAT_LONG) | ||
502 | min_length = sizeof(compat_ulong_t); | ||
503 | 498 | ||
504 | if (len < min_length) | 499 | if ((len * BITS_PER_BYTE) < nr_cpu_ids) |
500 | return -EINVAL; | ||
501 | if (len & (sizeof(compat_ulong_t)-1)) | ||
505 | return -EINVAL; | 502 | return -EINVAL; |
506 | 503 | ||
507 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) | 504 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) |
508 | return -ENOMEM; | 505 | return -ENOMEM; |
509 | 506 | ||
510 | ret = sched_getaffinity(pid, mask); | 507 | ret = sched_getaffinity(pid, mask); |
511 | if (ret < 0) | 508 | if (ret == 0) { |
512 | goto out; | 509 | size_t retlen = min_t(size_t, len, cpumask_size()); |
513 | 510 | ||
514 | k = cpumask_bits(mask); | 511 | if (compat_put_bitmap(user_mask_ptr, cpumask_bits(mask), retlen * 8)) |
515 | ret = compat_put_bitmap(user_mask_ptr, k, min_length * 8); | 512 | ret = -EFAULT; |
516 | if (ret == 0) | 513 | else |
517 | ret = min_length; | 514 | ret = retlen; |
518 | 515 | } | |
519 | out: | ||
520 | free_cpumask_var(mask); | 516 | free_cpumask_var(mask); |
517 | |||
521 | return ret; | 518 | return ret; |
522 | } | 519 | } |
523 | 520 | ||