150339cc59
The loop here was counting the bytes in the cpus array, but the lcores are represented by bits. While here, add a unit test that exposes this bug and demonstrates it is now fixed with this patch. Fixes #1570. Signed-off-by: Jim Harris <james.r.harris@intel.com> Change-Id: I3a1fc48a8085254f41587e3b3d5d732154b90134 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/3931 Community-CI: Mellanox Build Bot Reviewed-by: Paul Luse <paul.e.luse@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Ziye Yang <ziye.yang@intel.com> Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
337 lines
7.3 KiB
C
337 lines
7.3 KiB
C
/*-
|
|
* BSD LICENSE
|
|
*
|
|
* Copyright(c) Intel Corporation. All rights reserved.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "spdk/cpuset.h"
|
|
#include "spdk/log.h"
|
|
|
|
struct spdk_cpuset *
|
|
spdk_cpuset_alloc(void)
|
|
{
|
|
return (struct spdk_cpuset *)calloc(sizeof(struct spdk_cpuset), 1);
|
|
}
|
|
|
|
void
|
|
spdk_cpuset_free(struct spdk_cpuset *set)
|
|
{
|
|
free(set);
|
|
}
|
|
|
|
bool
|
|
spdk_cpuset_equal(const struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
|
|
{
|
|
assert(set1 != NULL);
|
|
assert(set2 != NULL);
|
|
return memcmp(set1->cpus, set2->cpus, sizeof(set2->cpus)) == 0;
|
|
}
|
|
|
|
void
|
|
spdk_cpuset_copy(struct spdk_cpuset *dst, const struct spdk_cpuset *src)
|
|
{
|
|
assert(dst != NULL);
|
|
assert(src != NULL);
|
|
memcpy(&dst->cpus, &src->cpus, sizeof(src->cpus));
|
|
}
|
|
|
|
void
|
|
spdk_cpuset_negate(struct spdk_cpuset *set)
|
|
{
|
|
unsigned int i;
|
|
assert(set != NULL);
|
|
for (i = 0; i < sizeof(set->cpus); i++) {
|
|
set->cpus[i] = ~set->cpus[i];
|
|
}
|
|
}
|
|
|
|
void
|
|
spdk_cpuset_and(struct spdk_cpuset *dst, const struct spdk_cpuset *src)
|
|
{
|
|
unsigned int i;
|
|
assert(dst != NULL);
|
|
assert(src != NULL);
|
|
for (i = 0; i < sizeof(src->cpus); i++) {
|
|
dst->cpus[i] &= src->cpus[i];
|
|
}
|
|
}
|
|
|
|
void
|
|
spdk_cpuset_or(struct spdk_cpuset *dst, const struct spdk_cpuset *src)
|
|
{
|
|
unsigned int i;
|
|
assert(dst != NULL);
|
|
assert(src != NULL);
|
|
for (i = 0; i < sizeof(src->cpus); i++) {
|
|
dst->cpus[i] |= src->cpus[i];
|
|
}
|
|
}
|
|
|
|
void
|
|
spdk_cpuset_xor(struct spdk_cpuset *dst, const struct spdk_cpuset *src)
|
|
{
|
|
unsigned int i;
|
|
assert(dst != NULL);
|
|
assert(src != NULL);
|
|
for (i = 0; i < sizeof(src->cpus); i++) {
|
|
dst->cpus[i] ^= src->cpus[i];
|
|
}
|
|
}
|
|
|
|
void
|
|
spdk_cpuset_zero(struct spdk_cpuset *set)
|
|
{
|
|
assert(set != NULL);
|
|
memset(set->cpus, 0, sizeof(set->cpus));
|
|
}
|
|
|
|
void
|
|
spdk_cpuset_set_cpu(struct spdk_cpuset *set, uint32_t cpu, bool state)
|
|
{
|
|
assert(set != NULL);
|
|
assert(cpu < sizeof(set->cpus) * 8);
|
|
if (state) {
|
|
set->cpus[cpu / 8] |= (1U << (cpu % 8));
|
|
} else {
|
|
set->cpus[cpu / 8] &= ~(1U << (cpu % 8));
|
|
}
|
|
}
|
|
|
|
bool
|
|
spdk_cpuset_get_cpu(const struct spdk_cpuset *set, uint32_t cpu)
|
|
{
|
|
assert(set != NULL);
|
|
assert(cpu < sizeof(set->cpus) * 8);
|
|
return (set->cpus[cpu / 8] >> (cpu % 8)) & 1U;
|
|
}
|
|
|
|
uint32_t
|
|
spdk_cpuset_count(const struct spdk_cpuset *set)
|
|
{
|
|
uint32_t count = 0;
|
|
uint8_t n;
|
|
unsigned int i;
|
|
for (i = 0; i < sizeof(set->cpus); i++) {
|
|
n = set->cpus[i];
|
|
while (n) {
|
|
n &= (n - 1);
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
const char *
|
|
spdk_cpuset_fmt(struct spdk_cpuset *set)
|
|
{
|
|
uint32_t lcore, lcore_max = 0;
|
|
int val, i, n;
|
|
char *ptr;
|
|
static const char *hex = "0123456789abcdef";
|
|
|
|
assert(set != NULL);
|
|
|
|
for (lcore = 0; lcore < sizeof(set->cpus) * 8; lcore++) {
|
|
if (spdk_cpuset_get_cpu(set, lcore)) {
|
|
lcore_max = lcore;
|
|
}
|
|
}
|
|
|
|
ptr = set->str;
|
|
n = lcore_max / 8;
|
|
val = set->cpus[n];
|
|
|
|
/* Store first number only if it is not leading zero */
|
|
if ((val & 0xf0) != 0) {
|
|
*(ptr++) = hex[(val & 0xf0) >> 4];
|
|
}
|
|
*(ptr++) = hex[val & 0x0f];
|
|
|
|
for (i = n - 1; i >= 0; i--) {
|
|
val = set->cpus[i];
|
|
*(ptr++) = hex[(val & 0xf0) >> 4];
|
|
*(ptr++) = hex[val & 0x0f];
|
|
}
|
|
*ptr = '\0';
|
|
|
|
return set->str;
|
|
}
|
|
|
|
static int
|
|
hex_value(uint8_t c)
|
|
{
|
|
#define V(x, y) [x] = y + 1
|
|
static const int8_t val[256] = {
|
|
V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4),
|
|
V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9),
|
|
V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF),
|
|
V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF),
|
|
};
|
|
#undef V
|
|
|
|
return val[c] - 1;
|
|
}
|
|
|
|
static int
|
|
parse_list(const char *mask, struct spdk_cpuset *set)
|
|
{
|
|
char *end;
|
|
const char *ptr = mask;
|
|
uint32_t lcore;
|
|
uint32_t lcore_min, lcore_max;
|
|
|
|
spdk_cpuset_zero(set);
|
|
lcore_min = UINT32_MAX;
|
|
|
|
ptr++;
|
|
end = (char *)ptr;
|
|
do {
|
|
while (isblank(*ptr)) {
|
|
ptr++;
|
|
}
|
|
if (*ptr == '\0' || *ptr == ']' || *ptr == '-' || *ptr == ',') {
|
|
goto invalid_character;
|
|
}
|
|
|
|
errno = 0;
|
|
lcore = strtoul(ptr, &end, 10);
|
|
if (errno) {
|
|
SPDK_ERRLOG("Conversion of core mask in '%s' failed\n", mask);
|
|
return -1;
|
|
}
|
|
|
|
if (lcore >= sizeof(set->cpus) * 8) {
|
|
SPDK_ERRLOG("Core number %" PRIu32 " is out of range in '%s'\n", lcore, mask);
|
|
return -1;
|
|
}
|
|
|
|
while (isblank(*end)) {
|
|
end++;
|
|
}
|
|
|
|
if (*end == '-') {
|
|
lcore_min = lcore;
|
|
} else if (*end == ',' || *end == ']') {
|
|
lcore_max = lcore;
|
|
if (lcore_min == UINT32_MAX) {
|
|
lcore_min = lcore;
|
|
}
|
|
if (lcore_min > lcore_max) {
|
|
SPDK_ERRLOG("Invalid range of CPUs (%" PRIu32 " > %" PRIu32 ")\n",
|
|
lcore_min, lcore_max);
|
|
return -1;
|
|
}
|
|
for (lcore = lcore_min; lcore <= lcore_max; lcore++) {
|
|
spdk_cpuset_set_cpu(set, lcore, true);
|
|
}
|
|
lcore_min = UINT32_MAX;
|
|
} else {
|
|
goto invalid_character;
|
|
}
|
|
|
|
ptr = end + 1;
|
|
|
|
} while (*end != ']');
|
|
|
|
return 0;
|
|
|
|
invalid_character:
|
|
if (*end == '\0') {
|
|
SPDK_ERRLOG("Unexpected end of core list '%s'\n", mask);
|
|
} else {
|
|
SPDK_ERRLOG("Parsing of core list '%s' failed on character '%c'\n", mask, *end);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
parse_mask(const char *mask, struct spdk_cpuset *set, size_t len)
|
|
{
|
|
int i, j;
|
|
char c;
|
|
int val;
|
|
uint32_t lcore = 0;
|
|
|
|
if (mask[0] == '0' && (mask[1] == 'x' || mask[1] == 'X')) {
|
|
mask += 2;
|
|
len -= 2;
|
|
}
|
|
|
|
spdk_cpuset_zero(set);
|
|
for (i = len - 1; i >= 0; i--) {
|
|
c = mask[i];
|
|
val = hex_value(c);
|
|
if (val < 0) {
|
|
/* Invalid character */
|
|
SPDK_ERRLOG("Invalid character in core mask '%s' (%c)\n", mask, c);
|
|
return -1;
|
|
}
|
|
for (j = 0; j < 4 && lcore < SPDK_CPUSET_SIZE; j++, lcore++) {
|
|
if ((1 << j) & val) {
|
|
spdk_cpuset_set_cpu(set, lcore, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
spdk_cpuset_parse(struct spdk_cpuset *set, const char *mask)
|
|
{
|
|
int ret;
|
|
size_t len;
|
|
|
|
if (mask == NULL || set == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
while (isblank(*mask)) {
|
|
mask++;
|
|
}
|
|
|
|
len = strlen(mask);
|
|
while (len > 0 && isblank(mask[len - 1])) {
|
|
len--;
|
|
}
|
|
|
|
if (len == 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (mask[0] == '[') {
|
|
ret = parse_list(mask, set);
|
|
} else {
|
|
ret = parse_mask(mask, set, len);
|
|
}
|
|
|
|
return ret;
|
|
}
|