diff --git a/include/spdk/cpuset.h b/include/spdk/cpuset.h new file mode 100644 index 0000000000..59f98d4211 --- /dev/null +++ b/include/spdk/cpuset.h @@ -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 */ diff --git a/include/spdk/event.h b/include/spdk/event.h index fd3e4f5473..7c45be713a 100644 --- a/include/spdk/event.h +++ b/include/spdk/event.h @@ -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 diff --git a/include/spdk/vhost.h b/include/spdk/vhost.h index b0482d1360..3f59d9ba1a 100644 --- a/include/spdk/vhost.h +++ b/include/spdk/vhost.h @@ -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 diff --git a/lib/env_dpdk/init.c b/lib/env_dpdk/init.c index 4f51a96693..750ea14e37 100644 --- a/lib/env_dpdk/init.c +++ b/lib/env_dpdk/init.c @@ -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; } diff --git a/lib/event/app.c b/lib/event/app.c index bb2a27ecb5..da765492e6 100644 --- a/lib/event/app.c +++ b/lib/event/app.c @@ -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 diff --git a/lib/event/reactor.c b/lib/event/reactor.c index 279b94af12..9626de4407 100644 --- a/lib/event/reactor.c +++ b/lib/event/reactor.c @@ -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 diff --git a/lib/iscsi/conn.c b/lib/iscsi/conn.c index 552452e7f6..117bf15270 100644 --- a/lib/iscsi/conn.c +++ b/lib/iscsi/conn.c @@ -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; } diff --git a/lib/iscsi/conn.h b/lib/iscsi/conn.h index e1f9d1a911..1cadaad96b 100644 --- a/lib/iscsi/conn.h +++ b/lib/iscsi/conn.h @@ -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; diff --git a/lib/iscsi/iscsi_rpc.c b/lib/iscsi/iscsi_rpc.c index f4f151d7fb..18e9f51ae3 100644 --- a/lib/iscsi/iscsi_rpc.c +++ b/lib/iscsi/iscsi_rpc.c @@ -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, diff --git a/lib/iscsi/iscsi_subsystem.c b/lib/iscsi/iscsi_subsystem.c index 67a8d4cb23..c63d75ae00 100644 --- a/lib/iscsi/iscsi_subsystem.c +++ b/lib/iscsi/iscsi_subsystem.c @@ -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)); } } } diff --git a/lib/iscsi/portal_grp.c b/lib/iscsi/portal_grp.c index 039efdbcde..76c841938e 100644 --- a/lib/iscsi/portal_grp.c +++ b/lib/iscsi/portal_grp.c @@ -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); } diff --git a/lib/iscsi/portal_grp.h b/lib/iscsi/portal_grp.h index 5f03f3bb48..52286668f2 100644 --- a/lib/iscsi/portal_grp.h +++ b/lib/iscsi/portal_grp.h @@ -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; diff --git a/lib/util/Makefile b/lib/util/Makefile index 433f0f0606..8bbc575066 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -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 diff --git a/lib/util/cpuset.c b/lib/util/cpuset.c new file mode 100644 index 0000000000..6d74ea77e0 --- /dev/null +++ b/lib/util/cpuset.c @@ -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; +} diff --git a/lib/vhost/vhost.c b/lib/vhost/vhost.c index d5107f8163..d4dafd13a7 100644 --- a/lib/vhost/vhost.c +++ b/lib/vhost/vhost.c @@ -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; } diff --git a/lib/vhost/vhost_internal.h b/lib/vhost/vhost_internal.h index 9ecfacd6e8..66ef7b3b7b 100644 --- a/lib/vhost/vhost_internal.h +++ b/lib/vhost/vhost_internal.h @@ -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; diff --git a/lib/vhost/vhost_rpc.c b/lib/vhost/vhost_rpc.c index 39a1fef6fb..baed56cfe9 100644 --- a/lib/vhost/vhost_rpc.c +++ b/lib/vhost/vhost_rpc.c @@ -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"); diff --git a/test/unit/lib/iscsi/common.c b/test/unit/lib/iscsi/common.c index 912dba4718..8ef8acc863 100644 --- a/test/unit/lib/iscsi/common.c +++ b/test/unit/lib/iscsi/common.c @@ -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; } diff --git a/test/unit/lib/iscsi/portal_grp.c/portal_grp_ut.c b/test/unit/lib/iscsi/portal_grp.c/portal_grp_ut.c index 13066b9cc7..6006ca5628 100644 --- a/test/unit/lib/iscsi/portal_grp.c/portal_grp_ut.c +++ b/test/unit/lib/iscsi/portal_grp.c/portal_grp_ut.c @@ -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)); diff --git a/test/unit/lib/util/Makefile b/test/unit/lib/util/Makefile index a14904eae3..8ce9670e92 100644 --- a/test/unit/lib/util/Makefile +++ b/test/unit/lib/util/Makefile @@ -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) diff --git a/test/unit/lib/util/cpuset.c/.gitignore b/test/unit/lib/util/cpuset.c/.gitignore new file mode 100644 index 0000000000..2ca1a2d362 --- /dev/null +++ b/test/unit/lib/util/cpuset.c/.gitignore @@ -0,0 +1 @@ +cpuset_ut diff --git a/test/unit/lib/util/cpuset.c/Makefile b/test/unit/lib/util/cpuset.c/Makefile new file mode 100644 index 0000000000..ac9b5656ed --- /dev/null +++ b/test/unit/lib/util/cpuset.c/Makefile @@ -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 diff --git a/test/unit/lib/util/cpuset.c/cpuset_ut.c b/test/unit/lib/util/cpuset.c/cpuset_ut.c new file mode 100644 index 0000000000..058b6c0491 --- /dev/null +++ b/test/unit/lib/util/cpuset.c/cpuset_ut.c @@ -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; +} diff --git a/test/unit/lib/vhost/vhost.c/vhost_ut.c b/test/unit/lib/vhost/vhost.c/vhost_ut.c index 19ecf9d764..1774c5c80c 100644 --- a/test/unit/lib/vhost/vhost.c/vhost_ut.c +++ b/test/unit/lib/vhost/vhost.c/vhost_ut.c @@ -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();