[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