hc
2024-05-13 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e
kernel/kernel/bpf/percpu_freelist.c
....@@ -1,8 +1,5 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /* Copyright (c) 2016 Facebook
2
- *
3
- * This program is free software; you can redistribute it and/or
4
- * modify it under the terms of version 2 of the GNU General Public
5
- * License as published by the Free Software Foundation.
63 */
74 #include "percpu_freelist.h"
85
....@@ -20,6 +17,8 @@
2017 raw_spin_lock_init(&head->lock);
2118 head->first = NULL;
2219 }
20
+ raw_spin_lock_init(&s->extralist.lock);
21
+ s->extralist.first = NULL;
2322 return 0;
2423 }
2524
....@@ -28,21 +27,65 @@
2827 free_percpu(s->freelist);
2928 }
3029
30
+static inline void pcpu_freelist_push_node(struct pcpu_freelist_head *head,
31
+ struct pcpu_freelist_node *node)
32
+{
33
+ node->next = head->first;
34
+ head->first = node;
35
+}
36
+
3137 static inline void ___pcpu_freelist_push(struct pcpu_freelist_head *head,
3238 struct pcpu_freelist_node *node)
3339 {
3440 raw_spin_lock(&head->lock);
35
- node->next = head->first;
36
- head->first = node;
41
+ pcpu_freelist_push_node(head, node);
3742 raw_spin_unlock(&head->lock);
43
+}
44
+
45
+static inline bool pcpu_freelist_try_push_extra(struct pcpu_freelist *s,
46
+ struct pcpu_freelist_node *node)
47
+{
48
+ if (!raw_spin_trylock(&s->extralist.lock))
49
+ return false;
50
+
51
+ pcpu_freelist_push_node(&s->extralist, node);
52
+ raw_spin_unlock(&s->extralist.lock);
53
+ return true;
54
+}
55
+
56
+static inline void ___pcpu_freelist_push_nmi(struct pcpu_freelist *s,
57
+ struct pcpu_freelist_node *node)
58
+{
59
+ int cpu, orig_cpu;
60
+
61
+ orig_cpu = cpu = raw_smp_processor_id();
62
+ while (1) {
63
+ struct pcpu_freelist_head *head;
64
+
65
+ head = per_cpu_ptr(s->freelist, cpu);
66
+ if (raw_spin_trylock(&head->lock)) {
67
+ pcpu_freelist_push_node(head, node);
68
+ raw_spin_unlock(&head->lock);
69
+ return;
70
+ }
71
+ cpu = cpumask_next(cpu, cpu_possible_mask);
72
+ if (cpu >= nr_cpu_ids)
73
+ cpu = 0;
74
+
75
+ /* cannot lock any per cpu lock, try extralist */
76
+ if (cpu == orig_cpu &&
77
+ pcpu_freelist_try_push_extra(s, node))
78
+ return;
79
+ }
3880 }
3981
4082 void __pcpu_freelist_push(struct pcpu_freelist *s,
4183 struct pcpu_freelist_node *node)
4284 {
43
- struct pcpu_freelist_head *head = this_cpu_ptr(s->freelist);
44
-
45
- ___pcpu_freelist_push(head, node);
85
+ if (in_nmi())
86
+ ___pcpu_freelist_push_nmi(s, node);
87
+ else
88
+ ___pcpu_freelist_push(this_cpu_ptr(s->freelist), node);
4689 }
4790
4891 void pcpu_freelist_push(struct pcpu_freelist *s,
....@@ -59,32 +102,25 @@
59102 u32 nr_elems)
60103 {
61104 struct pcpu_freelist_head *head;
62
- unsigned long flags;
63
- int i, cpu, pcpu_entries;
105
+ unsigned int cpu, cpu_idx, i, j, n, m;
64106
65
- pcpu_entries = nr_elems / num_possible_cpus() + 1;
66
- i = 0;
107
+ n = nr_elems / num_possible_cpus();
108
+ m = nr_elems % num_possible_cpus();
67109
68
- /* disable irq to workaround lockdep false positive
69
- * in bpf usage pcpu_freelist_populate() will never race
70
- * with pcpu_freelist_push()
71
- */
72
- local_irq_save(flags);
110
+ cpu_idx = 0;
73111 for_each_possible_cpu(cpu) {
74
-again:
75112 head = per_cpu_ptr(s->freelist, cpu);
76
- ___pcpu_freelist_push(head, buf);
77
- i++;
78
- buf += elem_size;
79
- if (i == nr_elems)
80
- break;
81
- if (i % pcpu_entries)
82
- goto again;
113
+ j = n + (cpu_idx < m ? 1 : 0);
114
+ for (i = 0; i < j; i++) {
115
+ /* No locking required as this is not visible yet. */
116
+ pcpu_freelist_push_node(head, buf);
117
+ buf += elem_size;
118
+ }
119
+ cpu_idx++;
83120 }
84
- local_irq_restore(flags);
85121 }
86122
87
-struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
123
+static struct pcpu_freelist_node *___pcpu_freelist_pop(struct pcpu_freelist *s)
88124 {
89125 struct pcpu_freelist_head *head;
90126 struct pcpu_freelist_node *node;
....@@ -105,8 +141,59 @@
105141 if (cpu >= nr_cpu_ids)
106142 cpu = 0;
107143 if (cpu == orig_cpu)
108
- return NULL;
144
+ break;
109145 }
146
+
147
+ /* per cpu lists are all empty, try extralist */
148
+ raw_spin_lock(&s->extralist.lock);
149
+ node = s->extralist.first;
150
+ if (node)
151
+ s->extralist.first = node->next;
152
+ raw_spin_unlock(&s->extralist.lock);
153
+ return node;
154
+}
155
+
156
+static struct pcpu_freelist_node *
157
+___pcpu_freelist_pop_nmi(struct pcpu_freelist *s)
158
+{
159
+ struct pcpu_freelist_head *head;
160
+ struct pcpu_freelist_node *node;
161
+ int orig_cpu, cpu;
162
+
163
+ orig_cpu = cpu = raw_smp_processor_id();
164
+ while (1) {
165
+ head = per_cpu_ptr(s->freelist, cpu);
166
+ if (raw_spin_trylock(&head->lock)) {
167
+ node = head->first;
168
+ if (node) {
169
+ head->first = node->next;
170
+ raw_spin_unlock(&head->lock);
171
+ return node;
172
+ }
173
+ raw_spin_unlock(&head->lock);
174
+ }
175
+ cpu = cpumask_next(cpu, cpu_possible_mask);
176
+ if (cpu >= nr_cpu_ids)
177
+ cpu = 0;
178
+ if (cpu == orig_cpu)
179
+ break;
180
+ }
181
+
182
+ /* cannot pop from per cpu lists, try extralist */
183
+ if (!raw_spin_trylock(&s->extralist.lock))
184
+ return NULL;
185
+ node = s->extralist.first;
186
+ if (node)
187
+ s->extralist.first = node->next;
188
+ raw_spin_unlock(&s->extralist.lock);
189
+ return node;
190
+}
191
+
192
+struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
193
+{
194
+ if (in_nmi())
195
+ return ___pcpu_freelist_pop_nmi(s);
196
+ return ___pcpu_freelist_pop(s);
110197 }
111198
112199 struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s)