]> err.no Git - util-linux/commitdiff
taskset: independent of hardcoded NR_CPUS max.
authorCliff Wickman <cpw@sgi.com>
Mon, 11 Jun 2007 20:24:45 +0000 (15:24 -0500)
committerKarel Zak <kzak@redhat.com>
Tue, 19 Jun 2007 13:00:10 +0000 (15:00 +0200)
This patch makes the taskset command independent of the system's maximum
number of cpus (CONFIG_NR_CPUS).  The maximum for CONFIG_NR_CPUS is a
moving target.

With this patch the size of the systems's cpumask_t is gotten from
sched_getaffinity(2).

This patch uses variable length bitmasks borrowed from Paul Jackson's
variable size bitmask routines (hence I kept his copyright notice).
This replaces the use of the glibc CPU_SETSIZE, CPU_SET, CPU_ZERO and
CPU_ISSET macros which depend on a hardcoded size for cpu_set_t.

(also fixes one little nit: the -V option is "-v" in the built-in help, so
 changed the built-in help)

Signed-off-by: Cliff Wickman <cpw@sgi.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
schedutils/taskset.c

index 2590d995690929842af37513f5316dfbe836ad65..96ae3d3d28df81723c3ea27f403c14f85d03ded7 100644 (file)
 #include <errno.h>
 #include <string.h>
 #include <ctype.h>
