Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/security/test/https-first/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 6 kB image not shown  

Quelle  basic_percpu_ops_test.c   Sprache: unbekannt

 
// SPDX-License-Identifier: LGPL-2.1
#define _GNU_SOURCE
#include <assert.h>
#include <pthread.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

#include "../kselftest.h"
#include "rseq.h"

#ifdef BUILDOPT_RSEQ_PERCPU_MM_CID
define RSEQ_PERCPU RSEQ_PERCPU_MM_CID
static
int get_current_cpu_id(void)
{
 return rseq_current_mm_cid();
}
static
bool rseq_validate_cpu_id(void)
{
 return rseq_mm_cid_available();
}
static
bool rseq_use_cpu_index(void)
{
 return false/* Use mm_cid */
}
#else
define RSEQ_PERCPU RSEQ_PERCPU_CPU_ID
static
int get_current_cpu_id(void)
{
 return rseq_cpu_start();
}
static
bool rseq_validate_cpu_id(void)
{
 return rseq_current_cpu_raw() >= 0;
}
static
bool rseq_use_cpu_index(void)
{
 return true/* Use cpu_id as index. */
}
#endif

struct percpu_lock_entry {
 intptr_t v;
} __attribute__((aligned(128)));

struct percpu_lock {
 struct percpu_lock_entry c[CPU_SETSIZE];
};

struct test_data_entry {
 intptr_t count;
} __attribute__((aligned(128)));

struct spinlock_test_data {
 struct percpu_lock lock;
 struct test_data_entry c[CPU_SETSIZE];
 int reps;
};

struct percpu_list_node {
 intptr_t data;
 struct percpu_list_node *next;
};

struct percpu_list_entry {
 struct percpu_list_node *head;
} __attribute__((aligned(128)));

struct percpu_list {
 struct percpu_list_entry c[CPU_SETSIZE];
};

/* A simple percpu spinlock.  Returns the cpu lock was acquired on. */
int rseq_this_cpu_lock(struct percpu_lock *lock)
{
 int cpu;

 for (;;) {
  int ret;

  cpu = get_current_cpu_id();
  ret = rseq_cmpeqv_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU,
      &lock->c[cpu].v, 01, cpu);
  if (rseq_likely(!ret))
   break;
  /* Retry if comparison fails or rseq aborts. */
 }
 /*
 * Acquire semantic when taking lock after control dependency.
 * Matches rseq_smp_store_release().
 */

 rseq_smp_acquire__after_ctrl_dep();
 return cpu;
}

void rseq_percpu_unlock(struct percpu_lock *lock, int cpu)
{
 assert(lock->c[cpu].v == 1);
 /*
 * Release lock, with release semantic. Matches
 * rseq_smp_acquire__after_ctrl_dep().
 */

 rseq_smp_store_release(&lock->c[cpu].v, 0);
}

void *test_percpu_spinlock_thread(void *arg)
{
 struct spinlock_test_data *data = arg;
 int i, cpu;

 if (rseq_register_current_thread()) {
  fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  abort();
 }
 for (i = 0; i < data->reps; i++) {
  cpu = rseq_this_cpu_lock(&data->lock);
  data->c[cpu].count++;
  rseq_percpu_unlock(&data->lock, cpu);
 }
 if (rseq_unregister_current_thread()) {
  fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  abort();
 }

 return NULL;
}

/*
 * A simple test which implements a sharded counter using a per-cpu
 * lock.  Obviously real applications might prefer to simply use a
 * per-cpu increment; however, this is reasonable for a test and the
 * lock can be extended to synchronize more complicated operations.
 */

void test_percpu_spinlock(void)
{
 const int num_threads = 200;
 int i;
 uint64_t sum;
 pthread_t test_threads[num_threads];
 struct spinlock_test_data data;

 memset(&data, 0sizeof(data));
 data.reps = 5000;

 for (i = 0; i < num_threads; i++)
  pthread_create(&test_threads[i], NULL,
          test_percpu_spinlock_thread, &data);

 for (i = 0; i < num_threads; i++)
  pthread_join(test_threads[i], NULL);

 sum = 0;
 for (i = 0; i < CPU_SETSIZE; i++)
  sum += data.c[i].count;

 assert(sum == (uint64_t)data.reps * num_threads);
}

void this_cpu_list_push(struct percpu_list *list,
   struct percpu_list_node *node,
   int *_cpu)
{
 int cpu;

 for (;;) {
  intptr_t *targetptr, newval, expect;
  int ret;

  cpu = get_current_cpu_id();
  /* Load list->c[cpu].head with single-copy atomicity. */
  expect = (intptr_t)RSEQ_READ_ONCE(list->c[cpu].head);
  newval = (intptr_t)node;
  targetptr = (intptr_t *)&list->c[cpu].head;
  node->next = (struct percpu_list_node *)expect;
  ret = rseq_cmpeqv_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU,
      targetptr, expect, newval, cpu);
  if (rseq_likely(!ret))
   break;
  /* Retry if comparison fails or rseq aborts. */
 }
 if (_cpu)
  *_cpu = cpu;
}

/*
 * Unlike a traditional lock-less linked list; the availability of a
 * rseq primitive allows us to implement pop without concerns over
 * ABA-type races.
 */

struct percpu_list_node *this_cpu_list_pop(struct percpu_list *list,
        int *_cpu)
{
 for (;;) {
  struct percpu_list_node *head;
  intptr_t *targetptr, expectnot, *load;
  long offset;
  int ret, cpu;

  cpu = get_current_cpu_id();
  targetptr = (intptr_t *)&list->c[cpu].head;
  expectnot = (intptr_t)NULL;
  offset = offsetof(struct percpu_list_node, next);
  load = (intptr_t *)&head;
  ret = rseq_cmpnev_storeoffp_load(RSEQ_MO_RELAXED, RSEQ_PERCPU,
       targetptr, expectnot,
       offset, load, cpu);
  if (rseq_likely(!ret)) {
   if (_cpu)
    *_cpu = cpu;
   return head;
  }
  if (ret > 0)
   return NULL;
  /* Retry if rseq aborts. */
 }
}

/*
 * __percpu_list_pop is not safe against concurrent accesses. Should
 * only be used on lists that are not concurrently modified.
 */

struct percpu_list_node *__percpu_list_pop(struct percpu_list *list, int cpu)
{
 struct percpu_list_node *node;

 node = list->c[cpu].head;
 if (!node)
  return NULL;
 list->c[cpu].head = node->next;
 return node;
}

void *test_percpu_list_thread(void *arg)
{
 int i;
 struct percpu_list *list = (struct percpu_list *)arg;

 if (rseq_register_current_thread()) {
  fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  abort();
 }

 for (i = 0; i < 100000; i++) {
  struct percpu_list_node *node;

  node = this_cpu_list_pop(list, NULL);
  sched_yield();  /* encourage shuffling */
  if (node)
   this_cpu_list_push(list, node, NULL);
 }

 if (rseq_unregister_current_thread()) {
  fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  abort();
 }

 return NULL;
}

/* Simultaneous modification to a per-cpu linked list from many threads.  */
void test_percpu_list(void)
{
 int i, j;
 uint64_t sum = 0, expected_sum = 0;
 struct percpu_list list;
 pthread_t test_threads[200];
 cpu_set_t allowed_cpus;

 memset(&list, 0sizeof(list));

 /* Generate list entries for every usable cpu. */
 sched_getaffinity(0sizeof(allowed_cpus), &allowed_cpus);
 for (i = 0; i < CPU_SETSIZE; i++) {
  if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
   continue;
  for (j = 1; j <= 100; j++) {
   struct percpu_list_node *node;

   expected_sum += j;

   node = malloc(sizeof(*node));
   assert(node);
   node->data = j;
   node->next = list.c[i].head;
   list.c[i].head = node;
  }
 }

 for (i = 0; i < 200; i++)
  pthread_create(&test_threads[i], NULL,
         test_percpu_list_thread, &list);

 for (i = 0; i < 200; i++)
  pthread_join(test_threads[i], NULL);

 for (i = 0; i < CPU_SETSIZE; i++) {
  struct percpu_list_node *node;

  if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
   continue;

  while ((node = __percpu_list_pop(&list, i))) {
   sum += node->data;
   free(node);
  }
 }

 /*
 * All entries should now be accounted for (unless some external
 * actor is interfering with our allowed affinity while this
 * test is running).
 */

 assert(sum == expected_sum);
}

int main(int argc, char **argv)
{
 if (rseq_register_current_thread()) {
  fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  goto error;
 }
 if (!rseq_validate_cpu_id()) {
  fprintf(stderr, "Error: cpu id getter unavailable\n");
  goto error;
 }
 printf("spinlock\n");
 test_percpu_spinlock();
 printf("percpu_list\n");
 test_percpu_list();
 if (rseq_unregister_current_thread()) {
  fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  goto error;
 }
 return 0;

error:
 return -1;
}

Messung V0.5 in Prozent
C=98 H=94 G=95

[Dauer der Verarbeitung: 0.5 Sekunden, vorverarbeitet 2026-06-08]