percpu alloc: Introduce stride parameter
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 8 Mar 2024 19:07:11 +0000 (14:07 -0500)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 8 Mar 2024 19:07:11 +0000 (14:07 -0500)
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: I6469725da2610560872d2fe678128455d7ff22f1

include/rseq/mempool.h
src/rseq-mempool.c
src/rseq-utils.h
tests/mempool_test.c
tests/param_test.c

index aecf902467141cf6658ec47bdd51b9fbf4a9b42e..3be30926c62282420adbd89154747462c1319c6f 100644 (file)
@@ -4,6 +4,7 @@
 #ifndef _RSEQ_MEMPOOL_H
 #define _RSEQ_MEMPOOL_H
 
+#include <rseq/compiler.h>
 #include <stddef.h>
 #include <sys/types.h>
 #include <sys/mman.h>
 extern "C" {
 #endif
 
+#if RSEQ_BITS_PER_LONG == 64
+# define RSEQ_PERCPU_STRIDE    (1U << 24)      /* 64-bit stride: 16MB */
+#else
+# define RSEQ_PERCPU_STRIDE    (1U << 16)      /* 32-bit stride: 64kB */
+#endif
+
 /*
  * Tag pointers returned by:
  * - rseq_percpu_malloc(),
@@ -71,7 +78,7 @@ struct rseq_percpu_pool;
  * This API is MT-safe.
  */
 struct rseq_percpu_pool *rseq_percpu_pool_create(const char *pool_name,
-               size_t item_len, size_t percpu_len, int max_nr_cpus,
+               size_t item_len, size_t percpu_stride, int max_nr_cpus,
                const struct rseq_pool_attr *attr);
 
 /*
@@ -138,7 +145,9 @@ void __rseq_percpu *rseq_percpu_zmalloc(struct rseq_percpu_pool *pool);
  *
  * This API is MT-safe.
  */
-void rseq_percpu_free(void __rseq_percpu *ptr);
+void __rseq_percpu_free(void __rseq_percpu *ptr, size_t percpu_stride);
+
+#define rseq_percpu_free(ptr)  __rseq_percpu_free(ptr, RSEQ_PERCPU_STRIDE)
 
 /*
  * rseq_percpu_ptr: Decode a per-cpu pointer.
@@ -162,8 +171,9 @@ void rseq_percpu_free(void __rseq_percpu *ptr);
  *
  * This API is MT-safe.
  */
-void *__rseq_percpu_ptr(void __rseq_percpu *ptr, int cpu);
-#define rseq_percpu_ptr(ptr, cpu)      ((__typeof__(*(ptr)) *) __rseq_percpu_ptr(ptr, cpu))
+void *__rseq_percpu_ptr(void __rseq_percpu *ptr, int cpu, size_t percpu_stride);
+#define rseq_percpu_ptr(ptr, cpu)      \
+       ((__typeof__(*(ptr)) *) __rseq_percpu_ptr(ptr, cpu, RSEQ_PERCPU_STRIDE))
 
 /*
  * rseq_percpu_pool_set_create: Create a pool set.
index f85fc45e119764a5cc0bc3f5835c58f15b712b5b..71734e2df3004d9c62021848d0d95cde9722b62e 100644 (file)
@@ -105,7 +105,7 @@ struct rseq_percpu_pool {
 
        unsigned int index;
        size_t item_len;
-       size_t percpu_len;
+       size_t percpu_stride;
        int item_order;
        int max_nr_cpus;
 
@@ -140,13 +140,14 @@ struct rseq_percpu_pool_set {
 };
 
 static
-void *__rseq_pool_percpu_ptr(struct rseq_percpu_pool *pool, int cpu, uintptr_t item_offset)
+void *__rseq_pool_percpu_ptr(struct rseq_percpu_pool *pool, int cpu,
+               uintptr_t item_offset, size_t stride)
 {
        /* TODO: Implement multi-ranges support. */
-       return pool->ranges->base + (pool->percpu_len * cpu) + item_offset;
+       return pool->ranges->base + (stride * cpu) + item_offset;
 }
 
-void *__rseq_percpu_ptr(void __rseq_percpu *_ptr, int cpu)
+void *__rseq_percpu_ptr(void __rseq_percpu *_ptr, int cpu, size_t stride)
 {
        uintptr_t ptr = (uintptr_t) _ptr;
        uintptr_t item_offset = ptr & MAX_POOL_LEN_MASK;
@@ -154,7 +155,7 @@ void *__rseq_percpu_ptr(void __rseq_percpu *_ptr, int cpu)
        struct rseq_percpu_pool *pool = &rseq_percpu_pool[pool_index];
 
        assert(cpu >= 0);
-       return __rseq_pool_percpu_ptr(pool, cpu, item_offset);
+       return __rseq_pool_percpu_ptr(pool, cpu, item_offset, stride);
 }
 
 static
