0001
0002 #define _GNU_SOURCE
0003 #include <assert.h>
0004 #include <pthread.h>
0005 #include <sched.h>
0006 #include <stdint.h>
0007 #include <stdio.h>
0008 #include <stdlib.h>
0009 #include <string.h>
0010 #include <stddef.h>
0011
0012 #include "../kselftest.h"
0013 #include "rseq.h"
0014
0015 struct percpu_lock_entry {
0016 intptr_t v;
0017 } __attribute__((aligned(128)));
0018
0019 struct percpu_lock {
0020 struct percpu_lock_entry c[CPU_SETSIZE];
0021 };
0022
0023 struct test_data_entry {
0024 intptr_t count;
0025 } __attribute__((aligned(128)));
0026
0027 struct spinlock_test_data {
0028 struct percpu_lock lock;
0029 struct test_data_entry c[CPU_SETSIZE];
0030 int reps;
0031 };
0032
0033 struct percpu_list_node {
0034 intptr_t data;
0035 struct percpu_list_node *next;
0036 };
0037
0038 struct percpu_list_entry {
0039 struct percpu_list_node *head;
0040 } __attribute__((aligned(128)));
0041
0042 struct percpu_list {
0043 struct percpu_list_entry c[CPU_SETSIZE];
0044 };
0045
0046
0047 int rseq_this_cpu_lock(struct percpu_lock *lock)
0048 {
0049 int cpu;
0050
0051 for (;;) {
0052 int ret;
0053
0054 cpu = rseq_cpu_start();
0055 ret = rseq_cmpeqv_storev(&lock->c[cpu].v,
0056 0, 1, cpu);
0057 if (rseq_likely(!ret))
0058 break;
0059
0060 }
0061
0062
0063
0064
0065 rseq_smp_acquire__after_ctrl_dep();
0066 return cpu;
0067 }
0068
0069 void rseq_percpu_unlock(struct percpu_lock *lock, int cpu)
0070 {
0071 assert(lock->c[cpu].v == 1);
0072
0073
0074
0075
0076 rseq_smp_store_release(&lock->c[cpu].v, 0);
0077 }
0078
0079 void *test_percpu_spinlock_thread(void *arg)
0080 {
0081 struct spinlock_test_data *data = arg;
0082 int i, cpu;
0083
0084 if (rseq_register_current_thread()) {
0085 fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
0086 errno, strerror(errno));
0087 abort();
0088 }
0089 for (i = 0; i < data->reps; i++) {
0090 cpu = rseq_this_cpu_lock(&data->lock);
0091 data->c[cpu].count++;
0092 rseq_percpu_unlock(&data->lock, cpu);
0093 }
0094 if (rseq_unregister_current_thread()) {
0095 fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
0096 errno, strerror(errno));
0097 abort();
0098 }
0099
0100 return NULL;
0101 }
0102
0103
0104
0105
0106
0107
0108
0109 void test_percpu_spinlock(void)
0110 {
0111 const int num_threads = 200;
0112 int i;
0113 uint64_t sum;
0114 pthread_t test_threads[num_threads];
0115 struct spinlock_test_data data;
0116
0117 memset(&data, 0, sizeof(data));
0118 data.reps = 5000;
0119
0120 for (i = 0; i < num_threads; i++)
0121 pthread_create(&test_threads[i], NULL,
0122 test_percpu_spinlock_thread, &data);
0123
0124 for (i = 0; i < num_threads; i++)
0125 pthread_join(test_threads[i], NULL);
0126
0127 sum = 0;
0128 for (i = 0; i < CPU_SETSIZE; i++)
0129 sum += data.c[i].count;
0130
0131 assert(sum == (uint64_t)data.reps * num_threads);
0132 }
0133
0134 void this_cpu_list_push(struct percpu_list *list,
0135 struct percpu_list_node *node,
0136 int *_cpu)
0137 {
0138 int cpu;
0139
0140 for (;;) {
0141 intptr_t *targetptr, newval, expect;
0142 int ret;
0143
0144 cpu = rseq_cpu_start();
0145
0146 expect = (intptr_t)RSEQ_READ_ONCE(list->c[cpu].head);
0147 newval = (intptr_t)node;
0148 targetptr = (intptr_t *)&list->c[cpu].head;
0149 node->next = (struct percpu_list_node *)expect;
0150 ret = rseq_cmpeqv_storev(targetptr, expect, newval, cpu);
0151 if (rseq_likely(!ret))
0152 break;
0153
0154 }
0155 if (_cpu)
0156 *_cpu = cpu;
0157 }
0158
0159
0160
0161
0162
0163
0164 struct percpu_list_node *this_cpu_list_pop(struct percpu_list *list,
0165 int *_cpu)
0166 {
0167 for (;;) {
0168 struct percpu_list_node *head;
0169 intptr_t *targetptr, expectnot, *load;
0170 long offset;
0171 int ret, cpu;
0172
0173 cpu = rseq_cpu_start();
0174 targetptr = (intptr_t *)&list->c[cpu].head;
0175 expectnot = (intptr_t)NULL;
0176 offset = offsetof(struct percpu_list_node, next);
0177 load = (intptr_t *)&head;
0178 ret = rseq_cmpnev_storeoffp_load(targetptr, expectnot,
0179 offset, load, cpu);
0180 if (rseq_likely(!ret)) {
0181 if (_cpu)
0182 *_cpu = cpu;
0183 return head;
0184 }
0185 if (ret > 0)
0186 return NULL;
0187
0188 }
0189 }
0190
0191
0192
0193
0194
0195 struct percpu_list_node *__percpu_list_pop(struct percpu_list *list, int cpu)
0196 {
0197 struct percpu_list_node *node;
0198
0199 node = list->c[cpu].head;
0200 if (!node)
0201 return NULL;
0202 list->c[cpu].head = node->next;
0203 return node;
0204 }
0205
0206 void *test_percpu_list_thread(void *arg)
0207 {
0208 int i;
0209 struct percpu_list *list = (struct percpu_list *)arg;
0210
0211 if (rseq_register_current_thread()) {
0212 fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
0213 errno, strerror(errno));
0214 abort();
0215 }
0216
0217 for (i = 0; i < 100000; i++) {
0218 struct percpu_list_node *node;
0219
0220 node = this_cpu_list_pop(list, NULL);
0221 sched_yield();
0222 if (node)
0223 this_cpu_list_push(list, node, NULL);
0224 }
0225
0226 if (rseq_unregister_current_thread()) {
0227 fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
0228 errno, strerror(errno));
0229 abort();
0230 }
0231
0232 return NULL;
0233 }
0234
0235
0236 void test_percpu_list(void)
0237 {
0238 int i, j;
0239 uint64_t sum = 0, expected_sum = 0;
0240 struct percpu_list list;
0241 pthread_t test_threads[200];
0242 cpu_set_t allowed_cpus;
0243
0244 memset(&list, 0, sizeof(list));
0245
0246
0247 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
0248 for (i = 0; i < CPU_SETSIZE; i++) {
0249 if (!CPU_ISSET(i, &allowed_cpus))
0250 continue;
0251 for (j = 1; j <= 100; j++) {
0252 struct percpu_list_node *node;
0253
0254 expected_sum += j;
0255
0256 node = malloc(sizeof(*node));
0257 assert(node);
0258 node->data = j;
0259 node->next = list.c[i].head;
0260 list.c[i].head = node;
0261 }
0262 }
0263
0264 for (i = 0; i < 200; i++)
0265 pthread_create(&test_threads[i], NULL,
0266 test_percpu_list_thread, &list);
0267
0268 for (i = 0; i < 200; i++)
0269 pthread_join(test_threads[i], NULL);
0270
0271 for (i = 0; i < CPU_SETSIZE; i++) {
0272 struct percpu_list_node *node;
0273
0274 if (!CPU_ISSET(i, &allowed_cpus))
0275 continue;
0276
0277 while ((node = __percpu_list_pop(&list, i))) {
0278 sum += node->data;
0279 free(node);
0280 }
0281 }
0282
0283
0284
0285
0286
0287
0288 assert(sum == expected_sum);
0289 }
0290
0291 int main(int argc, char **argv)
0292 {
0293 if (rseq_register_current_thread()) {
0294 fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
0295 errno, strerror(errno));
0296 goto error;
0297 }
0298 printf("spinlock\n");
0299 test_percpu_spinlock();
0300 printf("percpu_list\n");
0301 test_percpu_list();
0302 if (rseq_unregister_current_thread()) {
0303 fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
0304 errno, strerror(errno));
0305 goto error;
0306 }
0307 return 0;
0308
0309 error:
0310 return -1;
0311 }