| @@ -6,6 +6,25 @@ |
| * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
| */ |
| |
| + | //config:config TASKSET |
| + | //config: bool "taskset" |
| + | //config: default n # doesn't build on some non-x86 targets (m68k) |
| + | //config: help |
| + | //config: Retrieve or set a processes's CPU affinity. |
| + | //config: This requires sched_{g,s}etaffinity support in your libc. |
| + | //config: |
| + | //config:config FEATURE_TASKSET_FANCY |
| + | //config: bool "Fancy output" |
| + | //config: default y |
| + | //config: depends on TASKSET |
| + | //config: help |
| + | //config: Add code for fancy output. This merely silences a compiler-warning |
| + | //config: and adds about 135 Bytes. May be needed for machines with alot |
| + | //config: of CPUs. |
| + | |
| + | //applet:IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP)) |
| + | //kbuild:lib-$(CONFIG_TASKSET) += taskset.o |
| + | |
| //usage:#define taskset_trivial_usage |
| //usage: "[-p] [MASK] [PID | PROG ARGS]" |
| //usage:#define taskset_full_usage "\n\n" |
| @@ -22,6 +41,11 @@ |
| //usage: "pid 6671's new affinity mask: 1\n" |
| //usage: "$ taskset -p 1\n" |
| //usage: "pid 1's current affinity mask: 3\n" |
| + | /* |
| + | Not yet implemented: |
| + | * -a/--all-tasks (affect all threads) |
| + | * -c/--cpu-list (specify CPUs via "1,3,5-7") |
| + | */ |
| |
| #include <sched.h> |
| #include "libbb.h" |
| @@ -128,17 +152,65 @@ int taskset_main(int argc UNUSED_PARAM, char **argv) |
| current_new += 8; /* "new" */ |
| } |
| |
| - | { /* Affinity was specified, translate it into cpu_set_t */ |
| + | /* Affinity was specified, translate it into cpu_set_t */ |
| + | CPU_ZERO(&mask); |
| + | if (!ENABLE_FEATURE_TASKSET_FANCY) { |
| unsigned i; |
| + | unsigned long long m; |
| + | |
| /* Do not allow zero mask: */ |
| - | unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX); |
| - | enum { CNT_BIT = CPU_SETSIZE < sizeof(m)*8 ? CPU_SETSIZE : sizeof(m)*8 }; |
| + | m = xstrtoull_range(aff, 0, 1, ULLONG_MAX); |
| + | i = 0; |
| + | do { |
| + | if (m & 1) |
| + | CPU_SET(i, &mask); |
| + | i++; |
| + | m >>= 1; |
| + | } while (m != 0); |
| + | } else { |
| + | unsigned i; |
| + | char *last_byte; |
| + | char *bin; |
| + | uint8_t bit_in_byte; |
| + | |
| + | /* Cheap way to get "long enough" buffer */ |
| + | bin = xstrdup(aff); |
| + | |
| + | if (aff[0] != '0' && (aff[1]|0x20) != 'x') { |
| + | /* TODO: decimal/octal masks are still limited to 2^64 */ |
| + | unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX); |
| + | bin += strlen(bin); |
| + | last_byte = bin - 1; |
| + | while (m) { |
| + | *--bin = m & 0xff; |
| + | m >>= 8; |
| + | } |
| + | } else { |
| + | /* aff is "0x.....", we accept very long masks in this form */ |
| + | last_byte = hex2bin(bin, aff + 2, INT_MAX); |
| + | if (!last_byte) { |
| + | bad_aff: |
| + | bb_error_msg_and_die("bad affinity '%s'", aff); |
| + | } |
| + | last_byte--; /* now points to the last byte */ |
| + | } |
| |
| - | CPU_ZERO(&mask); |
| - | for (i = 0; i < CNT_BIT; i++) { |
| - | unsigned long long bit = (1ULL << i); |
| - | if (bit & m) |
| + | i = 0; |
| + | bit_in_byte = 1; |
| + | while (last_byte >= bin) { |
| + | if (bit_in_byte & *last_byte) { |
| + | if (i >= CPU_SETSIZE) |
| + | goto bad_aff; |
| CPU_SET(i, &mask); |
| + | //bb_error_msg("bit %d set", i); |
| + | } |
| + | i++; |
| + | /* bit_in_byte is uint8_t! & 0xff is implied */ |
| + | bit_in_byte = (bit_in_byte << 1); |
| + | if (!bit_in_byte) { |
| + | bit_in_byte = 1; |
| + | last_byte--; |
| + | } |
| } |
| } |
| |