Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa9dc265 authored by KOSAKI Motohiro's avatar KOSAKI Motohiro Committed by Linus Torvalds
Browse files

cpumask: fix compat getaffinity



Commit a45185d2 "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: default avatarKOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Acked-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Reported-by: default avatarKen Werner <ken.werner@web.de>
Cc: stable@kernel.org
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent cf77e988
Loading
Loading
Loading
Loading
+11 −14
Original line number Diff line number Diff line
@@ -495,29 +495,26 @@ asmlinkage long compat_sys_sched_getaffinity(compat_pid_t pid, unsigned int len,
{
	int ret;
	cpumask_var_t mask;
	unsigned long *k;
	unsigned int min_length = cpumask_size();

	if (nr_cpu_ids <= BITS_PER_COMPAT_LONG)
		min_length = sizeof(compat_ulong_t);

	if (len < min_length)
	if ((len * BITS_PER_BYTE) < nr_cpu_ids)
		return -EINVAL;
	if (len & (sizeof(compat_ulong_t)-1))
		return -EINVAL;

	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
		return -ENOMEM;

	ret = sched_getaffinity(pid, mask);
	if (ret < 0)
		goto out;

	k = cpumask_bits(mask);
	ret = compat_put_bitmap(user_mask_ptr, k, min_length * 8);
	if (ret == 0)
		ret = min_length;
	if (ret == 0) {
		size_t retlen = min_t(size_t, len, cpumask_size());

out:
		if (compat_put_bitmap(user_mask_ptr, cpumask_bits(mask), retlen * 8))
			ret = -EFAULT;
		else
			ret = retlen;
	}
	free_cpumask_var(mask);

	return ret;
}