taskset: use libc based cpu_set_t

The glibc already supports dynamically allocated CPU sets. We don't
have to maintains our private non-compatible implementation.

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2010-05-27 11:32:50 +02:00
parent 125b6a9191
commit ff5a6d2067
3 changed files with 162 additions and 158 deletions

View File

@ -1,23 +1,18 @@
#ifndef UTIL_LINUX_CPUSET_H #ifndef UTIL_LINUX_CPUSET_H
#define UTIL_LINUX_CPUSET_H #define UTIL_LINUX_CPUSET_H
struct bitmask { #include <sched.h>
unsigned int size;
unsigned long *maskp;
};
#define howmany(x,y) (((x)+((y)-1))/(y)) #define cpuset_nbits(setsize) (8 * (setsize))
#define bitsperlong (8 * sizeof(unsigned long))
#define longsperbits(n) howmany(n, bitsperlong)
#define bytesperbits(x) ((x+7)/8)
extern unsigned int bitmask_nbytes(struct bitmask *bmp); extern cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits);
extern struct bitmask *bitmask_alloc(unsigned int n); extern void cpuset_free(cpu_set_t *set);
extern char *cpuset_to_cstr(struct bitmask *mask, char *str); extern char *cpulist_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
extern char *cpuset_to_str(struct bitmask *mask, char *str); extern int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize);
extern int str_to_cpuset(struct bitmask *mask, const char* str);
extern int cstr_to_cpuset(struct bitmask *mask, const char* str); 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 */ #endif /* UTIL_LINUX_CPUSET_H */

View File

@ -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 <kzak@redhat.com>
*/
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@ -6,7 +18,6 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <sys/syscall.h>
#include "cpuset.h" #include "cpuset.h"
@ -20,73 +31,6 @@ static inline int val_to_char(int v)
return -1; 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) static inline int char_to_val(int c)
{ {
int cl; int cl;
@ -109,102 +53,148 @@ static const char *nexttoken(const char *q, int sep)
return q; 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; int i;
char *ptr = str; char *ptr = str;
int entry_made = 0; int entry_made = 0;
size_t max = cpuset_nbits(setsize);
for (i = 0; i < mask->size; i++) { for (i = 0; i < max; i++) {
if (bitmask_isbitset(mask, i)) { if (CPU_ISSET_S(i, setsize, set)) {
int j; int j, rlen;
int run = 0; int run = 0;
entry_made = 1; entry_made = 1;
for (j = i + 1; j < mask->size; j++) { for (j = i + 1; j < max; j++) {
if (bitmask_isbitset(mask, j)) if (CPU_ISSET_S(j, setsize, set))
run++; run++;
else else
break; break;
} }
if (!run) if (!run)
sprintf(ptr, "%d,", i); rlen = snprintf(ptr, len, "%d,", i);
else if (run == 1) { else if (run == 1) {
sprintf(ptr, "%d,%d,", i, i + 1); rlen = snprintf(ptr, len, "%d,%d,", i, i + 1);
i++; i++;
} else { } else {
sprintf(ptr, "%d-%d,", i, i + run); rlen = snprintf(ptr, len, "%d-%d,", i, i + run);
i += run; i += run;
} }
while (*ptr != 0) if (rlen < 0 || rlen + 1 > len)
ptr++; return NULL;
ptr += rlen;
len -= rlen;
} }
} }
ptr -= entry_made; ptr -= entry_made;
*ptr = 0; *ptr = '\0';
return str; 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 *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; char val = 0;
if (bitmask_isbitset(mask, base))
if (len == (ptr - str))
break;
if (CPU_ISSET_S(cpu, setsize, set))
val |= 1; val |= 1;
if (bitmask_isbitset(mask, base + 1)) if (CPU_ISSET_S(cpu + 1, setsize, set))
val |= 2; val |= 2;
if (bitmask_isbitset(mask, base + 2)) if (CPU_ISSET_S(cpu + 2, setsize, set))
val |= 4; val |= 4;
if (bitmask_isbitset(mask, base + 3)) if (CPU_ISSET_S(cpu + 3, setsize, set))
val |= 8; val |= 8;
if (!ret && val) if (!ret && val)
ret = ptr; ret = ptr;
*ptr++ = val_to_char(val); *ptr++ = val_to_char(val);
} }
*ptr = 0; *ptr = '\0';
return ret ? ret : ptr - 1; 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); int len = strlen(str);
const char *ptr = str + len - 1; const char *ptr = str + len - 1;
int base = 0; int cpu = 0;
/* skip 0x, it's all hex anyway */ /* skip 0x, it's all hex anyway */
if (len > 1 && !memcmp(str, "0x", 2L)) if (len > 1 && !memcmp(str, "0x", 2L))
str += 2; str += 2;
bitmask_clearall(mask); CPU_ZERO_S(setsize, set);
while (ptr >= str) { while (ptr >= str) {
char val = char_to_val(*ptr); char val = char_to_val(*ptr);
if (val == (char) -1) if (val == (char) -1)
return -1; return -1;
if (val & 1) if (val & 1)
bitmask_setbit(mask, base); CPU_SET_S(cpu, setsize, set);
if (val & 2) if (val & 2)
bitmask_setbit(mask, base + 1); CPU_SET_S(cpu + 1, setsize, set);
if (val & 4) if (val & 4)
bitmask_setbit(mask, base + 2); CPU_SET_S(cpu + 2, setsize, set);
if (val & 8) if (val & 8)
bitmask_setbit(mask, base + 3); CPU_SET_S(cpu + 3, setsize, set);
len--; len--;
ptr--; ptr--;
base += 4; cpu += 4;
} }
return 0; 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; const char *p, *q;
q = str; q = str;
bitmask_clearall(mask);
CPU_ZERO_S(setsize, set);
while (p = q, q = nexttoken(q, ','), p) { while (p = q, q = nexttoken(q, ','), p) {
unsigned int a; /* beginning of range */ unsigned int a; /* beginning of range */
@ -232,7 +222,7 @@ int cstr_to_cpuset(struct bitmask *mask, const char* str)
if (!(a <= b)) if (!(a <= b))
return 1; return 1;
while (a <= b) { while (a <= b) {
bitmask_setbit(mask, a); CPU_SET_S(a, setsize, set);
a += s; a += s;
} }
} }
@ -247,7 +237,8 @@ int cstr_to_cpuset(struct bitmask *mask, const char* str)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct bitmask *set; cpu_set_t *set;
size_t setsize, buflen, nbits;
char *buf, *mask = NULL, *range = NULL; char *buf, *mask = NULL, *range = NULL;
int ncpus = 2048, rc, c; int ncpus = 2048, rc, c;
@ -277,28 +268,34 @@ int main(int argc, char *argv[])
if (!mask && !range) if (!mask && !range)
goto usage_err; goto usage_err;
set = bitmask_alloc(ncpus); set = cpuset_alloc(ncpus, &setsize, &nbits);
if (!set) if (!set)
err(EXIT_FAILURE, "failed to allocate cpu 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) if (!buf)
err(EXIT_FAILURE, "failed to allocate cpu set buffer"); err(EXIT_FAILURE, "failed to allocate cpu set buffer");
if (mask) if (mask)
rc = str_to_cpuset(set, mask); rc = cpumask_parse(mask, set, setsize);
else else
rc = cstr_to_cpuset(set, range); rc = cpulist_parse(range, set, setsize);
if (rc) if (rc)
errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range); errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range);
printf("%-15s = %15s ", mask ? : range, cpuset_to_str(set, buf)); printf("%-15s = %15s ", mask ? : range,
printf("[%s]\n", cpuset_to_cstr(set, buf)); cpumask_create(buf, buflen, set, setsize));
printf("[%s]\n", cpulist_create(buf, buflen, set, setsize));
free(buf); free(buf);
free(set->maskp); cpuset_free(set);
free(set);
return EXIT_SUCCESS; return EXIT_SUCCESS;

