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