util: extend cpumask to hold more than 64 cpus

Fixes github issue #218.

This patch introduces spdk_cpuset object to store and manipulate
the set of individual CPUs. The main objective of this object is
to replace cpumask declared as uint64_t and extend the limitation
of supported CPUs (lcores) above 64 CPUs.

spdk_cpuset is always allocated dynamically and accessed by opaque
pointer, what makes it easier to extend in the future without
breaking API/ABI.

This patch also extends parsing function allowing to set cpumask
using a list of cpus e.g. "[0-4,10,12]" sets mask of 0,1,2,3,4,10,12
as well as hexadecimal string with and without "0x" prefix.

Change-Id: I475c3ba7fab629021a22e03176e57e400dd24a49
Signed-off-by: Tomasz Kulasek <tomaszx.kulasek@intel.com>
Reviewed-on: https://review.gerrithub.io/390794
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Tomasz Kulasek 2017-12-21 17:48:31 +01:00 committed by Jim Harris
parent 0ff878d02e
commit 601bcbcf66
24 changed files with 979 additions and 110 deletions

155
include/spdk/cpuset.h Normal file
View File

@ -0,0 +1,155 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* 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.
*/
/**
* \file
* CPU set management functions
*/
#ifndef SPDK_CPUSET_H
#define SPDK_CPUSET_H
#include "spdk/stdinc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SPDK_CPUSET_SIZE 1024
/**
* List of CPUs.
*/
struct spdk_cpuset;
/**
* Allocate CPU set object.
*
* \return Allocated zeroed cpuset or NULL if fails.
*/
struct spdk_cpuset *spdk_cpuset_alloc(void);
/**
* Free allocated CPU set.
*
* \param set CPU set to be freed.
*/
void spdk_cpuset_free(struct spdk_cpuset *set);
/**
* Compare two CPU sets.
*
* \return True if both CPU sets are equal.
*/
bool spdk_cpuset_equal(const struct spdk_cpuset *set1, const struct spdk_cpuset *set2);
/**
* Copy the content of CPU set to another.
*
* \param dst Destination CPU set
* \param src Source CPU set
*/
void spdk_cpuset_copy(struct spdk_cpuset *dst, const struct spdk_cpuset *src);
/**
* Perform AND operation on two CPU sets. The result is stored in dst.
*
* \param dst First argument of operation. This value also stores the result of operation.
* \param src Second argument of operation.
*/
void spdk_cpuset_and(struct spdk_cpuset *dst, const struct spdk_cpuset *src);
/**
* Perform OR operation on two CPU sets. The result is stored in dst.
*
* \param dst First argument of operation. This value also stores the result of operation.
* \param src Second argument of operation.
*/
void spdk_cpuset_or(struct spdk_cpuset *dst, const struct spdk_cpuset *src);
/**
* Clear all CPUs in CPU set.
*
* \param set CPU set to be cleared.
*/
void spdk_cpuset_zero(struct spdk_cpuset *set);
/**
* Set or clear CPU state in CPU set.
*
* \param set CPU set object.
* \param cpu CPU index to be set or cleared.
* \param state *true* to set cpu, *false* to clear.
*/
void spdk_cpuset_set_cpu(struct spdk_cpuset *set, uint32_t cpu, bool state);
/**
* Get the state of CPU in CPU set.
*
* \param set CPU set object.
* \param cpu CPU index.
* \return State of selected CPU.
*/
bool spdk_cpuset_get_cpu(const struct spdk_cpuset *set, uint32_t cpu);
/**
* Get the number of CPUs that are set in CPU set.
*
* \param set CPU set object.
* \return Number of CPUs.
*/
uint32_t spdk_cpuset_count(const struct spdk_cpuset *set);
/**
* Convert a CPU set to hex string.
*
* \param CPU set.
* \return Pointer to hexadecimal representation of CPU set. Buffer to store a
* string is dynamically allocated internally and freed with CPU set object.
*/
char *spdk_cpuset_fmt(struct spdk_cpuset *set);
/**
* Convert a string containing a CPU core mask into a CPU set.
*
* \param set
* \param mask String defining CPU set. By default hexadecimal value is used or
* as CPU list enclosed in square brackets defined as: 'c1[-c2][,c3[-c4],...]'
* \return Zero if success, non zero if fails.
*/
int spdk_cpuset_parse(struct spdk_cpuset *set, const char *mask);
#ifdef __cplusplus
}
#endif
#endif /* SPDK_CPUSET_H */

View File

@ -43,6 +43,7 @@
#include "spdk/stdinc.h"
#include "spdk/cpuset.h"
#include "spdk/queue.h"
#include "spdk/log.h"
@ -140,12 +141,12 @@ int spdk_app_get_shm_id(void);
/**
* \brief Convert a string containing a CPU core mask into a bitmask
*/
int spdk_app_parse_core_mask(const char *mask, uint64_t *cpumask);
int spdk_app_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask);
/**
* \brief Return a mask of the CPU cores active for this application
*/
uint64_t spdk_app_get_core_mask(void);
struct spdk_cpuset *spdk_app_get_core_mask(void);
/**
* \brief Return the number of CPU cores utilized by this application

View File

@ -100,15 +100,15 @@ typedef int (*spdk_vhost_event_fn)(struct spdk_vhost_dev *vdev, void *arg);
const char *spdk_vhost_dev_get_name(struct spdk_vhost_dev *vdev);
/**
* Get cpumask of the vhost device. The mask is constant
* Get cpuset of the vhost device. The cpuset is constant
* throughout the lifetime of a vdev. It is be a subset
* of SPDK app cpumask vhost was started with.
* of SPDK app cpuset vhost was started with.
*
* \param dev vhost device
* \return cpumask of the vdev. The mask is constructed as:
* ((1 << cpu0) | (1 << cpu1) | ... | (1 << cpuN)).
* \param cpuset pointer to the cpuset of the vdev.
*/
uint64_t spdk_vhost_dev_get_cpumask(struct spdk_vhost_dev *vdev);
void spdk_vhost_dev_get_cpumask(struct spdk_vhost_dev *vdev,
struct spdk_cpuset *cpuset);
/**
* By default, events are generated when asked, but for high queue depth and

View File

@ -190,7 +190,19 @@ spdk_build_eal_cmdline(const struct spdk_env_opts *opts)
}
/* set the coremask */
args = spdk_push_arg(args, &argcount, _sprintf_alloc("-c %s", opts->core_mask));
/* NOTE: If coremask starts with '[' and ends with ']' it is a core list
*/
if (opts->core_mask[0] == '[') {
char *l_arg = _sprintf_alloc("-l %s", opts->core_mask + 1);
int len = strlen(l_arg);
if (l_arg[len - 1] == ']') {
l_arg[len - 1] = '\0';
}
args = spdk_push_arg(args, &argcount, l_arg);
} else {
args = spdk_push_arg(args, &argcount, _sprintf_alloc("-c %s", opts->core_mask));
}
if (args == NULL) {
return -1;
}

View File

@ -91,7 +91,7 @@ spdk_app_get_shm_id(void)
" # specifying a ReactorMask. Default is to allow work items to run\n" \
" # on all cores. Core 0 must be set in the mask if one is specified.\n" \
" # Default: 0xFFFF (cores 0-15)\n" \
" ReactorMask \"0x%" PRIX64 "\"\n" \
" ReactorMask \"0x%s\"\n" \
"\n" \
" # Tracepoint group mask for spdk trace buffers\n" \
" # Default: 0x0 (all tracepoint groups disabled)\n" \
@ -102,12 +102,16 @@ spdk_app_get_shm_id(void)
static void
spdk_app_config_dump_global_section(FILE *fp)
{
struct spdk_cpuset *coremask;
if (NULL == fp) {
return;
}
fprintf(fp, GLOBAL_CONFIG_TMPL,
spdk_app_get_core_mask(), spdk_trace_get_tpoint_group_mask());
coremask = spdk_app_get_core_mask();
fprintf(fp, GLOBAL_CONFIG_TMPL, spdk_cpuset_fmt(coremask),
spdk_trace_get_tpoint_group_mask());
}
int

View File

@ -124,6 +124,8 @@ static void spdk_reactor_construct(struct spdk_reactor *w, uint32_t lcore,
static struct spdk_mempool *g_spdk_event_mempool[SPDK_MAX_SOCKET];
static struct spdk_cpuset *g_spdk_app_core_mask;
static struct spdk_reactor *
spdk_reactor_get(uint32_t lcore)
{
@ -559,46 +561,26 @@ spdk_app_get_current_core(void)
}
int
spdk_app_parse_core_mask(const char *mask, uint64_t *cpumask)
spdk_app_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
{
uint32_t i;
char *end;
uint64_t validmask;
int ret;
struct spdk_cpuset *validmask;
if (mask == NULL || cpumask == NULL) {
return -1;
ret = spdk_cpuset_parse(cpumask, mask);
if (ret < 0) {
return ret;
}
errno = 0;
*cpumask = strtoull(mask, &end, 16);
if (*end != '\0' || errno) {
return -1;
}
validmask = 0;
SPDK_ENV_FOREACH_CORE(i) {
if (i >= 64) {
break;
}
validmask |= 1ULL << i;
}
*cpumask &= validmask;
validmask = spdk_app_get_core_mask();
spdk_cpuset_and(cpumask, validmask);
return 0;
}
uint64_t
struct spdk_cpuset *
spdk_app_get_core_mask(void)
{
uint32_t i;
uint64_t mask = 0;
SPDK_ENV_FOREACH_CORE(i) {
mask |= 1ULL << i;
}
return mask;
return g_spdk_app_core_mask;
}
@ -625,6 +607,7 @@ spdk_reactors_start(void)
int rc;
g_reactor_state = SPDK_REACTOR_STATE_RUNNING;
g_spdk_app_core_mask = spdk_cpuset_alloc();
current_core = spdk_env_get_current_core();
SPDK_ENV_FOREACH_CORE(i) {
@ -637,6 +620,7 @@ spdk_reactors_start(void)
return;
}
}
spdk_cpuset_set_cpu(g_spdk_app_core_mask, i, true);
}
/* Start the master reactor */
@ -646,6 +630,8 @@ spdk_reactors_start(void)
spdk_env_thread_wait_all();
g_reactor_state = SPDK_REACTOR_STATE_SHUTDOWN;
spdk_cpuset_free(g_spdk_app_core_mask);
g_spdk_app_core_mask = NULL;
}
void

View File

