[PATCH] util-linux/taskset: add support for taking/printing CPU list (-c option)
Denys Vlasenko
vda.linux at googlemail.com
Fri Nov 1 14:48:09 UTC 2019
Applied a modified version, please try current git.
On Wed, Oct 30, 2019 at 6:55 PM Fryderyk Wrobel <frd1996 at 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 at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox
More information about the busybox
mailing list