[PATCH] Fixed set_affinity/get_affinity syscalls

Rusty Russell (rusty@rustcorp.com.au)
Fri, 21 Jun 2002 02:30:22 +1000


It's a little more kernel code, but it allows userspace to do:

unsigned int mask = (1 << cpu);
sys_setaffinity(getpid(), LINUX_AFFINITY_INCLUDE, mask, sizeof(mask));

And it will always do the Right Thing.

Applies on top of last patch.
Comments?

Name: Modified set_affinity/get_affinity syscalls
Author: Rusty Russell
Status: Experimental
Depends: Hotcpu/cpumask.patch.gz

D: This allows userspace to have cpu affinity control without needing
D: to know the size of kernel datastructures and allows them to
D: control what happens when new CPUs are brought online. It also
D: means that in the future we can sanely interpret affinity in
D: HyperThreading.

diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-2.5.23-sent-to-linus/include/linux/affinity.h working-2.5.23-sched/include/linux/affinity.h
--- working-2.5.23-sent-to-linus/include/linux/affinity.h Thu Jan 1 10:00:00 1970
+++ working-2.5.23-sched/include/linux/affinity.h Fri Jun 21 02:09:06 2002
@@ -0,0 +1,9 @@
+#ifndef _LINUX_AFFINITY_H
+#define _LINUX_AFFINITY_H
+enum {
+ /* Set affinity to these processors */
+ LINUX_AFFINITY_INCLUDE,
+ /* Set affinity to all *but* these processors */
+ LINUX_AFFINITY_EXCLUDE,
+};
+#endif
diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-2.5.23-sent-to-linus/kernel/sched.c working-2.5.23-sched/kernel/sched.c
--- working-2.5.23-sent-to-linus/kernel/sched.c Fri Jun 21 01:49:47 2002
+++ working-2.5.23-sched/kernel/sched.c Fri Jun 21 02:08:54 2002
@@ -25,6 +25,7 @@
#include <asm/mmu_context.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
+#include <linux/affinity.h>
#include <linux/kernel_stat.h>

/*
@@ -1314,25 +1315,54 @@
/**
* sys_sched_setaffinity - set the cpu affinity of a process
* @pid: pid of the process
+ * @include: is this include or exclude?
* @len: length in bytes of the bitmask pointed to by user_mask_ptr
- * @user_mask_ptr: user-space pointer to the new cpu mask
+ * @user_mask_ptr: user-space pointer to bitmask of cpus to include/exclude
*/
-asmlinkage int sys_sched_setaffinity(pid_t pid, unsigned int len,
- unsigned long *user_mask_ptr)
+asmlinkage int sys_sched_setaffinity(pid_t pid,
+ int include,
+ unsigned int len,
+ unsigned char *user_mask_ptr)
{
- cpu_mask_t online_mask, new_mask;
+ cpu_mask_t new_mask, tmp;
int retval;
task_t *p;
+ unsigned int i;

- if (len < sizeof(new_mask))
- return -EINVAL;
-
- if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask)))
+ memset(&new_mask, 0, sizeof(new_mask));
+ if (copy_from_user(&new_mask, user_mask_ptr,
+ min((size_t)len, sizeof(new_mask))))
return -EFAULT;

- cpu_online_mask(&online_mask, new_mask);
- if (find_first_bit((unsigned long *)online_mask, NR_CPUS) == NR_CPUS)
+ /* longer is OK, as long as they don't actually set any of the bits. */
+ if (len > sizeof(new_mask)) {
+ unsigned char c;
+ for (i = sizeof(new_mask); i < len; i++) {
+ if (get_user(c, user_mask_ptr+i))
+ return -EFAULT;
+ if (c != 0)
+ return -ENOENT;
+ }
+ }
+
+ /* Check for mention of cpus that aren't online/don't exist */
+ cpu_online_mask(&tmp, new_mask);
+ if (memcmp(&tmp, &new_mask, sizeof(new_mask)) != 0)
+ return -ENOENT;
+
+ /* Invert the mask in the exclude case. */
+ if (include == LINUX_AFFINITY_EXCLUDE) {
+ for (i = 0; i < sizeof(new_mask)/sizeof(long); i++)
+ ((unsigned long *)&new_mask)[i] ^= -1UL;
+ } else if (include != LINUX_AFFINITY_INCLUDE) {
return -EINVAL;
+ }
+
+ /* The new mask must mention some online cpus */
+ cpu_online_mask(&tmp, new_mask);
+ if (find_first_bit((unsigned long *)&tmp, NR_CPUS) == NR_CPUS)
+ /* This is kinda true... */
+ return -EWOULDBLOCK;

read_lock(&tasklist_lock);

@@ -1356,7 +1386,7 @@
goto out_unlock;

retval = 0;
- set_cpus_allowed(p, online_mask);
+ set_cpus_allowed(p, new_mask);

out_unlock:
put_task_struct(p);
@@ -1368,36 +1398,27 @@
* @pid: pid of the process
* @len: length in bytes of the bitmask pointed to by user_mask_ptr
* @user_mask_ptr: user-space pointer to hold the current cpu mask
+ * Returns the size required to hold the complete cpu mask.
*/
asmlinkage int sys_sched_getaffinity(pid_t pid, unsigned int len,
- unsigned long *user_mask_ptr)
+ void *user_mask_ptr)
{
- unsigned int real_len;
cpu_mask_t mask;
- int retval;
task_t *p;

- real_len = sizeof(mask);
- if (len < real_len)
- return -EINVAL;
-
read_lock(&tasklist_lock);
-
- retval = -ESRCH;
p = find_process_by_pid(pid);
- if (!p)
- goto out_unlock;
-
- retval = 0;
- cpu_online_mask(&mask, p->cpus_allowed);
-
-out_unlock:
+ if (!p) {
+ read_unlock(&tasklist_lock);
+ return -ESRCH;
+ }
+ mask = p->cpus_allowed;
read_unlock(&tasklist_lock);
- if (retval)
- return retval;
- if (copy_to_user(user_mask_ptr, &mask, real_len))
+
+ if (copy_to_user(user_mask_ptr, &mask,
+ min((unsigned int)sizeof(mask), len)))
return -EFAULT;
- return real_len;
+ return sizeof(mask);
}

asmlinkage long sys_sched_yield(void)

--
  Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/