@ -73,7 +73,7 @@ static pthread_mutex_t g_conns_mutex;
static struct spdk_poller *g_shutdown_timer = NULL;
static uint32_t spdk_iscsi_conn_allocate_reactor(uint64_t cpumask);
static uint32_t spdk_iscsi_conn_allocate_reactor(const struct spdk_cpuset *cpumask);
void spdk_iscsi_conn_login_do_work(void *arg);
void spdk_iscsi_conn_full_feature_do_work(void *arg);
@ -1421,7 +1421,7 @@ spdk_iscsi_conn_get_min_per_core(void)
}
static uint32_t
spdk_iscsi_conn_allocate_reactor(uint64_t cpumask)
spdk_iscsi_conn_allocate_reactor(const struct spdk_cpuset *cpumask)
{
uint32_t i, selected_core;
int32_t num_pollers, min_pollers;
@ -1429,9 +1429,8 @@ spdk_iscsi_conn_allocate_reactor(uint64_t cpumask)
min_pollers = INT_MAX;
selected_core = spdk_env_get_first_core();
/* we use u64 as CPU core mask */
SPDK_ENV_FOREACH_CORE(i) {
if (!((1ULL << i) & cpumask)) {
if (!spdk_cpuset_get_cpu(cpumask, i)) {
continue;
}

View File

@ -39,6 +39,7 @@
#include "iscsi/iscsi.h"
#include "spdk/queue.h"
#include "spdk/cpuset.h"
/*
* MAX_CONNECTION_PARAMS: The numbers of the params in conn_param_table
@ -80,7 +81,7 @@ struct spdk_iscsi_conn {
int pg_tag;
char *portal_host;
char *portal_port;
uint64_t portal_cpumask;
struct spdk_cpuset *portal_cpumask;
uint32_t lcore;
int sock;
struct spdk_iscsi_sess *sess;

View File

@ -733,7 +733,7 @@ spdk_rpc_get_portal_groups(struct spdk_jsonrpc_request *request,
spdk_json_write_name(w, "port");
spdk_json_write_string(w, portal->port);
spdk_json_write_name(w, "cpumask");
spdk_json_write_string_fmt(w, "%#" PRIx64, portal->cpumask);
spdk_json_write_string_fmt(w, "0x%s", spdk_cpuset_fmt(portal->cpumask));
spdk_json_write_object_end(w);
}
spdk_json_write_array_end(w);
@ -838,7 +838,6 @@ spdk_rpc_add_portal_group(struct spdk_jsonrpc_request *request,
goto out;
}
for (i = 0; i < req.portal_list.num_portals; i++) {
portal_list[i] = spdk_iscsi_portal_create(req.portal_list.portals[i].host,
req.portal_list.portals[i].port,

View File

@ -135,7 +135,7 @@ static const char *portal_group_section = \
" Comment \"Portal%d\"\n"
#define PORTAL_TMPL \
" Portal DA1 %s:%s@0x%" PRIx64 "\n"
" Portal DA1 %s:%s@0x%s\n"
static void
spdk_iscsi_config_dump_portal_groups(FILE *fp)
@ -153,7 +153,8 @@ spdk_iscsi_config_dump_portal_groups(FILE *fp)
/* Dump portals */
TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
if (NULL == p) { continue; }
fprintf(fp, PORTAL_TMPL, p->host, p->port, p->cpumask);
fprintf(fp, PORTAL_TMPL, p->host, p->port,
spdk_cpuset_fmt(p->cpumask));
}
}
}

View File

@ -70,7 +70,7 @@ struct spdk_iscsi_portal *
spdk_iscsi_portal_create(const char *host, const char *port, const char *cpumask)
{
struct spdk_iscsi_portal *p = NULL;
uint64_t core_mask;
struct spdk_cpuset *core_mask = NULL;
int rc;
assert(host != NULL);
@ -113,23 +113,29 @@ spdk_iscsi_portal_create(const char *host, const char *port, const char *cpumask
goto error_out;
}
core_mask = spdk_app_get_core_mask();
core_mask = spdk_cpuset_alloc();
if (!core_mask) {
SPDK_ERRLOG("spdk_cpuset_alloc() failed for host\n");
goto error_out;
}
if (cpumask != NULL) {
rc = spdk_app_parse_core_mask(cpumask, &p->cpumask);
rc = spdk_app_parse_core_mask(cpumask, core_mask);
if (rc < 0) {
SPDK_ERRLOG("cpumask (%s) is invalid\n", cpumask);
goto error_out;
}
if (p->cpumask == 0) {
SPDK_ERRLOG("cpumask (%s) does not contain core mask (0x%" PRIx64 ")\n",
cpumask, core_mask);
if (spdk_cpuset_count(core_mask) == 0) {
SPDK_ERRLOG("cpumask (%s) does not contain core mask (0x%s)\n",
cpumask, spdk_cpuset_fmt(spdk_app_get_core_mask()));
goto error_out;
}
} else {
p->cpumask = core_mask;
spdk_cpuset_copy(core_mask, spdk_app_get_core_mask());
}
p->cpumask = core_mask;
p->sock = -1;
p->group = NULL; /* set at a later time by caller */
p->acceptor_poller = NULL;
@ -139,6 +145,7 @@ spdk_iscsi_portal_create(const char *host, const char *port, const char *cpumask
return p;
error_out:
spdk_cpuset_free(core_mask);
free(p->port);
free(p->host);
free(p);
@ -155,6 +162,7 @@ spdk_iscsi_portal_destroy(struct spdk_iscsi_portal *p)
TAILQ_REMOVE(&g_spdk_iscsi.portal_head, p, g_tailq);
free(p->host);
free(p->port);
spdk_cpuset_free(p->cpumask);
free(p);
}

View File

@ -36,13 +36,14 @@
#define SPDK_PORTAL_GRP_H
#include "spdk/conf.h"
#include "spdk/cpuset.h"
struct spdk_iscsi_portal {
struct spdk_iscsi_portal_grp *group;
char *host;
char *port;
int sock;
uint64_t cpumask;
struct spdk_cpuset *cpumask;
struct spdk_poller *acceptor_poller;
TAILQ_ENTRY(spdk_iscsi_portal) per_pg_tailq;
TAILQ_ENTRY(spdk_iscsi_portal) g_tailq;

View File

@ -34,7 +34,7 @@
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
C_SRCS = bit_array.c crc16.c crc32.c crc32c.c crc32_ieee.c fd.c io_channel.c strerror_tls.c string.c
C_SRCS = bit_array.c cpuset.c crc16.c crc32.c crc32c.c crc32_ieee.c fd.c io_channel.c strerror_tls.c string.c
LIBNAME = util
include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk

320
lib/util/cpuset.c Normal file
View File

@ -0,0 +1,320 @@
/*-
* 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 {
char str[SPDK_CPUSET_SIZE / 4];
uint8_t cpus[SPDK_CPUSET_SIZE / 8];
};
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 *set1, const struct spdk_cpuset *set2)
{
assert(set1 != NULL);
assert(set2 != NULL);
memcpy(&set1->cpus, &set2->cpus, sizeof(set2->cpus));
}
void
spdk_cpuset_and(struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
{
unsigned int i;
assert(set1 != NULL);
assert(set2 != NULL);
for (i = 0; i < sizeof(set2->cpus); i++) {
set1->cpus[i] &= set2->cpus[i];
}
}
void
spdk_cpuset_or(struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
{
unsigned int i;
assert(set1 != NULL);
assert(set2 != NULL);
for (i = 0; i < sizeof(set2->cpus); i++) {
set1->cpus[i] |= set2->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;
}
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 < sizeof(set->cpus); 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;
}

View File

@ -495,7 +495,7 @@ spdk_vhost_dev_find(const char *ctrlr_name)
}
static int
spdk_vhost_parse_core_mask(const char *mask, uint64_t *cpumask)
spdk_vhost_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
{
int rc;
@ -504,21 +504,19 @@ spdk_vhost_parse_core_mask(const char *mask, uint64_t *cpumask)
}
if (mask == NULL) {
*cpumask = spdk_app_get_core_mask();
spdk_cpuset_copy(cpumask, spdk_app_get_core_mask());
return 0;
}
*cpumask = 0;
rc = spdk_app_parse_core_mask(mask, cpumask);
if (rc != 0) {
if (rc < 0) {
SPDK_ERRLOG("invalid cpumask %s\n", mask);
return -1;
}
if (*cpumask == 0) {
SPDK_ERRLOG("no cpu is selected among reactor mask(=%jx)\n",
spdk_app_get_core_mask());
if (spdk_cpuset_count(cpumask) == 0) {
SPDK_ERRLOG("no cpu is selected among reactor mask(=%s)\n",
spdk_cpuset_fmt(spdk_app_get_core_mask()));
return -1;
}
@ -532,7 +530,8 @@ spdk_vhost_dev_construct(struct spdk_vhost_dev *vdev, const char *name, const ch
unsigned ctrlr_num;
char path[PATH_MAX];
struct stat file_stat;
uint64_t cpumask;
struct spdk_cpuset *cpumask;
int rc;
assert(vdev);
@ -541,15 +540,23 @@ spdk_vhost_dev_construct(struct spdk_vhost_dev *vdev, const char *name, const ch
return -EINVAL;
}
if (spdk_vhost_parse_core_mask(mask_str, &cpumask) != 0) {
SPDK_ERRLOG("cpumask %s is invalid (app mask is 0x%jx)\n",
mask_str, spdk_app_get_core_mask());
return -EINVAL;
cpumask = spdk_cpuset_alloc();
if (!cpumask) {
SPDK_ERRLOG("spdk_cpuset_alloc failed\n");
return -1;
}
if (spdk_vhost_parse_core_mask(mask_str, cpumask) != 0) {
SPDK_ERRLOG("cpumask %s is invalid (app mask is 0x%s)\n",
mask_str, spdk_cpuset_fmt(spdk_app_get_core_mask()));
rc = -EINVAL;
goto out;
}
if (spdk_vhost_dev_find(name)) {
SPDK_ERRLOG("vhost controller %s already exists.\n", name);
return -EEXIST;
rc = -EEXIST;
goto out;
}
for (ctrlr_num = 0; ctrlr_num < MAX_VHOST_DEVICES; ctrlr_num++) {
@ -560,13 +567,15 @@ spdk_vhost_dev_construct(struct spdk_vhost_dev *vdev, const char *name, const ch
if (ctrlr_num == MAX_VHOST_DEVICES) {
SPDK_ERRLOG("Max controllers reached (%d).\n", MAX_VHOST_DEVICES);
return -ENOSPC;
rc = -ENOSPC;
goto out;
}
if (snprintf(path, sizeof(path), "%s%s", dev_dirname, name) >= (int)sizeof(path)) {
SPDK_ERRLOG("Resulting socket path for controller %s is too long: %s%s\n", name, dev_dirname,
name);
return -EINVAL;
rc = -EINVAL;
goto out;
}
/* Register vhost driver to handle vhost messages. */
@ -575,32 +584,37 @@ spdk_vhost_dev_construct(struct spdk_vhost_dev *vdev, const char *name, const ch
SPDK_ERRLOG("Cannot create a domain socket at path \"%s\": "
"The file already exists and is not a socket.\n",
path);
return -EIO;
rc = -EIO;
goto out;
} else if (unlink(path) != 0) {
SPDK_ERRLOG("Cannot create a domain socket at path \"%s\": "
"The socket already exists and failed to unlink.\n",
path);
return -EIO;
rc = -EIO;
goto out;
}
}
if (rte_vhost_driver_register(path, 0) != 0) {
SPDK_ERRLOG("Could not register controller %s with vhost library\n", name);
SPDK_ERRLOG("Check if domain socket %s already exists\n", path);
return -EIO;
rc = -EIO;
goto out;
}
if (rte_vhost_driver_set_features(path, backend->virtio_features) ||
rte_vhost_driver_disable_features(path, backend->disabled_features)) {
SPDK_ERRLOG("Couldn't set vhost features for controller %s\n", name);
rte_vhost_driver_unregister(path);
return -EIO;
rc = -EIO;
goto out;
}
if (rte_vhost_driver_callback_register(path, &g_spdk_vhost_ops) != 0) {
rte_vhost_driver_unregister(path);
SPDK_ERRLOG("Couldn't register callbacks for controller %s\n", name);
return -EIO;
rc = -EIO;
goto out;
}
vdev->name = strdup(name);
@ -628,6 +642,10 @@ spdk_vhost_dev_construct(struct spdk_vhost_dev *vdev, const char *name, const ch
SPDK_NOTICELOG("Controller %s: new controller added\n", vdev->name);
return 0;
out:
spdk_cpuset_free(cpumask);
return rc;
}
int
@ -662,6 +680,7 @@ spdk_vhost_dev_remove(struct spdk_vhost_dev *vdev)
free(vdev->name);
free(vdev->path);
spdk_cpuset_free(vdev->cpumask);
g_spdk_vhost_devices[ctrlr_num] = NULL;
return 0;
}
@ -687,15 +706,16 @@ spdk_vhost_dev_get_name(struct spdk_vhost_dev *vdev)
return vdev->name;
}
uint64_t
spdk_vhost_dev_get_cpumask(struct spdk_vhost_dev *vdev)
void
spdk_vhost_dev_get_cpumask(struct spdk_vhost_dev *vdev, struct spdk_cpuset *cpumask)
{
assert(vdev != NULL);
return vdev->cpumask;
assert(cpumask != NULL);
spdk_cpuset_copy(cpumask, vdev->cpumask);
}
static uint32_t
spdk_vhost_allocate_reactor(uint64_t cpumask)
spdk_vhost_allocate_reactor(struct spdk_cpuset *cpumask)
{
uint32_t i, selected_core;
uint32_t min_ctrlrs;
@ -704,7 +724,7 @@ spdk_vhost_allocate_reactor(uint64_t cpumask)
selected_core = spdk_env_get_first_core();
SPDK_ENV_FOREACH_CORE(i) {
if (!((1ULL << i) & cpumask)) {
if (!spdk_cpuset_get_cpu(cpumask, i)) {
continue;
}

View File

@ -146,7 +146,7 @@ struct spdk_vhost_dev {
int vid;
int task_cnt;
int32_t lcore;
uint64_t cpumask;
struct spdk_cpuset *cpumask;
enum spdk_vhost_dev_type type;
const struct spdk_vhost_dev_backend *backend;

View File

@ -471,7 +471,7 @@ spdk_rpc_get_vhost_controllers_cb(struct spdk_vhost_dev *vdev, void *arg)
spdk_json_write_string(ctx->w, spdk_vhost_dev_get_name(vdev));
spdk_json_write_name(ctx->w, "cpumask");
spdk_json_write_string_fmt(ctx->w, "%#" PRIx64, spdk_vhost_dev_get_cpumask(vdev));
spdk_json_write_string_fmt(ctx->w, "0x%s", spdk_cpuset_fmt(vdev->cpumask));
spdk_json_write_name(ctx->w, "backend_specific");

View File

@ -126,26 +126,34 @@ spdk_sock_close(int sock)
return 0;
}
uint64_t
static struct spdk_cpuset *g_app_core_mask;
struct spdk_cpuset *
spdk_app_get_core_mask(void)
{
return 0xFFFFFFFFFFFFFFFF;
int i;
if (!g_app_core_mask) {
g_app_core_mask = spdk_cpuset_alloc();
for (i = 0; i < SPDK_CPUSET_SIZE; i++) {
spdk_cpuset_set_cpu(g_app_core_mask, i, true);
}
}
return g_app_core_mask;
}
int
spdk_app_parse_core_mask(const char *mask, uint64_t *cpumask)
spdk_app_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
{
char *end;
int rc;
if (mask == NULL || cpumask == NULL) {
return -1;
}
*cpumask = strtoull(mask, &end, 16);
if (*end != '\0' || errno) {
rc = spdk_cpuset_parse(cpumask, mask);
if (rc < 0) {
return -1;
}
return 0;
}

View File

@ -166,18 +166,25 @@ portal_create_from_configline_ipv4_normal_case(void)
const char *string = "192.168.2.0:3260@1";
const char *host_str = "192.168.2.0";
const char *port_str = "3260";
uint64_t cpumask_val = 1;
struct spdk_cpuset *cpumask_val;
struct spdk_iscsi_portal *p;
int rc;
cpumask_val = spdk_cpuset_alloc();
CU_ASSERT_FATAL(cpumask_val != NULL);
spdk_cpuset_set_cpu(cpumask_val, 0, true);
rc = spdk_iscsi_portal_create_from_configline(string, &p, 0);
CU_ASSERT(rc == 0);
CU_ASSERT(strcmp(p->host, host_str) == 0);
CU_ASSERT(strcmp(p->port, port_str) == 0);
CU_ASSERT(p->cpumask == cpumask_val);
CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val));
spdk_iscsi_portal_destroy(p);
CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head));
spdk_cpuset_free(cpumask_val);
}
static void
@ -186,18 +193,25 @@ portal_create_from_configline_ipv6_normal_case(void)
const char *string = "[2001:ad6:1234::]:3260@1";
const char *host_str = "[2001:ad6:1234::]";
const char *port_str = "3260";
uint64_t cpumask_val = 1;
struct spdk_cpuset *cpumask_val;
struct spdk_iscsi_portal *p;
int rc;
cpumask_val = spdk_cpuset_alloc();
CU_ASSERT_FATAL(cpumask_val != NULL);
spdk_cpuset_set_cpu(cpumask_val, 0, true);
rc = spdk_iscsi_portal_create_from_configline(string, &p, 0);
CU_ASSERT(rc == 0);
CU_ASSERT(strcmp(p->host, host_str) == 0);
CU_ASSERT(strcmp(p->port, port_str) == 0);
CU_ASSERT(p->cpumask == cpumask_val);
CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val));
spdk_iscsi_portal_destroy(p);
CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head));
spdk_cpuset_free(cpumask_val);
}
static void
@ -206,15 +220,17 @@ portal_create_from_configline_ipv4_skip_cpumask_case(void)
const char *string = "192.168.2.0:3260";
const char *host_str = "192.168.2.0";
const char *port_str = "3260";
uint64_t cpumask_val = 0xFFFFFFFFFFFFFFFF;
struct spdk_cpuset *cpumask_val;
struct spdk_iscsi_portal *p;
int rc;
cpumask_val = spdk_app_get_core_mask();
rc = spdk_iscsi_portal_create_from_configline(string, &p, 0);
CU_ASSERT(rc == 0);
CU_ASSERT(strcmp(p->host, host_str) == 0);
CU_ASSERT(strcmp(p->port, port_str) == 0);
CU_ASSERT(p->cpumask == cpumask_val);
CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val));
spdk_iscsi_portal_destroy(p);
CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head));
@ -226,15 +242,17 @@ portal_create_from_configline_ipv6_skip_cpumask_case(void)
const char *string = "[2001:ad6:1234::]:3260";
const char *host_str = "[2001:ad6:1234::]";
const char *port_str = "3260";
uint64_t cpumask_val = 0xFFFFFFFFFFFFFFFF;
struct spdk_cpuset *cpumask_val;
struct spdk_iscsi_portal *p;
int rc;
cpumask_val = spdk_app_get_core_mask();
rc = spdk_iscsi_portal_create_from_configline(string, &p, 0);
CU_ASSERT(rc == 0);
CU_ASSERT(strcmp(p->host, host_str) == 0);
CU_ASSERT(strcmp(p->port, port_str) == 0);
CU_ASSERT(p->cpumask == cpumask_val);
CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val));
spdk_iscsi_portal_destroy(p);
CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head));
@ -246,15 +264,17 @@ portal_create_from_configline_ipv4_skip_port_and_cpumask_case(void)
const char *string = "192.168.2.0";
const char *host_str = "192.168.2.0";
const char *port_str = "3260";
uint64_t cpumask_val = 0xFFFFFFFFFFFFFFFF;
struct spdk_cpuset *cpumask_val;
struct spdk_iscsi_portal *p;
int rc;
cpumask_val = spdk_app_get_core_mask();
rc = spdk_iscsi_portal_create_from_configline(string, &p, 0);
CU_ASSERT(rc == 0);
CU_ASSERT(strcmp(p->host, host_str) == 0);
CU_ASSERT(strcmp(p->port, port_str) == 0);
CU_ASSERT(p->cpumask == cpumask_val);
CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val));
spdk_iscsi_portal_destroy(p);
CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head));
@ -266,15 +286,17 @@ portal_create_from_configline_ipv6_skip_port_and_cpumask_case(void)
const char *string = "[2001:ad6:1234::]";
const char *host_str = "[2001:ad6:1234::]";
const char *port_str = "3260";
uint64_t cpumask_val = 0xFFFFFFFFFFFFFFFF;
struct spdk_cpuset *cpumask_val;
struct spdk_iscsi_portal *p;
int rc;
cpumask_val = spdk_app_get_core_mask();
rc = spdk_iscsi_portal_create_from_configline(string, &p, 0);
CU_ASSERT(rc == 0);
CU_ASSERT(strcmp(p->host, host_str) == 0);
CU_ASSERT(strcmp(p->port, port_str) == 0);
CU_ASSERT(p->cpumask == cpumask_val);
CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val));
spdk_iscsi_portal_destroy(p);
CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head));

