Hi,

This patch implements '-c' option in taskset. With this option taskset
will
print or take the affinity as CPUs list, for example: "1,3,5-7".

Limitations:
  * Pattern specifiers after a range (e.g. "0-255:2/64") are not
supported.
  * Leading/trailing white-spaces are not allowed, e.g. list " 1,2 " will fail.

Kind Regards,
Fryderyk

taskset: make CPU list support an optional feature

function                                             old     new   delta
taskset_main                                         598    1035    +437
.rodata                                           158112  158151     +39
packed_usage                                       33417   33419      +2
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 478/0)             Total: 478 bytes

diff --git a/util-linux/taskset.c b/util-linux/taskset.c
index ed8878ad4..c12728802 100644
--- a/util-linux/taskset.c
+++ b/util-linux/taskset.c
@@ -20,16 +20,29 @@
 //config:      Needed for machines with more than 32-64 CPUs:
 //config:      affinity parameter 0xHHHHHHHHHHHHHHHHHHHH can be arbitrarily 
long
 //config:      in this case. Otherwise, it is limited to sizeof(long).
+//config:
+//config:config FEATURE_TASKSET_CPULIST
+//config:      bool "CPU list support (-c option)"
+//config:      default n
+//config:      depends on TASKSET
+//config:      help
+//config:      Add support for taking/printing affinity as CPU list when '-c'
+//config:      option is used. For example, it prints '0-3,7' instead of mask 
'8f'.
+//config:
+

 //applet:IF_TASKSET(APPLET_NOEXEC(taskset, taskset, BB_DIR_USR_BIN,
BB_SUID_DROP, taskset))

 //kbuild:lib-$(CONFIG_TASKSET) += taskset.o

 //usage:#define taskset_trivial_usage
-//usage:       "[-p] [HEXMASK] PID | PROG ARGS"
+//usage:       IF_FEATURE_TASKSET_CPULIST("[-c] ") "[-p] [HEXMASK"
IF_FEATURE_TASKSET_CPULIST("|LIST") "] PID | PROG ARGS"
 //usage:#define taskset_full_usage "\n\n"
 //usage:       "Set or get CPU affinity\n"
 //usage:     "\n       -p      Operate on an existing PID"
+//usage:       IF_FEATURE_TASKSET_CPULIST(
+//usage:     "\n       -c      Display and specify CPUs in list format"
+//usage:       )
 //usage:
 //usage:#define taskset_example_usage
 //usage:       "$ taskset 0x7 ./dgemm_test&\n"
@@ -45,7 +58,6 @@
  * Not yet implemented:
  * -a/--all-tasks (affect all threads)
  *     needs to get TIDs from /proc/PID/task/ and use _them_ as "pid" in
sched_setaffinity(pid)
- * -c/--cpu-list  (specify CPUs via "1,3,5-7")
  */

 #include <sched.h>
@@ -87,6 +99,117 @@ static unsigned long long from_mask(ul *mask,
unsigned sz_in_bytes UNUSED_PARAM)
 }
 #endif

