diff --git a/include/cpuset.h b/include/cpuset.h index 373fd9ba0..bc7961cad 100644 --- a/include/cpuset.h +++ b/include/cpuset.h @@ -1,23 +1,18 @@ #ifndef UTIL_LINUX_CPUSET_H #define UTIL_LINUX_CPUSET_H -struct bitmask { - unsigned int size; - unsigned long *maskp; -}; +#include -#define howmany(x,y) (((x)+((y)-1))/(y)) -#define bitsperlong (8 * sizeof(unsigned long)) -#define longsperbits(n) howmany(n, bitsperlong) -#define bytesperbits(x) ((x+7)/8) +#define cpuset_nbits(setsize) (8 * (setsize)) -extern unsigned int bitmask_nbytes(struct bitmask *bmp); -extern struct bitmask *bitmask_alloc(unsigned int n); +extern cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits); +extern void cpuset_free(cpu_set_t *set); -extern char *cpuset_to_cstr(struct bitmask *mask, char *str); -extern char *cpuset_to_str(struct bitmask *mask, char *str); -extern int str_to_cpuset(struct bitmask *mask, const char* str); -extern int cstr_to_cpuset(struct bitmask *mask, const char* str); +extern char *cpulist_create(char *str, size_t len, cpu_set_t *set, size_t setsize); +extern int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize); + +extern char *cpumask_create(char *str, size_t len, cpu_set_t *set, size_t setsize); +extern int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize); #endif /* UTIL_LINUX_CPUSET_H */ diff --git a/lib/cpuset.c b/lib/cpuset.c index 92ed73043..189c23ea3 100644 --- a/lib/cpuset.c +++ b/lib/cpuset.c @@ -1,3 +1,15 @@ +/* + * Terminology: + * + * cpuset - (libc) cpu_set_t data structure represents set of CPUs + * cpumask - string with hex mask (e.g. "0x00000001") + * cpulist - string with CPU ranges (e.g. "0-3,5,7,8") + * + * Based on code from taskset.c and Linux kernel. + * + * Copyright (C) 2010 Karel Zak + */ + #include #include #include @@ -6,7 +18,6 @@ #include #include #include -#include #include "cpuset.h" @@ -20,73 +31,6 @@ static inline int val_to_char(int v) return -1; } -/* - * The following bitmask declarations, bitmask_*() routines, and associated - * _setbit() and _getbit() routines are: - * Copyright (c) 2004 Silicon Graphics, Inc. (SGI) All rights reserved. - * SGI publishes it under the terms of the GNU General Public License, v2, - * as published by the Free Software Foundation. - */ - -static unsigned int _getbit(const struct bitmask *bmp, unsigned int n) -{ - if (n < bmp->size) - return (bmp->maskp[n/bitsperlong] >> (n % bitsperlong)) & 1; - else - return 0; -} - -static void _setbit(struct bitmask *bmp, unsigned int n, unsigned int v) -{ - if (n < bmp->size) { - if (v) - bmp->maskp[n/bitsperlong] |= 1UL << (n % bitsperlong); - else - bmp->maskp[n/bitsperlong] &= ~(1UL << (n % bitsperlong)); - } -} - -static int bitmask_isbitset(const struct bitmask *bmp, unsigned int i) -{ - return _getbit(bmp, i); -} - -struct bitmask *bitmask_clearall(struct bitmask *bmp) -{ - unsigned int i; - for (i = 0; i < bmp->size; i++) - _setbit(bmp, i, 0); - return bmp; -} - -struct bitmask *bitmask_setbit(struct bitmask *bmp, unsigned int i) -{ - _setbit(bmp, i, 1); - return bmp; -} - -unsigned int bitmask_nbytes(struct bitmask *bmp) -{ - return longsperbits(bmp->size) * sizeof(unsigned long); -} - - -struct bitmask *bitmask_alloc(unsigned int n) -{ - struct bitmask *bmp; - - bmp = malloc(sizeof(*bmp)); - if (!bmp) - return 0; - bmp->size = n; - bmp->maskp = calloc(longsperbits(n), sizeof(unsigned long)); - if (!bmp->maskp) { - free(bmp); - return 0; - } - return bmp; -} - static inline int char_to_val(int c) { int cl; @@ -109,102 +53,148 @@ static const char *nexttoken(const char *q, int sep) return q; } -char *cpuset_to_cstr(struct bitmask *mask, char *str) +/* + * Allocates a new set for ncpus and returns size in bytes and size in bits + */ +cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits) +{ + cpu_set_t *set = CPU_ALLOC(ncpus); + + if (!set) + return NULL; + if (setsize) + *setsize = CPU_ALLOC_SIZE(ncpus); + if (nbits) + *nbits = cpuset_nbits(CPU_ALLOC_SIZE(ncpus)); + return set; +} + +void cpuset_free(cpu_set_t *set) +{ + CPU_FREE(set); +} + +/* + * Returns human readable representation of the cpuset. The output format is + * a list of CPUs with ranges (for example, "0,1,3-9"). + */ +char *cpulist_create(char *str, size_t len, + cpu_set_t *set, size_t setsize) { int i; char *ptr = str; int entry_made = 0; + size_t max = cpuset_nbits(setsize); - for (i = 0; i < mask->size; i++) { - if (bitmask_isbitset(mask, i)) { - int j; + for (i = 0; i < max; i++) { + if (CPU_ISSET_S(i, setsize, set)) { + int j, rlen; int run = 0; entry_made = 1; - for (j = i + 1; j < mask->size; j++) { - if (bitmask_isbitset(mask, j)) + for (j = i + 1; j < max; j++) { + if (CPU_ISSET_S(j, setsize, set)) run++; else break; } if (!run) - sprintf(ptr, "%d,", i); + rlen = snprintf(ptr, len, "%d,", i); else if (run == 1) { - sprintf(ptr, "%d,%d,", i, i + 1); + rlen = snprintf(ptr, len, "%d,%d,", i, i + 1); i++; } else { - sprintf(ptr, "%d-%d,", i, i + run); + rlen = snprintf(ptr, len, "%d-%d,", i, i + run); i += run; } - while (*ptr != 0) - ptr++; + if (rlen < 0 || rlen + 1 > len) + return NULL; + ptr += rlen; + len -= rlen; } } ptr -= entry_made; - *ptr = 0; + *ptr = '\0'; return str; } -char *cpuset_to_str(struct bitmask *mask, char *str) +/* + * Returns string with CPU mask. + */ +char *cpumask_create(char *str, size_t len, + cpu_set_t *set, size_t setsize) { - int base; char *ptr = str; - char *ret = 0; + char *ret = NULL; + int cpu; - for (base = mask->size - 4; base >= 0; base -= 4) { + for (cpu = cpuset_nbits(setsize) - 4; cpu >= 0; cpu -= 4) { char val = 0; - if (bitmask_isbitset(mask, base)) + + if (len == (ptr - str)) + break; + + if (CPU_ISSET_S(cpu, setsize, set)) val |= 1; - if (bitmask_isbitset(mask, base + 1)) + if (CPU_ISSET_S(cpu + 1, setsize, set)) val |= 2; - if (bitmask_isbitset(mask, base + 2)) + if (CPU_ISSET_S(cpu + 2, setsize, set)) val |= 4; - if (bitmask_isbitset(mask, base + 3)) + if (CPU_ISSET_S(cpu + 3, setsize, set)) val |= 8; + if (!ret && val) ret = ptr; *ptr++ = val_to_char(val); } - *ptr = 0; + *ptr = '\0'; return ret ? ret : ptr - 1; } -int str_to_cpuset(struct bitmask *mask, const char* str) +/* + * Parses string with list of CPU ranges. + */ +int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize) { int len = strlen(str); const char *ptr = str + len - 1; - int base = 0; + int cpu = 0; /* skip 0x, it's all hex anyway */ if (len > 1 && !memcmp(str, "0x", 2L)) str += 2; - bitmask_clearall(mask); + CPU_ZERO_S(setsize, set); + while (ptr >= str) { char val = char_to_val(*ptr); if (val == (char) -1) return -1; if (val & 1) - bitmask_setbit(mask, base); + CPU_SET_S(cpu, setsize, set); if (val & 2) - bitmask_setbit(mask, base + 1); + CPU_SET_S(cpu + 1, setsize, set); if (val & 4) - bitmask_setbit(mask, base + 2); + CPU_SET_S(cpu + 2, setsize, set); if (val & 8) - bitmask_setbit(mask, base + 3); + CPU_SET_S(cpu + 3, setsize, set); len--; ptr--; - base += 4; + cpu += 4; } return 0; } -int cstr_to_cpuset(struct bitmask *mask, const char* str) +/* + * Parses string with CPUs mask. + */ +int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize) { const char *p, *q; q = str; - bitmask_clearall(mask); + + CPU_ZERO_S(setsize, set); while (p = q, q = nexttoken(q, ','), p) { unsigned int a; /* beginning of range */ @@ -232,7 +222,7 @@ int cstr_to_cpuset(struct bitmask *mask, const char* str) if (!(a <= b)) return 1; while (a <= b) { - bitmask_setbit(mask, a); + CPU_SET_S(a, setsize, set); a += s; } } @@ -247,7 +237,8 @@ int cstr_to_cpuset(struct bitmask *mask, const char* str) int main(int argc, char *argv[]) { - struct bitmask *set; + cpu_set_t *set; + size_t setsize, buflen, nbits; char *buf, *mask = NULL, *range = NULL; int ncpus = 2048, rc, c; @@ -277,28 +268,34 @@ int main(int argc, char *argv[]) if (!mask && !range) goto usage_err; - set = bitmask_alloc(ncpus); + set = cpuset_alloc(ncpus, &setsize, &nbits); if (!set) err(EXIT_FAILURE, "failed to allocate cpu set"); - buf = malloc(7 * ncpus); + /* + fprintf(stderr, "ncpus: %d, cpuset bits: %zd, cpuset bytes: %zd\n", + ncpus, nbits, setsize); + */ + + buflen = 7 * nbits; + buf = malloc(buflen); if (!buf) err(EXIT_FAILURE, "failed to allocate cpu set buffer"); if (mask) - rc = str_to_cpuset(set, mask); + rc = cpumask_parse(mask, set, setsize); else - rc = cstr_to_cpuset(set, range); + rc = cpulist_parse(range, set, setsize); if (rc) errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range); - printf("%-15s = %15s ", mask ? : range, cpuset_to_str(set, buf)); - printf("[%s]\n", cpuset_to_cstr(set, buf)); + printf("%-15s = %15s ", mask ? : range, + cpumask_create(buf, buflen, set, setsize)); + printf("[%s]\n", cpulist_create(buf, buflen, set, setsize)); free(buf); - free(set->maskp); - free(set); + cpuset_free(set); return EXIT_SUCCESS; diff --git a/schedutils/taskset.c b/schedutils/taskset.c index ed63a2363..154268eb1 100644 --- a/schedutils/taskset.c +++ b/schedutils/taskset.c @@ -72,19 +72,31 @@ max_number_of_cpus(void) { int n; int cpus = 2048; + size_t setsize; + cpu_set_t *set = cpuset_alloc(cpus, &setsize, NULL); + + if (!set) + goto err; for (;;) { - unsigned long buffer[longsperbits(cpus)]; - memset(buffer, 0, sizeof(buffer)); + CPU_ZERO_S(setsize, set); + /* the library version does not return size of cpumask_t */ - n = syscall(SYS_sched_getaffinity, 0, bytesperbits(cpus), - &buffer); + n = syscall(SYS_sched_getaffinity, 0, setsize, set); + if (n < 0 && errno == EINVAL && cpus < 1024*1024) { + cpuset_free(set); cpus *= 2; + set = cpuset_alloc(cpus, &setsize, NULL); + if (!set) + goto err; continue; } + cpuset_free(set); return n*8; } + +err: fprintf (stderr, "cannot determine NR_CPUS; aborting"); exit(1); return 0; @@ -92,14 +104,13 @@ max_number_of_cpus(void) int main(int argc, char *argv[]) { - struct bitmask *new_mask, *cur_mask; + cpu_set_t *new_set, *cur_set; pid_t pid = 0; int opt, err; - char *mstr; - char *cstr; + char *buf; int c_opt = 0; - unsigned int cpus_configured; - int new_mask_byte_size, cur_mask_byte_size; + unsigned int ncpus; + size_t new_setsize, cur_setsize, cur_nbits, buflen; struct option longopts[] = { { "pid", 0, NULL, 'p' }, @@ -136,37 +147,34 @@ int main(int argc, char *argv[]) return 1; } - cpus_configured = max_number_of_cpus(); + ncpus = max_number_of_cpus(); /* * cur_mask is always used for the sched_getaffinity call * On the sched_getaffinity the kernel demands a user mask of * at least the size of its own cpumask_t. */ - cur_mask = bitmask_alloc(cpus_configured); - if (!cur_mask) { - fprintf (stderr, "bitmask_alloc failed\n"); + cur_set = cpuset_alloc(ncpus, &cur_setsize, &cur_nbits); + if (!cur_set) { + fprintf (stderr, "cpuset_alloc failed\n"); exit(1); } - cur_mask_byte_size = bitmask_nbytes(cur_mask); - mstr = malloc(1 + cur_mask->size / 4); - cstr = malloc(7 * cur_mask->size); + buflen = 7 * cur_nbits; + buf = malloc(buflen); /* * new_mask is always used for the sched_setaffinity call * On the sched_setaffinity the kernel will zero-fill its * cpumask_t if the user's mask is shorter. */ - new_mask = bitmask_alloc(cpus_configured); - if (!new_mask) { - fprintf (stderr, "bitmask_alloc failed\n"); + new_set = cpuset_alloc(ncpus, &new_setsize, NULL); + if (!new_set) { + fprintf (stderr, "cpuset_alloc failed\n"); exit(1); } - new_mask_byte_size = bitmask_nbytes(new_mask); if (pid) { - if (sched_getaffinity(pid, cur_mask_byte_size, - (cpu_set_t *)cur_mask->maskp) < 0) { + if (sched_getaffinity(pid, cur_setsize, cur_set) < 0) { perror("sched_getaffinity"); fprintf(stderr, "failed to get pid %d's affinity\n", pid); @@ -174,19 +182,19 @@ int main(int argc, char *argv[]) } if (c_opt) printf("pid %d's current affinity list: %s\n", pid, - cpuset_to_cstr(cur_mask, cstr)); + cpulist_create(buf, buflen, cur_set, cur_setsize)); else printf("pid %d's current affinity mask: %s\n", pid, - cpuset_to_str(cur_mask, mstr)); + cpumask_create(buf, buflen, cur_set, cur_setsize)); if (argc - optind == 1) return 0; } if (c_opt) - err = cstr_to_cpuset(new_mask, argv[optind]); + err = cpulist_parse(argv[optind], new_set, new_setsize); else - err = str_to_cpuset(new_mask, argv[optind]); + err = cpumask_parse(argv[optind], new_set, new_setsize); if (err) { if (c_opt) @@ -198,15 +206,13 @@ int main(int argc, char *argv[]) return 1; } - if (sched_setaffinity(pid, new_mask_byte_size, - (cpu_set_t *) new_mask->maskp) < 0) { + if (sched_setaffinity(pid, new_setsize, new_set) < 0) { perror("sched_setaffinity"); fprintf(stderr, "failed to set pid %d's affinity.\n", pid); return 1; } - if (sched_getaffinity(pid, cur_mask_byte_size, - (cpu_set_t *)cur_mask->maskp) < 0) { + if (sched_getaffinity(pid, cur_setsize, cur_set) < 0) { perror("sched_getaffinity"); fprintf(stderr, "failed to get pid %d's affinity.\n", pid); return 1; @@ -214,12 +220,18 @@ int main(int argc, char *argv[]) if (pid) { if (c_opt) - printf("pid %d's new affinity list: %s\n", pid, - cpuset_to_cstr(cur_mask, cstr)); + printf("pid %d's new affinity list: %s\n", pid, + cpulist_create(buf, buflen, cur_set, cur_setsize)); else - printf("pid %d's new affinity mask: %s\n", pid, - cpuset_to_str(cur_mask, mstr)); - } else { + printf("pid %d's new affinity mask: %s\n", pid, + cpumask_create(buf, buflen, cur_set, cur_setsize)); + } + + free(buf); + cpuset_free(cur_set); + cpuset_free(new_set); + + if (!pid) { argv += optind + 1; execvp(argv[0], argv); perror("execvp");