Works for me. Thanks!
2019-11-01 14:48 GMT, Denys Vlasenko <[email protected]>: > Applied a modified version, please try current git. > > On Wed, Oct 30, 2019 at 6:55 PM Fryderyk Wrobel <[email protected]> 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 >> [email protected] >> http://lists.busybox.net/mailman/listinfo/busybox > _______________________________________________ busybox mailing list [email protected] http://lists.busybox.net/mailman/listinfo/busybox