View File

@ -72,19 +72,31 @@ max_number_of_cpus(void)
{ {
int n; int n;
int cpus = 2048; int cpus = 2048;
size_t setsize;
cpu_set_t *set = cpuset_alloc(cpus, &setsize, NULL);
if (!set)
goto err;
for (;;) { for (;;) {
unsigned long buffer[longsperbits(cpus)]; CPU_ZERO_S(setsize, set);
memset(buffer, 0, sizeof(buffer));
/* the library version does not return size of cpumask_t */ /* the library version does not return size of cpumask_t */
n = syscall(SYS_sched_getaffinity, 0, bytesperbits(cpus), n = syscall(SYS_sched_getaffinity, 0, setsize, set);
&buffer);
if (n < 0 && errno == EINVAL && cpus < 1024*1024) { if (n < 0 && errno == EINVAL && cpus < 1024*1024) {
cpuset_free(set);
cpus *= 2; cpus *= 2;
set = cpuset_alloc(cpus, &setsize, NULL);
if (!set)
goto err;
continue; continue;
} }
cpuset_free(set);
return n*8; return n*8;
} }
err:
fprintf (stderr, "cannot determine NR_CPUS; aborting"); fprintf (stderr, "cannot determine NR_CPUS; aborting");
exit(1); exit(1);
return 0; return 0;
@ -92,14 +104,13 @@ max_number_of_cpus(void)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct bitmask *new_mask, *cur_mask; cpu_set_t *new_set, *cur_set;
pid_t pid = 0; pid_t pid = 0;
int opt, err; int opt, err;
char *mstr; char *buf;
char *cstr;
int c_opt = 0; int c_opt = 0;
unsigned int cpus_configured; unsigned int ncpus;
int new_mask_byte_size, cur_mask_byte_size; size_t new_setsize, cur_setsize, cur_nbits, buflen;
struct option longopts[] = { struct option longopts[] = {
{ "pid", 0, NULL, 'p' }, { "pid", 0, NULL, 'p' },
@ -136,37 +147,34 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
cpus_configured = max_number_of_cpus(); ncpus = max_number_of_cpus();
/* /*
* cur_mask is always used for the sched_getaffinity call * cur_mask is always used for the sched_getaffinity call
* On the sched_getaffinity the kernel demands a user mask of * On the sched_getaffinity the kernel demands a user mask of
* at least the size of its own cpumask_t. * at least the size of its own cpumask_t.
*/ */
cur_mask = bitmask_alloc(cpus_configured); cur_set = cpuset_alloc(ncpus, &cur_setsize, &cur_nbits);
if (!cur_mask) { if (!cur_set) {
fprintf (stderr, "bitmask_alloc failed\n"); fprintf (stderr, "cpuset_alloc failed\n");
exit(1); exit(1);
} }
cur_mask_byte_size = bitmask_nbytes(cur_mask); buflen = 7 * cur_nbits;
mstr = malloc(1 + cur_mask->size / 4); buf = malloc(buflen);
cstr = malloc(7 * cur_mask->size);
/* /*
* new_mask is always used for the sched_setaffinity call * new_mask is always used for the sched_setaffinity call
* On the sched_setaffinity the kernel will zero-fill its * On the sched_setaffinity the kernel will zero-fill its
* cpumask_t if the user's mask is shorter. * cpumask_t if the user's mask is shorter.
*/ */
new_mask = bitmask_alloc(cpus_configured); new_set = cpuset_alloc(ncpus, &new_setsize, NULL);
if (!new_mask) { if (!new_set) {
fprintf (stderr, "bitmask_alloc failed\n"); fprintf (stderr, "cpuset_alloc failed\n");
exit(1); exit(1);
} }
new_mask_byte_size = bitmask_nbytes(new_mask);
if (pid) { if (pid) {
if (sched_getaffinity(pid, cur_mask_byte_size, if (sched_getaffinity(pid, cur_setsize, cur_set) < 0) {
(cpu_set_t *)cur_mask->maskp) < 0) {
perror("sched_getaffinity"); perror("sched_getaffinity");
fprintf(stderr, "failed to get pid %d's affinity\n", fprintf(stderr, "failed to get pid %d's affinity\n",
pid); pid);
@ -174,19 +182,19 @@ int main(int argc, char *argv[])
} }
if (c_opt) if (c_opt)
printf("pid %d's current affinity list: %s\n", pid, 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 else
printf("pid %d's current affinity mask: %s\n", pid, 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) if (argc - optind == 1)
return 0; return 0;
} }
if (c_opt) if (c_opt)
err = cstr_to_cpuset(new_mask, argv[optind]); err = cpulist_parse(argv[optind], new_set, new_setsize);
else else
err = str_to_cpuset(new_mask, argv[optind]); err = cpumask_parse(argv[optind], new_set, new_setsize);
if (err) { if (err) {
if (c_opt) if (c_opt)
@ -198,15 +206,13 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
if (sched_setaffinity(pid, new_mask_byte_size, if (sched_setaffinity(pid, new_setsize, new_set) < 0) {
(cpu_set_t *) new_mask->maskp) < 0) {
perror("sched_setaffinity"); perror("sched_setaffinity");
fprintf(stderr, "failed to set pid %d's affinity.\n", pid); fprintf(stderr, "failed to set pid %d's affinity.\n", pid);
return 1; return 1;
} }
if (sched_getaffinity(pid, cur_mask_byte_size, if (sched_getaffinity(pid, cur_setsize, cur_set) < 0) {
(cpu_set_t *)cur_mask->maskp) < 0) {
perror("sched_getaffinity"); perror("sched_getaffinity");
fprintf(stderr, "failed to get pid %d's affinity.\n", pid); fprintf(stderr, "failed to get pid %d's affinity.\n", pid);
return 1; return 1;
@ -214,12 +220,18 @@ int main(int argc, char *argv[])
if (pid) { if (pid) {
if (c_opt) if (c_opt)
printf("pid %d's new affinity list: %s\n", pid, printf("pid %d's new affinity list: %s\n", pid,
cpuset_to_cstr(cur_mask, cstr)); cpulist_create(buf, buflen, cur_set, cur_setsize));
else else
printf("pid %d's new affinity mask: %s\n", pid, printf("pid %d's new affinity mask: %s\n", pid,
cpuset_to_str(cur_mask, mstr)); cpumask_create(buf, buflen, cur_set, cur_setsize));
} else { }
free(buf);
cpuset_free(cur_set);
cpuset_free(new_set);
if (!pid) {
argv += optind + 1; argv += optind + 1;
execvp(argv[0], argv); execvp(argv[0], argv);
perror("execvp"); perror("execvp");