+#include <sys/syscall.h>
+
+struct bitmask {
+       unsigned int size;
+       unsigned long *maskp;
+};
 
 static void show_usage(const char *cmd)
 {
@@ -48,7 +54,7 @@ static void show_usage(const char *cmd)
         fprintf(stderr, "  -c, --cpu-list             "\
                "display and specify cpus in list format\n");
        fprintf(stderr, "  -h, --help                 display this help\n");
-       fprintf(stderr, "  -v, --version              "\
+       fprintf(stderr, "  -V, --version              "\
                "output version information\n\n");
        fprintf(stderr, "The default behavior is to run a new command:\n");
        fprintf(stderr, "  %s 03 sshd -b 1024\n", cmd);
@@ -73,21 +79,75 @@ static inline int val_to_char(int v)
                return -1;
 }
 
-static char * cpuset_to_str(cpu_set_t *mask, char *str)
+/*
+ * The following bitmask declarations, bitmask_*() routines, and associated
+ * _setbit() and _getbit() routines are:
+ * Copyright (c) 2004 Silicon Graphics, Inc. (SGI) All rights reserved.
+ * SGI publishes it under the terms of the GNU General Public License, v2,
+ * as published by the Free Software Foundation.
+ */
+#define howmany(x,y) (((x)+((y)-1))/(y))
+#define bitsperlong (8 * sizeof(unsigned long))
+#define longsperbits(n) howmany(n, bitsperlong)
+#define bytesperbits(x) ((x+7)/8)
+
+static unsigned int _getbit(const struct bitmask *bmp, unsigned int n)
+{
+       if (n < bmp->size)
+               return (bmp->maskp[n/bitsperlong] >> (n % bitsperlong)) & 1;
+       else
+               return 0;
+}
+
+static void _setbit(struct bitmask *bmp, unsigned int n, unsigned int v)
+{
+       if (n < bmp->size) {
+               if (v)
+                       bmp->maskp[n/bitsperlong] |= 1UL << (n % bitsperlong);
+               else
+                       bmp->maskp[n/bitsperlong] &= ~(1UL << (n % bitsperlong));
+       }
+}
+
+int bitmask_isbitset(const struct bitmask *bmp, unsigned int i)
+{
+       return _getbit(bmp, i);
+}
+
+struct bitmask *bitmask_clearall(struct bitmask *bmp)
+{
+       unsigned int i;
+       for (i = 0; i < bmp->size; i++)
+               _setbit(bmp, i, 0);
+       return bmp;
+}
+
+struct bitmask *bitmask_setbit(struct bitmask *bmp, unsigned int i)
+{
+       _setbit(bmp, i, 1);
+       return bmp;
+}
+
+unsigned int bitmask_nbytes(struct bitmask *bmp)
+{
+       return longsperbits(bmp->size) * sizeof(unsigned long);
+}
+
+static char * cpuset_to_str(struct bitmask *mask, char *str)
 {
        int base;
        char *ptr = str;
        char *ret = 0;
 
-       for (base = CPU_SETSIZE - 4; base >= 0; base -= 4) {
+       for (base = mask->size - 4; base >= 0; base -= 4) {
                char val = 0;
-               if (CPU_ISSET(base, mask))
+               if (bitmask_isbitset(mask, base))
                        val |= 1;
-               if (CPU_ISSET(base + 1, mask))
+               if (bitmask_isbitset(mask, base + 1))
                        val |= 2;
-               if (CPU_ISSET(base + 2, mask))
+               if (bitmask_isbitset(mask, base + 2))
                        val |= 4;
-               if (CPU_ISSET(base + 3, mask))
+               if (bitmask_isbitset(mask, base + 3))
                        val |= 8;
                if (!ret && val)
                        ret = ptr;
@@ -97,19 +157,35 @@ static char * cpuset_to_str(cpu_set_t *mask, char *str)
        return ret ? ret : ptr - 1;
 }
 
-static char * cpuset_to_cstr(cpu_set_t *mask, char *str)
+struct bitmask *bitmask_alloc(unsigned int n)
+{
+       struct bitmask *bmp;
+
+       bmp = malloc(sizeof(*bmp));
+       if (!bmp)
+               return 0;
+       bmp->size = n;
+       bmp->maskp = calloc(longsperbits(n), sizeof(unsigned long));
+       if (!bmp->maskp) {
+               free(bmp);
+               return 0;
+       }
+       return bmp;
+}
+
+static char * cpuset_to_cstr(struct bitmask *mask, char *str)
 {
        int i;
        char *ptr = str;
        int entry_made = 0;
 
-       for (i = 0; i < CPU_SETSIZE; i++) {
-               if (CPU_ISSET(i, mask)) {
+       for (i = 0; i < mask->size; i++) {
+               if (bitmask_isbitset(mask, i)) {
                        int j;
                        int run = 0;
                        entry_made = 1;
-                       for (j = i + 1; j < CPU_SETSIZE; j++) {
-                               if (CPU_ISSET(j, mask))
+                       for (j = i + 1; j < mask->size; j++) {
+                               if (bitmask_isbitset(mask, j))
                                        run++;
                                else
                                        break;
@@ -146,7 +222,7 @@ static inline int char_to_val(int c)
                return -1;
 }
 
-static int str_to_cpuset(cpu_set_t *mask, const char* str)
+static int str_to_cpuset(struct bitmask *mask, const char* str)
 {
        int len = strlen(str);
        const char *ptr = str + len - 1;
@@ -156,19 +232,19 @@ static int str_to_cpuset(cpu_set_t *mask, const char* str)
        if (len > 1 && !memcmp(str, "0x", 2L))
                str += 2;
 
-       CPU_ZERO(mask);
+       bitmask_clearall(mask);
        while (ptr >= str) {
                char val = char_to_val(*ptr);
                if (val == (char) -1)
                        return -1;
                if (val & 1)
-                       CPU_SET(base, mask);
+                       bitmask_setbit(mask, base);
                if (val & 2)
-                       CPU_SET(base + 1, mask);
+                       bitmask_setbit(mask, base + 1);
                if (val & 4)
-                       CPU_SET(base + 2, mask);
+                       bitmask_setbit(mask, base + 2);
                if (val & 8)
-                       CPU_SET(base + 3, mask);
+                       bitmask_setbit(mask, base + 3);
                len--;
                ptr--;
                base += 4;
@@ -186,11 +262,11 @@ static const char *nexttoken(const char *q,  int sep)
        return q;
 }
 
-static int cstr_to_cpuset(cpu_set_t *mask, const char* str)
+static int cstr_to_cpuset(struct bitmask *mask, const char* str)
 {
        const char *p, *q;
        q = str;
-       CPU_ZERO(mask);
+       bitmask_clearall(mask);
 
        while (p = q, q = nexttoken(q, ','), p) {
                unsigned int a; /* beginning of range */
@@ -218,7 +294,7 @@ static int cstr_to_cpuset(cpu_set_t *mask, const char* str)
                if (!(a <= b))
                        return 1;
                while (a <= b) {
-                       CPU_SET(a, mask);                       
+                       bitmask_setbit(mask, a);
                        a += s;
                }
        }
@@ -226,14 +302,42 @@ static int cstr_to_cpuset(cpu_set_t *mask, const char* str)
        return 0;
 }
 
+/*
+ * Number of bits in a CPU bitmask on current system
+ */
+static int
+max_number_of_cpus(void)
+{
+       int n;
+       int cpus = 2048;
+
+       for (;;) {
+               unsigned long buffer[longsperbits(cpus)];
+               memset(buffer, 0, sizeof(buffer));
+               /* the library version does not return size of cpumask_t */
+               n = syscall(SYS_sched_getaffinity, 0, bytesperbits(cpus),
+                                                               &buffer);
+               if (n < 0 && errno == EINVAL && cpus < 1024*1024) {
+                       cpus *= 2;
+                       continue;
+               }
+               return n*8;
+       }
+       fprintf (stderr, "cannot determine NR_CPUS; aborting");
+       exit(1);
+       return 0;
+}
+
 int main(int argc, char *argv[])
 {
-       cpu_set_t new_mask, cur_mask;
+       struct bitmask *new_mask, *cur_mask;
        pid_t pid = 0;
        int opt, err;
-       char mstr[1 + CPU_SETSIZE / 4];
-       char cstr[7 * CPU_SETSIZE];
+       char *mstr;
+       char *cstr;
        int c_opt = 0;
+        unsigned int cpus_configured;
+       int new_mask_byte_size, cur_mask_byte_size;
 
        struct option longopts[] = {
                { "pid",        0, NULL, 'p' },
@@ -270,8 +374,37 @@ int main(int argc, char *argv[])
                return 1;
        }
 
+       cpus_configured = max_number_of_cpus();
+
+       /*
+        * cur_mask is always used for the sched_getaffinity call
+        * On the sched_getaffinity the kernel demands a user mask of
+        * at least the size of its own cpumask_t.
+        */
+       cur_mask = bitmask_alloc(cpus_configured);
+       if (!cur_mask) {
+               fprintf (stderr, "bitmask_alloc failed\n");
+               exit(1);
+       }
+       cur_mask_byte_size = bitmask_nbytes(cur_mask);
+       mstr = malloc(1 + cur_mask->size / 4);
+       cstr = malloc(7 * cur_mask->size);
+
+       /*
+        * new_mask is always used for the sched_setaffinity call
+        * On the sched_setaffinity the kernel will zero-fill its
+        * cpumask_t if the user's mask is shorter.
+        */
+       new_mask = bitmask_alloc(cpus_configured);
+       if (!new_mask) {
+               fprintf (stderr, "bitmask_alloc failed\n");
+               exit(1);
+       }
+       new_mask_byte_size = bitmask_nbytes(new_mask);
+
        if (pid) {
-               if (sched_getaffinity(pid, sizeof (cur_mask), &cur_mask) < 0) {
+               if (sched_getaffinity(pid, cur_mask_byte_size,
+                                       (cpu_set_t *)cur_mask->maskp) < 0) {
                        perror("sched_getaffinity");
                        fprintf(stderr, "failed to get pid %d's affinity\n",
                                pid);
@@ -279,19 +412,19 @@ int main(int argc, char *argv[])
                }
                if (c_opt)
                        printf("pid %d's current affinity list: %s\n", pid,
-                               cpuset_to_cstr(&cur_mask, cstr));
+                               cpuset_to_cstr(cur_mask, cstr));
                else
                        printf("pid %d's current affinity mask: %s\n", pid,
-                               cpuset_to_str(&cur_mask, mstr));
+                               cpuset_to_str(cur_mask, mstr));
 
                if (argc - optind == 1)
                        return 0;
        }
 
        if (c_opt)
-               err = cstr_to_cpuset(&new_mask, argv[optind]);
+               err = cstr_to_cpuset(new_mask, argv[optind]);
        else
-               err = str_to_cpuset(&new_mask, argv[optind]);
+               err = str_to_cpuset(new_mask, argv[optind]);
 
        if (err) {
                if (c_opt)
@@ -303,13 +436,15 @@ int main(int argc, char *argv[])
                return 1;
        }
 
-       if (sched_setaffinity(pid, sizeof (new_mask), &new_mask)) {
+       if (sched_setaffinity(pid, new_mask_byte_size,
+                                       (cpu_set_t *) new_mask->maskp) < 0) {
                perror("sched_setaffinity");
                fprintf(stderr, "failed to set pid %d's affinity.\n", pid);
                return 1;
        }
 
-       if (sched_getaffinity(pid, sizeof (cur_mask), &cur_mask) < 0) {
+       if (sched_getaffinity(pid, cur_mask_byte_size,
+                                       (cpu_set_t *)cur_mask->maskp) < 0) {
                perror("sched_getaffinity");
                fprintf(stderr, "failed to get pid %d's affinity.\n", pid);
                return 1;
@@ -318,10 +453,10 @@ int main(int argc, char *argv[])
        if (pid) {
                if (c_opt)
                        printf("pid %d's new affinity list: %s\n", pid, 
-                              cpuset_to_cstr(&cur_mask, cstr));
+                              cpuset_to_cstr(cur_mask, cstr));
                else
                        printf("pid %d's new affinity mask: %s\n", pid, 
-                              cpuset_to_str(&cur_mask, mstr));
+                              cpuset_to_str(cur_mask, mstr));
        } else {
                argv += optind + 1;
                execvp(argv[0], argv);