[prev in list] [next in list] [prev in thread] [next in thread]
List: busybox
Subject: [PATCH] util-linux/taskset: add support for taking/printing CPU list (-c option)
From: Fryderyk Wrobel <frd1996 () gmail ! com>
Date: 2019-10-30 17:54:55
Message-ID: CAL25=yEgs9BSGemEoQgsZg36m5mMj9BsFE0pFUA5s--q3DOpHg () mail ! gmail ! com
[Download RAW message or body]
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
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic