]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_labelpool.c
build, vtysh: extract vtysh commands from .xref
[mirror_frr.git] / bgpd / bgp_labelpool.c
CommitLineData
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 */
48extern struct zclient *zclient;
49
80853c2e
PZ
50#if BGP_LABELPOOL_ENABLE_TESTS
51static void lptest_init(void);
52static void lptest_finish(void);
53#endif
54
955bfd98
PZ
55/*
56 * Remember where pool data are kept
57 */
58static 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
74DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk");
75DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO item");
76DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment");
77DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback");
955bfd98 78
955bfd98
PZ
79struct 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 */
90struct 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 102struct lp_fifo {
41397f2e 103 struct lp_fifo_item fifo;
955bfd98
PZ
104 struct lp_lcb lcb;
105};
106
960b9a53 107DECLARE_LIST(lp_fifo, struct lp_fifo, fifo);
41397f2e 108
955bfd98
PZ
109struct 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
117static 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
175static void lp_cbq_item_free(struct work_queue *wq, void *data)
176{
177 XFREE(MTYPE_BGP_LABEL_CBQ, data);
178}
179
180static void lp_lcb_free(void *goner)
181{
0a22ddfb 182 XFREE(MTYPE_BGP_LABEL_CB, goner);
955bfd98
PZ
183}
184
185static 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
193void 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
219static 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
226static 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
232void 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
271static 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 */
332static 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 */
377void 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
473void 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 */
519void 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
622finishedrequest:
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 */
631void 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 */
641void 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
727DEFUN(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
747CPP_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
780DEFUN(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
863DEFUN(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
952DEFUN(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
1024DEFUN(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
1084DEFINE_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
1092const char *lpt_counter_names[] = {
1093 "sl insert failures",
1094 "sl delete failures",
1095 "labels allocated",
1096 "labels deallocated",
1097};
1098
1099static uint8_t lpt_generation;
1100static bool lpt_inprogress;
1101static struct skiplist *lp_tests;
1102static unsigned int lpt_test_cb_tcb_lookup_fails;
1103static unsigned int lpt_release_tcb_lookup_fails;
1104static unsigned int lpt_test_event_tcb_lookup_fails;
1105static unsigned int lpt_stop_tcb_lookup_fails;
1106
1107struct 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
1127static 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
1176static 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
1212static 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
1233static 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
1295DEFPY(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
1305static 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
1359DEFPY(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
1381DEFPY(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
1397DEFPY(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 */
1421DEFPY(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
1489static 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
1524static void lptest_init(void)
1525{
1526 lp_tests = skiplist_new(0, NULL, lptest_delete);
1527}
1528
1529static 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
1542void 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}