]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_tc.c
zebra: do not load/store wider-than-ptr atomics
[mirror_frr.git] / zebra / zebra_tc.c
CommitLineData
c317d3f2
SY
1/*
2 * Zebra Traffic Control (TC) main handling.
3 *
4 * Copyright (C) 2022 Shichu Yang
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <zebra.h>
22
23#include <jhash.h>
24#include <hash.h>
25#include <memory.h>
26#include <hook.h>
27
28#include "zebra/zebra_router.h"
29#include "zebra/zebra_dplane.h"
30#include "zebra/zebra_tc.h"
31#include "zebra/debug.h"
32
33DEFINE_MTYPE_STATIC(ZEBRA, TC_QDISC, "TC queue discipline");
34DEFINE_MTYPE_STATIC(ZEBRA, TC_CLASS, "TC class");
35DEFINE_MTYPE_STATIC(ZEBRA, TC_FILTER, "TC filter");
36
37const struct message tc_qdisc_kinds[] = {
38 {TC_QDISC_HTB, "htb"},
39 {TC_QDISC_NOQUEUE, "noqueue"},
40 {0},
41};
42
43const struct message tc_filter_kinds[] = {
44 {TC_FILTER_BPF, "bpf"},
45 {TC_FILTER_FLOW, "flow"},
46 {TC_FILTER_FLOWER, "flower"},
47 {TC_FILTER_U32, "u32"},
48 {0},
49};
50
51const struct message *tc_class_kinds = tc_qdisc_kinds;
52
53static uint32_t lookup_key(const struct message *mz, const char *msg,
54 uint32_t nf)
55{
56 static struct message nt = {0};
57 uint32_t rz = nf ? nf : UINT32_MAX;
58 const struct message *pnt;
59
60 for (pnt = mz; memcmp(pnt, &nt, sizeof(struct message)); pnt++)
61 if (strcmp(pnt->str, msg) == 0) {
62 rz = pnt->key;
63 break;
64 }
65 return rz;
66}
67
68const char *tc_qdisc_kind2str(uint32_t type)
69{
70 return lookup_msg(tc_qdisc_kinds, type, "Unrecognized QDISC Type");
71}
72
73enum tc_qdisc_kind tc_qdisc_str2kind(const char *type)
74{
75 return lookup_key(tc_qdisc_kinds, type, TC_QDISC_UNSPEC);
76}
77
78uint32_t zebra_tc_qdisc_hash_key(const void *arg)
79{
80 const struct zebra_tc_qdisc *qdisc;
81 uint32_t key;
82
83 qdisc = arg;
84
85 key = jhash_1word(qdisc->qdisc.ifindex, 0);
86
87 return key;
88}
89
90bool zebra_tc_qdisc_hash_equal(const void *arg1, const void *arg2)
91{
92 const struct zebra_tc_qdisc *q1, *q2;
93
94 q1 = (const struct zebra_tc_qdisc *)arg1;
95 q2 = (const struct zebra_tc_qdisc *)arg2;
96
97 if (q1->qdisc.ifindex != q2->qdisc.ifindex)
98 return false;
99
100 return true;
101}
102
103struct tc_qdisc_ifindex_lookup {
104 struct zebra_tc_qdisc *qdisc;
105 ifindex_t ifindex;
106};
107
108
109static int tc_qdisc_lookup_ifindex_walker(struct hash_bucket *b, void *data)
110{
111 struct tc_qdisc_ifindex_lookup *lookup = data;
112 struct zebra_tc_qdisc *qdisc = b->data;
113
114 if (lookup->ifindex == qdisc->qdisc.ifindex) {
115 lookup->qdisc = qdisc;
116 return HASHWALK_ABORT;
117 }
118
119 return HASHWALK_CONTINUE;
120}
121
122static struct zebra_tc_qdisc *
123tc_qdisc_lookup_ifindex(struct zebra_tc_qdisc *qdisc)
124{
125 struct tc_qdisc_ifindex_lookup lookup;
126
127 lookup.ifindex = qdisc->qdisc.ifindex;
128 lookup.qdisc = NULL;
129 hash_walk(zrouter.rules_hash, &tc_qdisc_lookup_ifindex_walker, &lookup);
130
131 return lookup.qdisc;
132}
133
134static void *tc_qdisc_alloc_intern(void *arg)
135{
136 struct zebra_tc_qdisc *ztq;
137 struct zebra_tc_qdisc *new;
138
139 ztq = (struct zebra_tc_qdisc *)arg;
140
141 new = XCALLOC(MTYPE_TC_QDISC, sizeof(*new));
142
143 memcpy(new, ztq, sizeof(*ztq));
144
145 return new;
146}
147
148static struct zebra_tc_qdisc *tc_qdisc_free(struct zebra_tc_qdisc *hash_data,
149 bool free_data)
150{
151 hash_release(zrouter.qdisc_hash, hash_data);
152
153 if (free_data) {
154 XFREE(MTYPE_TC_QDISC, hash_data);
155 return NULL;
156 }
157
158 return hash_data;
159}
160
161static struct zebra_tc_qdisc *tc_qdisc_release(struct zebra_tc_qdisc *qdisc,
162 bool free_data)
163{
164 struct zebra_tc_qdisc *lookup;
165
166 lookup = hash_lookup(zrouter.qdisc_hash, qdisc);
167
168 if (!lookup)
169 return NULL;
170
171 return tc_qdisc_free(lookup, free_data);
172}
173
174void zebra_tc_qdisc_install(struct zebra_tc_qdisc *qdisc)
175{
176 if (IS_ZEBRA_DEBUG_TC)
177 zlog_debug("%s: install tc qdisc ifindex %d kind %s", __func__,
178 qdisc->qdisc.ifindex,
179 tc_qdisc_kind2str(qdisc->qdisc.kind));
180
181 struct zebra_tc_qdisc *found;
182 struct zebra_tc_qdisc *old;
183 struct zebra_tc_qdisc *new;
184
185 found = tc_qdisc_lookup_ifindex(qdisc);
186
187 if (found) {
188 if (!zebra_tc_qdisc_hash_equal(qdisc, found)) {
189 old = tc_qdisc_release(found, false);
190 (void)dplane_tc_qdisc_uninstall(old);
191 new = hash_get(zrouter.qdisc_hash, qdisc,
192 tc_qdisc_alloc_intern);
193 (void)dplane_tc_qdisc_install(new);
194 XFREE(MTYPE_TC_QDISC, old);
195 }
196 } else {
197 new = hash_get(zrouter.qdisc_hash, qdisc,
198 tc_qdisc_alloc_intern);
199 (void)dplane_tc_qdisc_install(new);
200 }
201}
202
203void zebra_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc)
204{
205 if (IS_ZEBRA_DEBUG_TC)
206 zlog_debug("%s: uninstall tc qdisc ifindex %d kind %s",
207 __func__, qdisc->qdisc.ifindex,
208 tc_qdisc_kind2str(qdisc->qdisc.kind));
209
210 (void)dplane_tc_qdisc_uninstall(qdisc);
211
212 if (tc_qdisc_release(qdisc, true))
213 zlog_debug("%s: tc qdisc being deleted we know nothing about",
214 __func__);
215}
216
217uint32_t zebra_tc_class_hash_key(const void *arg)
218{
219 const struct zebra_tc_class *class;
220 uint32_t key;
221
222 class = arg;
223
224 key = jhash_2words(class->class.ifindex, class->class.handle, 0);
225
226 return key;
227}
228
229bool zebra_tc_class_hash_equal(const void *arg1, const void *arg2)
230{
231 const struct zebra_tc_class *c1, *c2;
232
233 c1 = (const struct zebra_tc_class *)arg1;
234 c2 = (const struct zebra_tc_class *)arg2;
235
236 if (c1->class.ifindex != c2->class.ifindex)
237 return false;
238
239 if (c1->class.handle != c2->class.handle)
240 return false;
241
242 return true;
243}
244
245static void *tc_class_alloc_intern(void *arg)
246{
247 struct zebra_tc_class *class;
248 struct zebra_tc_class *new;
249
250 class = (struct zebra_tc_class *)arg;
251
252 new = XCALLOC(MTYPE_TC_CLASS, sizeof(*new));
253
254 memcpy(new, class, sizeof(*class));
255
256 return new;
257}
258
259static struct zebra_tc_class *tc_class_free(struct zebra_tc_class *hash_data,
260 bool free_data)
261{
262 hash_release(zrouter.class_hash, hash_data);
263
264 if (free_data) {
265 XFREE(MTYPE_TC_CLASS, hash_data);
266 return NULL;
267 }
268
269 return hash_data;
270}
271
272static struct zebra_tc_class *tc_class_release(struct zebra_tc_class *class,
273 bool free_data)
274{
275 struct zebra_tc_class *lookup;
276
277 lookup = hash_lookup(zrouter.class_hash, class);
278
279 if (!lookup)
280 return NULL;
281
282 return tc_class_free(lookup, free_data);
283}
284
285void zebra_tc_class_add(struct zebra_tc_class *class)
286{
287 if (IS_ZEBRA_DEBUG_TC)
288 zlog_debug(
289 "%s: add tc class ifindex %d handle %04x:%04x kind %s",
290 __func__, class->class.ifindex,
291 (class->class.handle & 0xffff0000u) >> 16,
292 class->class.handle & 0x0000ffffu,
293 tc_qdisc_kind2str(class->class.kind));
294
295 struct zebra_tc_class *found;
296 struct zebra_tc_class *new;
297
298 /*
299 * We find the class in the hash by (ifindex, handle) directly, and by
300 * testing their deep equality to seek out whether it's an update.
301 *
302 * Currently deep equality is not checked since it will be okay to
303 * update the totally same class again.
304 */
305 found = hash_lookup(zrouter.class_hash, class);
306 new = hash_get(zrouter.class_hash, class, tc_class_alloc_intern);
307
308 if (found)
309 (void)dplane_tc_class_update(new);
310 else
311 (void)dplane_tc_class_add(new);
312}
313
314void zebra_tc_class_delete(struct zebra_tc_class *class)
315{
316 if (IS_ZEBRA_DEBUG_TC)
317 zlog_debug(
318 "%s: delete tc class ifindex %d handle %04x:%04x kind %s",
319 __func__, class->class.ifindex,
320 (class->class.handle & 0xffff0000u) >> 16,
321 class->class.handle & 0x0000ffffu,
322 tc_qdisc_kind2str(class->class.kind));
323
324 (void)dplane_tc_class_delete(class);
325
326 if (tc_class_release(class, true))
327 zlog_debug("%s: tc class being deleted we know nothing about",
328 __func__);
329}
330
331const char *tc_filter_kind2str(uint32_t type)
332{
333 return lookup_msg(tc_filter_kinds, type, "Unrecognized TFILTER Type");
334}
335
336enum tc_qdisc_kind tc_filter_str2kind(const char *type)
337{
338 return lookup_key(tc_filter_kinds, type, TC_FILTER_UNSPEC);
339}
340
341uint32_t zebra_tc_filter_hash_key(const void *arg)
342{
343 const struct zebra_tc_filter *filter;
344 uint32_t key;
345
346 filter = arg;
347
348 key = jhash_2words(filter->filter.ifindex, filter->filter.handle, 0);
349
350 return key;
351}
352
353bool zebra_tc_filter_hash_equal(const void *arg1, const void *arg2)
354{
355 const struct zebra_tc_filter *f1, *f2;
356
357 f1 = (const struct zebra_tc_filter *)arg1;
358 f2 = (const struct zebra_tc_filter *)arg2;
359
360 if (f1->filter.ifindex != f2->filter.ifindex)
361 return false;
362
363 if (f1->filter.handle != f2->filter.handle)
364 return false;
365
366 return true;
367}
368
369static struct zebra_tc_filter *tc_filter_free(struct zebra_tc_filter *hash_data,
370 bool free_data)
371{
372 hash_release(zrouter.filter_hash, hash_data);
373
374 if (free_data) {
375 XFREE(MTYPE_TC_FILTER, hash_data);
376 return NULL;
377 }
378
379 return hash_data;
380}
381
382static struct zebra_tc_filter *tc_filter_release(struct zebra_tc_filter *filter,
383 bool free_data)
384{
385 struct zebra_tc_filter *lookup;
386
387 lookup = hash_lookup(zrouter.filter_hash, filter);
388
389 if (!lookup)
390 return NULL;
391
392 return tc_filter_free(lookup, free_data);
393}
394
395static void *tc_filter_alloc_intern(void *arg)
396{
397 struct zebra_tc_filter *ztf;
398 struct zebra_tc_filter *new;
399
400 ztf = (struct zebra_tc_filter *)arg;
401
402 new = XCALLOC(MTYPE_TC_FILTER, sizeof(*new));
403
404 memcpy(new, ztf, sizeof(*ztf));
405
406 return new;
407}
408
409void zebra_tc_filter_add(struct zebra_tc_filter *filter)
410{
411 if (IS_ZEBRA_DEBUG_TC)
412 zlog_debug(
413 "%s: add tc filter ifindex %d priority %u handle %08x kind %s",
414 __func__, filter->filter.ifindex,
415 filter->filter.priority, filter->filter.handle,
416 tc_filter_kind2str(filter->filter.kind));
417
418 struct zebra_tc_filter *found;
419 struct zebra_tc_filter *new;
420
421 found = hash_lookup(zrouter.filter_hash, filter);
422 new = hash_get(zrouter.filter_hash, filter, tc_filter_alloc_intern);
423
424 if (found)
425 (void)dplane_tc_filter_update(new);
426 else
427 (void)dplane_tc_filter_add(new);
428}
429
430void zebra_tc_filter_delete(struct zebra_tc_filter *filter)
431{
432 if (IS_ZEBRA_DEBUG_PBR)
433 zlog_debug(
434 "%s: delete tc filter ifindex %d priority %u handle %08x kind %s",
435 __func__, filter->filter.ifindex,
436 filter->filter.priority, filter->filter.handle,
437 tc_filter_kind2str(filter->filter.kind));
438
439 (void)dplane_tc_filter_delete(filter);
440
441 if (tc_filter_release(filter, true))
442 zlog_debug("%s: tc filter being deleted we know nothing about",
443 __func__);
444}