View File

@ -34,7 +34,7 @@
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
DIRS-y = bit_array.c crc16.c crc32_ieee.c crc32c.c io_channel.c string.c
DIRS-y = bit_array.c cpuset.c crc16.c crc32_ieee.c crc32c.c io_channel.c string.c
.PHONY: all clean $(DIRS-y)

View File

@ -0,0 +1 @@
cpuset_ut

View File

@ -0,0 +1,56 @@
#
# BSD LICENSE
#
# Copyright (c) Intel Corporation.
# 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.
#
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
include $(SPDK_ROOT_DIR)/mk/spdk.app.mk
SPDK_LIB_LIST = log
CFLAGS += -I$(SPDK_ROOT_DIR)/test
CFLAGS += -I$(SPDK_ROOT_DIR)/lib
LIBS += $(SPDK_LIB_LINKER_ARGS)
LIBS += -lcunit
APP = cpuset_ut
C_SRCS = $(APP).c
all: $(APP)
$(APP): $(OBJS) $(SPDK_LIB_FILES)
$(LINK_C)
clean:
$(CLEAN_C) $(APP)
include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk

View File

@ -0,0 +1,265 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* 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/stdinc.h"
#include "spdk/cpuset.h"
#include "spdk_cunit.h"
#include "util/cpuset.c"
static int
cpuset_check_range(struct spdk_cpuset *core_mask, uint32_t min, uint32_t max, bool isset)
{
uint32_t core;
for (core = min; core <= max; core++) {
if (isset != spdk_cpuset_get_cpu(core_mask, core)) {
return -1;
}
}
return 0;
}
static void
test_cpuset(void)
{
uint32_t cpu;
struct spdk_cpuset *set = spdk_cpuset_alloc();
SPDK_CU_ASSERT_FATAL(set != NULL);
CU_ASSERT(spdk_cpuset_count(set) == 0);
/* Set cpu 0 */
spdk_cpuset_set_cpu(set, 0, true);
CU_ASSERT(spdk_cpuset_get_cpu(set, 0) == true);
CU_ASSERT(cpuset_check_range(set, 1, SPDK_CPUSET_SIZE - 1, false) == 0);
CU_ASSERT(spdk_cpuset_count(set) == 1);
/* Set last cpu (cpu 0 already set) */
spdk_cpuset_set_cpu(set, SPDK_CPUSET_SIZE - 1, true);
CU_ASSERT(spdk_cpuset_get_cpu(set, 0) == true);
CU_ASSERT(spdk_cpuset_get_cpu(set, SPDK_CPUSET_SIZE - 1) == true);
CU_ASSERT(cpuset_check_range(set, 1, SPDK_CPUSET_SIZE - 2, false) == 0);
CU_ASSERT(spdk_cpuset_count(set) == 2);
/* Clear cpu 0 (last cpu already set) */
spdk_cpuset_set_cpu(set, 0, false);
CU_ASSERT(spdk_cpuset_get_cpu(set, 0) == false);
CU_ASSERT(cpuset_check_range(set, 1, SPDK_CPUSET_SIZE - 2, false) == 0);
CU_ASSERT(spdk_cpuset_get_cpu(set, SPDK_CPUSET_SIZE - 1) == true);
CU_ASSERT(spdk_cpuset_count(set) == 1);
/* Set middle cpu (last cpu already set) */
cpu = (SPDK_CPUSET_SIZE - 1) / 2;
spdk_cpuset_set_cpu(set, cpu, true);
CU_ASSERT(spdk_cpuset_get_cpu(set, cpu) == true);
CU_ASSERT(spdk_cpuset_get_cpu(set, SPDK_CPUSET_SIZE - 1) == true);
CU_ASSERT(cpuset_check_range(set, 1, cpu - 1, false) == 0);
CU_ASSERT(cpuset_check_range(set, cpu + 1, SPDK_CPUSET_SIZE - 2, false) == 0);
CU_ASSERT(spdk_cpuset_count(set) == 2);
/* Set all cpus */
for (cpu = 0; cpu < SPDK_CPUSET_SIZE; cpu++) {
spdk_cpuset_set_cpu(set, cpu, true);
}
CU_ASSERT(cpuset_check_range(set, 0, SPDK_CPUSET_SIZE - 1, true) == 0);
CU_ASSERT(spdk_cpuset_count(set) == SPDK_CPUSET_SIZE);
/* Clear all cpus */
spdk_cpuset_zero(set);
CU_ASSERT(cpuset_check_range(set, 0, SPDK_CPUSET_SIZE - 1, false) == 0);
CU_ASSERT(spdk_cpuset_count(set) == 0);
spdk_cpuset_free(set);
}
static void
test_cpuset_parse(void)
{
int rc;
struct spdk_cpuset *core_mask;
char buf[1024];
core_mask = spdk_cpuset_alloc();
SPDK_CU_ASSERT_FATAL(core_mask != NULL);
/* Only core 0 should be set */
rc = spdk_cpuset_parse(core_mask, "0x1");
CU_ASSERT(rc >= 0);
CU_ASSERT(cpuset_check_range(core_mask, 0, 0, true) == 0);
CU_ASSERT(cpuset_check_range(core_mask, 1, SPDK_CPUSET_SIZE - 1, false) == 0);
/* Only core 1 should be set */
rc = spdk_cpuset_parse(core_mask, "[1]");
CU_ASSERT(rc >= 0);
CU_ASSERT(cpuset_check_range(core_mask, 0, 0, false) == 0);
CU_ASSERT(cpuset_check_range(core_mask, 1, 1, true) == 0);
CU_ASSERT(cpuset_check_range(core_mask, 2, SPDK_CPUSET_SIZE - 1, false) == 0);
/* Set cores 0-10,12,128-254 */
rc = spdk_cpuset_parse(core_mask, "[0-10,12,128-254]");
CU_ASSERT(rc >= 0);
CU_ASSERT(cpuset_check_range(core_mask, 0, 10, true) == 0);
CU_ASSERT(cpuset_check_range(core_mask, 11, 11, false) == 0);
CU_ASSERT(cpuset_check_range(core_mask, 12, 12, true) == 0);
CU_ASSERT(cpuset_check_range(core_mask, 13, 127, false) == 0);
CU_ASSERT(cpuset_check_range(core_mask, 128, 254, true) == 0);
CU_ASSERT(cpuset_check_range(core_mask, 255, SPDK_CPUSET_SIZE - 1, false) == 0);
/* Set all cores */
snprintf(buf, sizeof(buf), "[0-%d]", SPDK_CPUSET_SIZE - 1);
rc = spdk_cpuset_parse(core_mask, buf);
CU_ASSERT(rc >= 0);
CU_ASSERT(cpuset_check_range(core_mask, 0, SPDK_CPUSET_SIZE - 1, true) == 0);
/* Null parameters not allowed */
rc = spdk_cpuset_parse(core_mask, NULL);
CU_ASSERT(rc < 0);
rc = spdk_cpuset_parse(NULL, "[1]");
CU_ASSERT(rc < 0);
/* Wrong formated core lists */
rc = spdk_cpuset_parse(core_mask, "");
CU_ASSERT(rc < 0);
rc = spdk_cpuset_parse(core_mask, "[");
CU_ASSERT(rc < 0);
rc = spdk_cpuset_parse(core_mask, "[]");
CU_ASSERT(rc < 0);
rc = spdk_cpuset_parse(core_mask, "[10--11]");
CU_ASSERT(rc < 0);
rc = spdk_cpuset_parse(core_mask, "[11-10]");
CU_ASSERT(rc < 0);
rc = spdk_cpuset_parse(core_mask, "[10-11,]");
CU_ASSERT(rc < 0);
rc = spdk_cpuset_parse(core_mask, "[,10-11]");
CU_ASSERT(rc < 0);
/* Out of range value */
snprintf(buf, sizeof(buf), "[%d]", SPDK_CPUSET_SIZE + 1);
rc = spdk_cpuset_parse(core_mask, buf);
CU_ASSERT(rc < 0);
/* Overflow value (UINT64_MAX * 10) */
rc = spdk_cpuset_parse(core_mask, "[184467440737095516150]");
CU_ASSERT(rc < 0);
spdk_cpuset_free(core_mask);
}
static void
test_cpuset_fmt(void)
{
int i;
uint32_t lcore;
struct spdk_cpuset *core_mask = spdk_cpuset_alloc();
char *hex_mask;
char hex_mask_ref[SPDK_CPUSET_SIZE / 4 + 1];
/* Clear coremask. hex_mask should be "0" */
spdk_cpuset_zero(core_mask);
hex_mask = spdk_cpuset_fmt(core_mask);
SPDK_CU_ASSERT_FATAL(hex_mask != NULL);
CU_ASSERT(strcmp("0", hex_mask) == 0);
/* Set coremask 0x51234. Result should be "51234" */
spdk_cpuset_zero(core_mask);
spdk_cpuset_set_cpu(core_mask, 2, true);
spdk_cpuset_set_cpu(core_mask, 4, true);
spdk_cpuset_set_cpu(core_mask, 5, true);
spdk_cpuset_set_cpu(core_mask, 9, true);
spdk_cpuset_set_cpu(core_mask, 12, true);
spdk_cpuset_set_cpu(core_mask, 16, true);
spdk_cpuset_set_cpu(core_mask, 18, true);
hex_mask = spdk_cpuset_fmt(core_mask);
SPDK_CU_ASSERT_FATAL(hex_mask != NULL);
CU_ASSERT(strcmp("51234", hex_mask) == 0);
/* Set all cores */
spdk_cpuset_zero(core_mask);
for (lcore = 0; lcore < SPDK_CPUSET_SIZE; lcore++) {
spdk_cpuset_set_cpu(core_mask, lcore, true);
}
for (i = 0; i < SPDK_CPUSET_SIZE / 4 - 1; i++) {
hex_mask_ref[i] = 'f';
}
hex_mask_ref[SPDK_CPUSET_SIZE / 4 - 1] = '\0';
hex_mask = spdk_cpuset_fmt(core_mask);
CU_ASSERT(hex_mask != NULL);
if (hex_mask != NULL) {
CU_ASSERT(strcmp(hex_mask_ref, hex_mask) == 0);
}
spdk_cpuset_free(core_mask);
}
int
main(int argc, char **argv)
{
CU_pSuite suite = NULL;
unsigned int num_failures;
if (CU_initialize_registry() != CUE_SUCCESS) {
return CU_get_error();
}
suite = CU_add_suite("cpuset", NULL, NULL);
if (suite == NULL) {
CU_cleanup_registry();
return CU_get_error();
}
if (
CU_add_test(suite, "test_cpuset", test_cpuset) == NULL ||
CU_add_test(suite, "test_cpuset_parse", test_cpuset_parse) == NULL ||
CU_add_test(suite, "test_cpuset_fmt", test_cpuset_fmt) == NULL) {
CU_cleanup_registry();
return CU_get_error();
}
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
num_failures = CU_get_number_of_failures();
CU_cleanup_registry();
return num_failures;
}

View File

@ -46,8 +46,18 @@ DEFINE_STUB(spdk_event_allocate, struct spdk_event *,
(uint32_t lcore, spdk_event_fn fn, void *arg1, void *arg2), NULL);
DEFINE_STUB(spdk_mem_register, int, (void *vaddr, size_t len), 0);
DEFINE_STUB(spdk_mem_unregister, int, (void *vaddr, size_t len), 0);
DEFINE_STUB(spdk_app_get_core_mask, uint64_t, (void), 0);
DEFINE_STUB(spdk_app_parse_core_mask, int, (const char *mask, uint64_t *cpumask), 0);
static struct spdk_cpuset *g_app_core_mask;
struct spdk_cpuset *spdk_app_get_core_mask(void)
{
if (g_app_core_mask == NULL) {
g_app_core_mask = spdk_cpuset_alloc();
spdk_cpuset_set_cpu(g_app_core_mask, 0, true);
}
return g_app_core_mask;
}
DEFINE_STUB(spdk_app_parse_core_mask, int, (const char *mask, struct spdk_cpuset *cpumask), 0);
DEFINE_STUB(spdk_env_get_first_core, uint32_t, (void), 0);
DEFINE_STUB(spdk_env_get_next_core, uint32_t, (uint32_t prev_core), 0);
DEFINE_STUB(spdk_env_get_last_core, uint32_t, (void), 0);
@ -205,7 +215,7 @@ create_controller_test(void)
char long_name[PATH_MAX];
struct spdk_vhost_dev_backend backend;
MOCK_SET(spdk_app_get_core_mask, uint64_t, 1);
/* NOTE: spdk_app_get_core_mask stub always sets coremask 0x01 */
/* Create device with no name */
vdev = alloc_vdev();