* limitations under the License.
*/
-/* On non-Linux, these functions are defined inline in ovs-numa.h. */
-#ifdef __linux__
-
#include <config.h>
#include "ovs-numa.h"
#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
+#ifdef __linux__
+#include <dirent.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
+#endif /* __linux__ */
#include "hash.h"
-#include "hmap.h"
-#include "list.h"
+#include "openvswitch/hmap.h"
+#include "openvswitch/list.h"
#include "ovs-thread.h"
#include "openvswitch/vlog.h"
struct hmap_node hmap_node;/* In the 'all_cpu_cores'. */
struct ovs_list list_node; /* In 'numa_node->cores' list. */
struct numa_node *numa; /* numa node containing the core. */
- int core_id; /* Core id. */
+ unsigned core_id; /* Core id. */
bool available; /* If the core can be pinned. */
bool pinned; /* If a thread has been pinned to the core. */
};
static struct hmap all_cpu_cores = HMAP_INITIALIZER(&all_cpu_cores);
/* True if numa node and core info are correctly extracted. */
static bool found_numa_and_core;
+/* True if the module was initialized with dummy options. In this case, the
+ * module must not interact with the actual cpus/nodes in the system. */
+static bool dummy_numa = false;
+/* If 'dummy_numa' is true, contains a copy of the dummy numa configuration
+ * parameter */
+static char *dummy_config;
+static struct numa_node *get_numa_by_numa_id(int numa_id);
+
+#ifdef __linux__
/* Returns true if 'str' contains all digits. Returns false otherwise. */
static bool
contain_all_digits(const char *str)
{
return str[strspn(str, "0123456789")] == '\0';
}
+#endif /* __linux__ */
+
+static struct numa_node *
+insert_new_numa_node(int numa_id)
+{
+ struct numa_node *n = xzalloc(sizeof *n);
+
+ hmap_insert(&all_numa_nodes, &n->hmap_node, hash_int(numa_id, 0));
+ ovs_list_init(&n->cores);
+ n->numa_id = numa_id;
+
+ return n;
+}
+
+static struct cpu_core *
+insert_new_cpu_core(struct numa_node *n, unsigned core_id)
+{
+ struct cpu_core *c = xzalloc(sizeof *c);
+
+ hmap_insert(&all_cpu_cores, &c->hmap_node, hash_int(core_id, 0));
+ ovs_list_insert(&n->cores, &c->list_node);
+ c->core_id = core_id;
+ c->numa = n;
+ c->available = true;
+
+ return c;
+}
+
+/* Has the same effect as discover_numa_and_core(), but instead of reading
+ * sysfs entries, extracts the info from 'dummy_config'.
+ *
+ * 'dummy_config' lists the numa_ids of each CPU separated by a comma, e.g.
+ * - "0,0,0,0": four cores on numa socket 0.
+ * - "0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1": 16 cores on two numa sockets.
+ * - "0,0,0,0,1,1,1,1": 8 cores on two numa sockets.
+ *
+ * The different numa ids must be consecutives or the function will abort. */
+static void
+discover_numa_and_core_dummy(const char *dummy_config)
+{
+ char *conf = xstrdup(dummy_config);
+ char *id, *saveptr = NULL;
+ unsigned i = 0;
+ long max_numa_id = 0;
+
+ for (id = strtok_r(conf, ",", &saveptr); id;
+ id = strtok_r(NULL, ",", &saveptr)) {
+ struct hmap_node *hnode;
+ struct numa_node *n;
+ long numa_id;
+
+ numa_id = strtol(id, NULL, 10);
+ if (numa_id < 0 || numa_id >= MAX_NUMA_NODES) {
+ VLOG_WARN("Invalid numa node %ld", numa_id);
+ continue;
+ }
+
+ max_numa_id = MAX(max_numa_id, numa_id);
+
+ hnode = hmap_first_with_hash(&all_numa_nodes, hash_int(numa_id, 0));
+
+ if (hnode) {
+ n = CONTAINER_OF(hnode, struct numa_node, hmap_node);
+ } else {
+ n = insert_new_numa_node(numa_id);
+ }
+
+ insert_new_cpu_core(n, i);
+
+ i++;
+ }
+
+ free(conf);
+
+ if (max_numa_id + 1 != hmap_count(&all_numa_nodes)) {
+ ovs_fatal(0, "dummy numa contains non consecutive numa ids");
+ }
+}
/* Discovers all numa nodes and the corresponding cpu cores.
* Constructs the 'struct numa_node' and 'struct cpu_core'. */
static void
discover_numa_and_core(void)
{
- int n_cpus = 0;
+#ifdef __linux__
int i;
+ DIR *dir;
+ bool numa_supported = true;
+
+ /* Check if NUMA supported on this system. */
+ dir = opendir("/sys/devices/system/node");
+
+ if (!dir && errno == ENOENT) {
+ numa_supported = false;
+ }
+ if (dir) {
+ closedir(dir);
+ }
for (i = 0; i < MAX_NUMA_NODES; i++) {
- DIR *dir;
char* path;
- /* Constructs the path to node /sys/devices/system/nodeX. */
- path = xasprintf("/sys/devices/system/node/node%d", i);
+ if (numa_supported) {
+ /* Constructs the path to node /sys/devices/system/nodeX. */
+ path = xasprintf("/sys/devices/system/node/node%d", i);
+ } else {
+ path = xasprintf("/sys/devices/system/cpu/");
+ }
+
dir = opendir(path);
/* Creates 'struct numa_node' if the 'dir' is non-null. */
if (dir) {
- struct numa_node *n = xzalloc(sizeof *n);
+ struct numa_node *n;
struct dirent *subdir;
- hmap_insert(&all_numa_nodes, &n->hmap_node, hash_int(i, 0));
- list_init(&n->cores);
- n->numa_id = i;
+ n = insert_new_numa_node(i);
while ((subdir = readdir(dir)) != NULL) {
if (!strncmp(subdir->d_name, "cpu", 3)
- && contain_all_digits(subdir->d_name + 3)){
- struct cpu_core *c = xzalloc(sizeof *c);
- uint32_t core_id;
+ && contain_all_digits(subdir->d_name + 3)) {
+ unsigned core_id;
core_id = strtoul(subdir->d_name + 3, NULL, 10);
- hmap_insert(&all_cpu_cores, &c->hmap_node,
- hash_int(core_id, 0));
- list_insert(&n->cores, &c->list_node);
- c->core_id = core_id;
- c->numa = n;
- c->available = true;
- n_cpus++;
+ insert_new_cpu_core(n, core_id);
}
}
- VLOG_INFO("Discovered %"PRIuSIZE" CPU cores on NUMA node %d",
- list_size(&n->cores), n->numa_id);
- free(path);
closedir(dir);
- } else {
- if (errno != ENOENT) {
- VLOG_WARN("opendir(%s) failed (%s)", path,
- ovs_strerror(errno));
- }
- free(path);
- break;
+ } else if (errno != ENOENT) {
+ VLOG_WARN("opendir(%s) failed (%s)", path,
+ ovs_strerror(errno));
}
- }
- VLOG_INFO("Discovered %"PRIuSIZE" NUMA nodes and %d CPU cores",
- hmap_count(&all_numa_nodes), n_cpus);
- if (hmap_count(&all_numa_nodes) && hmap_count(&all_cpu_cores)) {
- found_numa_and_core = true;
+ free(path);
+ if (!dir || !numa_supported) {
+ break;
+ }
}
+#endif /* __linux__ */
}
/* Gets 'struct cpu_core' by 'core_id'. */
static struct cpu_core*
-get_core_by_core_id(int core_id)
+get_core_by_core_id(unsigned core_id)
{
struct cpu_core *core = NULL;
}
\f
-/* Extracts the numa node and core info from the 'sysfs'. */
-void
-ovs_numa_init(void)
+
+static bool
+ovs_numa_init__(const char *dummy_config)
{
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
if (ovsthread_once_start(&once)) {
- discover_numa_and_core();
+ const struct numa_node *n;
+
+ if (!dummy_config) {
+ discover_numa_and_core();
+ } else {
+ discover_numa_and_core_dummy(dummy_config);
+ }
+
+ HMAP_FOR_EACH(n, hmap_node, &all_numa_nodes) {
+ VLOG_INFO("Discovered %"PRIuSIZE" CPU cores on NUMA node %d",
+ ovs_list_size(&n->cores), n->numa_id);
+ }
+
+ VLOG_INFO("Discovered %"PRIuSIZE" NUMA nodes and %"PRIuSIZE" CPU cores",
+ hmap_count(&all_numa_nodes), hmap_count(&all_cpu_cores));
+
+ if (hmap_count(&all_numa_nodes) && hmap_count(&all_cpu_cores)) {
+ found_numa_and_core = true;
+ }
+
ovsthread_once_done(&once);
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* Extracts the numa node and core info from the 'config'. This is useful for
+ * testing purposes. The function must be called once, before ovs_numa_init().
+ *
+ * The format of 'config' is explained in the comment above
+ * discover_numa_and_core_dummy().*/
+void
+ovs_numa_set_dummy(const char *config)
+{
+ dummy_numa = true;
+ ovs_assert(config);
+ free(dummy_config);
+ dummy_config = xstrdup(config);
+}
+
+/* Initializes the numa module. */
+void
+ovs_numa_init(void)
+{
+ if (dummy_numa) {
+ ovs_numa_init__(dummy_config);
+ } else {
+ ovs_numa_init__(NULL);
}
}
}
bool
-ovs_numa_core_id_is_valid(int core_id)
+ovs_numa_core_id_is_valid(unsigned core_id)
{
return found_numa_and_core && core_id < ovs_numa_get_n_cores();
}
bool
-ovs_numa_core_is_pinned(int core_id)
+ovs_numa_core_is_pinned(unsigned core_id)
{
struct cpu_core *core = get_core_by_core_id(core_id);
/* Given 'core_id', returns the corresponding numa node id. Returns
* OVS_NUMA_UNSPEC if 'core_id' is invalid. */
int
-ovs_numa_get_numa_id(int core_id)
+ovs_numa_get_numa_id(unsigned core_id)
{
struct cpu_core *core = get_core_by_core_id(core_id);
struct numa_node *numa = get_numa_by_numa_id(numa_id);
if (numa) {
- return list_size(&numa->cores);
+ return ovs_list_size(&numa->cores);
}
return OVS_CORE_UNSPEC;
* False, if the core has already been pinned, or if it is invalid or
* not available. */
bool
-ovs_numa_try_pin_core_specific(int core_id)
+ovs_numa_try_pin_core_specific(unsigned core_id)
{
struct cpu_core *core = get_core_by_core_id(core_id);
/* Searches through all cores for an unpinned and available core. Returns
* the 'core_id' if found and sets the 'core->pinned' to true. Otherwise,
* returns OVS_CORE_UNSPEC. */
-int
+unsigned
ovs_numa_get_unpinned_core_any(void)
{
struct cpu_core *core;
/* Searches through all cores on numa node with 'numa_id' for an
* unpinned and available core. Returns the core_id if found and
* sets the 'core->pinned' to true. Otherwise, returns OVS_CORE_UNSPEC. */
-int
+unsigned
ovs_numa_get_unpinned_core_on_numa(int numa_id)
{
struct numa_node *numa = get_numa_by_numa_id(numa_id);
/* Unpins the core with 'core_id'. */
void
-ovs_numa_unpin_core(int core_id)
+ovs_numa_unpin_core(unsigned core_id)
{
struct cpu_core *core = get_core_by_core_id(core_id);
struct ovs_numa_dump *
ovs_numa_dump_cores_on_numa(int numa_id)
{
- struct ovs_numa_dump *dump = NULL;
+ struct ovs_numa_dump *dump = xmalloc(sizeof *dump);
struct numa_node *numa = get_numa_by_numa_id(numa_id);
+ ovs_list_init(&dump->dump);
+
if (numa) {
struct cpu_core *core;
- dump = xmalloc(sizeof *dump);
- list_init(&dump->dump);
LIST_FOR_EACH(core, list_node, &numa->cores) {
struct ovs_numa_info *info = xmalloc(sizeof *info);
info->numa_id = numa->numa_id;
info->core_id = core->core_id;
- list_insert(&dump->dump, &info->list_node);
+ ovs_list_insert(&dump->dump, &info->list_node);
}
}
void
ovs_numa_dump_destroy(struct ovs_numa_dump *dump)
{
- struct ovs_numa_info *iter, *next;
+ struct ovs_numa_info *iter;
+
+ if (!dump) {
+ return;
+ }
- LIST_FOR_EACH_SAFE (iter, next, list_node, &dump->dump) {
- list_remove(&iter->list_node);
+ LIST_FOR_EACH_POP (iter, list_node, &dump->dump) {
free(iter);
}
}
for (i = strlen(cmask) - 1; i >= 0; i--) {
- char hex = toupper(cmask[i]);
+ char hex = toupper((unsigned char)cmask[i]);
int bin, j;
if (hex >= '0' && hex <= '9') {
}
}
+int ovs_numa_thread_setaffinity_core(unsigned core_id OVS_UNUSED)
+{
+ if (dummy_numa) {
+ /* Nothing to do */
+ return 0;
+ }
+
+#ifdef __linux__
+ cpu_set_t cpuset;
+ int err;
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(core_id, &cpuset);
+ err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
+ if (err) {
+ VLOG_ERR("Thread affinity error %d",err);
+ return err;
+ }
+
+ return 0;
+#else /* !__linux__ */
+ return EOPNOTSUPP;
#endif /* __linux__ */
+}