+#if ENABLE_FEATURE_TASKSET_CPULIST
+
+/*
+ * Parse the CPU list and set the mask accordingly.
+ *
+ * The list element can be either a CPU index or a range of CPU indices.
+ * Example: "1,3,5-7".
+ *
+ * note1: pattern specifiers after a range (e.g. 0-255:2/64) are not supported
+ * note2: leading/trailing white-spaces are not allowed
+ */
+static int parse_cpulist(ul *mask, unsigned max, const char *s)
+{
+       enum parser_state {
+               RDY,     /* Ready */
+               LD0,     /* Loading first number */
+               LD1      /* Loading second number */
+       };
+       enum parser_state state = RDY;
+       unsigned v[2] = { 0, 0 };
+       unsigned i;
+       char c;
+
+       /* Nesting switch statements is not the prettiest thing in the
+        * world but this one produces a bit shorter code vs if/else
+        * when compiled with -O2/-O3 on x86_64 */
+       do {
+               c = *s++;
+
+               switch (c) {
+               case '0' ... '9':
+                       /* Append a digit to the current number */
+                       switch (state) {
+                       case RDY:
+                               v[0] = v[1] = 0;
+                               state = LD0;
+                               /* fall through */
+                       case LD0:
+                               v[0] = 10 * v[0] + c - '0';
+                               break;
+                       case LD1:
+                               v[1] = 10 * v[1] + c - '0';
+                               break;
+                       }
+                       break;
+
+               case '-':
+                       /* It may be a range */
+                       if (state != LD0)
+                               return -1;
+                       state = LD1;                  /* Move to the second 
number */
+                       break;
+
+               case ',':
+               case '\0':
+                       /* End of number/range */
+                       switch (state) {
+                       case LD0:                     /* Have a single number 
loaded */
+                               v[1] = v[0];          /* Make it a range: N-N */
+                               /* fall through */
+                       case LD1:                     /* Have two numbers 
loaded */
+                               if (v[1] < v[0])
+                                       return -1;    /* Bad range, e.g. 3-1 */
+
+                               for (i = v[0]; i <= v[1] && i < max; i++)
+                                       mask[i / BITS_UL] |= (1UL << (i & 
MASK_UL));
+
+                               state = RDY;          /* Try to load next 
number/range  */
+                               break;
+                       default:
+                               return -1;
+                       }
+                       break;
+
+               default:
+                       return -1;
+               }
+       } while (c != '\0');
+
+       return 0;
+}
+
+static void print_cpulist(const ul *mask, unsigned max)
+{
+       const char *delim = "";
+       unsigned i, j;
+
+#define MASK_ISSET(m, b)  (m[(b) / BITS_UL] & (1UL << ((b) & MASK_UL)))
+
+       for (i = 0; i < max; i++) {
+               if (MASK_ISSET(mask, i)) {
+                       for (j = i + 1; j < max && MASK_ISSET(mask, j); j++) {}
+                       j--;
+
+                       if (i == j) {
+                               printf("%s%u", delim, i);
+                       } else {
+                               printf("%s%u-%u", delim, i, j);
+                               i = j;
+                       }
+
+                       delim=",";
+               }
+       }
+
+       putchar('\n');
+#undef MASK_ISSET
+}
+
+#endif /* ENABLE_FEATURE_TASKSET_CPULIST */
+
 static unsigned long *get_aff(int pid, unsigned *sz)
 {
        int r;
@@ -114,20 +237,22 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
        ul *mask;
        unsigned mask_size_in_bytes;
        pid_t pid = 0;
-       unsigned opt_p;
+       unsigned opts;
        const char *current_new;
        char *aff;

+       enum { OPT_p = 1, OPT_c = 2 };
+
        /* NB: we mimic util-linux's taskset: -p does not take
         * an argument, i.e., "-pN" is NOT valid, only "-p N"!
         * Indeed, util-linux-2.13-pre7 uses:
         * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */

-       opt_p = getopt32(argv, "^+" "p" "\0" "-1" /* at least 1 arg */);
+       opts = getopt32(argv, "^+" "p" IF_FEATURE_TASKSET_CPULIST("c") "\0"
"-1" /* at least 1 arg */);
        argv += optind;

        aff = *argv++;
-       if (opt_p) {
+       if (opts & OPT_p) {
                char *pid_str = aff;
                if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */
                        pid_str = *argv; /* NB: *argv != NULL in this case */
@@ -144,9 +269,16 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
        current_new = "current";
  print_aff:
        mask = get_aff(pid, &mask_size_in_bytes);
-       if (opt_p) {
-               printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n",
-                               pid, current_new, from_mask(mask, 
mask_size_in_bytes));
+       if (opts & OPT_p) {
+#if ENABLE_FEATURE_TASKSET_CPULIST
+               if (opts & OPT_c) {
+                       printf("pid %d's %s affinity list: ", pid, current_new);
+                       print_cpulist(mask, mask_size_in_bytes * 8);
+               } else
+#endif
+                       printf("pid %d's %s affinity mask: " 
TASKSET_PRINTF_MASK"\n",
+                                       pid, current_new, from_mask(mask, 
mask_size_in_bytes));
+
                if (*argv == NULL) {
                        /* Either it was just "-p <pid>",
                         * or it was "-p <aff> <pid>" and we came here
@@ -158,55 +290,67 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
        }
        memset(mask, 0, mask_size_in_bytes);

-       /* Affinity was specified, translate it into mask */
-       /* it is always in hex, skip "0x" if it exists */
-       if (aff[0] == '0' && (aff[1]|0x20) == 'x')
-               aff += 2;
-
-       if (!ENABLE_FEATURE_TASKSET_FANCY) {
-               mask[0] = xstrtoul(aff, 16);
+#if ENABLE_FEATURE_TASKSET_CPULIST
+       if (opts & OPT_c) {
+               /* Cpulist */
+               if (parse_cpulist(mask, mask_size_in_bytes * 8, aff) < 0)
+                       bb_error_msg_and_die("bad affinity '%s'", aff);
        } else {
-               unsigned i;
-               char *last_char;
-
-               i = 0; /* bit pos in mask[] */
-
-               /* aff is ASCII hex string, accept very long masks in this form.
-                * Process hex string AABBCCDD... to ulong mask[]
-                * from the rightmost nibble, which is least-significant.
-                * Bits not fitting into mask[] are ignored: (example: 1234
-                * in 12340000000000000000000000000000000000000ff)
-                */
-               last_char = strchrnul(aff, '\0');
-               while (last_char > aff) {
-                       char c;
-                       ul val;
-
-                       last_char--;
-                       c = *last_char;
-                       if (isdigit(c))
-                               val = c - '0';
-                       else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
-                               val = (c|0x20) - ('a' - 10);
-                       else
-                               bb_error_msg_and_die("bad affinity '%s'", aff);
-
-                       if (i < mask_size_in_bytes * 8) {
-                               mask[i / BITS_UL] |= val << (i & MASK_UL);
-                               //bb_error_msg("bit %d set", i);
-                       }
-                       /* else:
-                        * We can error out here, but we don't.
-                        * For one, kernel itself ignores bits in mask[]
-                        * which do not map to any CPUs:
-                        * if mask[] has one 32-bit long element,
-                        * but you have only 8 CPUs, all bits beyond first 8
-                        * are ignored, silently.
-                        * No point in making bits past 31th to be errors.
+#endif
+               /* Bitmask */
+
+               /* Affinity was specified, translate it into mask */
+               /* it is always in hex, skip "0x" if it exists */
+               if (aff[0] == '0' && (aff[1]|0x20) == 'x')
+                       aff += 2;
+
+               if (!ENABLE_FEATURE_TASKSET_FANCY) {
+                       mask[0] = xstrtoul(aff, 16);
+               } else {
+                       unsigned i;
+                       char *last_char;
+
+                       i = 0; /* bit pos in mask[] */
+
+                       /* aff is ASCII hex string, accept very long masks in 
this form.
+                        * Process hex string AABBCCDD... to ulong mask[]
+                        * from the rightmost nibble, which is 
least-significant.
+                        * Bits not fitting into mask[] are ignored: (example: 
1234
+                        * in 12340000000000000000000000000000000000000ff)
                         */
-                       i += 4;
+                       last_char = strchrnul(aff, '\0');
+                       while (last_char > aff) {
+                               char c;
+                               ul val;
+
+                               last_char--;
+                               c = *last_char;
+                               if (isdigit(c))
+                                       val = c - '0';
+                               else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
+                                       val = (c|0x20) - ('a' - 10);
+                               else
+                                       bb_error_msg_and_die("bad affinity 
'%s'", aff);
+
+                               if (i < mask_size_in_bytes * 8) {
+                                       mask[i / BITS_UL] |= val << (i & 
MASK_UL);
+                                       //bb_error_msg("bit %d set", i);
+                               }
+                               /* else:
+                                * We can error out here, but we don't.
+                                * For one, kernel itself ignores bits in mask[]
+                                * which do not map to any CPUs:
+                                * if mask[] has one 32-bit long element,
+                                * but you have only 8 CPUs, all bits beyond 
first 8
+                                * are ignored, silently.
+                                * No point in making bits past 31th to be 
errors.
+                                */
+                               i += 4;
+                       }
                }
+#if ENABLE_FEATURE_TASKSET_CPULIST
        }
+#endif

        /* Set pid's or our own (pid==0) affinity */
        if (sched_setaffinity(pid, mask_size_in_bytes, (void*)mask))
_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to