[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    Re: [PATCH] util-linux/taskset: add support for taking/printing CPU list (-c option)
From:       Fryderyk Wrobel <frd1996 () gmail ! com>
Date:       2019-11-02 17:23:36
Message-ID: CAL25=yFWb2zAmtYb3Pnibef+mzazohtPUT3sKHBf2VO-QK76Mg () mail ! gmail ! com
[Download RAW message or body]

Works for me. Thanks!

2019-11-01 14:48 GMT, Denys Vlasenko <vda.linux@googlemail.com>:
> Applied a modified version, please try current git.
>
> On Wed, Oct 30, 2019 at 6:55 PM Fryderyk Wrobel <frd1996@gmail.com> wrote:
>>
>> 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
>> busybox@busybox.net
>> http://lists.busybox.net/mailman/listinfo/busybox
>
_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic