hc
2023-11-06 15ade055295d13f95d49e3d99b09f3bbfb4a43e7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/* Copyright (c) 2016 Facebook
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 */
#include "percpu_freelist.h"
 
int pcpu_freelist_init(struct pcpu_freelist *s)
{
   int cpu;
 
   s->freelist = alloc_percpu(struct pcpu_freelist_head);
   if (!s->freelist)
       return -ENOMEM;
 
   for_each_possible_cpu(cpu) {
       struct pcpu_freelist_head *head = per_cpu_ptr(s->freelist, cpu);
 
       raw_spin_lock_init(&head->lock);
       head->first = NULL;
   }
   return 0;
}
 
void pcpu_freelist_destroy(struct pcpu_freelist *s)
{
   free_percpu(s->freelist);
}
 
static inline void ___pcpu_freelist_push(struct pcpu_freelist_head *head,
                    struct pcpu_freelist_node *node)
{
   raw_spin_lock(&head->lock);
   node->next = head->first;
   head->first = node;
   raw_spin_unlock(&head->lock);
}
 
void __pcpu_freelist_push(struct pcpu_freelist *s,
           struct pcpu_freelist_node *node)
{
   struct pcpu_freelist_head *head = this_cpu_ptr(s->freelist);
 
   ___pcpu_freelist_push(head, node);
}
 
void pcpu_freelist_push(struct pcpu_freelist *s,
           struct pcpu_freelist_node *node)
{
   unsigned long flags;
 
   local_irq_save(flags);
   __pcpu_freelist_push(s, node);
   local_irq_restore(flags);
}
 
void pcpu_freelist_populate(struct pcpu_freelist *s, void *buf, u32 elem_size,
               u32 nr_elems)
{
   struct pcpu_freelist_head *head;
   unsigned long flags;
   int i, cpu, pcpu_entries;
 
   pcpu_entries = nr_elems / num_possible_cpus() + 1;
   i = 0;
 
   /* disable irq to workaround lockdep false positive
    * in bpf usage pcpu_freelist_populate() will never race
    * with pcpu_freelist_push()
    */
   local_irq_save(flags);
   for_each_possible_cpu(cpu) {
again:
       head = per_cpu_ptr(s->freelist, cpu);
       ___pcpu_freelist_push(head, buf);
       i++;
       buf += elem_size;
       if (i == nr_elems)
           break;
       if (i % pcpu_entries)
           goto again;
   }
   local_irq_restore(flags);
}
 
struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
{
   struct pcpu_freelist_head *head;
   struct pcpu_freelist_node *node;
   int orig_cpu, cpu;
 
   orig_cpu = cpu = raw_smp_processor_id();
   while (1) {
       head = per_cpu_ptr(s->freelist, cpu);
       raw_spin_lock(&head->lock);
       node = head->first;
       if (node) {
           head->first = node->next;
           raw_spin_unlock(&head->lock);
           return node;
       }
       raw_spin_unlock(&head->lock);
       cpu = cpumask_next(cpu, cpu_possible_mask);
       if (cpu >= nr_cpu_ids)
           cpu = 0;
       if (cpu == orig_cpu)
           return NULL;
   }
}
 
struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s)
{
   struct pcpu_freelist_node *ret;
   unsigned long flags;
 
   local_irq_save(flags);
   ret = __pcpu_freelist_pop(s);
   local_irq_restore(flags);
   return ret;
}