@@ -163,7 +164,8 @@ void rseq_percpu_zero_item(struct rseq_percpu_pool *pool, uintptr_t item_offset)
        int i;
 
        for (i = 0; i < pool->max_nr_cpus; i++) {
-               char *p = __rseq_pool_percpu_ptr(pool, i, item_offset);
+               char *p = __rseq_pool_percpu_ptr(pool, i,
+                               item_offset, pool->percpu_stride);
                memset(p, 0, pool->item_len);
        }
 }
@@ -174,14 +176,14 @@ void rseq_percpu_zero_item(struct rseq_percpu_pool *pool, uintptr_t item_offset)
 static
 int rseq_percpu_pool_range_init_numa(struct rseq_percpu_pool *pool, struct rseq_percpu_pool_range *range, int numa_flags)
 {
-       unsigned long nr_pages;
-       long ret, page_len;
+       unsigned long nr_pages, page_len;
+       long ret;
        int cpu;
 
        if (!numa_flags)
                return 0;
        page_len = rseq_get_page_len();
-       nr_pages = pool->percpu_len >> rseq_get_count_order_ulong(page_len);
+       nr_pages = pool->percpu_stride >> rseq_get_count_order_ulong(page_len);
        for (cpu = 0; cpu < pool->max_nr_cpus; cpu++) {
 
                int status[MOVE_PAGES_BATCH_SIZE];
@@ -271,7 +273,7 @@ int create_alloc_bitmap(struct rseq_percpu_pool *pool, struct rseq_percpu_pool_r
 {
        size_t count;
 
-       count = ((pool->percpu_len >> pool->item_order) + BIT_PER_ULONG - 1) / BIT_PER_ULONG;
+       count = ((pool->percpu_stride >> pool->item_order) + BIT_PER_ULONG - 1) / BIT_PER_ULONG;
 
        /*
         * Not being able to create the validation bitmap is an error
@@ -313,8 +315,8 @@ void check_free_list(const struct rseq_percpu_pool *pool)
                return;
 
        for (range = pool->ranges; range; range = range->next) {
-               total_item += pool->percpu_len >> pool->item_order;
-               total_never_allocated += (pool->percpu_len - range->next_unused) >> pool->item_order;
+               total_item += pool->percpu_stride >> pool->item_order;
+               total_never_allocated += (pool->percpu_stride - range->next_unused) >> pool->item_order;
        }
        max_list_traversal = total_item - total_never_allocated;
 
@@ -363,7 +365,7 @@ void destroy_alloc_bitmap(struct rseq_percpu_pool *pool, struct rseq_percpu_pool
        if (!bitmap)
                return;
 
-       count = ((pool->percpu_len >> pool->item_order) + BIT_PER_ULONG - 1) / BIT_PER_ULONG;
+       count = ((pool->percpu_stride >> pool->item_order) + BIT_PER_ULONG - 1) / BIT_PER_ULONG;
 
        /* Assert that all items in the pool were freed. */
        for (size_t k = 0; k < count; ++k)
@@ -384,7 +386,7 @@ int rseq_percpu_pool_range_destroy(struct rseq_percpu_pool *pool,
 {
        destroy_alloc_bitmap(pool, range);
        return pool->attr.munmap_func(pool->attr.mmap_priv, range->base,
-                       pool->percpu_len * pool->max_nr_cpus);
+                       pool->percpu_stride * pool->max_nr_cpus);
 }
 
 static
@@ -398,7 +400,7 @@ struct rseq_percpu_pool_range *rseq_percpu_pool_range_create(struct rseq_percpu_
                return NULL;
        range->pool = pool;
 
-       base = pool->attr.mmap_func(pool->attr.mmap_priv, pool->percpu_len * pool->max_nr_cpus);
+       base = pool->attr.mmap_func(pool->attr.mmap_priv, pool->percpu_stride * pool->max_nr_cpus);
        if (!base)
                goto error_alloc;
        range->base = base;
@@ -451,7 +453,7 @@ int rseq_percpu_pool_destroy(struct rseq_percpu_pool *pool)
 }
 
 struct rseq_percpu_pool *rseq_percpu_pool_create(const char *pool_name,
-               size_t item_len, size_t percpu_len, int max_nr_cpus,
+               size_t item_len, size_t percpu_stride, int max_nr_cpus,
                const struct rseq_pool_attr *_attr)
 {
        struct rseq_percpu_pool *pool;
@@ -471,11 +473,13 @@ struct rseq_percpu_pool *rseq_percpu_pool_create(const char *pool_name,
        }
        item_len = 1UL << order;
 
-       /* Align percpu_len on page size. */
-       percpu_len = rseq_align(percpu_len, rseq_get_page_len());
+       if (!percpu_stride)
+               percpu_stride = RSEQ_PERCPU_STRIDE;     /* Use default */
 
-       if (max_nr_cpus < 0 || item_len > percpu_len ||
-                       percpu_len > (UINTPTR_MAX >> POOL_INDEX_BITS)) {
+       if (max_nr_cpus < 0 || item_len > percpu_stride ||
+                       percpu_stride > (UINTPTR_MAX >> POOL_INDEX_BITS) ||
+                       percpu_stride < (size_t) rseq_get_page_len() ||
+                       !is_pow2(percpu_stride)) {
                errno = EINVAL;
                return NULL;
        }
@@ -502,7 +506,7 @@ struct rseq_percpu_pool *rseq_percpu_pool_create(const char *pool_name,
 found_empty:
        memcpy(&pool->attr, &attr, sizeof(attr));
        pthread_mutex_init(&pool->lock, NULL);
-       pool->percpu_len = percpu_len;
+       pool->percpu_stride = percpu_stride;
        pool->max_nr_cpus = max_nr_cpus;
        pool->index = i;
        pool->item_len = item_len;
@@ -570,7 +574,7 @@ void __rseq_percpu *__rseq_percpu_malloc(struct rseq_percpu_pool *pool, bool zer
                addr = (void *) (((uintptr_t) pool->index << POOL_INDEX_SHIFT) | item_offset);
                goto end;
        }
-       if (pool->ranges->next_unused + pool->item_len > pool->percpu_len) {
+       if (pool->ranges->next_unused + pool->item_len > pool->percpu_stride) {
                errno = ENOMEM;
                addr = NULL;
                goto end;
@@ -622,7 +626,7 @@ void clear_alloc_slot(struct rseq_percpu_pool *pool, size_t item_offset)
        bitmap[k] &= ~mask;
 }
 
-void rseq_percpu_free(void __rseq_percpu *_ptr)
+void __rseq_percpu_free(void __rseq_percpu *_ptr, size_t percpu_stride)
 {
        uintptr_t ptr = (uintptr_t) _ptr;
        uintptr_t item_offset = ptr & MAX_POOL_LEN_MASK;
@@ -635,7 +639,7 @@ void rseq_percpu_free(void __rseq_percpu *_ptr)
        /* Add ptr to head of free list */
        head = pool->free_list_head;
        /* Free-list is in CPU 0 range. */
-       item = (struct free_list_node *)__rseq_pool_percpu_ptr(pool, 0, item_offset);
+       item = (struct free_list_node *)__rseq_pool_percpu_ptr(pool, 0, item_offset, percpu_stride);
        item->next = head;
        pool->free_list_head = item;
        pthread_mutex_unlock(&pool->lock);
index 8ff0b6b603dc5eaecb91cb4709d3597e38ddf408..f6749d3c24e3da32820dca1cfc07b52d4d6a05d7 100644 (file)
@@ -100,13 +100,13 @@ int rseq_get_count_order_ulong(unsigned long x)
 #define RSEQ_DEFAULT_PAGE_SIZE 4096
 
 static inline
-long rseq_get_page_len(void)
+unsigned long rseq_get_page_len(void)
 {
        long page_len = sysconf(_SC_PAGE_SIZE);
 
        if (page_len < 0)
                page_len = RSEQ_DEFAULT_PAGE_SIZE;
-       return page_len;
+       return (unsigned long) page_len;
 }
 
 static inline
@@ -115,4 +115,20 @@ int rseq_hweight_ulong(unsigned long v)
        return __builtin_popcountl(v);
 }
 
+static inline
+bool is_pow2(uint64_t x)
+{
+       return !(x & (x - 1));
+}
+
+/*
+ * Calculate offset needed to align p on alignment towards higher
+ * addresses. Alignment must be a power of 2
+ */
+static inline
+off_t offset_align(uintptr_t p, size_t alignment)
+{
+       return (alignment - p) & (alignment - 1);
+}
+
 #endif /* _RSEQ_UTILS_H */
index c66cdd0a6ac8672e5a7b0df9c91c0ec4e1ac6cf0..9bc819b7581e351d28193526ea5f736507aada25 100644 (file)
@@ -27,7 +27,7 @@ struct test_data {
        struct list_head node;
 };
 
-static void test_mempool_fill(size_t len)
+static void test_mempool_fill(size_t stride)
 {
        struct test_data __rseq_percpu *ptr;
        struct test_data *iter, *tmp;
@@ -44,8 +44,8 @@ static void test_mempool_fill(size_t len)
 
        mempool = rseq_percpu_pool_create("test_data",
                        sizeof(struct test_data),
-                       len, CPU_SETSIZE, attr);
-       ok(mempool, "Create mempool of size %zu", len);
+                       stride, CPU_SETSIZE, attr);
+       ok(mempool, "Create mempool of size %zu", stride);
        rseq_pool_attr_destroy(attr);
 
        for (;;) {
@@ -55,7 +55,7 @@ static void test_mempool_fill(size_t len)
                if (!ptr)
                        break;
                /* Link items in cpu 0. */
-               cpuptr = rseq_percpu_ptr(ptr, 0);
+               cpuptr = (struct test_data *) __rseq_percpu_ptr(ptr, 0, stride);
                cpuptr->backref = ptr;
                /* Randomize items in list. */
                if (count & 1)
@@ -65,12 +65,12 @@ static void test_mempool_fill(size_t len)
                count++;
        }
 
-       ok(count * sizeof(struct test_data) == len, "Allocated %" PRIu64 " objects in pool", count);
+       ok(count * sizeof(struct test_data) == stride, "Allocated %" PRIu64 " objects in pool", count);
 
        list_for_each_entry(iter, &list, node) {
                ptr = iter->backref;
                for (i = 0; i < CPU_SETSIZE; i++) {
-                       struct test_data *cpuptr = rseq_percpu_ptr(ptr, i);
+                       struct test_data *cpuptr = (struct test_data *) __rseq_percpu_ptr(ptr, i, stride);
 
                        if (cpuptr->value != 0)
                                abort();
@@ -82,7 +82,7 @@ static void test_mempool_fill(size_t len)
 
        list_for_each_entry_safe(iter, tmp, &list, node) {
                ptr = iter->backref;
-               rseq_percpu_free(ptr);
+               __rseq_percpu_free(ptr, stride);
        }
        ret = rseq_percpu_pool_destroy(mempool);
        ok(ret == 0, "Destroy mempool");
index b3c4ce017a4d7cdafb1f8a173e1d686e65cb365c..c3b5e8d53e9830f8a9b68908917402ec84d4eac6 100644 (file)
@@ -22,8 +22,6 @@
 #include <stdbool.h>
 #include <rseq/mempool.h>
 
-#define PERCPU_POOL_LEN                (1024*1024)     /* 1MB */
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)
 enum {
        MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ                   = (1 << 7),
@@ -501,7 +499,7 @@ static void test_percpu_spinlock(void)
 
        mempool = rseq_percpu_pool_create("spinlock_test_data",
                        sizeof(struct spinlock_test_data),
-                       PERCPU_POOL_LEN, CPU_SETSIZE, NULL);
+                       0, CPU_SETSIZE, NULL);
        if (!mempool) {
                perror("rseq_percpu_pool_create");
                abort();
@@ -597,7 +595,7 @@ static void test_percpu_inc(void)
 
        mempool = rseq_percpu_pool_create("inc_test_data",
                        sizeof(struct inc_test_data),
-                       PERCPU_POOL_LEN, CPU_SETSIZE, NULL);
+                       0, CPU_SETSIZE, NULL);
        if (!mempool) {
                perror("rseq_percpu_pool_create");
                abort();
@@ -770,7 +768,7 @@ static void test_percpu_list(void)
        struct rseq_percpu_pool *mempool;
 
        mempool = rseq_percpu_pool_create("percpu_list", sizeof(struct percpu_list),
-                       PERCPU_POOL_LEN, CPU_SETSIZE, NULL);
+                       0, CPU_SETSIZE, NULL);
        if (!mempool) {
                perror("rseq_percpu_pool_create");
                abort();
@@ -981,7 +979,7 @@ static void test_percpu_buffer(void)
        struct rseq_percpu_pool *mempool;
 
        mempool = rseq_percpu_pool_create("percpu_buffer", sizeof(struct percpu_buffer),
-                       PERCPU_POOL_LEN, CPU_SETSIZE, NULL);
+                       0, CPU_SETSIZE, NULL);
        if (!mempool) {
                perror("rseq_percpu_pool_create");
                abort();
@@ -1222,7 +1220,7 @@ static void test_percpu_memcpy_buffer(void)
 
        mempool = rseq_percpu_pool_create("percpu_memcpy_buffer",
                        sizeof(struct percpu_memcpy_buffer),
-                       PERCPU_POOL_LEN, CPU_SETSIZE, NULL);
+                       0, CPU_SETSIZE, NULL);
        if (!mempool) {
                perror("rseq_percpu_pool_create");
                abort();
@@ -1468,7 +1466,7 @@ void *test_membarrier_manager_thread(void *arg)
        long long total_count = 0;
 
        mempool = rseq_percpu_pool_create("percpu_list", sizeof(struct percpu_list),
-                       PERCPU_POOL_LEN, CPU_SETSIZE, NULL);
+                       0, CPU_SETSIZE, NULL);
        if (!mempool) {
                perror("rseq_percpu_pool_create");
                abort();
This page took 0.033712 seconds and 4 git commands to generate.