]>
Commit | Line | Data |
---|---|---|
955bfd98 PZ |
1 | /* |
2 | * BGP Label Pool - Manage label chunk allocations from zebra asynchronously | |
3 | * | |
4 | * Copyright (C) 2018 LabN Consulting, L.L.C. | |
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 "log.h" | |
24 | #include "memory.h" | |
25 | #include "stream.h" | |
26 | #include "mpls.h" | |
27 | #include "vty.h" | |
955bfd98 PZ |
28 | #include "linklist.h" |
29 | #include "skiplist.h" | |
30 | #include "workqueue.h" | |
31 | #include "zclient.h" | |
0e3b6a92 | 32 | #include "mpls.h" |
955bfd98 PZ |
33 | |
34 | #include "bgpd/bgpd.h" | |
35 | #include "bgpd/bgp_labelpool.h" | |
36 | #include "bgpd/bgp_debug.h" | |
14454c9f | 37 | #include "bgpd/bgp_errors.h" |
668cfa98 | 38 | #include "bgpd/bgp_route.h" |
955bfd98 | 39 | |
80853c2e PZ |
40 | #define BGP_LABELPOOL_ENABLE_TESTS 0 |
41 | ||
80853c2e | 42 | #include "bgpd/bgp_labelpool_clippy.c" |
80853c2e PZ |
43 | |
44 | ||
955bfd98 PZ |
45 | /* |
46 | * Definitions and external declarations. | |
47 | */ | |
48 | extern struct zclient *zclient; | |
49 | ||
80853c2e PZ |
50 | #if BGP_LABELPOOL_ENABLE_TESTS |
51 | static void lptest_init(void); | |
52 | static void lptest_finish(void); | |
53 | #endif | |
54 | ||
955bfd98 PZ |
55 | /* |
56 | * Remember where pool data are kept | |
57 | */ | |
58 | static struct labelpool *lp; | |
59 | ||
80853c2e PZ |
60 | /* |
61 | * Number of labels requested at a time from the zebra label manager. | |
62 | * We start small but double the request size each time up to a | |
63 | * maximum size. | |
64 | * | |
65 | * The label space is 20 bits which is shared with other FRR processes | |
66 | * on this host, so to avoid greedily requesting a mostly wasted chunk, | |
67 | * we limit the chunk size to 1/16 of the label space (that's the -4 bits | |
68 | * in the definition below). This limit slightly increases our cost of | |
69 | * finding free labels in our allocated chunks. | |
70 | */ | |
71 | #define LP_CHUNK_SIZE_MIN 128 | |
72 | #define LP_CHUNK_SIZE_MAX (1 << (20 - 4)) | |
955bfd98 | 73 | |
bf8d3d6a DL |
74 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk"); |
75 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO item"); | |
76 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment"); | |
77 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback"); | |
955bfd98 | 78 | |
955bfd98 PZ |
79 | struct lp_chunk { |
80 | uint32_t first; | |
81 | uint32_t last; | |
80853c2e PZ |
82 | uint32_t nfree; /* un-allocated count */ |
83 | uint32_t idx_last_allocated; /* start looking here */ | |
84 | bitfield_t allocated_map; | |
955bfd98 PZ |
85 | }; |
86 | ||
87 | /* | |
88 | * label control block | |
89 | */ | |
90 | struct lp_lcb { | |
91 | mpls_label_t label; /* MPLS_LABEL_NONE = not allocated */ | |
92 | int type; | |
93 | void *labelid; /* unique ID */ | |
94 | /* | |
95 | * callback for label allocation and loss | |
96 | * | |
97 | * allocated: false = lost | |
98 | */ | |
99 | int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); | |
100 | }; | |
101 | ||
955bfd98 | 102 | struct lp_fifo { |
41397f2e | 103 | struct lp_fifo_item fifo; |
955bfd98 PZ |
104 | struct lp_lcb lcb; |
105 | }; | |
106 | ||
960b9a53 | 107 | DECLARE_LIST(lp_fifo, struct lp_fifo, fifo); |
41397f2e | 108 | |
955bfd98 PZ |
109 | struct lp_cbq_item { |
110 | int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); | |
111 | int type; | |
112 | mpls_label_t label; | |
113 | void *labelid; | |
114 | bool allocated; /* false = lost */ | |
115 | }; | |
116 | ||
117 | static wq_item_status lp_cbq_docallback(struct work_queue *wq, void *data) | |
118 | { | |
119 | struct lp_cbq_item *lcbq = data; | |
120 | int rc; | |
121 | int debug = BGP_DEBUG(labelpool, LABELPOOL); | |
122 | ||
123 | if (debug) | |
124 | zlog_debug("%s: calling callback with labelid=%p label=%u allocated=%d", | |
125 | __func__, lcbq->labelid, lcbq->label, lcbq->allocated); | |
126 | ||
127 | if (lcbq->label == MPLS_LABEL_NONE) { | |
128 | /* shouldn't happen */ | |
e50f7cfd | 129 | flog_err(EC_BGP_LABEL, "%s: error: label==MPLS_LABEL_NONE", |
1c50c1c0 | 130 | __func__); |
955bfd98 PZ |
131 | return WQ_SUCCESS; |
132 | } | |
133 | ||
134 | rc = (*(lcbq->cbfunc))(lcbq->label, lcbq->labelid, lcbq->allocated); | |
135 | ||
136 | if (lcbq->allocated && rc) { | |
137 | /* | |
138 | * Callback rejected allocation. This situation could arise | |
139 | * if there was a label request followed by the requestor | |
140 | * deciding it didn't need the assignment (e.g., config | |
141 | * change) while the reply to the original request (with | |
142 | * label) was in the work queue. | |
143 | */ | |
144 | if (debug) | |
145 | zlog_debug("%s: callback rejected allocation, releasing labelid=%p label=%u", | |
146 | __func__, lcbq->labelid, lcbq->label); | |
147 | ||
148 | uintptr_t lbl = lcbq->label; | |
149 | void *labelid; | |
150 | struct lp_lcb *lcb; | |
151 | ||
152 | /* | |
153 | * If the rejected label was marked inuse by this labelid, | |
154 | * release the label back to the pool. | |
155 | * | |
156 | * Further, if the rejected label was still assigned to | |
157 | * this labelid in the LCB, delete the LCB. | |
158 | */ | |
159 | if (!skiplist_search(lp->inuse, (void *)lbl, &labelid)) { | |
160 | if (labelid == lcbq->labelid) { | |
161 | if (!skiplist_search(lp->ledger, labelid, | |
162 | (void **)&lcb)) { | |
163 | if (lcbq->label == lcb->label) | |
164 | skiplist_delete(lp->ledger, | |
165 | labelid, NULL); | |
166 | } | |
167 | skiplist_delete(lp->inuse, (void *)lbl, NULL); | |
168 | } | |
169 | } | |
170 | } | |
171 | ||
172 | return WQ_SUCCESS; | |
173 | } | |
174 | ||
175 | static void lp_cbq_item_free(struct work_queue *wq, void *data) | |
176 | { | |
177 | XFREE(MTYPE_BGP_LABEL_CBQ, data); | |
178 | } | |
179 | ||
180 | static void lp_lcb_free(void *goner) | |
181 | { | |
0a22ddfb | 182 | XFREE(MTYPE_BGP_LABEL_CB, goner); |
955bfd98 PZ |
183 | } |
184 | ||
185 | static void lp_chunk_free(void *goner) | |
186 | { | |
80853c2e PZ |
187 | struct lp_chunk *chunk = (struct lp_chunk *)goner; |
188 | ||
189 | bf_free(chunk->allocated_map); | |
0a22ddfb | 190 | XFREE(MTYPE_BGP_LABEL_CHUNK, goner); |
955bfd98 PZ |
191 | } |
192 | ||
193 | void bgp_lp_init(struct thread_master *master, struct labelpool *pool) | |
194 | { | |
195 | if (BGP_DEBUG(labelpool, LABELPOOL)) | |
196 | zlog_debug("%s: entry", __func__); | |
197 | ||
198 | lp = pool; /* Set module pointer to pool data */ | |
199 | ||
200 | lp->ledger = skiplist_new(0, NULL, lp_lcb_free); | |
201 | lp->inuse = skiplist_new(0, NULL, NULL); | |
202 | lp->chunks = list_new(); | |
203 | lp->chunks->del = lp_chunk_free; | |
41397f2e | 204 | lp_fifo_init(&lp->requests); |
955bfd98 | 205 | lp->callback_q = work_queue_new(master, "label callbacks"); |
955bfd98 PZ |
206 | |
207 | lp->callback_q->spec.workfunc = lp_cbq_docallback; | |
208 | lp->callback_q->spec.del_item_data = lp_cbq_item_free; | |
209 | lp->callback_q->spec.max_retries = 0; | |
80853c2e PZ |
210 | |
211 | lp->next_chunksize = LP_CHUNK_SIZE_MIN; | |
212 | ||
213 | #if BGP_LABELPOOL_ENABLE_TESTS | |
214 | lptest_init(); | |
215 | #endif | |
955bfd98 PZ |
216 | } |
217 | ||
992dd67e | 218 | /* check if a label callback was for a BGP LU node, and if so, unlock it */ |
ea63ff6b EDP |
219 | static void check_bgp_lu_cb_unlock(struct lp_lcb *lcb) |
220 | { | |
221 | if (lcb->type == LP_TYPE_BGP_LU) | |
992dd67e | 222 | bgp_dest_unlock_node(lcb->labelid); |
ea63ff6b EDP |
223 | } |
224 | ||
992dd67e | 225 | /* check if a label callback was for a BGP LU node, and if so, lock it */ |
ea63ff6b EDP |
226 | static void check_bgp_lu_cb_lock(struct lp_lcb *lcb) |
227 | { | |
228 | if (lcb->type == LP_TYPE_BGP_LU) | |
992dd67e | 229 | bgp_dest_lock_node(lcb->labelid); |
ea63ff6b EDP |
230 | } |
231 | ||
955bfd98 PZ |
232 | void bgp_lp_finish(void) |
233 | { | |
234 | struct lp_fifo *lf; | |
ea63ff6b | 235 | struct work_queue_item *item, *titem; |
955bfd98 | 236 | |
80853c2e PZ |
237 | #if BGP_LABELPOOL_ENABLE_TESTS |
238 | lptest_finish(); | |
239 | #endif | |
955bfd98 PZ |
240 | if (!lp) |
241 | return; | |
242 | ||
243 | skiplist_free(lp->ledger); | |
244 | lp->ledger = NULL; | |
245 | ||
246 | skiplist_free(lp->inuse); | |
247 | lp->inuse = NULL; | |
248 | ||
6a154c88 | 249 | list_delete(&lp->chunks); |
955bfd98 | 250 | |
ea63ff6b EDP |
251 | while ((lf = lp_fifo_pop(&lp->requests))) { |
252 | check_bgp_lu_cb_unlock(&lf->lcb); | |
955bfd98 | 253 | XFREE(MTYPE_BGP_LABEL_FIFO, lf); |
ea63ff6b | 254 | } |
41397f2e | 255 | lp_fifo_fini(&lp->requests); |
955bfd98 | 256 | |
ea63ff6b EDP |
257 | /* we must unlock path infos for LU callbacks; but we cannot do that |
258 | * in the deletion callback of the workqueue, as that is also called | |
259 | * to remove an element from the queue after it has been run, resulting | |
260 | * in a double unlock. Hence we need to iterate over our queues and | |
261 | * lists and manually perform the unlocking (ugh) | |
262 | */ | |
263 | STAILQ_FOREACH_SAFE (item, &lp->callback_q->items, wq, titem) | |
264 | check_bgp_lu_cb_unlock(item->data); | |
265 | ||
955bfd98 PZ |
266 | work_queue_free_and_null(&lp->callback_q); |
267 | ||
268 | lp = NULL; | |
269 | } | |
270 | ||
271 | static mpls_label_t get_label_from_pool(void *labelid) | |
272 | { | |
273 | struct listnode *node; | |
274 | struct lp_chunk *chunk; | |
275 | int debug = BGP_DEBUG(labelpool, LABELPOOL); | |
276 | ||
277 | /* | |
278 | * Find a free label | |
955bfd98 PZ |
279 | */ |
280 | for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) { | |
281 | uintptr_t lbl; | |
80853c2e | 282 | unsigned int index; |
955bfd98 PZ |
283 | |
284 | if (debug) | |
285 | zlog_debug("%s: chunk first=%u last=%u", | |
286 | __func__, chunk->first, chunk->last); | |
287 | ||
80853c2e PZ |
288 | /* |
289 | * don't look in chunks with no available labels | |
290 | */ | |
291 | if (!chunk->nfree) | |
292 | continue; | |
293 | ||
294 | /* | |
295 | * roll through bitfield starting where we stopped | |
296 | * last time | |
297 | */ | |
298 | index = bf_find_next_clear_bit_wrap( | |
299 | &chunk->allocated_map, chunk->idx_last_allocated + 1, | |
300 | 0); | |
301 | ||
302 | /* | |
303 | * since chunk->nfree is non-zero, we should always get | |
304 | * a valid index | |
305 | */ | |
306 | assert(index != WORD_MAX); | |
307 | ||
308 | lbl = chunk->first + index; | |
309 | if (skiplist_insert(lp->inuse, (void *)lbl, labelid)) { | |
310 | /* something is very wrong */ | |
311 | zlog_err("%s: unable to insert inuse label %u (id %p)", | |
312 | __func__, (uint32_t)lbl, labelid); | |
313 | return MPLS_LABEL_NONE; | |
955bfd98 | 314 | } |
80853c2e PZ |
315 | |
316 | /* | |
317 | * Success | |
318 | */ | |
319 | bf_set_bit(chunk->allocated_map, index); | |
320 | chunk->idx_last_allocated = index; | |
321 | chunk->nfree -= 1; | |
322 | ||
323 | return lbl; | |
955bfd98 | 324 | } |
80853c2e | 325 | |
955bfd98 PZ |
326 | return MPLS_LABEL_NONE; |
327 | } | |
328 | ||
329 | /* | |
330 | * Success indicated by value of "label" field in returned LCB | |
331 | */ | |
332 | static struct lp_lcb *lcb_alloc( | |
333 | int type, | |
334 | void *labelid, | |
335 | int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)) | |
336 | { | |
337 | /* | |
338 | * Set up label control block | |
339 | */ | |
340 | struct lp_lcb *new = XCALLOC(MTYPE_BGP_LABEL_CB, | |
341 | sizeof(struct lp_lcb)); | |
342 | ||
343 | new->label = get_label_from_pool(labelid); | |
344 | new->type = type; | |
345 | new->labelid = labelid; | |
346 | new->cbfunc = cbfunc; | |
347 | ||
348 | return new; | |
349 | } | |
350 | ||
351 | /* | |
352 | * Callers who need labels must supply a type, labelid, and callback. | |
353 | * The type is a value defined in bgp_labelpool.h (add types as needed). | |
354 | * The callback is for asynchronous notification of label allocation. | |
355 | * The labelid is passed as an argument to the callback. It should be unique | |
356 | * to the requested label instance. | |
357 | * | |
358 | * If zebra is not connected, callbacks with labels will be delayed | |
359 | * until connection is established. If zebra connection is lost after | |
360 | * labels have been assigned, existing assignments via this labelpool | |
361 | * module will continue until reconnection. | |
362 | * | |
363 | * When connection to zebra is reestablished, previous label assignments | |
364 | * will be invalidated (via callbacks having the "allocated" parameter unset) | |
365 | * and new labels will be automatically reassigned by this labelpool module | |
80853c2e | 366 | * (that is, a requestor does not need to call bgp_lp_get() again if it is |
955bfd98 PZ |
367 | * notified via callback that its label has been lost: it will eventually |
368 | * get another callback with a new label assignment). | |
369 | * | |
80853c2e PZ |
370 | * The callback function should return 0 to accept the allocation |
371 | * and non-zero to refuse it. The callback function return value is | |
372 | * ignored for invalidations (i.e., when the "allocated" parameter is false) | |
373 | * | |
955bfd98 PZ |
374 | * Prior requests for a given labelid are detected so that requests and |
375 | * assignments are not duplicated. | |
376 | */ | |
377 | void bgp_lp_get( | |
378 | int type, | |
379 | void *labelid, | |
380 | int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)) | |
381 | { | |
382 | struct lp_lcb *lcb; | |
383 | int requested = 0; | |
384 | int debug = BGP_DEBUG(labelpool, LABELPOOL); | |
385 | ||
386 | if (debug) | |
387 | zlog_debug("%s: labelid=%p", __func__, labelid); | |
388 | ||
389 | /* | |
390 | * Have we seen this request before? | |
391 | */ | |
392 | if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { | |
393 | requested = 1; | |
394 | } else { | |
395 | lcb = lcb_alloc(type, labelid, cbfunc); | |
396 | if (debug) | |
397 | zlog_debug("%s: inserting lcb=%p label=%u", | |
398 | __func__, lcb, lcb->label); | |
399 | int rc = skiplist_insert(lp->ledger, labelid, lcb); | |
400 | ||
401 | if (rc) { | |
402 | /* shouldn't happen */ | |
e50f7cfd | 403 | flog_err(EC_BGP_LABEL, |
1c50c1c0 QY |
404 | "%s: can't insert new LCB into ledger list", |
405 | __func__); | |
955bfd98 PZ |
406 | XFREE(MTYPE_BGP_LABEL_CB, lcb); |
407 | return; | |
408 | } | |
409 | } | |
410 | ||
411 | if (lcb->label != MPLS_LABEL_NONE) { | |
412 | /* | |
413 | * Fast path: we filled the request from local pool (or | |
414 | * this is a duplicate request that we filled already). | |
415 | * Enqueue response work item with new label. | |
416 | */ | |
417 | struct lp_cbq_item *q; | |
418 | ||
419 | q = XCALLOC(MTYPE_BGP_LABEL_CBQ, sizeof(struct lp_cbq_item)); | |
420 | ||
421 | q->cbfunc = lcb->cbfunc; | |
422 | q->type = lcb->type; | |
423 | q->label = lcb->label; | |
424 | q->labelid = lcb->labelid; | |
425 | q->allocated = true; | |
426 | ||
992dd67e | 427 | /* if this is a LU request, lock node before queueing */ |
ea63ff6b EDP |
428 | check_bgp_lu_cb_lock(lcb); |
429 | ||
955bfd98 PZ |
430 | work_queue_add(lp->callback_q, q); |
431 | ||
432 | return; | |
433 | } | |
434 | ||
435 | if (requested) | |
436 | return; | |
437 | ||
438 | if (debug) | |
439 | zlog_debug("%s: slow path. lcb=%p label=%u", | |
440 | __func__, lcb, lcb->label); | |
441 | ||
442 | /* | |
443 | * Slow path: we are out of labels in the local pool, | |
444 | * so remember the request and also get another chunk from | |
445 | * the label manager. | |
446 | * | |
447 | * We track number of outstanding label requests: don't | |
448 | * need to get a chunk for each one. | |
449 | */ | |
450 | ||
451 | struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO, | |
452 | sizeof(struct lp_fifo)); | |
453 | ||
454 | lf->lcb = *lcb; | |
992dd67e | 455 | /* if this is a LU request, lock node before queueing */ |
ea63ff6b EDP |
456 | check_bgp_lu_cb_lock(lcb); |
457 | ||
41397f2e | 458 | lp_fifo_add_tail(&lp->requests, lf); |
955bfd98 | 459 | |
41397f2e | 460 | if (lp_fifo_count(&lp->requests) > lp->pending_count) { |
ea63ff6b | 461 | if (!zclient || zclient->sock < 0) |
955bfd98 | 462 | return; |
80853c2e PZ |
463 | if (zclient_send_get_label_chunk(zclient, 0, lp->next_chunksize, |
464 | MPLS_LABEL_BASE_ANY) != | |
465 | ZCLIENT_SEND_FAILURE) { | |
466 | lp->pending_count += lp->next_chunksize; | |
467 | if ((lp->next_chunksize << 1) <= LP_CHUNK_SIZE_MAX) | |
468 | lp->next_chunksize <<= 1; | |
469 | } | |
955bfd98 PZ |
470 | } |
471 | } | |
472 | ||
473 | void bgp_lp_release( | |
474 | int type, | |
475 | void *labelid, | |
476 | mpls_label_t label) | |
477 | { | |
478 | struct lp_lcb *lcb; | |
479 | ||
480 | if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { | |
481 | if (label == lcb->label && type == lcb->type) { | |
80853c2e PZ |
482 | struct listnode *node; |
483 | struct lp_chunk *chunk; | |
955bfd98 | 484 | uintptr_t lbl = label; |
80853c2e | 485 | bool deallocated = false; |
955bfd98 PZ |
486 | |
487 | /* no longer in use */ | |
488 | skiplist_delete(lp->inuse, (void *)lbl, NULL); | |
489 | ||
490 | /* no longer requested */ | |
491 | skiplist_delete(lp->ledger, labelid, NULL); | |
80853c2e PZ |
492 | |
493 | /* | |
494 | * Find the chunk this label belongs to and | |
495 | * deallocate the label | |
496 | */ | |
497 | for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) { | |
498 | uint32_t index; | |
499 | ||
500 | if ((label < chunk->first) || | |
501 | (label > chunk->last)) | |
502 | continue; | |
503 | ||
504 | index = label - chunk->first; | |
505 | assert(bf_test_index(chunk->allocated_map, | |
506 | index)); | |
507 | bf_release_index(chunk->allocated_map, index); | |
508 | chunk->nfree += 1; | |
509 | deallocated = true; | |
510 | } | |
511 | assert(deallocated); | |
955bfd98 PZ |
512 | } |
513 | } | |
514 | } | |
515 | ||
516 | /* | |
517 | * zebra response giving us a chunk of labels | |
518 | */ | |
519 | void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) | |
520 | { | |
521 | struct lp_chunk *chunk; | |
522 | int debug = BGP_DEBUG(labelpool, LABELPOOL); | |
523 | struct lp_fifo *lf; | |
80853c2e | 524 | uint32_t labelcount; |
955bfd98 PZ |
525 | |
526 | if (last < first) { | |
e50f7cfd | 527 | flog_err(EC_BGP_LABEL, |
1c50c1c0 QY |
528 | "%s: zebra label chunk invalid: first=%u, last=%u", |
529 | __func__, first, last); | |
955bfd98 PZ |
530 | return; |
531 | } | |
532 | ||
533 | chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk)); | |
534 | ||
80853c2e PZ |
535 | labelcount = last - first + 1; |
536 | ||
955bfd98 PZ |
537 | chunk->first = first; |
538 | chunk->last = last; | |
80853c2e PZ |
539 | chunk->nfree = labelcount; |
540 | bf_init(chunk->allocated_map, labelcount); | |
955bfd98 | 541 | |
80853c2e PZ |
542 | /* |
543 | * Optimize for allocation by adding the new (presumably larger) | |
544 | * chunk at the head of the list so it is examined first. | |
545 | */ | |
546 | listnode_add_head(lp->chunks, chunk); | |
955bfd98 | 547 | |
80853c2e | 548 | lp->pending_count -= labelcount; |
955bfd98 PZ |
549 | |
550 | if (debug) { | |
41397f2e DL |
551 | zlog_debug("%s: %zu pending requests", __func__, |
552 | lp_fifo_count(&lp->requests)); | |
955bfd98 PZ |
553 | } |
554 | ||
80853c2e | 555 | while (labelcount && (lf = lp_fifo_first(&lp->requests))) { |
955bfd98 PZ |
556 | |
557 | struct lp_lcb *lcb; | |
558 | void *labelid = lf->lcb.labelid; | |
559 | ||
560 | if (skiplist_search(lp->ledger, labelid, (void **)&lcb)) { | |
561 | /* request no longer in effect */ | |
562 | ||
563 | if (debug) { | |
564 | zlog_debug("%s: labelid %p: request no longer in effect", | |
565 | __func__, labelid); | |
566 | } | |
992dd67e | 567 | /* if this was a BGP_LU request, unlock node |
331bd0eb PR |
568 | */ |
569 | check_bgp_lu_cb_unlock(lcb); | |
955bfd98 PZ |
570 | goto finishedrequest; |
571 | } | |
572 | ||
573 | /* have LCB */ | |
574 | if (lcb->label != MPLS_LABEL_NONE) { | |
575 | /* request already has a label */ | |
576 | if (debug) { | |
577 | zlog_debug("%s: labelid %p: request already has a label: %u=0x%x, lcb=%p", | |
578 | __func__, labelid, | |
579 | lcb->label, lcb->label, lcb); | |
580 | } | |
992dd67e | 581 | /* if this was a BGP_LU request, unlock node |
ea63ff6b EDP |
582 | */ |
583 | check_bgp_lu_cb_unlock(lcb); | |
584 | ||
955bfd98 PZ |
585 | goto finishedrequest; |
586 | } | |
587 | ||
588 | lcb->label = get_label_from_pool(lcb->labelid); | |
589 | ||
590 | if (lcb->label == MPLS_LABEL_NONE) { | |
591 | /* | |
592 | * Out of labels in local pool, await next chunk | |
593 | */ | |
594 | if (debug) { | |
595 | zlog_debug("%s: out of labels, await more", | |
596 | __func__); | |
597 | } | |
598 | break; | |
599 | } | |
600 | ||
80853c2e PZ |
601 | labelcount -= 1; |
602 | ||
955bfd98 PZ |
603 | /* |
604 | * we filled the request from local pool. | |
605 | * Enqueue response work item with new label. | |
606 | */ | |
607 | struct lp_cbq_item *q = XCALLOC(MTYPE_BGP_LABEL_CBQ, | |
608 | sizeof(struct lp_cbq_item)); | |
609 | ||
610 | q->cbfunc = lcb->cbfunc; | |
611 | q->type = lcb->type; | |
612 | q->label = lcb->label; | |
613 | q->labelid = lcb->labelid; | |
614 | q->allocated = true; | |
615 | ||
616 | if (debug) | |
617 | zlog_debug("%s: assigning label %u to labelid %p", | |
618 | __func__, q->label, q->labelid); | |
619 | ||
620 | work_queue_add(lp->callback_q, q); | |
621 | ||
622 | finishedrequest: | |
41397f2e | 623 | lp_fifo_del(&lp->requests, lf); |
955bfd98 PZ |
624 | XFREE(MTYPE_BGP_LABEL_FIFO, lf); |
625 | } | |
626 | } | |
627 | ||
628 | /* | |
629 | * continue using allocated labels until zebra returns | |
630 | */ | |
631 | void bgp_lp_event_zebra_down(void) | |
632 | { | |
633 | /* rats. */ | |
634 | } | |
635 | ||
636 | /* | |
637 | * Inform owners of previously-allocated labels that their labels | |
638 | * are not valid. Request chunk from zebra large enough to satisfy | |
639 | * previously-allocated labels plus any outstanding requests. | |
640 | */ | |
641 | void bgp_lp_event_zebra_up(void) | |
642 | { | |
80853c2e PZ |
643 | unsigned int labels_needed; |
644 | unsigned int chunks_needed; | |
955bfd98 PZ |
645 | void *labelid; |
646 | struct lp_lcb *lcb; | |
f533be73 | 647 | int lm_init_ok; |
955bfd98 | 648 | |
e3ea6503 | 649 | lp->reconnect_count++; |
955bfd98 PZ |
650 | /* |
651 | * Get label chunk allocation request dispatched to zebra | |
652 | */ | |
41397f2e | 653 | labels_needed = lp_fifo_count(&lp->requests) + |
955bfd98 PZ |
654 | skiplist_count(lp->inuse); |
655 | ||
80853c2e PZ |
656 | if (labels_needed > lp->next_chunksize) { |
657 | while ((lp->next_chunksize < labels_needed) && | |
658 | (lp->next_chunksize << 1 <= LP_CHUNK_SIZE_MAX)) | |
659 | ||
660 | lp->next_chunksize <<= 1; | |
661 | } | |
662 | ||
955bfd98 | 663 | /* round up */ |
80853c2e PZ |
664 | chunks_needed = (labels_needed / lp->next_chunksize) + 1; |
665 | labels_needed = chunks_needed * lp->next_chunksize; | |
955bfd98 | 666 | |
f533be73 | 667 | lm_init_ok = lm_label_manager_connect(zclient, 1) == 0; |
668 | ||
ea63ff6b | 669 | if (!lm_init_ok) { |
f533be73 | 670 | zlog_err("%s: label manager connection error", __func__); |
ea63ff6b EDP |
671 | return; |
672 | } | |
f533be73 | 673 | |
0e3b6a92 EDP |
674 | zclient_send_get_label_chunk(zclient, 0, labels_needed, |
675 | MPLS_LABEL_BASE_ANY); | |
955bfd98 PZ |
676 | lp->pending_count = labels_needed; |
677 | ||
678 | /* | |
679 | * Invalidate current list of chunks | |
680 | */ | |
681 | list_delete_all_node(lp->chunks); | |
682 | ||
683 | /* | |
684 | * Invalidate any existing labels and requeue them as requests | |
685 | */ | |
686 | while (!skiplist_first(lp->inuse, NULL, &labelid)) { | |
687 | ||
688 | /* | |
689 | * Get LCB | |
690 | */ | |
691 | if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { | |
692 | ||
693 | if (lcb->label != MPLS_LABEL_NONE) { | |
694 | /* | |
695 | * invalidate | |
696 | */ | |
697 | struct lp_cbq_item *q; | |
698 | ||
699 | q = XCALLOC(MTYPE_BGP_LABEL_CBQ, | |
700 | sizeof(struct lp_cbq_item)); | |
701 | q->cbfunc = lcb->cbfunc; | |
702 | q->type = lcb->type; | |
703 | q->label = lcb->label; | |
704 | q->labelid = lcb->labelid; | |
705 | q->allocated = false; | |
ea63ff6b | 706 | check_bgp_lu_cb_lock(lcb); |
955bfd98 PZ |
707 | work_queue_add(lp->callback_q, q); |
708 | ||
709 | lcb->label = MPLS_LABEL_NONE; | |
710 | } | |
711 | ||
712 | /* | |
713 | * request queue | |
714 | */ | |
715 | struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO, | |
716 | sizeof(struct lp_fifo)); | |
717 | ||
718 | lf->lcb = *lcb; | |
ea63ff6b | 719 | check_bgp_lu_cb_lock(lcb); |
41397f2e | 720 | lp_fifo_add_tail(&lp->requests, lf); |
955bfd98 PZ |
721 | } |
722 | ||
723 | skiplist_delete_first(lp->inuse); | |
724 | } | |
725 | } | |
e3ea6503 PR |
726 | |
727 | DEFUN(show_bgp_labelpool_summary, show_bgp_labelpool_summary_cmd, | |
728 | "show bgp labelpool summary [json]", | |
729 | SHOW_STR BGP_STR | |
730 | "BGP Labelpool information\n" | |
731 | "BGP Labelpool summary\n" JSON_STR) | |
732 | { | |
733 | bool uj = use_json(argc, argv); | |
734 | json_object *json = NULL; | |
735 | ||
736 | if (!lp) { | |
737 | if (uj) | |
738 | vty_out(vty, "{}\n"); | |
739 | else | |
740 | vty_out(vty, "No existing BGP labelpool\n"); | |
741 | return (CMD_WARNING); | |
742 | } | |
743 | ||
744 | if (uj) { | |
745 | json = json_object_new_object(); | |
77a2f8e5 DA |
746 | #if CONFDATE > 20230131 |
747 | CPP_NOTICE("Remove JSON object commands with keys starting with capital") | |
748 | #endif | |
e3ea6503 | 749 | json_object_int_add(json, "Ledger", skiplist_count(lp->ledger)); |
77a2f8e5 | 750 | json_object_int_add(json, "ledger", skiplist_count(lp->ledger)); |
e3ea6503 | 751 | json_object_int_add(json, "InUse", skiplist_count(lp->inuse)); |
77a2f8e5 | 752 | json_object_int_add(json, "inUse", skiplist_count(lp->inuse)); |
e3ea6503 PR |
753 | json_object_int_add(json, "Requests", |
754 | lp_fifo_count(&lp->requests)); | |
77a2f8e5 DA |
755 | json_object_int_add(json, "requests", |
756 | lp_fifo_count(&lp->requests)); | |
e3ea6503 | 757 | json_object_int_add(json, "LabelChunks", listcount(lp->chunks)); |
77a2f8e5 | 758 | json_object_int_add(json, "labelChunks", listcount(lp->chunks)); |
e3ea6503 | 759 | json_object_int_add(json, "Pending", lp->pending_count); |
77a2f8e5 | 760 | json_object_int_add(json, "pending", lp->pending_count); |
e3ea6503 | 761 | json_object_int_add(json, "Reconnects", lp->reconnect_count); |
77a2f8e5 | 762 | json_object_int_add(json, "reconnects", lp->reconnect_count); |
75eeda93 | 763 | vty_json(vty, json); |
e3ea6503 PR |
764 | } else { |
765 | vty_out(vty, "Labelpool Summary\n"); | |
766 | vty_out(vty, "-----------------\n"); | |
767 | vty_out(vty, "%-13s %d\n", | |
768 | "Ledger:", skiplist_count(lp->ledger)); | |
769 | vty_out(vty, "%-13s %d\n", "InUse:", skiplist_count(lp->inuse)); | |
770 | vty_out(vty, "%-13s %zu\n", | |
771 | "Requests:", lp_fifo_count(&lp->requests)); | |
772 | vty_out(vty, "%-13s %d\n", | |
773 | "LabelChunks:", listcount(lp->chunks)); | |
774 | vty_out(vty, "%-13s %d\n", "Pending:", lp->pending_count); | |
775 | vty_out(vty, "%-13s %d\n", "Reconnects:", lp->reconnect_count); | |
776 | } | |
777 | return CMD_SUCCESS; | |
778 | } | |
779 | ||
780 | DEFUN(show_bgp_labelpool_ledger, show_bgp_labelpool_ledger_cmd, | |
781 | "show bgp labelpool ledger [json]", | |
782 | SHOW_STR BGP_STR | |
783 | "BGP Labelpool information\n" | |
784 | "BGP Labelpool ledger\n" JSON_STR) | |
785 | { | |
786 | bool uj = use_json(argc, argv); | |
787 | json_object *json = NULL, *json_elem = NULL; | |
788 | struct lp_lcb *lcb = NULL; | |
992dd67e | 789 | struct bgp_dest *dest; |
e3ea6503 PR |
790 | void *cursor = NULL; |
791 | const struct prefix *p; | |
792 | int rc, count; | |
793 | ||
794 | if (!lp) { | |
795 | if (uj) | |
796 | vty_out(vty, "{}\n"); | |
797 | else | |
798 | vty_out(vty, "No existing BGP labelpool\n"); | |
799 | return (CMD_WARNING); | |
800 | } | |
801 | ||
802 | if (uj) { | |
803 | count = skiplist_count(lp->ledger); | |
804 | if (!count) { | |
805 | vty_out(vty, "{}\n"); | |
806 | return CMD_SUCCESS; | |
807 | } | |
808 | json = json_object_new_array(); | |
809 | } else { | |
810 | vty_out(vty, "Prefix Label\n"); | |
811 | vty_out(vty, "---------------------------\n"); | |
812 | } | |
813 | ||
992dd67e | 814 | for (rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb, |
e3ea6503 | 815 | &cursor); |
992dd67e | 816 | !rc; rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb, |
e3ea6503 PR |
817 | &cursor)) { |
818 | if (uj) { | |
819 | json_elem = json_object_new_object(); | |
820 | json_object_array_add(json, json_elem); | |
821 | } | |
822 | switch (lcb->type) { | |
823 | case LP_TYPE_BGP_LU: | |
992dd67e | 824 | if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) |
e3ea6503 PR |
825 | if (uj) { |
826 | json_object_string_add( | |
827 | json_elem, "prefix", "INVALID"); | |
828 | json_object_int_add(json_elem, "label", | |
829 | lcb->label); | |
830 | } else | |
831 | vty_out(vty, "%-18s %u\n", | |
832 | "INVALID", lcb->label); | |
833 | else { | |
992dd67e | 834 | p = bgp_dest_get_prefix(dest); |
e3ea6503 | 835 | if (uj) { |
511211bf DA |
836 | json_object_string_addf( |
837 | json_elem, "prefix", "%pFX", p); | |
e3ea6503 PR |
838 | json_object_int_add(json_elem, "label", |
839 | lcb->label); | |
840 | } else | |
511211bf | 841 | vty_out(vty, "%-18pFX %u\n", p, |
e3ea6503 PR |
842 | lcb->label); |
843 | } | |
844 | break; | |
845 | case LP_TYPE_VRF: | |
846 | if (uj) { | |
847 | json_object_string_add(json_elem, "prefix", | |
848 | "VRF"); | |
849 | json_object_int_add(json_elem, "label", | |
850 | lcb->label); | |
851 | } else | |
852 | vty_out(vty, "%-18s %u\n", "VRF", | |
853 | lcb->label); | |
854 | ||
855 | break; | |
856 | } | |
857 | } | |
c48349e3 | 858 | if (uj) |
75eeda93 | 859 | vty_json(vty, json); |
e3ea6503 PR |
860 | return CMD_SUCCESS; |
861 | } | |
862 | ||
863 | DEFUN(show_bgp_labelpool_inuse, show_bgp_labelpool_inuse_cmd, | |
864 | "show bgp labelpool inuse [json]", | |
865 | SHOW_STR BGP_STR | |
866 | "BGP Labelpool information\n" | |
867 | "BGP Labelpool inuse\n" JSON_STR) | |
868 | { | |
869 | bool uj = use_json(argc, argv); | |
870 | json_object *json = NULL, *json_elem = NULL; | |
992dd67e | 871 | struct bgp_dest *dest; |
e3ea6503 PR |
872 | mpls_label_t label; |
873 | struct lp_lcb *lcb; | |
874 | void *cursor = NULL; | |
875 | const struct prefix *p; | |
876 | int rc, count; | |
877 | ||
878 | if (!lp) { | |
879 | vty_out(vty, "No existing BGP labelpool\n"); | |
880 | return (CMD_WARNING); | |
881 | } | |
882 | if (!lp) { | |
883 | if (uj) | |
884 | vty_out(vty, "{}\n"); | |
885 | else | |
886 | vty_out(vty, "No existing BGP labelpool\n"); | |
887 | return (CMD_WARNING); | |
888 | } | |
889 | ||
890 | if (uj) { | |
891 | count = skiplist_count(lp->inuse); | |
892 | if (!count) { | |
893 | vty_out(vty, "{}\n"); | |
894 | return CMD_SUCCESS; | |
895 | } | |
896 | json = json_object_new_array(); | |
897 | } else { | |
898 | vty_out(vty, "Prefix Label\n"); | |
899 | vty_out(vty, "---------------------------\n"); | |
900 | } | |
992dd67e | 901 | for (rc = skiplist_next(lp->inuse, (void **)&label, (void **)&dest, |
e3ea6503 | 902 | &cursor); |
992dd67e PR |
903 | !rc; rc = skiplist_next(lp->ledger, (void **)&label, |
904 | (void **)&dest, &cursor)) { | |
905 | if (skiplist_search(lp->ledger, dest, (void **)&lcb)) | |
e3ea6503 PR |
906 | continue; |
907 | ||
908 | if (uj) { | |
909 | json_elem = json_object_new_object(); | |
910 | json_object_array_add(json, json_elem); | |
911 | } | |
912 | ||
913 | switch (lcb->type) { | |
914 | case LP_TYPE_BGP_LU: | |
992dd67e | 915 | if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) |
e3ea6503 PR |
916 | if (uj) { |
917 | json_object_string_add( | |
918 | json_elem, "prefix", "INVALID"); | |
919 | json_object_int_add(json_elem, "label", | |
920 | label); | |
921 | } else | |
922 | vty_out(vty, "INVALID %u\n", | |
923 | label); | |
924 | else { | |
992dd67e | 925 | p = bgp_dest_get_prefix(dest); |
e3ea6503 | 926 | if (uj) { |
511211bf DA |
927 | json_object_string_addf( |
928 | json_elem, "prefix", "%pFX", p); | |
e3ea6503 PR |
929 | json_object_int_add(json_elem, "label", |
930 | label); | |
931 | } else | |
511211bf | 932 | vty_out(vty, "%-18pFX %u\n", p, |
e3ea6503 PR |
933 | label); |
934 | } | |
935 | break; | |
936 | case LP_TYPE_VRF: | |
937 | if (uj) { | |
938 | json_object_string_add(json_elem, "prefix", | |
939 | "VRF"); | |
940 | json_object_int_add(json_elem, "label", label); | |
941 | } else | |
942 | vty_out(vty, "%-18s %u\n", "VRF", | |
943 | label); | |
944 | break; | |
945 | } | |
946 | } | |
c48349e3 | 947 | if (uj) |
75eeda93 | 948 | vty_json(vty, json); |
e3ea6503 PR |
949 | return CMD_SUCCESS; |
950 | } | |
951 | ||
952 | DEFUN(show_bgp_labelpool_requests, show_bgp_labelpool_requests_cmd, | |
953 | "show bgp labelpool requests [json]", | |
954 | SHOW_STR BGP_STR | |
955 | "BGP Labelpool information\n" | |
956 | "BGP Labelpool requests\n" JSON_STR) | |
957 | { | |
958 | bool uj = use_json(argc, argv); | |
959 | json_object *json = NULL, *json_elem = NULL; | |
992dd67e | 960 | struct bgp_dest *dest; |
e3ea6503 | 961 | const struct prefix *p; |
e3ea6503 PR |
962 | struct lp_fifo *item, *next; |
963 | int count; | |
964 | ||
965 | if (!lp) { | |
966 | if (uj) | |
967 | vty_out(vty, "{}\n"); | |
968 | else | |
969 | vty_out(vty, "No existing BGP labelpool\n"); | |
970 | return (CMD_WARNING); | |
971 | } | |
972 | ||
973 | if (uj) { | |
974 | count = lp_fifo_count(&lp->requests); | |
975 | if (!count) { | |
976 | vty_out(vty, "{}\n"); | |
977 | return CMD_SUCCESS; | |
978 | } | |
979 | json = json_object_new_array(); | |
980 | } else { | |
981 | vty_out(vty, "Prefix \n"); | |
982 | vty_out(vty, "----------------\n"); | |
983 | } | |
984 | ||
985 | for (item = lp_fifo_first(&lp->requests); item; item = next) { | |
986 | next = lp_fifo_next_safe(&lp->requests, item); | |
992dd67e | 987 | dest = item->lcb.labelid; |
e3ea6503 PR |
988 | if (uj) { |
989 | json_elem = json_object_new_object(); | |
990 | json_object_array_add(json, json_elem); | |
991 | } | |
992 | switch (item->lcb.type) { | |
993 | case LP_TYPE_BGP_LU: | |
992dd67e PR |
994 | if (!CHECK_FLAG(dest->flags, |
995 | BGP_NODE_LABEL_REQUESTED)) { | |
e3ea6503 PR |
996 | if (uj) |
997 | json_object_string_add( | |
998 | json_elem, "prefix", "INVALID"); | |
999 | else | |
1000 | vty_out(vty, "INVALID\n"); | |
1001 | } else { | |
992dd67e | 1002 | p = bgp_dest_get_prefix(dest); |
e3ea6503 | 1003 | if (uj) |
511211bf DA |
1004 | json_object_string_addf( |
1005 | json_elem, "prefix", "%pFX", p); | |
e3ea6503 | 1006 | else |
511211bf | 1007 | vty_out(vty, "%-18pFX\n", p); |
e3ea6503 PR |
1008 | } |
1009 | break; | |
1010 | case LP_TYPE_VRF: | |
1011 | if (uj) | |
1012 | json_object_string_add(json_elem, "prefix", | |
1013 | "VRF"); | |
1014 | else | |
1015 | vty_out(vty, "VRF\n"); | |
1016 | break; | |
1017 | } | |
1018 | } | |
c48349e3 | 1019 | if (uj) |
75eeda93 | 1020 | vty_json(vty, json); |
e3ea6503 PR |
1021 | return CMD_SUCCESS; |
1022 | } | |
1023 | ||
1024 | DEFUN(show_bgp_labelpool_chunks, show_bgp_labelpool_chunks_cmd, | |
1025 | "show bgp labelpool chunks [json]", | |
1026 | SHOW_STR BGP_STR | |
1027 | "BGP Labelpool information\n" | |
1028 | "BGP Labelpool chunks\n" JSON_STR) | |
1029 | { | |
1030 | bool uj = use_json(argc, argv); | |
1031 | json_object *json = NULL, *json_elem; | |
1032 | struct listnode *node; | |
1033 | struct lp_chunk *chunk; | |
1034 | int count; | |
1035 | ||
1036 | if (!lp) { | |
1037 | if (uj) | |
1038 | vty_out(vty, "{}\n"); | |
1039 | else | |
1040 | vty_out(vty, "No existing BGP labelpool\n"); | |
1041 | return (CMD_WARNING); | |
1042 | } | |
1043 | ||
1044 | if (uj) { | |
1045 | count = listcount(lp->chunks); | |
1046 | if (!count) { | |
1047 | vty_out(vty, "{}\n"); | |
1048 | return CMD_SUCCESS; | |
1049 | } | |
1050 | json = json_object_new_array(); | |
1051 | } else { | |
80853c2e PZ |
1052 | vty_out(vty, "%10s %10s %10s %10s\n", "First", "Last", "Size", |
1053 | "nfree"); | |
1054 | vty_out(vty, "-------------------------------------------\n"); | |
e3ea6503 PR |
1055 | } |
1056 | ||
1057 | for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) { | |
80853c2e PZ |
1058 | uint32_t size; |
1059 | ||
1060 | size = chunk->last - chunk->first + 1; | |
1061 | ||
e3ea6503 PR |
1062 | if (uj) { |
1063 | json_elem = json_object_new_object(); | |
1064 | json_object_array_add(json, json_elem); | |
1065 | json_object_int_add(json_elem, "first", chunk->first); | |
1066 | json_object_int_add(json_elem, "last", chunk->last); | |
80853c2e PZ |
1067 | json_object_int_add(json_elem, "size", size); |
1068 | json_object_int_add(json_elem, "numberFree", | |
1069 | chunk->nfree); | |
e3ea6503 | 1070 | } else |
80853c2e PZ |
1071 | vty_out(vty, "%10u %10u %10u %10u\n", chunk->first, |
1072 | chunk->last, size, chunk->nfree); | |
e3ea6503 | 1073 | } |
c48349e3 | 1074 | if (uj) |
75eeda93 | 1075 | vty_json(vty, json); |
e3ea6503 PR |
1076 | return CMD_SUCCESS; |
1077 | } | |
1078 | ||
80853c2e PZ |
1079 | #if BGP_LABELPOOL_ENABLE_TESTS |
1080 | /*------------------------------------------------------------------------ | |
1081 | * Testing code start | |
1082 | *------------------------------------------------------------------------*/ | |
1083 | ||
1084 | DEFINE_MTYPE_STATIC(BGPD, LABELPOOL_TEST, "Label pool test"); | |
1085 | ||
1086 | #define LPT_STAT_INSERT_FAIL 0 | |
1087 | #define LPT_STAT_DELETE_FAIL 1 | |
1088 | #define LPT_STAT_ALLOCATED 2 | |
1089 | #define LPT_STAT_DEALLOCATED 3 | |
1090 | #define LPT_STAT_MAX 4 | |
1091 | ||
1092 | const char *lpt_counter_names[] = { | |
1093 | "sl insert failures", | |
1094 | "sl delete failures", | |
1095 | "labels allocated", | |
1096 | "labels deallocated", | |
1097 | }; | |
1098 | ||
1099 | static uint8_t lpt_generation; | |
1100 | static bool lpt_inprogress; | |
1101 | static struct skiplist *lp_tests; | |
1102 | static unsigned int lpt_test_cb_tcb_lookup_fails; | |
1103 | static unsigned int lpt_release_tcb_lookup_fails; | |
1104 | static unsigned int lpt_test_event_tcb_lookup_fails; | |
1105 | static unsigned int lpt_stop_tcb_lookup_fails; | |
1106 | ||
1107 | struct lp_test { | |
1108 | uint8_t generation; | |
1109 | unsigned int request_maximum; | |
1110 | unsigned int request_blocksize; | |
1111 | uintptr_t request_count; /* match type of labelid */ | |
1112 | int label_type; | |
1113 | struct skiplist *labels; | |
1114 | struct timeval starttime; | |
1115 | struct skiplist *timestamps_alloc; | |
1116 | struct skiplist *timestamps_dealloc; | |
1117 | struct thread *event_thread; | |
1118 | unsigned int counter[LPT_STAT_MAX]; | |
1119 | }; | |
1120 | ||
1121 | /* test parameters */ | |
1122 | #define LPT_MAX_COUNT 500000 /* get this many labels in all */ | |
1123 | #define LPT_BLKSIZE 10000 /* this many at a time, then yield */ | |
1124 | #define LPT_TS_INTERVAL 10000 /* timestamp every this many labels */ | |
1125 | ||
1126 | ||
1127 | static int test_cb(mpls_label_t label, void *labelid, bool allocated) | |
1128 | { | |
1129 | uintptr_t generation; | |
1130 | struct lp_test *tcb; | |
1131 | ||
1132 | generation = ((uintptr_t)labelid >> 24) & 0xff; | |
1133 | ||
1134 | if (skiplist_search(lp_tests, (void *)generation, (void **)&tcb)) { | |
1135 | ||
1136 | /* couldn't find current test in progress */ | |
1137 | ++lpt_test_cb_tcb_lookup_fails; | |
1138 | return -1; /* reject allocation */ | |
1139 | } | |
1140 | ||
1141 | if (allocated) { | |
1142 | ++tcb->counter[LPT_STAT_ALLOCATED]; | |
1143 | if (!(tcb->counter[LPT_STAT_ALLOCATED] % LPT_TS_INTERVAL)) { | |
1144 | uintptr_t time_ms; | |
1145 | ||
1146 | time_ms = monotime_since(&tcb->starttime, NULL) / 1000; | |
1147 | skiplist_insert(tcb->timestamps_alloc, | |
1148 | (void *)(uintptr_t)tcb | |
1149 | ->counter[LPT_STAT_ALLOCATED], | |
1150 | (void *)time_ms); | |
1151 | } | |
1152 | if (skiplist_insert(tcb->labels, labelid, | |
1153 | (void *)(uintptr_t)label)) { | |
1154 | ++tcb->counter[LPT_STAT_INSERT_FAIL]; | |
1155 | return -1; | |
1156 | } | |
1157 | } else { | |
1158 | ++tcb->counter[LPT_STAT_DEALLOCATED]; | |
1159 | if (!(tcb->counter[LPT_STAT_DEALLOCATED] % LPT_TS_INTERVAL)) { | |
1160 | uintptr_t time_ms; | |
1161 | ||
1162 | time_ms = monotime_since(&tcb->starttime, NULL) / 1000; | |
1163 | skiplist_insert(tcb->timestamps_dealloc, | |
1164 | (void *)(uintptr_t)tcb | |
1165 | ->counter[LPT_STAT_ALLOCATED], | |
1166 | (void *)time_ms); | |
1167 | } | |
1168 | if (skiplist_delete(tcb->labels, labelid, 0)) { | |
1169 | ++tcb->counter[LPT_STAT_DELETE_FAIL]; | |
1170 | return -1; | |
1171 | } | |
1172 | } | |
1173 | return 0; | |
1174 | } | |
1175 | ||
1176 | static void labelpool_test_event_handler(struct thread *thread) | |
1177 | { | |
1178 | struct lp_test *tcb; | |
1179 | ||
1180 | if (skiplist_search(lp_tests, (void *)(uintptr_t)(lpt_generation), | |
1181 | (void **)&tcb)) { | |
1182 | ||
1183 | /* couldn't find current test in progress */ | |
1184 | ++lpt_test_event_tcb_lookup_fails; | |
1185 | return; | |
1186 | } | |
1187 | ||
1188 | /* | |
1189 | * request a bunch of labels | |
1190 | */ | |
1191 | for (unsigned int i = 0; (i < tcb->request_blocksize) && | |
1192 | (tcb->request_count < tcb->request_maximum); | |
1193 | ++i) { | |
1194 | ||
1195 | uintptr_t id; | |
1196 | ||
1197 | ++tcb->request_count; | |
1198 | ||
1199 | /* | |
1200 | * construct 32-bit id from request_count and generation | |
1201 | */ | |
1202 | id = ((uintptr_t)tcb->generation << 24) | | |
1203 | (tcb->request_count & 0x00ffffff); | |
1204 | bgp_lp_get(LP_TYPE_VRF, (void *)id, test_cb); | |
1205 | } | |
1206 | ||
1207 | if (tcb->request_count < tcb->request_maximum) | |
1208 | thread_add_event(bm->master, labelpool_test_event_handler, NULL, | |
1209 | 0, &tcb->event_thread); | |
1210 | } | |
1211 | ||
1212 | static void lptest_stop(void) | |
1213 | { | |
1214 | struct lp_test *tcb; | |
1215 | ||
1216 | if (!lpt_inprogress) | |
1217 | return; | |
1218 | ||
1219 | if (skiplist_search(lp_tests, (void *)(uintptr_t)(lpt_generation), | |
1220 | (void **)&tcb)) { | |
1221 | ||
1222 | /* couldn't find current test in progress */ | |
1223 | ++lpt_stop_tcb_lookup_fails; | |
1224 | return; | |
1225 | } | |
1226 | ||
1227 | if (tcb->event_thread) | |
1228 | thread_cancel(&tcb->event_thread); | |
1229 | ||
1230 | lpt_inprogress = false; | |
1231 | } | |
1232 | ||
1233 | static int lptest_start(struct vty *vty) | |
1234 | { | |
1235 | struct lp_test *tcb; | |
1236 | ||
1237 | if (lpt_inprogress) { | |
1238 | vty_out(vty, "test already in progress\n"); | |
1239 | return -1; | |
1240 | } | |
1241 | ||
1242 | if (skiplist_count(lp_tests) >= | |
1243 | (1 << (8 * sizeof(lpt_generation))) - 1) { | |
1244 | /* | |
1245 | * Too many test runs | |
1246 | */ | |
1247 | vty_out(vty, "too many tests: clear first\n"); | |
1248 | return -1; | |
1249 | } | |
1250 | ||
1251 | /* | |
1252 | * We pack the generation and request number into the labelid; | |
1253 | * make sure they fit. | |
1254 | */ | |
1255 | unsigned int n1 = LPT_MAX_COUNT; | |
1256 | unsigned int sh = 0; | |
1257 | unsigned int label_bits; | |
1258 | ||
1259 | label_bits = 8 * (sizeof(tcb->request_count) - sizeof(lpt_generation)); | |
1260 | ||
1261 | /* n1 should be same type as tcb->request_maximum */ | |
1262 | assert(sizeof(n1) == sizeof(tcb->request_maximum)); | |
1263 | ||
1264 | while (n1 >>= 1) | |
1265 | ++sh; | |
1266 | sh += 1; /* number of bits needed to hold LPT_MAX_COUNT */ | |
1267 | ||
1268 | if (sh > label_bits) { | |
1269 | vty_out(vty, | |
1270 | "Sorry, test iteration count too big on this platform (LPT_MAX_COUNT %u, need %u bits, but label_bits is only %u)\n", | |
1271 | LPT_MAX_COUNT, sh, label_bits); | |
1272 | return -1; | |
1273 | } | |
1274 | ||
1275 | lpt_inprogress = true; | |
1276 | ++lpt_generation; | |
1277 | ||
1278 | tcb = XCALLOC(MTYPE_LABELPOOL_TEST, sizeof(*tcb)); | |
1279 | ||
1280 | tcb->generation = lpt_generation; | |
1281 | tcb->label_type = LP_TYPE_VRF; | |
1282 | tcb->request_maximum = LPT_MAX_COUNT; | |
1283 | tcb->request_blocksize = LPT_BLKSIZE; | |
1284 | tcb->labels = skiplist_new(0, NULL, NULL); | |
1285 | tcb->timestamps_alloc = skiplist_new(0, NULL, NULL); | |
1286 | tcb->timestamps_dealloc = skiplist_new(0, NULL, NULL); | |
1287 | thread_add_event(bm->master, labelpool_test_event_handler, NULL, 0, | |
1288 | &tcb->event_thread); | |
1289 | monotime(&tcb->starttime); | |
1290 | ||
1291 | skiplist_insert(lp_tests, (void *)(uintptr_t)tcb->generation, tcb); | |
1292 | return 0; | |
1293 | } | |
1294 | ||
1295 | DEFPY(start_labelpool_perf_test, start_labelpool_perf_test_cmd, | |
1296 | "debug bgp lptest start", | |
1297 | DEBUG_STR BGP_STR | |
1298 | "label pool test\n" | |
1299 | "start\n") | |
1300 | { | |
1301 | lptest_start(vty); | |
1302 | return CMD_SUCCESS; | |
1303 | } | |
1304 | ||
1305 | static void lptest_print_stats(struct vty *vty, struct lp_test *tcb) | |
1306 | { | |
1307 | unsigned int i; | |
1308 | ||
1309 | vty_out(vty, "Global Lookup Failures in test_cb: %5u\n", | |
1310 | lpt_test_cb_tcb_lookup_fails); | |
1311 | vty_out(vty, "Global Lookup Failures in release: %5u\n", | |
1312 | lpt_release_tcb_lookup_fails); | |
1313 | vty_out(vty, "Global Lookup Failures in event: %5u\n", | |
1314 | lpt_test_event_tcb_lookup_fails); | |
1315 | vty_out(vty, "Global Lookup Failures in stop: %5u\n", | |
1316 | lpt_stop_tcb_lookup_fails); | |
1317 | vty_out(vty, "\n"); | |
1318 | ||
1319 | if (!tcb) { | |
1320 | if (skiplist_search(lp_tests, (void *)(uintptr_t)lpt_generation, | |
1321 | (void **)&tcb)) { | |
1322 | vty_out(vty, "Error: can't find test %u\n", | |
1323 | lpt_generation); | |
1324 | return; | |
1325 | } | |
1326 | } | |
1327 | ||
1328 | vty_out(vty, "Test Generation %u:\n", tcb->generation); | |
1329 | ||
1330 | vty_out(vty, "Counter Value\n"); | |
1331 | for (i = 0; i < LPT_STAT_MAX; ++i) { | |
1332 | vty_out(vty, "%20s: %10u\n", lpt_counter_names[i], | |
1333 | tcb->counter[i]); | |
1334 | } | |
1335 | vty_out(vty, "\n"); | |
1336 | ||
1337 | if (tcb->timestamps_alloc) { | |
1338 | void *Key; | |
1339 | void *Value; | |
1340 | void *cursor; | |
1341 | ||
1342 | float elapsed; | |
1343 | ||
1344 | vty_out(vty, "%10s %10s\n", "Count", "Seconds"); | |
1345 | ||
1346 | cursor = NULL; | |
1347 | while (!skiplist_next(tcb->timestamps_alloc, &Key, &Value, | |
1348 | &cursor)) { | |
1349 | ||
1350 | elapsed = ((float)(uintptr_t)Value) / 1000; | |
1351 | ||
1352 | vty_out(vty, "%10llu %10.3f\n", | |
1353 | (unsigned long long)(uintptr_t)Key, elapsed); | |
1354 | } | |
1355 | vty_out(vty, "\n"); | |
1356 | } | |
1357 | } | |
1358 | ||
1359 | DEFPY(show_labelpool_perf_test, show_labelpool_perf_test_cmd, | |
1360 | "debug bgp lptest show", | |
1361 | DEBUG_STR BGP_STR | |
1362 | "label pool test\n" | |
1363 | "show\n") | |
1364 | { | |
1365 | ||
1366 | if (lp_tests) { | |
1367 | void *Key; | |
1368 | void *Value; | |
1369 | void *cursor; | |
1370 | ||
1371 | cursor = NULL; | |
1372 | while (!skiplist_next(lp_tests, &Key, &Value, &cursor)) { | |
1373 | lptest_print_stats(vty, (struct lp_test *)Value); | |
1374 | } | |
1375 | } else { | |
1376 | vty_out(vty, "no test results\n"); | |
1377 | } | |
1378 | return CMD_SUCCESS; | |
1379 | } | |
1380 | ||
1381 | DEFPY(stop_labelpool_perf_test, stop_labelpool_perf_test_cmd, | |
1382 | "debug bgp lptest stop", | |
1383 | DEBUG_STR BGP_STR | |
1384 | "label pool test\n" | |
1385 | "stop\n") | |
1386 | { | |
1387 | ||
1388 | if (lpt_inprogress) { | |
1389 | lptest_stop(); | |
1390 | lptest_print_stats(vty, NULL); | |
1391 | } else { | |
1392 | vty_out(vty, "no test in progress\n"); | |
1393 | } | |
1394 | return CMD_SUCCESS; | |
1395 | } | |
1396 | ||
1397 | DEFPY(clear_labelpool_perf_test, clear_labelpool_perf_test_cmd, | |
1398 | "debug bgp lptest clear", | |
1399 | DEBUG_STR BGP_STR | |
1400 | "label pool test\n" | |
1401 | "clear\n") | |
1402 | { | |
1403 | ||
1404 | if (lpt_inprogress) { | |
1405 | lptest_stop(); | |
1406 | } | |
1407 | if (lp_tests) { | |
1408 | while (!skiplist_first(lp_tests, NULL, NULL)) | |
1409 | /* del function of skiplist cleans up tcbs */ | |
1410 | skiplist_delete_first(lp_tests); | |
1411 | } | |
1412 | return CMD_SUCCESS; | |
1413 | } | |
1414 | ||
1415 | /* | |
1416 | * With the "release" command, we can release labels at intervals through | |
1417 | * the ID space. Thus we can to exercise the bitfield-wrapping behavior | |
1418 | * of the allocator in a subsequent test. | |
1419 | */ | |
1420 | /* clang-format off */ | |
1421 | DEFPY(release_labelpool_perf_test, release_labelpool_perf_test_cmd, | |
1422 | "debug bgp lptest release test GENERATION$generation every (1-5)$every_nth", | |
1423 | DEBUG_STR | |
1424 | BGP_STR | |
1425 | "label pool test\n" | |
1426 | "release labels\n" | |
1427 | "\"test\"\n" | |
1428 | "test number\n" | |
1429 | "\"every\"\n" | |
1430 | "label fraction denominator\n") | |
1431 | { | |
1432 | /* clang-format on */ | |
1433 | ||
1434 | unsigned long testnum; | |
1435 | char *end; | |
1436 | struct lp_test *tcb; | |
1437 | ||
1438 | testnum = strtoul(generation, &end, 0); | |
1439 | if (*end) { | |
1440 | vty_out(vty, "Invalid test number: \"%s\"\n", generation); | |
1441 | return CMD_SUCCESS; | |
1442 | } | |
1443 | if (lpt_inprogress && (testnum == lpt_generation)) { | |
1444 | vty_out(vty, | |
1445 | "Error: Test %lu is still in progress (stop first)\n", | |
1446 | testnum); | |
1447 | return CMD_SUCCESS; | |
1448 | } | |
1449 | ||
1450 | if (skiplist_search(lp_tests, (void *)(uintptr_t)testnum, | |
1451 | (void **)&tcb)) { | |
1452 | ||
1453 | /* couldn't find current test in progress */ | |
1454 | vty_out(vty, "Error: Can't look up test number: \"%lu\"\n", | |
1455 | testnum); | |
1456 | ++lpt_release_tcb_lookup_fails; | |
1457 | return CMD_SUCCESS; | |
1458 | } | |
1459 | ||
1460 | void *Key, *cKey; | |
1461 | void *Value, *cValue; | |
1462 | void *cursor; | |
1463 | unsigned int iteration; | |
1464 | int rc; | |
1465 | ||
1466 | cursor = NULL; | |
1467 | iteration = 0; | |
1468 | rc = skiplist_next(tcb->labels, &Key, &Value, &cursor); | |
1469 | ||
1470 | while (!rc) { | |
1471 | cKey = Key; | |
1472 | cValue = Value; | |
1473 | ||
1474 | /* find next item before we delete this one */ | |
1475 | rc = skiplist_next(tcb->labels, &Key, &Value, &cursor); | |
1476 | ||
1477 | if (!(iteration % every_nth)) { | |
1478 | bgp_lp_release(tcb->label_type, cKey, | |
1479 | (mpls_label_t)(uintptr_t)cValue); | |
1480 | skiplist_delete(tcb->labels, cKey, NULL); | |
1481 | ++tcb->counter[LPT_STAT_DEALLOCATED]; | |
1482 | } | |
1483 | ++iteration; | |
1484 | } | |
1485 | ||
1486 | return CMD_SUCCESS; | |
1487 | } | |
1488 | ||
1489 | static void lptest_delete(void *val) | |
1490 | { | |
1491 | struct lp_test *tcb = (struct lp_test *)val; | |
1492 | void *Key; | |
1493 | void *Value; | |
1494 | void *cursor; | |
1495 | ||
1496 | if (tcb->labels) { | |
1497 | cursor = NULL; | |
1498 | while (!skiplist_next(tcb->labels, &Key, &Value, &cursor)) | |
1499 | bgp_lp_release(tcb->label_type, Key, | |
1500 | (mpls_label_t)(uintptr_t)Value); | |
1501 | skiplist_free(tcb->labels); | |
1502 | tcb->labels = NULL; | |
1503 | } | |
1504 | if (tcb->timestamps_alloc) { | |
1505 | cursor = NULL; | |
1506 | skiplist_free(tcb->timestamps_alloc); | |
1507 | tcb->timestamps_alloc = NULL; | |
1508 | } | |
1509 | ||
1510 | if (tcb->timestamps_dealloc) { | |
1511 | cursor = NULL; | |
1512 | skiplist_free(tcb->timestamps_dealloc); | |
1513 | tcb->timestamps_dealloc = NULL; | |
1514 | } | |
1515 | ||
1516 | if (tcb->event_thread) | |
1517 | thread_cancel(&tcb->event_thread); | |
1518 | ||
1519 | memset(tcb, 0, sizeof(*tcb)); | |
1520 | ||
1521 | XFREE(MTYPE_LABELPOOL_TEST, tcb); | |
1522 | } | |
1523 | ||
1524 | static void lptest_init(void) | |
1525 | { | |
1526 | lp_tests = skiplist_new(0, NULL, lptest_delete); | |
1527 | } | |
1528 | ||
1529 | static void lptest_finish(void) | |
1530 | { | |
1531 | if (lp_tests) { | |
1532 | skiplist_free(lp_tests); | |
1533 | lp_tests = NULL; | |
1534 | } | |
1535 | } | |
1536 | ||
1537 | /*------------------------------------------------------------------------ | |
1538 | * Testing code end | |
1539 | *------------------------------------------------------------------------*/ | |
1540 | #endif /* BGP_LABELPOOL_ENABLE_TESTS */ | |
1541 | ||
e3ea6503 PR |
1542 | void bgp_lp_vty_init(void) |
1543 | { | |
1544 | install_element(VIEW_NODE, &show_bgp_labelpool_summary_cmd); | |
1545 | install_element(VIEW_NODE, &show_bgp_labelpool_ledger_cmd); | |
1546 | install_element(VIEW_NODE, &show_bgp_labelpool_inuse_cmd); | |
1547 | install_element(VIEW_NODE, &show_bgp_labelpool_requests_cmd); | |
1548 | install_element(VIEW_NODE, &show_bgp_labelpool_chunks_cmd); | |
80853c2e PZ |
1549 | |
1550 | #if BGP_LABELPOOL_ENABLE_TESTS | |
1551 | install_element(ENABLE_NODE, &start_labelpool_perf_test_cmd); | |
1552 | install_element(ENABLE_NODE, &show_labelpool_perf_test_cmd); | |
1553 | install_element(ENABLE_NODE, &stop_labelpool_perf_test_cmd); | |
1554 | install_element(ENABLE_NODE, &release_labelpool_perf_test_cmd); | |
1555 | install_element(ENABLE_NODE, &clear_labelpool_perf_test_cmd); | |
1556 | #endif /* BGP_LABELPOOL_ENABLE_TESTS */ | |
e3ea6503 | 1557 | } |