From 095e1819289a6cfb46c86cdf7f02d56c29fa4a9c Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Wed, 17 Apr 2019 19:18:04 -0400 Subject: [PATCH 01/64] lib: Fix workqueue error function callback After exceeding the max retry number for a thread, we were passing the data rather than the work_queue_item struct. Signed-off-by: Stephen Worley --- lib/workqueue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/workqueue.c b/lib/workqueue.c index 066d81f350..54090d0d0f 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -280,7 +280,7 @@ int work_queue_run(struct thread *thread) if (item->ran > wq->spec.max_retries) { /* run error handler, if any */ if (wq->spec.errorfunc) - wq->spec.errorfunc(wq, item->data); + wq->spec.errorfunc(wq, item); work_queue_item_remove(wq, item); continue; } From 4cab68a20d81e95ef697a74b1a62e64d3f8b348f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 18 Sep 2018 18:38:49 +0200 Subject: [PATCH 02/64] lib: add cmpxchg_strong to frratomic.h Signed-off-by: David Lamparter --- lib/frratomic.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/frratomic.h b/lib/frratomic.h index e86030f83c..1e28253f2b 100644 --- a/lib/frratomic.h +++ b/lib/frratomic.h @@ -80,6 +80,9 @@ typedef std::atomic atomic_uint_fast32_t; #define atomic_compare_exchange_weak_explicit(atom, expect, desire, mem1, \ mem2) \ __atomic_compare_exchange_n(atom, expect, desire, 1, mem1, mem2) +#define atomic_compare_exchange_strong_explicit(atom, expect, desire, mem1, \ + mem2) \ + __atomic_compare_exchange_n(atom, expect, desire, 0, mem1, mem2) /* gcc 4.1 and newer, * clang 3.3 (possibly older) @@ -152,7 +155,7 @@ typedef std::atomic atomic_uint_fast32_t; rval; \ }) -#define atomic_compare_exchange_weak_explicit(atom, expect, desire, mem1, \ +#define atomic_compare_exchange_strong_explicit(atom, expect, desire, mem1, \ mem2) \ ({ \ typeof(atom) _atom = (atom); \ @@ -166,6 +169,8 @@ typedef std::atomic atomic_uint_fast32_t; *_expect = rval; \ ret; \ }) +#define atomic_compare_exchange_weak_explicit \ + atomic_compare_exchange_strong_explicit #define atomic_fetch_and_explicit(ptr, val, mem) \ ({ \ From 440d5faa3a328e226435785afe7793251746175f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 25 May 2015 06:36:10 +0200 Subject: [PATCH 03/64] lib: add "seqlock" wait/broadcast primitive Manually tested rather extensively in addition to included unit tests, should work as intended. NB: The OpenBSD futex() code is "future"; it's not actually in OpenBSD (yet?) and thus untested. Signed-off-by: David Lamparter --- configure.ac | 74 +++++++++++++++++ lib/seqlock.c | 167 +++++++++++++++++++++++++++++++++++++++ lib/seqlock.h | 106 +++++++++++++++++++++++++ lib/subdir.am | 2 + tests/.gitignore | 1 + tests/lib/test_seqlock.c | 122 ++++++++++++++++++++++++++++ tests/subdir.am | 5 ++ 7 files changed, 477 insertions(+) create mode 100644 lib/seqlock.c create mode 100644 lib/seqlock.h create mode 100644 tests/lib/test_seqlock.c diff --git a/configure.ac b/configure.ac index 9ae196fcb1..18c16634ba 100755 --- a/configure.ac +++ b/configure.ac @@ -926,6 +926,80 @@ AC_CHECK_HEADERS([pthread_np.h],,, [ ]) AC_CHECK_FUNCS([pthread_setname_np pthread_set_name_np]) +needsync=true + +AS_IF([$needsync], [ + dnl Linux + AC_MSG_CHECKING([for Linux futex() support]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include + +int main(void); +], +[ +{ + return syscall(SYS_futex, NULL, FUTEX_WAIT, 0, NULL, NULL, 0); +} +])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_SYNC_LINUX_FUTEX,,Have Linux futex support) + needsync=false + ], [ + AC_MSG_RESULT([no]) + ]) +]) + +AS_IF([$needsync], [ + dnl FreeBSD + AC_MSG_CHECKING([for FreeBSD _umtx_op() support]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include +#include +#include +#include +int main(void); +], +[ +{ + return _umtx_op(NULL, UMTX_OP_WAIT_UINT, 0, NULL, NULL); +} +])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_SYNC_UMTX_OP,,Have FreeBSD _umtx_op() support) + needsync=false + ], [ + AC_MSG_RESULT([no]) + ]) +]) + +AS_IF([$needsync], [ + dnl OpenBSD patch (not upstream at the time of writing this) + dnl https://marc.info/?l=openbsd-tech&m=147299508409549&w=2 + AC_MSG_CHECKING([for OpenBSD futex() support]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include +int main(void); +], +[ +{ + return futex(NULL, FUTEX_WAIT, 0, NULL, NULL, 0); +} +])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_SYNC_OPENBSD_FUTEX,,Have OpenBSD futex support) + needsync=false + ], [ + AC_MSG_RESULT([no]) + ]) +]) + dnl Utility macro to avoid retyping includes all the time m4_define([FRR_INCLUDES], [#ifdef SUNOS_5 diff --git a/lib/seqlock.c b/lib/seqlock.c new file mode 100644 index 0000000000..223d14952c --- /dev/null +++ b/lib/seqlock.c @@ -0,0 +1,167 @@ +/* + * "Sequence" lock primitive + * + * Copyright (C) 2015 David Lamparter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "seqlock.h" + +#ifdef HAVE_SYNC_LINUX_FUTEX +/* Linux-specific - sys_futex() */ +#include +#include + +static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, + void *addr2, int val3) +{ + return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); +} + +#define wait_once(sqlo, val) \ + sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0) +#define wait_poke(sqlo) \ + sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) + +#elif defined(HAVE_SYNC_OPENBSD_FUTEX) +/* OpenBSD variant of the above. untested, not upstream in OpenBSD. */ +#include +#include + +#define wait_once(sqlo, val) \ + futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0) +#define wait_poke(sqlo) \ + futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) + +#elif defined(HAVE_SYNC_UMTX_OP) +/* FreeBSD-specific: umtx_op() */ +#include + +#define wait_once(sqlo, val) \ + _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL) +#define wait_poke(sqlo) \ + _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL) + +#else +/* generic version. used on *BSD, Solaris and OSX. + */ + +#define wait_init(sqlo) do { \ + pthread_mutex_init(&sqlo->lock, NULL); \ + pthread_cond_init(&sqlo->wake, NULL); \ + } while (0) +#define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock) +#define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock) +#define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock) +#define wait_poke(sqlo) do { \ + pthread_mutex_lock(&sqlo->lock); \ + pthread_cond_broadcast(&sqlo->wake); \ + pthread_mutex_unlock(&sqlo->lock); \ + } while (0) + +#endif + +#ifndef wait_init +#define wait_init(sqlo) /**/ +#define wait_prep(sqlo) /**/ +#define wait_done(sqlo) /**/ +#endif /* wait_init */ + + +void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val) +{ + seqlock_val_t cur, cal; + + seqlock_assert_valid(val); + + wait_prep(sqlo); + while (1) { + cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire); + if (!(cur & 1)) + break; + cal = cur - val - 1; + assert(cal < 0x40000000 || cal > 0xc0000000); + if (cal < 0x80000000) + break; + + wait_once(sqlo, cur); + } + wait_done(sqlo); +} + +bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val) +{ + seqlock_val_t cur; + + seqlock_assert_valid(val); + + cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire); + if (!(cur & 1)) + return 1; + cur -= val; + assert(cur < 0x40000000 || cur > 0xc0000000); + return cur < 0x80000000; +} + +void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val) +{ + seqlock_assert_valid(val); + + atomic_store_explicit(&sqlo->pos, val, memory_order_release); + wait_poke(sqlo); +} + +void seqlock_release(struct seqlock *sqlo) +{ + atomic_store_explicit(&sqlo->pos, 0, memory_order_release); + wait_poke(sqlo); +} + +void seqlock_init(struct seqlock *sqlo) +{ + sqlo->pos = 0; + wait_init(sqlo); +} + + +seqlock_val_t seqlock_cur(struct seqlock *sqlo) +{ + return atomic_load_explicit(&sqlo->pos, memory_order_acquire); +} + +seqlock_val_t seqlock_bump(struct seqlock *sqlo) +{ + seqlock_val_t val; + + val = atomic_fetch_add_explicit(&sqlo->pos, 2, memory_order_release); + wait_poke(sqlo); + return val; +} diff --git a/lib/seqlock.h b/lib/seqlock.h new file mode 100644 index 0000000000..eef05a4307 --- /dev/null +++ b/lib/seqlock.h @@ -0,0 +1,106 @@ +/* + * "Sequence" lock primitive + * + * Copyright (C) 2015 David Lamparter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef _SEQLOCK_H +#define _SEQLOCK_H + +#include +#include +#include +#include "frratomic.h" + +/* + * this locking primitive is intended to use in a 1:N setup. + * + * - one "counter" seqlock issuing increasing numbers + * - multiple seqlock users hold references on these numbers + * + * this is intended for implementing RCU reference-holding. There is one + * global counter, with threads locking a seqlock whenever they take a + * reference. A seqlock can also be idle/unlocked. + * + * The "counter" seqlock will always stay locked; the RCU cleanup thread + * continuously counts it up, waiting for threads to release or progress to a + * sequence number further ahead. If all threads are > N, references dropped + * in N can be free'd. + * + * generally, the lock function is: + * + * Thread-A Thread-B + * + * seqlock_acquire(a) + * | running seqlock_wait(b) -- a <= b + * seqlock_release() | blocked + * OR: seqlock_acquire(a') | -- a' > b + * (resumes) + */ + +/* use sequentially increasing "ticket numbers". lowest bit will always + * be 1 to have a 'cleared' indication (i.e., counts 1,3,5,7,etc. ) + */ +typedef _Atomic uint32_t seqlock_ctr_t; +typedef uint32_t seqlock_val_t; +#define seqlock_assert_valid(val) assert(val & 1) + + +struct seqlock { +/* always used */ + seqlock_ctr_t pos; +/* used when futexes not available: (i.e. non-linux) */ + pthread_mutex_t lock; + pthread_cond_t wake; +}; + + +/* sqlo = 0 - init state: not held */ +extern void seqlock_init(struct seqlock *sqlo); + + +/* while (sqlo <= val) - wait until seqlock->pos > val, or seqlock unheld */ +extern void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val); +extern bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val); + +static inline bool seqlock_held(struct seqlock *sqlo) +{ + return !!atomic_load_explicit(&sqlo->pos, memory_order_relaxed); +} + +/* sqlo - get seqlock position -- for the "counter" seqlock */ +extern seqlock_val_t seqlock_cur(struct seqlock *sqlo); +/* sqlo++ - note: like x++, returns previous value, before bumping */ +extern seqlock_val_t seqlock_bump(struct seqlock *sqlo); + + +/* sqlo = val - can be used on held seqlock. */ +extern void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val); +/* sqlo = ref - standard pattern: acquire relative to other seqlock */ +static inline void seqlock_acquire(struct seqlock *sqlo, struct seqlock *ref) +{ + seqlock_acquire_val(sqlo, seqlock_cur(ref)); +} + +/* sqlo = 0 - set seqlock position to 0, marking as non-held */ +extern void seqlock_release(struct seqlock *sqlo); +/* release should normally be followed by a bump on the "counter", if + * anything other than reading RCU items was done + */ + +#endif /* _SEQLOCK_H */ diff --git a/lib/subdir.am b/lib/subdir.am index 3b14be4676..03629c14e9 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -65,6 +65,7 @@ lib_libfrr_la_SOURCES = \ lib/ringbuf.c \ lib/routemap.c \ lib/sbuf.c \ + lib/seqlock.c \ lib/sha256.c \ lib/sigevent.c \ lib/skiplist.c \ @@ -193,6 +194,7 @@ pkginclude_HEADERS += \ lib/ringbuf.h \ lib/routemap.h \ lib/sbuf.h \ + lib/seqlock.h \ lib/sha256.h \ lib/sigevent.h \ lib/skiplist.h \ diff --git a/tests/.gitignore b/tests/.gitignore index de648015f1..c29a6c3820 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -32,6 +32,7 @@ /lib/test_privs /lib/test_ringbuf /lib/test_segv +/lib/test_seqlock /lib/test_sig /lib/test_srcdest_table /lib/test_stream diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c new file mode 100644 index 0000000000..6b2b9ed8a5 --- /dev/null +++ b/tests/lib/test_seqlock.c @@ -0,0 +1,122 @@ +/* + * basic test for seqlock + * + * Copyright (C) 2015 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "monotime.h" +#include "seqlock.h" + +static struct seqlock sqlo; +static pthread_t thr1; +static struct timeval start; + +static void writestr(const char *str) +{ + struct iovec iov[2]; + char buf[32]; + int64_t usec = monotime_since(&start, NULL); + + snprintf(buf, sizeof(buf), "[%02"PRId64"] ", usec / 100000); + + iov[0].iov_base = buf; + iov[0].iov_len = strlen(buf); + iov[1].iov_base = (char *)str; + iov[1].iov_len = strlen(str); + writev(1, iov, 2); +} + +static void *thr1func(void *arg) +{ + assert(!seqlock_held(&sqlo)); + assert(seqlock_check(&sqlo, 1)); + seqlock_wait(&sqlo, 1); + writestr("thr1 (unheld)\n"); + + sleep(2); + + assert(seqlock_held(&sqlo)); + assert(seqlock_check(&sqlo, 1)); + seqlock_wait(&sqlo, 1); + writestr("thr1 @1\n"); + + seqlock_wait(&sqlo, 3); + writestr("thr1 @3\n"); + + seqlock_wait(&sqlo, 5); + writestr("thr1 @5\n"); + + seqlock_wait(&sqlo, 7); + writestr("thr1 @7\n"); + + seqlock_wait(&sqlo, 9); + writestr("thr1 @9\n"); + + seqlock_wait(&sqlo, 11); + writestr("thr1 @11\n"); + return NULL; +} + +int main(int argc, char **argv) +{ + monotime(&start); + + seqlock_init(&sqlo); + + assert(!seqlock_held(&sqlo)); + seqlock_acquire_val(&sqlo, 1); + assert(seqlock_held(&sqlo)); + + assert(seqlock_cur(&sqlo) == 1); + assert(seqlock_bump(&sqlo) == 1); + assert(seqlock_cur(&sqlo) == 3); + assert(seqlock_bump(&sqlo) == 3); + assert(seqlock_bump(&sqlo) == 5); + assert(seqlock_bump(&sqlo) == 7); + assert(seqlock_cur(&sqlo) == 9); + + assert(seqlock_held(&sqlo)); + seqlock_release(&sqlo); + assert(!seqlock_held(&sqlo)); + + pthread_create(&thr1, NULL, thr1func, NULL); + sleep(1); + + writestr("main @3\n"); + seqlock_acquire_val(&sqlo, 3); + sleep(2); + + writestr("main @5\n"); + seqlock_bump(&sqlo); + sleep(1); + + writestr("main @9\n"); + seqlock_acquire_val(&sqlo, 9); + sleep(1); + + writestr("main @release\n"); + seqlock_release(&sqlo); + sleep(1); +} diff --git a/tests/subdir.am b/tests/subdir.am index 365fe00cc6..167d8b43f1 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -59,6 +59,7 @@ check_PROGRAMS = \ tests/lib/test_ringbuf \ tests/lib/test_srcdest_table \ tests/lib/test_segv \ + tests/lib/test_seqlock \ tests/lib/test_sig \ tests/lib/test_stream \ tests/lib/test_table \ @@ -236,6 +237,10 @@ tests_lib_test_segv_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_segv_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_segv_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_segv_SOURCES = tests/lib/test_segv.c +tests_lib_test_seqlock_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_seqlock_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_seqlock_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_seqlock_SOURCES = tests/lib/test_seqlock.c tests_lib_test_sig_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD) From 0d019561bd855b6097b75d8d95a4dc69e410aae1 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 30 Jan 2019 23:18:06 +0100 Subject: [PATCH 04/64] lib: move/redo some macros Signed-off-by: David Lamparter --- lib/compiler.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/memory.h | 2 -- lib/zebra.h | 37 ------------------------ 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/lib/compiler.h b/lib/compiler.h index cb4f7531ec..02bdbd6afd 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -69,6 +69,10 @@ extern "C" { #define _FALLTHROUGH #endif +/* for helper functions defined inside macros */ +#define macro_inline static inline __attribute__((unused)) +#define macro_pure static inline __attribute__((unused, pure)) + /* * for warnings on macros, put in the macro content like this: * #define MACRO BLA CPP_WARN("MACRO has been deprecated") @@ -92,6 +96,80 @@ extern "C" { #define CPP_NOTICE(text) #endif +/* MAX / MIN are not commonly defined, but useful */ +/* note: glibc sys/param.h has #define MIN(a,b) (((a)<(b))?(a):(b)) */ +#ifdef MAX +#undef MAX +#endif +#define MAX(a, b) \ + ({ \ + typeof(a) _max_a = (a); \ + typeof(b) _max_b = (b); \ + _max_a > _max_b ? _max_a : _max_b; \ + }) +#ifdef MIN +#undef MIN +#endif +#define MIN(a, b) \ + ({ \ + typeof(a) _min_a = (a); \ + typeof(b) _min_b = (b); \ + _min_a < _min_b ? _min_a : _min_b; \ + }) + +#ifndef offsetof +#ifdef __compiler_offsetof +#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE,MEMBER) +#else +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif +#endif + +/* this variant of container_of() retains 'const' on pointers without needing + * to be told to do so. The following will all work without warning: + * + * struct member *p; + * const struct member *cp; + * + * const struct cont *x = container_of(cp, struct cont, member); + * const struct cont *x = container_of(cp, const struct cont, member); + * const struct cont *x = container_of(p, struct cont, member); + * const struct cont *x = container_of(p, const struct cont, member); + * struct cont *x = container_of(p, struct cont, member); + * + * but the following will generate warnings about stripping const: + * + * struct cont *x = container_of(cp, struct cont, member); + * struct cont *x = container_of(cp, const struct cont, member); + * struct cont *x = container_of(p, const struct cont, member); + */ +#ifdef container_of +#undef container_of +#endif +#define container_of(ptr, type, member) \ + (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(&((type *)0)->member), \ + typeof(ptr)) \ + || __builtin_types_compatible_p(void *, typeof(ptr)), \ + ({ \ + typeof(((type *)0)->member) *__mptr = (void *)(ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }), \ + ({ \ + typeof(((const type *)0)->member) *__mptr = (ptr); \ + (const type *)((const char *)__mptr - \ + offsetof(type, member)); \ + }) \ + )) + +#define container_of_null(ptr, type, member) \ + ({ \ + typeof(ptr) _tmp = (ptr); \ + _tmp ? container_of(_tmp, type, member) : NULL; \ + }) + +#define array_size(ar) (sizeof(ar) / sizeof(ar[0])) + #ifdef __cplusplus } #endif diff --git a/lib/memory.h b/lib/memory.h index 91a02b7966..0002ea3349 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -26,8 +26,6 @@ extern "C" { #endif -#define array_size(ar) (sizeof(ar) / sizeof(ar[0])) - #if defined(HAVE_MALLOC_SIZE) && !defined(HAVE_MALLOC_USABLE_SIZE) #define malloc_usable_size(x) malloc_size(x) #define HAVE_MALLOC_USABLE_SIZE diff --git a/lib/zebra.h b/lib/zebra.h index b96fb5a206..7b83fed926 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -335,43 +335,6 @@ struct in_pktinfo { #endif /* ndef BYTE_ORDER */ -/* MAX / MIN are not commonly defined, but useful */ -/* note: glibc sys/param.h has #define MIN(a,b) (((a)<(b))?(a):(b)) */ -#ifdef MAX -#undef MAX -#endif -#define MAX(a, b) \ - ({ \ - typeof(a) _max_a = (a); \ - typeof(b) _max_b = (b); \ - _max_a > _max_b ? _max_a : _max_b; \ - }) -#ifdef MIN -#undef MIN -#endif -#define MIN(a, b) \ - ({ \ - typeof(a) _min_a = (a); \ - typeof(b) _min_b = (b); \ - _min_a < _min_b ? _min_a : _min_b; \ - }) - -#ifndef offsetof -#ifdef __compiler_offsetof -#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) -#else -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) -#endif -#endif - -#ifndef container_of -#define container_of(ptr, type, member) \ - ({ \ - const typeof(((type *)0)->member) *__mptr = (ptr); \ - (type *)((char *)__mptr - offsetof(type, member)); \ - }) -#endif - #define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0])) /* For old definition. */ From 7e3a1ec742fab489eceeb23869063ebdedad12ff Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 23 Mar 2019 14:53:58 +0100 Subject: [PATCH 05/64] lib: ZEBRA_NUM_OF -> array_size The latter is widely used, e.g. in the Linux kernel. Signed-off-by: David Lamparter --- lib/privs.c | 2 +- lib/sockunion.c | 2 +- lib/zebra.h | 2 -- nhrpd/nhrp_main.c | 2 +- nhrpd/nhrp_vc.c | 6 +++--- nhrpd/resolver.c | 2 +- nhrpd/zbuf.c | 2 +- zebra/zebra_fpm_netlink.c | 2 +- zebra/zebra_fpm_protobuf.c | 2 +- zebra/zebra_rib.c | 2 +- zebra/zebra_vxlan.c | 2 +- 11 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/privs.c b/lib/privs.c index 59f24afe4a..50f80e3c02 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -854,7 +854,7 @@ void zprivs_init(struct zebra_privs_t *zprivs) zprivs->user, zprivs->vty_group); exit(1); } - if (i >= ngroups && ngroups < (int)ZEBRA_NUM_OF(groups)) { + if (i >= ngroups && ngroups < (int)array_size(groups)) { groups[i] = zprivs_state.vtygrp; } } diff --git a/lib/sockunion.c b/lib/sockunion.c index af4f41f37c..bb5426d74a 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -472,7 +472,7 @@ unsigned int sockunion_hash(const union sockunion *su) return jhash_1word(su->sin.sin_addr.s_addr, 0); case AF_INET6: return jhash2(su->sin6.sin6_addr.s6_addr32, - ZEBRA_NUM_OF(su->sin6.sin6_addr.s6_addr32), 0); + array_size(su->sin6.sin6_addr.s6_addr32), 0); } return 0; } diff --git a/lib/zebra.h b/lib/zebra.h index 7b83fed926..b1ea43c747 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -335,8 +335,6 @@ struct in_pktinfo { #endif /* ndef BYTE_ORDER */ -#define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0])) - /* For old definition. */ #ifndef IN6_ARE_ADDR_EQUAL #define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index 9b8599eded..d7c485f0a0 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -55,7 +55,7 @@ struct zebra_privs_t nhrpd_privs = { .vty_group = VTY_GROUP, #endif .caps_p = _caps_p, - .cap_num_p = ZEBRA_NUM_OF(_caps_p), + .cap_num_p = array_size(_caps_p), }; static void parse_arguments(int argc, char **argv) diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c index f92ea4ac90..fa3549f5ed 100644 --- a/nhrpd/nhrp_vc.c +++ b/nhrpd/nhrp_vc.c @@ -102,7 +102,7 @@ int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc) { char buf[2][SU_ADDRSTRLEN]; struct child_sa *sa = NULL, *lsa; - uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head); + uint32_t child_hash = child_id % array_size(childlist_head); int abort_migration = 0; list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry) @@ -202,7 +202,7 @@ void nhrp_vc_init(void) size_t i; nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp, "NHRP VC hash"); - for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) + for (i = 0; i < array_size(childlist_head); i++) list_init(&childlist_head[i]); } @@ -211,7 +211,7 @@ void nhrp_vc_reset(void) struct child_sa *sa, *n; size_t i; - for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) { + for (i = 0; i < array_size(childlist_head); i++) { list_for_each_entry_safe(sa, n, &childlist_head[i], childlist_entry) nhrp_vc_ipsec_updown(sa->id, 0); diff --git a/nhrpd/resolver.c b/nhrpd/resolver.c index 830f0e1c84..64b16e7ee3 100644 --- a/nhrpd/resolver.c +++ b/nhrpd/resolver.c @@ -171,7 +171,7 @@ static void ares_address_cb(void *arg, int status, int timeouts, return; } - for (i = 0; i < ZEBRA_NUM_OF(addr) && he->h_addr_list[i] != NULL; i++) { + for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) { memset(&addr[i], 0, sizeof(addr[i])); addr[i].sa.sa_family = he->h_addrtype; switch (he->h_addrtype) { diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c index c662295083..7f1475cc69 100644 --- a/nhrpd/zbuf.c +++ b/nhrpd/zbuf.c @@ -196,7 +196,7 @@ int zbufq_write(struct zbuf_queue *zbq, int fd) iov[iovcnt++] = (struct iovec){ .iov_base = zb->head, .iov_len = zbuf_used(zb), }; - if (iovcnt >= ZEBRA_NUM_OF(iov)) + if (iovcnt >= array_size(iov)) break; } diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 28333526a7..bdc1dcdff3 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -158,7 +158,7 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri, memset(&nhi, 0, sizeof(nhi)); src = NULL; - if (ri->num_nhs >= (int)ZEBRA_NUM_OF(ri->nhs)) + if (ri->num_nhs >= (int)array_size(ri->nhs)) return 0; nhi.recursive = nexthop->rparent ? 1 : 0; diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index be0f6a23be..0f95c9ba8b 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -176,7 +176,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, if (num_nhs >= multipath_num) break; - if (num_nhs >= ZEBRA_NUM_OF(nexthops)) + if (num_nhs >= array_size(nexthops)) break; if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index e47499b065..e675ca0a7a 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3482,7 +3482,7 @@ struct route_table *rib_tables_iter_next(rib_tables_iter_t *iter) while (1) { while (iter->afi_safi_ix - < (int)ZEBRA_NUM_OF(afi_safis)) { + < (int)array_size(afi_safis)) { table = zebra_vrf_table( afi_safis[iter->afi_safi_ix].afi, afi_safis[iter->afi_safi_ix].safi, diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 3a8426e772..285fe3200e 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -2135,7 +2135,7 @@ static unsigned int neigh_hash_keymake(void *p) return jhash_1word(ip->ipaddr_v4.s_addr, 0); return jhash2(ip->ipaddr_v6.s6_addr32, - ZEBRA_NUM_OF(ip->ipaddr_v6.s6_addr32), 0); + array_size(ip->ipaddr_v6.s6_addr32), 0); } /* From 2f6197b044edea555348f4c0c5acd2d3ab5feabf Mon Sep 17 00:00:00 2001 From: Biswajit Sadhu Date: Wed, 24 Apr 2019 00:40:01 -0700 Subject: [PATCH 06/64] bgpd: Prevent IPv6 routes received via a ibgp session with own ip as nexthop Prevent IPv6 routes received via a ibgp session with one of its own interface ip as nexthop from getting installed in the BGP table. Implemented IPV6 HASH table, where we need to add any ipv6 address as they gets configured and delete them from the HASH table as the ipv6 addresses get unconfigured. The above hash table is used to verify if any route learned via BGP has nexthop which is equal to one of its its connected ipv6 interface. Signed-off-by: Biswajit Sadhu sadhub@vmware.com --- bgpd/bgp_nexthop.c | 133 +++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_nexthop.h | 10 ++++ bgpd/bgp_route.c | 4 +- bgpd/bgpd.c | 1 + bgpd/bgpd.h | 3 + lib/jhash.c | 15 +++++ lib/jhash.h | 2 + 7 files changed, 167 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index de97b73c72..cf67a36238 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -47,6 +47,11 @@ DEFINE_MTYPE_STATIC(BGPD, MARTIAN_STRING, "BGP Martian Address Intf String"); +static void bgp_ipv6_address_add(struct bgp *bgp, struct connected *ifc, + struct prefix *p); +static void bgp_ipv6_address_del(struct bgp *bgp, struct connected *ifc, + struct prefix *p); + char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size) { prefix2str(&(bnc->node->p), buf, size); @@ -379,6 +384,8 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc) if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) return; + bgp_ipv6_address_add(bgp, ifc, addr); + rn = bgp_node_get(bgp->connected_table[AFI_IP6], (struct prefix *)&p); @@ -419,6 +426,8 @@ void bgp_connected_delete(struct bgp *bgp, struct connected *ifc) if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) return; + bgp_ipv6_address_del(bgp, ifc, addr); + rn = bgp_node_lookup(bgp->connected_table[AFI_IP6], (struct prefix *)&p); } @@ -772,3 +781,127 @@ void bgp_scan_finish(struct bgp *bgp) bgp->import_check_table[afi] = NULL; } } + +static void *bgp_ipv6_address_hash_alloc(void *p) +{ + const struct in6_addr *v6addr = (const struct in6_addr *)p; + struct bgp_addrv6 *addr; + + addr = XMALLOC(MTYPE_BGP_ADDR, sizeof(struct bgp_addrv6)); + addr->addrv6 = *v6addr; + + addr->ifp_name_list = list_new(); + addr->ifp_name_list->del = bgp_address_hash_string_del; + + return addr; +} + +static void bgp_ipv6_address_hash_free(void *data) +{ + struct bgp_addrv6 *v6addr = data; + + list_delete(&v6addr->ifp_name_list); + XFREE(MTYPE_BGP_ADDR, v6addr); +} + +static unsigned int bgp_ipv6_address_hash_key_make(void *p) +{ + const struct bgp_addrv6 *v6 = p; + + return __ipv6_addr_jhash(&v6->addrv6, 0); +} + +static bool bgp_ipv6_address_hash_cmp(const void *p1, + const void *p2) +{ + const struct bgp_addrv6 *addr1 = p1; + const struct bgp_addrv6 *addr2 = p2; + + return(!memcmp(&addr1->addrv6, &addr2->addrv6, + sizeof(struct in6_addr))); +} + +void bgp_ipv6_address_init(struct bgp *bgp) +{ + bgp->ipv6_address_hash = hash_create(bgp_ipv6_address_hash_key_make, + bgp_ipv6_address_hash_cmp, + "BGP IPV6 Address Hash"); +} + +void bgp_ipv6_address_destroy(struct bgp *bgp) +{ + if (bgp->ipv6_address_hash == NULL) + return; + hash_clean(bgp->ipv6_address_hash, + bgp_ipv6_address_hash_free); + + hash_free(bgp->ipv6_address_hash); + bgp->ipv6_address_hash = NULL; +} + +static void bgp_ipv6_address_add(struct bgp *bgp, struct connected *ifc, + struct prefix *p) +{ + struct bgp_addrv6 tmp = {0}; + struct bgp_addrv6 *addr = NULL; + struct listnode *node = NULL; + char *name = 0; + + tmp.addrv6 = p->u.prefix6; + + addr = hash_get(bgp->ipv6_address_hash, &tmp, + bgp_ipv6_address_hash_alloc); + if (!addr) + return; + + for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) { + if (strcmp(ifc->ifp->name, name) == 0) + break; + } + + if (!node) { + name = XSTRDUP(MTYPE_MARTIAN_STRING, ifc->ifp->name); + listnode_add(addr->ifp_name_list, name); + } +} + + +static void bgp_ipv6_address_del(struct bgp *bgp, struct connected *ifc, + struct prefix *p) +{ + struct bgp_addrv6 tmp; + struct bgp_addrv6 *addr; + struct listnode *node; + char *name; + + + tmp.addrv6 = p->u.prefix6; + + addr = hash_lookup(bgp->ipv6_address_hash, &tmp); + /* may have been deleted earlier by bgp_interface_down() */ + if (addr == NULL) + return; + + for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) { + if (strcmp(ifc->ifp->name, name) == 0) + break; + } + + if (node) { + list_delete_node(addr->ifp_name_list, node); + XFREE(MTYPE_MARTIAN_STRING, name); + } + + if (addr->ifp_name_list->count == 0) { + hash_release(bgp->ipv6_address_hash, addr); + list_delete(&addr->ifp_name_list); + XFREE(MTYPE_BGP_ADDR, addr); + } +} +int bgp_nexthop_self_ipv6(struct bgp *bgp, struct in6_addr *addr) +{ + struct bgp_addrv6 tmp; + + tmp.addrv6 = *addr; + return (!!hash_lookup(bgp->ipv6_address_hash, &tmp)); +} diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index f06fae5706..c26295f810 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -74,6 +74,12 @@ struct tip_addr { int refcnt; }; +/* ipv6 connected address info structure */ +struct bgp_addrv6 { + struct in6_addr addrv6; + struct list *ifp_name_list; +}; + extern void bgp_connected_add(struct bgp *bgp, struct connected *c); extern void bgp_connected_delete(struct bgp *bgp, struct connected *c); extern int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, @@ -96,4 +102,8 @@ extern void bgp_tip_hash_init(struct bgp *bgp); extern void bgp_tip_hash_destroy(struct bgp *bgp); extern void bgp_nexthop_show_address_hash(struct vty *vty, struct bgp *bgp); +extern void bgp_ipv6_address_init(struct bgp *bgp); +extern void bgp_ipv6_address_destroy(struct bgp *bgp); +extern int bgp_nexthop_self_ipv6(struct bgp *bgp, + struct in6_addr *addr); #endif /* _QUAGGA_BGP_NEXTHOP_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7850666085..5fb6d70119 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2932,7 +2932,9 @@ static int bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, ret = (IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) || IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) || IN6_IS_ADDR_MULTICAST( - &attr->mp_nexthop_global)); + &attr->mp_nexthop_global) + || bgp_nexthop_self_ipv6(bgp, + &attr->mp_nexthop_global)); break; default: diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index b2925cd512..730cc8bde7 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3213,6 +3213,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp->vrf_id = vrf_generate_id(); bgp_router_id_set(bgp, &bgp->router_id_zebra); bgp_address_init(bgp); + bgp_ipv6_address_init(bgp); bgp_tip_hash_init(bgp); bgp_scan_init(bgp); *bgp_val = bgp; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b0f6567534..cf03328f8c 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -393,6 +393,9 @@ struct bgp { struct hash *address_hash; + /* ipv6 connected address hash table pointer */ + struct hash *ipv6_address_hash; + /* DB for all local tunnel-ips - used mainly for martian checks Currently it only has all VxLan tunnel IPs*/ struct hash *tip_hash; diff --git a/lib/jhash.c b/lib/jhash.c index 0d561ef3a4..d0aff57d87 100644 --- a/lib/jhash.c +++ b/lib/jhash.c @@ -185,3 +185,18 @@ uint32_t jhash_1word(uint32_t a, uint32_t initval) { return jhash_3words(a, 0, 0, initval); } + +/* ipv6 hash function */ +uint32_t __ipv6_addr_jhash(const struct in6_addr *a, const uint32_t initval) +{ + uint32_t v = 0; + uint32_t y[4] = {0}; + + /* Since s6_addr32 is not available is few os like FreeBSD, NetBDS, + * OMIBDS & OpenBDS. So recreating in uint32_t format. + */ + memcpy(y, a->s6_addr, sizeof(struct in6_addr)); + v = y[0] ^ y[1]; + + return jhash_3words(v, y[2], y[3], initval); +} diff --git a/lib/jhash.h b/lib/jhash.h index 977421495c..1b6f879369 100644 --- a/lib/jhash.h +++ b/lib/jhash.h @@ -45,6 +45,8 @@ extern uint32_t jhash_3words(uint32_t a, uint32_t b, uint32_t c, uint32_t initval); extern uint32_t jhash_2words(uint32_t a, uint32_t b, uint32_t initval); extern uint32_t jhash_1word(uint32_t a, uint32_t initval); +extern uint32_t __ipv6_addr_jhash(const struct in6_addr *a, + const uint32_t initval); #ifdef __cplusplus } From ba5165ecccdbe1ff5aa1ca0203e389ef2c269df5 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 27 Apr 2019 19:55:21 -0400 Subject: [PATCH 07/64] zebra: Modify how we display/store os description The alias/description of an interface in linux was being used to override the internal description. As such let's fix the display to keep track of both if we have it. Config in FRR: ! interface docker0 description another combination ! interface enp3s0 description BAMBOOZLE ME WILL YOU ! Config in linux: sharpd@robot ~/f/zebra> ip link show 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 alias This is the loopback you cabbage 2: enp3s0: mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether 74:d0:2b:9c:16:eb brd ff:ff:ff:ff:ff:ff alias HI HI HI Now the 'show int descr' command: robot# show int description Interface Status Protocol Description docker0 up down another combination enp3s0 up up BAMBOOZLE ME WILL YOU HI HI HI lo up up This is the loopback you cabbage Fixes: #4191 Signed-off-by: Donald Sharp --- zebra/if_netlink.c | 23 +++++++++++++---------- zebra/interface.c | 21 ++++++++++++++++++++- zebra/interface.h | 3 +++ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index ba518ea576..ce0834f190 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -690,9 +690,6 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifp->speed = get_iflink_speed(ifp); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; - if (desc) - ifp->desc = XSTRDUP(MTYPE_TMP, desc); - /* Set zebra interface type */ zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); if (IS_ZEBRA_IF_VRF(ifp)) @@ -707,6 +704,11 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) zif = (struct zebra_if *)ifp->info; zif->link_ifindex = link_ifindex; + if (desc) { + XFREE(MTYPE_TMP, zif->desc); + zif->desc = XSTRDUP(MTYPE_TMP, desc); + } + /* Hardware type and address. */ ifp->ll_type = netlink_to_zebra_link_type(ifi->ifi_type); netlink_interface_update_hw_addr(tb, ifp); @@ -1106,7 +1108,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t bond_ifindex = IFINDEX_INTERNAL; ifindex_t link_ifindex = IFINDEX_INTERNAL; uint8_t old_hw_addr[INTERFACE_HWADDR_MAX]; - + struct zebra_if *zif; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -1186,12 +1188,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* See if interface is present. */ ifp = if_lookup_by_name_per_ns(zns, name); - if (ifp) { - XFREE(MTYPE_TMP, ifp->desc); - if (desc) - ifp->desc = XSTRDUP(MTYPE_TMP, desc); - } - if (h->nlmsg_type == RTM_NEWLINK) { if (tb[IFLA_MASTER]) { if (slave_kind && (strcmp(slave_kind, "vrf") == 0) @@ -1390,6 +1386,13 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if_delete_update(ifp); } + zif = ifp->info; + if (zif) { + XFREE(MTYPE_TMP, zif->desc); + if (desc) + zif->desc = XSTRDUP(MTYPE_TMP, desc); + } + return 0; } diff --git a/zebra/interface.c b/zebra/interface.c index 10f1f92100..b0ddcaf8bc 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -181,6 +181,7 @@ static int if_zebra_delete_hook(struct interface *ifp) list_delete(&rtadv->AdvDNSSLList); #endif /* HAVE_RTADV */ + XFREE(MTYPE_TMP, zebra_if->desc); THREAD_OFF(zebra_if->speed_update); XFREE(MTYPE_ZINFO, zebra_if); @@ -1303,6 +1304,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) if (ifp->desc) vty_out(vty, " Description: %s\n", ifp->desc); + if (zebra_if->desc) + vty_out(vty, " OS Description: %s\n", zebra_if->desc); + if (ifp->ifindex == IFINDEX_INTERNAL) { vty_out(vty, " pseudo interface\n"); return; @@ -1696,6 +1700,10 @@ static void if_show_description(struct vty *vty, vrf_id_t vrf_id) vty_out(vty, "Interface Status Protocol Description\n"); FOR_ALL_INTERFACES (vrf, ifp) { int len; + struct zebra_if *zif; + bool intf_desc; + + intf_desc = false; len = vty_out(vty, "%s", ifp->name); vty_out(vty, "%*s", (16 - len), " "); @@ -1715,8 +1723,19 @@ static void if_show_description(struct vty *vty, vrf_id_t vrf_id) vty_out(vty, "down down "); } - if (ifp->desc) + if (ifp->desc) { + intf_desc = true; vty_out(vty, "%s", ifp->desc); + } + zif = ifp->info; + if (zif && zif->desc) { + vty_out(vty, "%s%s", + intf_desc + ? "\n " + : "", + zif->desc); + } + vty_out(vty, "\n"); } } diff --git a/zebra/interface.h b/zebra/interface.h index ce404e8253..bbb5445cc6 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -342,6 +342,9 @@ struct zebra_if { bool v6_2_v4_ll_neigh_entry; char neigh_mac[6]; struct in6_addr v6_2_v4_ll_addr6; + + /* The description of the interface */ + char *desc; }; DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), From 6873d66a262ca65bd13355f2ea268b88116feafe Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 27 Apr 2019 14:09:05 +0000 Subject: [PATCH 08/64] vtysh: Fixup exit nexthop-group to include sharpd The exit of the nexthop-group should know about sharpd Signed-off-by: Donald Sharp --- vtysh/vtysh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index b8da90ca8e..eff1e996ed 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2172,7 +2172,7 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit", return vtysh_exit_vrf(self, vty, argc, argv); } -DEFUNSH(VTYSH_PBRD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, +DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); From abd71baa2e75444248299be81cbebdac1bdd2fc3 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Jan 2019 01:09:13 +0100 Subject: [PATCH 09/64] lib: typesafe lists, skiplist & hash By the power of the C preprocessor, these macros provide type-safe warppers for simple lists, skiplists and hash tables. Also, by changing the instantiation macro, it is easily possible to switch between algorithms; the code itself does not need to be changed since the API is identical across all algorithms. Signed-off-by: David Lamparter --- lib/subdir.am | 2 + lib/typesafe.c | 377 +++++++++++++++++++++++++++++ lib/typesafe.h | 640 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1019 insertions(+) create mode 100644 lib/typesafe.c create mode 100644 lib/typesafe.h diff --git a/lib/subdir.am b/lib/subdir.am index 03629c14e9..d60ebc5d9c 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -80,6 +80,7 @@ lib_libfrr_la_SOURCES = \ lib/table.c \ lib/termtable.c \ lib/thread.c \ + lib/typesafe.c \ lib/vector.c \ lib/vrf.c \ lib/vty.c \ @@ -208,6 +209,7 @@ pkginclude_HEADERS += \ lib/table.h \ lib/termtable.h \ lib/thread.h \ + lib/typesafe.h \ lib/vector.h \ lib/vlan.h \ lib/vrf.h \ diff --git a/lib/typesafe.c b/lib/typesafe.c new file mode 100644 index 0000000000..bd269e9b54 --- /dev/null +++ b/lib/typesafe.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "typesafe.h" +#include "memory.h" + +DEFINE_MTYPE_STATIC(LIB, TYPEDHASH_BUCKET, "Typed-hash bucket") +DEFINE_MTYPE_STATIC(LIB, SKIPLIST_OFLOW, "Skiplist overflow") + +#if 0 +static void hash_consistency_check(struct thash_head *head) +{ + uint32_t i; + struct thash_item *item, *prev; + + for (i = 0; i < HASH_SIZE(*head); i++) { + item = head->entries[i]; + prev = NULL; + while (item) { + assert(HASH_KEY(*head, item->hashval) == i); + assert(!prev || item->hashval >= prev->hashval); + prev = item; + item = item->next; + } + } +} +#else +#define hash_consistency_check(x) +#endif + +void typesafe_hash_grow(struct thash_head *head) +{ + uint32_t newsize = head->count, i, j; + uint8_t newshift, delta; + + hash_consistency_check(head); + + newsize |= newsize >> 1; + newsize |= newsize >> 2; + newsize |= newsize >> 4; + newsize |= newsize >> 8; + newsize |= newsize >> 16; + newsize++; + newshift = __builtin_ctz(newsize) + 1; + + if (head->maxshift && newshift > head->maxshift) + newshift = head->maxshift; + if (newshift == head->tabshift) + return; + newsize = _HASH_SIZE(newshift); + + head->entries = XREALLOC(MTYPE_TYPEDHASH_BUCKET, head->entries, + sizeof(head->entries[0]) * newsize); + memset(head->entries + HASH_SIZE(*head), 0, + sizeof(head->entries[0]) * + (newsize - HASH_SIZE(*head))); + + delta = newshift - head->tabshift; + + i = HASH_SIZE(*head); + if (i == 0) + goto out; + do { + struct thash_item **apos, *item; + + i--; + apos = &head->entries[i]; + + for (j = 0; j < (1U << delta); j++) { + item = *apos; + *apos = NULL; + + head->entries[(i << delta) + j] = item; + apos = &head->entries[(i << delta) + j]; + + while ((item = *apos)) { + uint32_t midbits; + midbits = _HASH_KEY(newshift, item->hashval); + midbits &= (1 << delta) - 1; + if (midbits > j) + break; + apos = &item->next; + } + } + } while (i > 0); + +out: + head->tabshift = newshift; + hash_consistency_check(head); +} + +void typesafe_hash_shrink(struct thash_head *head) +{ + uint32_t newsize = head->count, i, j; + uint8_t newshift, delta; + + hash_consistency_check(head); + + if (!head->count) { + XFREE(MTYPE_TYPEDHASH_BUCKET, head->entries); + head->tabshift = 0; + return; + } + + newsize |= newsize >> 1; + newsize |= newsize >> 2; + newsize |= newsize >> 4; + newsize |= newsize >> 8; + newsize |= newsize >> 16; + newsize++; + newshift = __builtin_ctz(newsize) + 1; + + if (head->minshift && newshift < head->minshift) + newshift = head->minshift; + if (newshift == head->tabshift) + return; + newsize = _HASH_SIZE(newshift); + + delta = head->tabshift - newshift; + + for (i = 0; i < newsize; i++) { + struct thash_item **apos = &head->entries[i]; + + for (j = 0; j < (1U << delta); j++) { + *apos = head->entries[(i << delta) + j]; + while (*apos) + apos = &(*apos)->next; + } + } + head->entries = XREALLOC(MTYPE_TYPEDHASH_BUCKET, head->entries, + sizeof(head->entries[0]) * newsize); + head->tabshift = newshift; + + hash_consistency_check(head); +} + +/* skiplist */ + +static inline struct sskip_item *sl_level_get(struct sskip_item *item, + size_t level) +{ + if (level < SKIPLIST_OVERFLOW) + return item->next[level]; + if (level == SKIPLIST_OVERFLOW && !((uintptr_t)item->next[level] & 1)) + return item->next[level]; + + uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW]; + ptrval &= UINTPTR_MAX - 3; + struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval; + return oflow->next[level - SKIPLIST_OVERFLOW]; +} + +static inline void sl_level_set(struct sskip_item *item, size_t level, + struct sskip_item *value) +{ + if (level < SKIPLIST_OVERFLOW) + item->next[level] = value; + else if (level == SKIPLIST_OVERFLOW && !((uintptr_t)item->next[level] & 1)) + item->next[level] = value; + else { + uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW]; + ptrval &= UINTPTR_MAX - 3; + struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval; + oflow->next[level - SKIPLIST_OVERFLOW] = value; + } +} + +struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, + struct sskip_item *item, + int (*cmpfn)(const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH, newlevel, auxlevel; + struct sskip_item *prev = &head->hitem, *next, *auxprev, *auxnext; + int cmpval; + + /* level / newlevel are 1-counted here */ + newlevel = __builtin_ctz(random()) + 1; + if (newlevel > SKIPLIST_MAXDEPTH) + newlevel = SKIPLIST_MAXDEPTH; + + next = NULL; + while (level >= newlevel) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + prev = next; + continue; + } else if (cmpval == 0) { + return next; + } + level--; + } + + /* check for duplicate item - could be removed if code doesn't rely + * on it, but not really work the complication. */ + auxlevel = level; + auxprev = prev; + while (auxlevel) { + auxlevel--; + auxnext = sl_level_get(auxprev, auxlevel); + cmpval = 1; + while (auxnext && (cmpval = cmpfn(auxnext, item)) < 0) { + auxprev = auxnext; + auxnext = sl_level_get(auxprev, auxlevel); + } + if (cmpval == 0) + return auxnext; + }; + + head->count++; + memset(item, 0, sizeof(*item)); + if (newlevel > SKIPLIST_EMBED) { + struct sskip_overflow *oflow; + oflow = XMALLOC(MTYPE_SKIPLIST_OFLOW, sizeof(void *) + * (newlevel - SKIPLIST_OVERFLOW)); + item->next[SKIPLIST_OVERFLOW] = (struct sskip_item *) + ((uintptr_t)oflow | 1); + } + + sl_level_set(item, level, next); + sl_level_set(prev, level, item); + /* level is now 0-counted and < newlevel*/ + while (level) { + level--; + next = sl_level_get(prev, level); + while (next && cmpfn(next, item) < 0) { + prev = next; + next = sl_level_get(prev, level); + } + + sl_level_set(item, level, next); + sl_level_set(prev, level, item); + }; + return NULL; +} + +/* NOTE: level counting below is 1-based since that makes the code simpler! */ + +struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next; + int cmpval; + + while (level) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + prev = next; + continue; + } + if (cmpval == 0) + return next; + level--; + } + return NULL; +} + +struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next; + int cmpval; + + while (level) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + prev = next; + continue; + } + if (cmpval == 0) + return next; + level--; + } + return next; +} + +struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next, *best = NULL; + int cmpval; + + while (level) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + best = prev = next; + continue; + } + level--; + } + return best; +} + +void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, + int (*cmpfn)(const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next; + int cmpval; + + while (level) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + if (next == item) { + sl_level_set(prev, level - 1, + sl_level_get(item, level - 1)); + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + prev = next; + continue; + } + level--; + } + + /* TBD: assert when trying to remove non-existing item? */ + head->count--; + + if ((uintptr_t)item->next[SKIPLIST_OVERFLOW] & 1) { + uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW]; + ptrval &= UINTPTR_MAX - 3; + struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval; + XFREE(MTYPE_SKIPLIST_OFLOW, oflow); + } + memset(item, 0, sizeof(*item)); +} diff --git a/lib/typesafe.h b/lib/typesafe.h new file mode 100644 index 0000000000..2fa4cae6e9 --- /dev/null +++ b/lib/typesafe.h @@ -0,0 +1,640 @@ +/* + * Copyright (c) 2016-2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_TYPESAFE_H +#define _FRR_TYPESAFE_H + +#include +#include +#include +#include +#include "compiler.h" + +/* generic macros for all list-like types */ + +#define for_each(prefix, head, item) \ + for (item = prefix##_first(head); item; \ + item = prefix##_next(head, item)) +#define for_each_safe(prefix, head, item) \ + for (typeof(prefix##_next_safe(head, NULL)) prefix##_safe = \ + prefix##_next_safe(head, \ + (item = prefix##_first(head))); \ + item; \ + item = prefix##_safe, \ + prefix##_safe = prefix##_next_safe(head, prefix##_safe)) +#define for_each_from(prefix, head, item, from) \ + for (item = from, from = prefix##_next_safe(head, item); \ + item; \ + item = from, from = prefix##_next_safe(head, from)) + +/* single-linked list, unsorted/arbitrary. + * can be used as queue with add_tail / pop + */ + +/* don't use these structs directly */ +struct slist_item { + struct slist_item *next; +}; + +struct slist_head { + struct slist_item *first, **last_next; + size_t count; +}; + +static inline void typesafe_list_add(struct slist_head *head, + struct slist_item **pos, struct slist_item *item) +{ + item->next = *pos; + *pos = item; + if (pos == head->last_next) + head->last_next = &item->next; + head->count++; +} + +/* use as: + * + * PREDECL_LIST(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_LIST(namelist, struct name, nlitem) + */ +#define PREDECL_LIST(prefix) \ +struct prefix ## _head { struct slist_head sh; }; \ +struct prefix ## _item { struct slist_item si; }; + +#define INIT_LIST(var) { .sh = { .last_next = &var.sh.first, }, } + +#define DECLARE_LIST(prefix, type, field) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ + h->sh.last_next = &h->sh.first; \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \ +{ \ + typesafe_list_add(&h->sh, &h->sh.first, &item->field.si); \ +} \ +macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ +{ \ + typesafe_list_add(&h->sh, h->sh.last_next, &item->field.si); \ +} \ +macro_inline void prefix ## _add_after(struct prefix##_head *h, \ + type *after, type *item) \ +{ \ + struct slist_item **nextp; \ + nextp = after ? &after->field.si.next : &h->sh.first; \ + typesafe_list_add(&h->sh, nextp, &item->field.si); \ +} \ +/* TODO: del_hint */ \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + struct slist_item **iter = &h->sh.first; \ + while (*iter && *iter != &item->field.si) \ + iter = &(*iter)->next; \ + if (!*iter) \ + return; \ + h->sh.count--; \ + *iter = item->field.si.next; \ + if (!item->field.si.next) \ + h->sh.last_next = iter; \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct slist_item *sitem = h->sh.first; \ + if (!sitem) \ + return NULL; \ + h->sh.count--; \ + h->sh.first = sitem->next; \ + if (h->sh.first == NULL) \ + h->sh.last_next = &h->sh.first; \ + return container_of(sitem, type, field.si); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + return container_of_null(h->sh.first, type, field.si); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \ +{ \ + struct slist_item *sitem = &item->field.si; \ + return container_of_null(sitem->next, type, field.si); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + struct slist_item *sitem; \ + if (!item) \ + return NULL; \ + sitem = &item->field.si; \ + return container_of_null(sitem->next, type, field.si); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->sh.count; \ +} \ +/* ... */ + +/* single-linked list, sorted. + * can be used as priority queue with add / pop + */ + +/* don't use these structs directly */ +struct ssort_item { + struct ssort_item *next; +}; + +struct ssort_head { + struct ssort_item *first; + size_t count; +}; + +/* use as: + * + * PREDECL_SORTLIST(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_SORTLIST(namelist, struct name, nlitem) + */ +#define _PREDECL_SORTLIST(prefix) \ +struct prefix ## _head { struct ssort_head sh; }; \ +struct prefix ## _item { struct ssort_item si; }; + +#define INIT_SORTLIST_UNIQ(var) { } +#define INIT_SORTLIST_NONUNIQ(var) { } + +#define PREDECL_SORTLIST_UNIQ(prefix) \ + _PREDECL_SORTLIST(prefix) +#define PREDECL_SORTLIST_NONUNIQ(prefix) \ + _PREDECL_SORTLIST(prefix) + +#define _DECLARE_SORTLIST(prefix, type, field, cmpfn_nuq, cmpfn_uq) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + struct ssort_item **np = &h->sh.first; \ + int c = 1; \ + while (*np && (c = cmpfn_uq( \ + container_of(*np, type, field.si), item)) < 0) \ + np = &(*np)->next; \ + if (c == 0) \ + return container_of(*np, type, field.si); \ + item->field.si.next = *np; \ + *np = &item->field.si; \ + h->sh.count++; \ + return NULL; \ +} \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct ssort_item *sitem = h->sh.first; \ + int cmpval = 0; \ + while (sitem && (cmpval = cmpfn_nuq( \ + container_of(sitem, type, field.si), item) < 0)) \ + sitem = sitem->next; \ + return container_of_null(sitem, type, field.si); \ +} \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct ssort_item *prev = NULL, *sitem = h->sh.first; \ + int cmpval = 0; \ + while (sitem && (cmpval = cmpfn_nuq( \ + container_of(sitem, type, field.si), item) < 0)) \ + sitem = (prev = sitem)->next; \ + return container_of_null(prev, type, field.si); \ +} \ +/* TODO: del_hint */ \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + struct ssort_item **iter = &h->sh.first; \ + while (*iter && *iter != &item->field.si) \ + iter = &(*iter)->next; \ + if (!*iter) \ + return; \ + h->sh.count--; \ + *iter = item->field.si.next; \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct ssort_item *sitem = h->sh.first; \ + if (!sitem) \ + return NULL; \ + h->sh.count--; \ + h->sh.first = sitem->next; \ + return container_of(sitem, type, field.si); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + return container_of_null(h->sh.first, type, field.si); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct ssort_item *sitem = &item->field.si; \ + return container_of_null(sitem->next, type, field.si); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + struct ssort_item *sitem; \ + if (!item) \ + return NULL; \ + sitem = &item->field.si; \ + return container_of_null(sitem->next, type, field.si); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->sh.count; \ +} \ +/* ... */ + +#define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \ + _DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn) \ + \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + struct ssort_item *sitem = h->sh.first; \ + int cmpval = 0; \ + while (sitem && (cmpval = cmpfn( \ + container_of(sitem, type, field.si), item) < 0)) \ + sitem = sitem->next; \ + if (!sitem || cmpval > 0) \ + return NULL; \ + return container_of(sitem, type, field.si); \ +} \ +/* ... */ + +#define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn) \ +macro_inline int _ ## prefix ## _cmp(const type *a, const type *b) \ +{ \ + int cmpval = cmpfn(a, b); \ + if (cmpval) \ + return cmpval; \ + if (a < b) \ + return -1; \ + if (a > b) \ + return 1; \ + return 0; \ +} \ + _DECLARE_SORTLIST(prefix, type, field, cmpfn, _ ## prefix ## _cmp) \ +/* ... */ + + +/* hash, "sorted" by hash value + */ + +/* don't use these structs directly */ +struct thash_item { + struct thash_item *next; + uint32_t hashval; +}; + +struct thash_head { + struct thash_item **entries; + uint32_t count; + + uint8_t tabshift; + uint8_t minshift, maxshift; +}; + +#define _HASH_SIZE(tabshift) \ + ((1U << (tabshift)) >> 1) +#define HASH_SIZE(head) \ + _HASH_SIZE((head).tabshift) +#define _HASH_KEY(tabshift, val) \ + ((val) >> (33 - (tabshift))) +#define HASH_KEY(head, val) \ + _HASH_KEY((head).tabshift, val) +#define HASH_GROW_THRESHOLD(head) \ + ((head).count >= HASH_SIZE(head)) +#define HASH_SHRINK_THRESHOLD(head) \ + ((head).count <= (HASH_SIZE(head) - 1) / 2) + +extern void typesafe_hash_grow(struct thash_head *head); +extern void typesafe_hash_shrink(struct thash_head *head); + +/* use as: + * + * PREDECL_HASH(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_HASH(namelist, struct name, nlitem, cmpfunc, hashfunc) + */ +#define PREDECL_HASH(prefix) \ +struct prefix ## _head { struct thash_head hh; }; \ +struct prefix ## _item { struct thash_item hi; }; + +#define INIT_HASH(var) { } + +#define DECLARE_HASH(prefix, type, field, cmpfn, hashfn) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + assert(h->hh.count == 0); \ + h->hh.minshift = 0; \ + typesafe_hash_shrink(&h->hh); \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + h->hh.count++; \ + if (!h->hh.tabshift || HASH_GROW_THRESHOLD(h->hh)) \ + typesafe_hash_grow(&h->hh); \ + \ + uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \ + item->field.hi.hashval = hval; \ + struct thash_item **np = &h->hh.entries[hbits]; \ + while (*np && (*np)->hashval < hval) \ + np = &(*np)->next; \ + if (*np && cmpfn(container_of(*np, type, field.hi), item) == 0) { \ + h->hh.count--; \ + return container_of(*np, type, field.hi); \ + } \ + item->field.hi.next = *np; \ + *np = &item->field.hi; \ + return NULL; \ +} \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + if (!h->hh.tabshift) \ + return NULL; \ + uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \ + struct thash_item *hitem = h->hh.entries[hbits]; \ + while (hitem && hitem->hashval < hval) \ + hitem = hitem->next; \ + while (hitem && hitem->hashval == hval) { \ + if (!cmpfn(container_of(hitem, type, field.hi), item)) \ + return container_of(hitem, type, field.hi); \ + hitem = hitem->next; \ + } \ + return NULL; \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + if (!h->hh.tabshift) \ + return; \ + uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \ + struct thash_item **np = &h->hh.entries[hbits]; \ + while (*np && (*np)->hashval < hval) \ + np = &(*np)->next; \ + while (*np && *np != &item->field.hi && (*np)->hashval == hval) \ + np = &(*np)->next; \ + if (*np != &item->field.hi) \ + return; \ + *np = item->field.hi.next; \ + item->field.hi.next = NULL; \ + h->hh.count--; \ + if (HASH_SHRINK_THRESHOLD(h->hh)) \ + typesafe_hash_shrink(&h->hh); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + uint32_t i; \ + for (i = 0; i < HASH_SIZE(h->hh); i++) \ + if (h->hh.entries[i]) { \ + struct thash_item *hitem = h->hh.entries[i]; \ + h->hh.entries[i] = hitem->next; \ + h->hh.count--; \ + hitem->next = NULL; \ + if (HASH_SHRINK_THRESHOLD(h->hh)) \ + typesafe_hash_shrink(&h->hh); \ + return container_of(hitem, type, field.hi); \ + } \ + return NULL; \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + uint32_t i; \ + for (i = 0; i < HASH_SIZE(h->hh); i++) \ + if (h->hh.entries[i]) \ + return container_of(h->hh.entries[i], type, field.hi); \ + return NULL; \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct thash_item *hitem = &item->field.hi; \ + if (hitem->next) \ + return container_of(hitem->next, type, field.hi); \ + uint32_t i = HASH_KEY(h->hh, hitem->hashval) + 1; \ + for (; i < HASH_SIZE(h->hh); i++) \ + if (h->hh.entries[i]) \ + return container_of(h->hh.entries[i], type, field.hi); \ + return NULL; \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + if (!item) \ + return NULL; \ + return prefix ## _next(h, item); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->hh.count; \ +} \ +/* ... */ + +/* skiplist, sorted. + * can be used as priority queue with add / pop + */ + +/* don't use these structs directly */ +#define SKIPLIST_MAXDEPTH 16 +#define SKIPLIST_EMBED 4 +#define SKIPLIST_OVERFLOW (SKIPLIST_EMBED - 1) + +struct sskip_item { + struct sskip_item *next[SKIPLIST_EMBED]; +}; + +struct sskip_overflow { + struct sskip_item *next[SKIPLIST_MAXDEPTH - SKIPLIST_OVERFLOW]; +}; + +struct sskip_head { + struct sskip_item hitem; + struct sskip_item *overflow[SKIPLIST_MAXDEPTH - SKIPLIST_OVERFLOW]; + size_t count; +}; + +/* use as: + * + * PREDECL_SKIPLIST(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_SKIPLIST(namelist, struct name, nlitem, cmpfunc) + */ +#define _PREDECL_SKIPLIST(prefix) \ +struct prefix ## _head { struct sskip_head sh; }; \ +struct prefix ## _item { struct sskip_item si; }; + +#define INIT_SKIPLIST_UNIQ(var) { } +#define INIT_SKIPLIST_NONUNIQ(var) { } + +#define _DECLARE_SKIPLIST(prefix, type, field, cmpfn_nuq, cmpfn_uq) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ + h->sh.hitem.next[SKIPLIST_OVERFLOW] = (struct sskip_item *) \ + ((uintptr_t)h->sh.overflow | 1); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + struct sskip_item *si; \ + si = typesafe_skiplist_add(&h->sh, &item->field.si, cmpfn_uq); \ + return container_of_null(si, type, field.si); \ +} \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \ + &item->field.si, cmpfn_nuq); \ + return container_of_null(sitem, type, field.si); \ +} \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \ + &item->field.si, cmpfn_nuq); \ + return container_of_null(sitem, type, field.si); \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + typesafe_skiplist_del(&h->sh, &item->field.si, cmpfn_uq); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct sskip_item *sitem = h->sh.hitem.next[0]; \ + if (!sitem) \ + return NULL; \ + typesafe_skiplist_del(&h->sh, sitem, cmpfn_uq); \ + return container_of(sitem, type, field.si); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + struct sskip_item *first = h->sh.hitem.next[0]; \ + return container_of_null(first, type, field.si); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct sskip_item *next = item->field.si.next[0]; \ + return container_of_null(next, type, field.si); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + struct sskip_item *next; \ + next = item ? item->field.si.next[0] : NULL; \ + return container_of_null(next, type, field.si); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->sh.count; \ +} \ +/* ... */ + +#define PREDECL_SKIPLIST_UNIQ(prefix) \ + _PREDECL_SKIPLIST(prefix) +#define DECLARE_SKIPLIST_UNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct sskip_item *a, \ + const struct sskip_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.si), \ + container_of(b, type, field.si)); \ +} \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \ + &item->field.si, &prefix ## __cmp); \ + return container_of_null(sitem, type, field.si); \ +} \ + \ +_DECLARE_SKIPLIST(prefix, type, field, \ + prefix ## __cmp, prefix ## __cmp) \ +/* ... */ + +#define PREDECL_SKIPLIST_NONUNIQ(prefix) \ + _PREDECL_SKIPLIST(prefix) +#define DECLARE_SKIPLIST_NONUNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct sskip_item *a, \ + const struct sskip_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.si), \ + container_of(b, type, field.si)); \ +} \ +macro_inline int prefix ## __cmp_uq(const struct sskip_item *a, \ + const struct sskip_item *b) \ +{ \ + int cmpval = cmpfn(container_of(a, type, field.si), \ + container_of(b, type, field.si)); \ + if (cmpval) \ + return cmpval; \ + if (a < b) \ + return -1; \ + if (a > b) \ + return 1; \ + return 0; \ +} \ + \ +_DECLARE_SKIPLIST(prefix, type, field, \ + prefix ## __cmp, prefix ## __cmp_uq) \ +/* ... */ + + +extern struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, + struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern void typesafe_skiplist_del(struct sskip_head *head, + struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); + +#endif /* _FRR_TYPESAFE_H */ From 80911bc26a44fd29eb311edbba7eb926d4eee1b8 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Jan 2019 04:25:38 +0100 Subject: [PATCH 10/64] lib: typesafe rb-tree Typesafe red-black tree, built out of the OpenBSD implementation and the macro soup layered on top. API compatible with skiplists & simple lists. Signed-off-by: David Lamparter --- lib/subdir.am | 2 + lib/typerb.c | 472 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/typerb.h | 182 +++++++++++++++++++ lib/typesafe.h | 5 + 4 files changed, 661 insertions(+) create mode 100644 lib/typerb.c create mode 100644 lib/typerb.h diff --git a/lib/subdir.am b/lib/subdir.am index d60ebc5d9c..7e9ee90785 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -80,6 +80,7 @@ lib_libfrr_la_SOURCES = \ lib/table.c \ lib/termtable.c \ lib/thread.c \ + lib/typerb.c \ lib/typesafe.c \ lib/vector.c \ lib/vrf.c \ @@ -209,6 +210,7 @@ pkginclude_HEADERS += \ lib/table.h \ lib/termtable.h \ lib/thread.h \ + lib/typerb.h \ lib/typesafe.h \ lib/vector.h \ lib/vlan.h \ diff --git a/lib/typerb.c b/lib/typerb.c new file mode 100644 index 0000000000..d361e7651e --- /dev/null +++ b/lib/typerb.c @@ -0,0 +1,472 @@ +/* RB-tree */ + +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 2016 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "typerb.h" + +#define RB_BLACK 0 +#define RB_RED 1 + +#define rb_entry typed_rb_entry +#define rbt_tree typed_rb_root + +#define RBE_LEFT(_rbe) (_rbe)->rbt_left +#define RBE_RIGHT(_rbe) (_rbe)->rbt_right +#define RBE_PARENT(_rbe) (_rbe)->rbt_parent +#define RBE_COLOR(_rbe) (_rbe)->rbt_color + +#define RBH_ROOT(_rbt) (_rbt)->rbt_root + +static inline void rbe_set(struct rb_entry *rbe, struct rb_entry *parent) +{ + RBE_PARENT(rbe) = parent; + RBE_LEFT(rbe) = RBE_RIGHT(rbe) = NULL; + RBE_COLOR(rbe) = RB_RED; +} + +static inline void rbe_set_blackred(struct rb_entry *black, + struct rb_entry *red) +{ + RBE_COLOR(black) = RB_BLACK; + RBE_COLOR(red) = RB_RED; +} + +static inline void rbe_rotate_left(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + struct rb_entry *parent; + struct rb_entry *tmp; + + tmp = RBE_RIGHT(rbe); + RBE_RIGHT(rbe) = RBE_LEFT(tmp); + if (RBE_RIGHT(rbe) != NULL) + RBE_PARENT(RBE_LEFT(tmp)) = rbe; + + parent = RBE_PARENT(rbe); + RBE_PARENT(tmp) = parent; + if (parent != NULL) { + if (rbe == RBE_LEFT(parent)) + RBE_LEFT(parent) = tmp; + else + RBE_RIGHT(parent) = tmp; + } else + RBH_ROOT(rbt) = tmp; + + RBE_LEFT(tmp) = rbe; + RBE_PARENT(rbe) = tmp; +} + +static inline void rbe_rotate_right(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + struct rb_entry *parent; + struct rb_entry *tmp; + + tmp = RBE_LEFT(rbe); + RBE_LEFT(rbe) = RBE_RIGHT(tmp); + if (RBE_LEFT(rbe) != NULL) + RBE_PARENT(RBE_RIGHT(tmp)) = rbe; + + parent = RBE_PARENT(rbe); + RBE_PARENT(tmp) = parent; + if (parent != NULL) { + if (rbe == RBE_LEFT(parent)) + RBE_LEFT(parent) = tmp; + else + RBE_RIGHT(parent) = tmp; + } else + RBH_ROOT(rbt) = tmp; + + RBE_RIGHT(tmp) = rbe; + RBE_PARENT(rbe) = tmp; +} + +static inline void rbe_insert_color(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + struct rb_entry *parent, *gparent, *tmp; + + rbt->count++; + + while ((parent = RBE_PARENT(rbe)) != NULL + && RBE_COLOR(parent) == RB_RED) { + gparent = RBE_PARENT(parent); + + if (parent == RBE_LEFT(gparent)) { + tmp = RBE_RIGHT(gparent); + if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) { + RBE_COLOR(tmp) = RB_BLACK; + rbe_set_blackred(parent, gparent); + rbe = gparent; + continue; + } + + if (RBE_RIGHT(parent) == rbe) { + rbe_rotate_left(rbt, parent); + tmp = parent; + parent = rbe; + rbe = tmp; + } + + rbe_set_blackred(parent, gparent); + rbe_rotate_right(rbt, gparent); + } else { + tmp = RBE_LEFT(gparent); + if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) { + RBE_COLOR(tmp) = RB_BLACK; + rbe_set_blackred(parent, gparent); + rbe = gparent; + continue; + } + + if (RBE_LEFT(parent) == rbe) { + rbe_rotate_right(rbt, parent); + tmp = parent; + parent = rbe; + rbe = tmp; + } + + rbe_set_blackred(parent, gparent); + rbe_rotate_left(rbt, gparent); + } + } + + RBE_COLOR(RBH_ROOT(rbt)) = RB_BLACK; +} + +static inline void rbe_remove_color(struct rbt_tree *rbt, + struct rb_entry *parent, + struct rb_entry *rbe) +{ + struct rb_entry *tmp; + + while ((rbe == NULL || RBE_COLOR(rbe) == RB_BLACK) + && rbe != RBH_ROOT(rbt) && parent) { + if (RBE_LEFT(parent) == rbe) { + tmp = RBE_RIGHT(parent); + if (RBE_COLOR(tmp) == RB_RED) { + rbe_set_blackred(tmp, parent); + rbe_rotate_left(rbt, parent); + tmp = RBE_RIGHT(parent); + } + if ((RBE_LEFT(tmp) == NULL + || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) + && (RBE_RIGHT(tmp) == NULL + || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) { + RBE_COLOR(tmp) = RB_RED; + rbe = parent; + parent = RBE_PARENT(rbe); + } else { + if (RBE_RIGHT(tmp) == NULL + || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK) { + struct rb_entry *oleft; + + oleft = RBE_LEFT(tmp); + if (oleft != NULL) + RBE_COLOR(oleft) = RB_BLACK; + + RBE_COLOR(tmp) = RB_RED; + rbe_rotate_right(rbt, tmp); + tmp = RBE_RIGHT(parent); + } + + RBE_COLOR(tmp) = RBE_COLOR(parent); + RBE_COLOR(parent) = RB_BLACK; + if (RBE_RIGHT(tmp)) + RBE_COLOR(RBE_RIGHT(tmp)) = RB_BLACK; + + rbe_rotate_left(rbt, parent); + rbe = RBH_ROOT(rbt); + break; + } + } else { + tmp = RBE_LEFT(parent); + if (RBE_COLOR(tmp) == RB_RED) { + rbe_set_blackred(tmp, parent); + rbe_rotate_right(rbt, parent); + tmp = RBE_LEFT(parent); + } + + if ((RBE_LEFT(tmp) == NULL + || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) + && (RBE_RIGHT(tmp) == NULL + || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) { + RBE_COLOR(tmp) = RB_RED; + rbe = parent; + parent = RBE_PARENT(rbe); + } else { + if (RBE_LEFT(tmp) == NULL + || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) { + struct rb_entry *oright; + + oright = RBE_RIGHT(tmp); + if (oright != NULL) + RBE_COLOR(oright) = RB_BLACK; + + RBE_COLOR(tmp) = RB_RED; + rbe_rotate_left(rbt, tmp); + tmp = RBE_LEFT(parent); + } + + RBE_COLOR(tmp) = RBE_COLOR(parent); + RBE_COLOR(parent) = RB_BLACK; + if (RBE_LEFT(tmp) != NULL) + RBE_COLOR(RBE_LEFT(tmp)) = RB_BLACK; + + rbe_rotate_right(rbt, parent); + rbe = RBH_ROOT(rbt); + break; + } + } + } + + if (rbe != NULL) + RBE_COLOR(rbe) = RB_BLACK; +} + +static inline struct rb_entry * +rbe_remove(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + struct rb_entry *child, *parent, *old = rbe; + unsigned int color; + + if (RBE_LEFT(rbe) == NULL) + child = RBE_RIGHT(rbe); + else if (RBE_RIGHT(rbe) == NULL) + child = RBE_LEFT(rbe); + else { + struct rb_entry *tmp; + + rbe = RBE_RIGHT(rbe); + while ((tmp = RBE_LEFT(rbe)) != NULL) + rbe = tmp; + + child = RBE_RIGHT(rbe); + parent = RBE_PARENT(rbe); + color = RBE_COLOR(rbe); + if (child != NULL) + RBE_PARENT(child) = parent; + if (parent != NULL) { + if (RBE_LEFT(parent) == rbe) + RBE_LEFT(parent) = child; + else + RBE_RIGHT(parent) = child; + } else + RBH_ROOT(rbt) = child; + if (RBE_PARENT(rbe) == old) + parent = rbe; + *rbe = *old; + + tmp = RBE_PARENT(old); + if (tmp != NULL) { + if (RBE_LEFT(tmp) == old) + RBE_LEFT(tmp) = rbe; + else + RBE_RIGHT(tmp) = rbe; + } else + RBH_ROOT(rbt) = rbe; + + RBE_PARENT(RBE_LEFT(old)) = rbe; + if (RBE_RIGHT(old)) + RBE_PARENT(RBE_RIGHT(old)) = rbe; + + goto color; + } + + parent = RBE_PARENT(rbe); + color = RBE_COLOR(rbe); + + if (child != NULL) + RBE_PARENT(child) = parent; + if (parent != NULL) { + if (RBE_LEFT(parent) == rbe) + RBE_LEFT(parent) = child; + else + RBE_RIGHT(parent) = child; + } else + RBH_ROOT(rbt) = child; +color: + if (color == RB_BLACK) + rbe_remove_color(rbt, parent, child); + + rbt->count--; + return (old); +} + +void typed_rb_remove(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + rbe_remove(rbt, rbe); +} + +struct typed_rb_entry *typed_rb_insert(struct rbt_tree *rbt, + struct rb_entry *rbe, int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)) +{ + struct rb_entry *tmp; + struct rb_entry *parent = NULL; + int comp = 0; + + tmp = RBH_ROOT(rbt); + while (tmp != NULL) { + parent = tmp; + + comp = cmpfn(rbe, tmp); + if (comp < 0) + tmp = RBE_LEFT(tmp); + else if (comp > 0) + tmp = RBE_RIGHT(tmp); + else + return tmp; + } + + rbe_set(rbe, parent); + + if (parent != NULL) { + if (comp < 0) + RBE_LEFT(parent) = rbe; + else + RBE_RIGHT(parent) = rbe; + } else + RBH_ROOT(rbt) = rbe; + + rbe_insert_color(rbt, rbe); + + return NULL; +} + +/* Finds the node with the same key as elm */ +struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)) +{ + struct rb_entry *tmp = RBH_ROOT(rbt); + int comp; + + while (tmp != NULL) { + comp = cmpfn(key, tmp); + if (comp < 0) + tmp = RBE_LEFT(tmp); + else if (comp > 0) + tmp = RBE_RIGHT(tmp); + else + return tmp; + } + + return (NULL); +} + +struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt, + const struct rb_entry *key, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)) +{ + struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; + int comp; + + while (tmp != NULL) { + comp = cmpfn(key, tmp); + if (comp < 0) { + best = tmp; + tmp = RBE_LEFT(tmp); + } else if (comp > 0) + tmp = RBE_RIGHT(tmp); + else + return tmp; + } + + return best; +} + +struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt, + const struct rb_entry *key, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)) +{ + struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; + int comp; + + while (tmp != NULL) { + comp = cmpfn(key, tmp); + if (comp <= 0) + tmp = RBE_LEFT(tmp); + else { + best = tmp; + tmp = RBE_RIGHT(tmp); + } + } + + return best; +} + +struct rb_entry *typed_rb_next(struct rb_entry *rbe) +{ + if (RBE_RIGHT(rbe) != NULL) { + rbe = RBE_RIGHT(rbe); + while (RBE_LEFT(rbe) != NULL) + rbe = RBE_LEFT(rbe); + } else { + if (RBE_PARENT(rbe) && (rbe == RBE_LEFT(RBE_PARENT(rbe)))) + rbe = RBE_PARENT(rbe); + else { + while (RBE_PARENT(rbe) + && (rbe == RBE_RIGHT(RBE_PARENT(rbe)))) + rbe = RBE_PARENT(rbe); + rbe = RBE_PARENT(rbe); + } + } + + return rbe; +} + +struct rb_entry *typed_rb_min(struct rbt_tree *rbt) +{ + struct rb_entry *rbe = RBH_ROOT(rbt); + struct rb_entry *parent = NULL; + + while (rbe != NULL) { + parent = rbe; + rbe = RBE_LEFT(rbe); + } + + return parent; +} diff --git a/lib/typerb.h b/lib/typerb.h new file mode 100644 index 0000000000..3d8db06fe0 --- /dev/null +++ b/lib/typerb.h @@ -0,0 +1,182 @@ +/* + * The following Red-Black tree implementation is based off code with + * original copyright: + * + * Copyright (c) 2016 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_TYPERB_H +#define _FRR_TYPERB_H + +#include "typesafe.h" + +struct typed_rb_entry { + struct typed_rb_entry *rbt_parent; + struct typed_rb_entry *rbt_left; + struct typed_rb_entry *rbt_right; + unsigned int rbt_color; +}; + +struct typed_rb_root { + struct typed_rb_entry *rbt_root; + size_t count; +}; + +struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *, + struct typed_rb_entry *rbe, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)); +void typed_rb_remove(struct typed_rb_root *, struct typed_rb_entry *rbe); +struct typed_rb_entry *typed_rb_find(struct typed_rb_root *, + const struct typed_rb_entry *rbe, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)); +struct typed_rb_entry *typed_rb_find_gteq(struct typed_rb_root *, + const struct typed_rb_entry *rbe, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)); +struct typed_rb_entry *typed_rb_find_lt(struct typed_rb_root *, + const struct typed_rb_entry *rbe, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)); +struct typed_rb_entry *typed_rb_min(struct typed_rb_root *); +struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *); + +#define _PREDECL_RBTREE(prefix) \ +struct prefix ## _head { struct typed_rb_root rr; }; \ +struct prefix ## _item { struct typed_rb_entry re; }; + +#define INIT_RBTREE_UNIQ(var) { } +#define INIT_RBTREE_NONUNIQ(var) { } + +#define _DECLARE_RBTREE(prefix, type, field, cmpfn_nuq, cmpfn_uq) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_insert(&h->rr, &item->field.re, cmpfn_uq); \ + return container_of_null(re, type, field.re); \ +} \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_find_gteq(&h->rr, &item->field.re, cmpfn_nuq); \ + return container_of_null(re, type, field.re); \ +} \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_find_lt(&h->rr, &item->field.re, cmpfn_nuq); \ + return container_of_null(re, type, field.re); \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + typed_rb_remove(&h->rr, &item->field.re); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_min(&h->rr); \ + if (!re) \ + return NULL; \ + typed_rb_remove(&h->rr, re); \ + return container_of(re, type, field.re); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_min(&h->rr); \ + return container_of_null(re, type, field.re); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_next(&item->field.re); \ + return container_of_null(re, type, field.re); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = item ? typed_rb_next(&item->field.re) : NULL; \ + return container_of_null(re, type, field.re); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->rr.count; \ +} \ +/* ... */ + +#define PREDECL_RBTREE_UNIQ(prefix) \ + _PREDECL_RBTREE(prefix) +#define DECLARE_RBTREE_UNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \ + const struct typed_rb_entry *b) \ +{ \ + return cmpfn(container_of(a, type, field.re), \ + container_of(b, type, field.re)); \ +} \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_find(&h->rr, &item->field.re, &prefix ## __cmp); \ + return container_of_null(re, type, field.re); \ +} \ + \ +_DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp) \ +/* ... */ + +#define PREDECL_RBTREE_NONUNIQ(prefix) \ + _PREDECL_RBTREE(prefix) +#define DECLARE_RBTREE_NONUNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \ + const struct typed_rb_entry *b) \ +{ \ + return cmpfn(container_of(a, type, field.re), \ + container_of(b, type, field.re)); \ +} \ +macro_inline int prefix ## __cmp_uq(const struct typed_rb_entry *a, \ + const struct typed_rb_entry *b) \ +{ \ + int cmpval = cmpfn(container_of(a, type, field.re), \ + container_of(b, type, field.re)); \ + if (cmpval) \ + return cmpval; \ + if (a < b) \ + return -1; \ + if (a > b) \ + return 1; \ + return 0; \ +} \ + \ +_DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp_uq) \ +/* ... */ + +#endif /* _FRR_TYPERB_H */ diff --git a/lib/typesafe.h b/lib/typesafe.h index 2fa4cae6e9..bbf3ce8f1c 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -637,4 +637,9 @@ extern void typesafe_skiplist_del(struct sskip_head *head, const struct sskip_item *a, const struct sskip_item *b)); +/* this needs to stay at the end because both files include each other. + * the resolved order is typesafe.h before typerb.h + */ +#include "typerb.h" + #endif /* _FRR_TYPESAFE_H */ From bcea0c0fde0ae5f69aae7bd9d20f643075a14d06 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 8 Nov 2016 18:11:20 +0100 Subject: [PATCH 11/64] lib: atomlist & atomsort These two are lock-free linked list implementations, the plain one is primarily intended for queues while the sorted one is for general data storage. Signed-off-by: David Lamparter --- lib/atomlist.c | 348 ++++++++++++++++++++++++++++++++ lib/atomlist.h | 347 +++++++++++++++++++++++++++++++ lib/compiler.h | 7 + lib/subdir.am | 2 + tests/.gitignore | 1 + tests/lib/test_atomlist.c | 404 +++++++++++++++++++++++++++++++++++++ tests/lib/test_atomlist.py | 6 + tests/subdir.am | 6 + 8 files changed, 1121 insertions(+) create mode 100644 lib/atomlist.c create mode 100644 lib/atomlist.h create mode 100644 tests/lib/test_atomlist.c create mode 100644 tests/lib/test_atomlist.py diff --git a/lib/atomlist.c b/lib/atomlist.c new file mode 100644 index 0000000000..8169ba9eb4 --- /dev/null +++ b/lib/atomlist.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "atomlist.h" + +void atomlist_add_head(struct atomlist_head *h, struct atomlist_item *item) +{ + atomptr_t prevval; + atomptr_t i = atomptr_i(item); + + atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed); + + /* updating ->last is possible here, but makes the code considerably + * more complicated... let's not. + */ + prevval = ATOMPTR_NULL; + item->next = ATOMPTR_NULL; + + /* head-insert atomically + * release barrier: item + item->next writes must be completed + */ + while (!atomic_compare_exchange_weak_explicit(&h->first, &prevval, i, + memory_order_release, memory_order_relaxed)) + atomic_store_explicit(&item->next, prevval, + memory_order_relaxed); +} + +void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item) +{ + atomptr_t prevval = ATOMPTR_NULL; + atomptr_t i = atomptr_i(item); + atomptr_t hint; + struct atomlist_item *prevptr; + _Atomic atomptr_t *prev; + + item->next = ATOMPTR_NULL; + + atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed); + + /* place new item into ->last + * release: item writes completed; acquire: DD barrier on hint + */ + hint = atomic_exchange_explicit(&h->last, i, memory_order_acq_rel); + + while (1) { + if (atomptr_p(hint) == NULL) + prev = &h->first; + else + prev = &atomlist_itemp(hint)->next; + + do { + prevval = atomic_load_explicit(prev, + memory_order_consume); + prevptr = atomlist_itemp(prevval); + if (prevptr == NULL) + break; + + prev = &prevptr->next; + } while (prevptr); + + /* last item is being deleted - start over */ + if (atomptr_l(prevval)) { + hint = ATOMPTR_NULL; + continue; + } + + /* no barrier - item->next is NULL and was so in xchg above */ + if (!atomic_compare_exchange_strong_explicit(prev, &prevval, i, + memory_order_consume, + memory_order_consume)) { + hint = prevval; + continue; + } + break; + } +} + +static void atomlist_del_core(struct atomlist_head *h, + struct atomlist_item *item, + _Atomic atomptr_t *hint, + atomptr_t next) +{ + _Atomic atomptr_t *prev = hint ? hint : &h->first, *upd; + atomptr_t prevval, updval; + struct atomlist_item *prevptr; + + /* drop us off "last" if needed. no r/w to barrier. */ + prevval = atomptr_i(item); + atomic_compare_exchange_strong_explicit(&h->last, &prevval, + ATOMPTR_NULL, + memory_order_relaxed, memory_order_relaxed); + + atomic_fetch_sub_explicit(&h->count, 1, memory_order_relaxed); + + /* the following code should be identical (except sort<>list) to + * atomsort_del_hint() + */ + while (1) { + upd = NULL; + updval = ATOMPTR_LOCK; + + do { + prevval = atomic_load_explicit(prev, + memory_order_consume); + + /* track the beginning of a chain of deleted items + * this is neccessary to make this lock-free; we can + * complete deletions started by other threads. + */ + if (!atomptr_l(prevval)) { + updval = prevval; + upd = prev; + } + + prevptr = atomlist_itemp(prevval); + if (prevptr == item) + break; + + prev = &prevptr->next; + } while (prevptr); + + if (prevptr != item) + /* another thread completed our deletion */ + return; + + if (!upd || atomptr_l(updval)) { + /* failed to find non-deleted predecessor... + * have to try again + */ + prev = &h->first; + continue; + } + + if (!atomic_compare_exchange_strong_explicit(upd, &updval, + next, memory_order_consume, + memory_order_consume)) { + /* prev doesn't point to item anymore, something + * was inserted. continue at same position forward. + */ + continue; + } + break; + } +} + +void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item, + _Atomic atomptr_t *hint) +{ + atomptr_t next; + + /* mark ourselves in-delete - full barrier */ + next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK, + memory_order_acquire); + assert(!atomptr_l(next)); /* delete race on same item */ + + atomlist_del_core(h, item, hint, next); +} + +struct atomlist_item *atomlist_pop(struct atomlist_head *h) +{ + struct atomlist_item *item; + atomptr_t next; + + /* grab head of the list - and remember it in replval for the + * actual delete below. No matter what, the head of the list is + * where we start deleting because either it's our item, or it's + * some delete-marked items and then our item. + */ + next = atomic_load_explicit(&h->first, memory_order_consume); + + do { + item = atomlist_itemp(next); + if (!item) + return NULL; + + /* try to mark deletion */ + next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK, + memory_order_acquire); + + } while (atomptr_l(next)); + /* if loop is taken: delete race on same item (another pop or del) + * => proceed to next item + * if loop exited here: we have our item selected and marked + */ + atomlist_del_core(h, item, &h->first, next); + return item; +} + +struct atomsort_item *atomsort_add(struct atomsort_head *h, + struct atomsort_item *item, int (*cmpfn)( + const struct atomsort_item *, + const struct atomsort_item *)) +{ + _Atomic atomptr_t *prev; + atomptr_t prevval; + atomptr_t i = atomptr_i(item); + struct atomsort_item *previtem; + int cmpval; + + do { + prev = &h->first; + + do { + prevval = atomic_load_explicit(prev, + memory_order_acquire); + previtem = atomptr_p(prevval); + + if (!previtem || (cmpval = cmpfn(previtem, item)) > 0) + break; + if (cmpval == 0) + return previtem; + + prev = &previtem->next; + } while (1); + + if (atomptr_l(prevval)) + continue; + + item->next = prevval; + if (atomic_compare_exchange_strong_explicit(prev, &prevval, i, + memory_order_release, memory_order_relaxed)) + break; + } while (1); + + atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed); + return NULL; +} + +static void atomsort_del_core(struct atomsort_head *h, + struct atomsort_item *item, _Atomic atomptr_t *hint, + atomptr_t next) +{ + _Atomic atomptr_t *prev = hint ? hint : &h->first, *upd; + atomptr_t prevval, updval; + struct atomsort_item *prevptr; + + atomic_fetch_sub_explicit(&h->count, 1, memory_order_relaxed); + + /* the following code should be identical (except sort<>list) to + * atomlist_del_core() + */ + while (1) { + upd = NULL; + updval = ATOMPTR_LOCK; + + do { + prevval = atomic_load_explicit(prev, + memory_order_consume); + + /* track the beginning of a chain of deleted items + * this is neccessary to make this lock-free; we can + * complete deletions started by other threads. + */ + if (!atomptr_l(prevval)) { + updval = prevval; + upd = prev; + } + + prevptr = atomsort_itemp(prevval); + if (prevptr == item) + break; + + prev = &prevptr->next; + } while (prevptr); + + if (prevptr != item) + /* another thread completed our deletion */ + return; + + if (!upd || atomptr_l(updval)) { + /* failed to find non-deleted predecessor... + * have to try again + */ + prev = &h->first; + continue; + } + + if (!atomic_compare_exchange_strong_explicit(upd, &updval, + next, memory_order_relaxed, + memory_order_relaxed)) { + /* prev doesn't point to item anymore, something + * was inserted. continue at same position forward. + */ + continue; + } + break; + } +} + +void atomsort_del_hint(struct atomsort_head *h, struct atomsort_item *item, + _Atomic atomptr_t *hint) +{ + atomptr_t next; + + /* mark ourselves in-delete - full barrier */ + next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK, + memory_order_seq_cst); + assert(!atomptr_l(next)); /* delete race on same item */ + + atomsort_del_core(h, item, hint, next); +} + +struct atomsort_item *atomsort_pop(struct atomsort_head *h) +{ + struct atomsort_item *item; + atomptr_t next; + + /* grab head of the list - and remember it in replval for the + * actual delete below. No matter what, the head of the list is + * where we start deleting because either it's our item, or it's + * some delete-marked items and then our item. + */ + next = atomic_load_explicit(&h->first, memory_order_consume); + + do { + item = atomsort_itemp(next); + if (!item) + return NULL; + + /* try to mark deletion */ + next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK, + memory_order_acquire); + + } while (atomptr_l(next)); + /* if loop is taken: delete race on same item (another pop or del) + * => proceed to next item + * if loop exited here: we have our item selected and marked + */ + atomsort_del_core(h, item, &h->first, next); + return item; +} diff --git a/lib/atomlist.h b/lib/atomlist.h new file mode 100644 index 0000000000..373c481f2a --- /dev/null +++ b/lib/atomlist.h @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2016-2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_ATOMLIST_H +#define _FRR_ATOMLIST_H + +#include "typesafe.h" +#include "frratomic.h" + +/* pointer with lock/deleted/invalid bit in lowest bit + * + * for atomlist/atomsort, "locked" means "this pointer can't be updated, the + * item is being deleted". it is permissible to assume the item will indeed + * be deleted (as there are no replace/etc. ops in this). + * + * in general, lowest 2/3 bits on 32/64bit architectures are available for + * uses like this; the only thing that will really break this is putting an + * atomlist_item in a struct with "packed" attribute. (it'll break + * immediately and consistently.) -- don't do that. + * + * ATOMPTR_USER is currently unused (and available for atomic hash or skiplist + * implementations.) + */ +typedef uintptr_t atomptr_t; +#define ATOMPTR_MASK (UINTPTR_MAX - 3) +#define ATOMPTR_LOCK (1) +#define ATOMPTR_USER (2) +#define ATOMPTR_NULL (0) + +static inline atomptr_t atomptr_i(void *val) +{ + atomptr_t atomval = (atomptr_t)val; + + assert(!(atomval & ATOMPTR_LOCK)); + return atomval; +} +static inline void *atomptr_p(atomptr_t val) +{ + return (void *)(val & ATOMPTR_MASK); +} +static inline bool atomptr_l(atomptr_t val) +{ + return (bool)(val & ATOMPTR_LOCK); +} +static inline bool atomptr_u(atomptr_t val) +{ + return (bool)(val & ATOMPTR_USER); +} + + +/* the problem with, find(), find_gteq() and find_lt() on atomic lists is that + * they're neither an "acquire" nor a "release" operation; the element that + * was found is still on the list and doesn't change ownership. Therefore, + * an atomic transition in ownership state can't be implemented. + * + * Contrast this with add() or pop(): both function calls atomically transfer + * ownership of an item to or from the list, which makes them "acquire" / + * "release" operations. + * + * What can be implemented atomically is a "find_pop()", i.e. try to locate an + * item and atomically try to remove it if found. It's not currently + * implemented but can be added when needed. + * + * Either way - for find(), generally speaking, if you need to use find() on + * a list then the whole thing probably isn't well-suited to atomic + * implementation and you'll need to have extra locks around to make it work + * correctly. + */ +#ifdef WNO_ATOMLIST_UNSAFE_FIND +# define atomic_find_warn +#else +# define atomic_find_warn __attribute__((_DEPRECATED( \ + "WARNING: find() on atomic lists cannot be atomic by principle; " \ + "check code to make sure usage pattern is OK and if it is, use " \ + "#define WNO_ATOMLIST_UNSAFE_FIND"))) +#endif + + +/* single-linked list, unsorted/arbitrary. + * can be used as queue with add_tail / pop + * + * all operations are lock-free, but not neccessarily wait-free. this means + * that there is no state where the system as a whole stops making process, + * but it *is* possible that a *particular* thread is delayed by some time. + * + * the only way for this to happen is for other threads to continuously make + * updates. an inactive / blocked / deadlocked other thread cannot cause such + * delays, and to cause such delays a thread must be heavily hitting the list - + * it's a rather theoretical concern. + */ + +/* don't use these structs directly */ +struct atomlist_item { + _Atomic atomptr_t next; +}; +#define atomlist_itemp(val) ((struct atomlist_item *)atomptr_p(val)) + +struct atomlist_head { + _Atomic atomptr_t first, last; + _Atomic size_t count; +}; + +/* use as: + * + * PREDECL_ATOMLIST(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_ATOMLIST(namelist, struct name, nlitem) + */ +#define PREDECL_ATOMLIST(prefix) \ +struct prefix ## _head { struct atomlist_head ah; }; \ +struct prefix ## _item { struct atomlist_item ai; }; + +#define INIT_ATOMLIST(var) { } + +#define DECLARE_ATOMLIST(prefix, type, field) \ +macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \ +{ atomlist_add_head(&h->ah, &item->field.ai); } \ +macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ +{ atomlist_add_tail(&h->ah, &item->field.ai); } \ +macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ + _Atomic atomptr_t *hint) \ +{ atomlist_del_hint(&h->ah, &item->field.ai, hint); } \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ atomlist_del_hint(&h->ah, &item->field.ai, NULL); } \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ char *p = (char *)atomlist_pop(&h->ah); \ + return p ? (type *)(p - offsetof(type, field)) : NULL; } \ +macro_inline type *prefix ## _first(struct prefix##_head *h) \ +{ char *p = atomptr_p(atomic_load_explicit(&h->ah.first, \ + memory_order_acquire)); \ + return p ? (type *)(p - offsetof(type, field)) : NULL; } \ +macro_inline type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ char *p = atomptr_p(atomic_load_explicit(&item->field.ai.next, \ + memory_order_acquire)); \ + return p ? (type *)(p - offsetof(type, field)) : NULL; } \ +macro_inline type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ return item ? prefix##_next(h, item) : NULL; } \ +macro_inline size_t prefix ## _count(struct prefix##_head *h) \ +{ return atomic_load_explicit(&h->ah.count, memory_order_relaxed); } \ +/* ... */ + +/* add_head: + * - contention on ->first pointer + * - return implies completion + */ +void atomlist_add_head(struct atomlist_head *h, struct atomlist_item *item); + +/* add_tail: + * - concurrent add_tail can cause wait but has progress guarantee + * - return does NOT imply completion. completion is only guaranteed after + * all other add_tail operations that started before this add_tail have + * completed as well. + */ +void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item); + +/* del/del_hint: + * + * OWNER MUST HOLD REFERENCE ON ITEM TO BE DELETED, ENSURING NO OTHER THREAD + * WILL TRY TO DELETE THE SAME ITEM. DELETING INCLUDES pop(). + * + * as with all deletions, threads that started reading earlier may still hold + * pointers to the deleted item. completion is however guaranteed for all + * reads starting later. + */ +void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item, + _Atomic atomptr_t *hint); + +/* pop: + * + * as with all deletions, threads that started reading earlier may still hold + * pointers to the deleted item. completion is however guaranteed for all + * reads starting later. + */ +struct atomlist_item *atomlist_pop(struct atomlist_head *h); + + + +struct atomsort_item { + _Atomic atomptr_t next; +}; +#define atomsort_itemp(val) ((struct atomsort_item *)atomptr_p(val)) + +struct atomsort_head { + _Atomic atomptr_t first; + _Atomic size_t count; +}; + +#define _PREDECL_ATOMSORT(prefix) \ +struct prefix ## _head { struct atomsort_head ah; }; \ +struct prefix ## _item { struct atomsort_item ai; }; + +#define INIT_ATOMSORT_UNIQ(var) { } +#define INIT_ATOMSORT_NONUNIQ(var) { } + +#define _DECLARE_ATOMSORT(prefix, type, field, cmpfn_nuq, cmpfn_uq) \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + assert(h->ah.count == 0); \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + struct atomsort_item *p; \ + p = atomsort_add(&h->ah, &item->field.ai, cmpfn_uq); \ + return container_of_null(p, type, field.ai); \ +} \ +macro_inline type *prefix ## _first(struct prefix##_head *h) \ +{ \ + struct atomsort_item *p; \ + p = atomptr_p(atomic_load_explicit(&h->ah.first, \ + memory_order_acquire)); \ + return container_of_null(p, type, field.ai); \ +} \ +macro_inline type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct atomsort_item *p; \ + p = atomptr_p(atomic_load_explicit(&item->field.ai.next, \ + memory_order_acquire)); \ + return container_of_null(p, type, field.ai); \ +} \ +macro_inline type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + return item ? prefix##_next(h, item) : NULL; \ +} \ +atomic_find_warn \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + type *p = prefix ## _first(h); \ + while (p && cmpfn_nuq(&p->field.ai, &item->field.ai) < 0) \ + p = prefix ## _next(h, p); \ + return p; \ +} \ +atomic_find_warn \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + type *p = prefix ## _first(h), *prev = NULL; \ + while (p && cmpfn_nuq(&p->field.ai, &item->field.ai) < 0) \ + p = prefix ## _next(h, (prev = p)); \ + return prev; \ +} \ +macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ + _Atomic atomptr_t *hint) \ +{ \ + atomsort_del_hint(&h->ah, &item->field.ai, hint); \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + atomsort_del_hint(&h->ah, &item->field.ai, NULL); \ +} \ +macro_inline size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return atomic_load_explicit(&h->ah.count, memory_order_relaxed); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct atomsort_item *p = atomsort_pop(&h->ah); \ + return p ? container_of(p, type, field.ai) : NULL; \ +} \ +/* ... */ + +#define PREDECL_ATOMSORT_UNIQ(prefix) \ + _PREDECL_ATOMSORT(prefix) +#define DECLARE_ATOMSORT_UNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct atomsort_item *a, \ + const struct atomsort_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.ai), \ + container_of(b, type, field.ai)); \ +} \ + \ +_DECLARE_ATOMSORT(prefix, type, field, \ + prefix ## __cmp, prefix ## __cmp) \ + \ +atomic_find_warn \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + type *p = prefix ## _first(h); \ + int cmpval = 0; \ + while (p && (cmpval = cmpfn(p, item)) < 0) \ + p = prefix ## _next(h, p); \ + if (!p || cmpval > 0) \ + return NULL; \ + return p; \ +} \ +/* ... */ + +#define PREDECL_ATOMSORT_NONUNIQ(prefix) \ + _PREDECL_ATOMSORT(prefix) +#define DECLARE_ATOMSORT_NONUNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct atomsort_item *a, \ + const struct atomsort_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.ai), \ + container_of(b, type, field.ai)); \ +} \ +macro_inline int prefix ## __cmp_uq(const struct atomsort_item *a, \ + const struct atomsort_item *b) \ +{ \ + int cmpval = cmpfn(container_of(a, type, field.ai), \ + container_of(b, type, field.ai)); \ + if (cmpval) \ + return cmpval; \ + if (a < b) \ + return -1; \ + if (a > b) \ + return 1; \ + return 0; \ +} \ + \ +_DECLARE_ATOMSORT(prefix, type, field, \ + prefix ## __cmp, prefix ## __cmp_uq) \ +/* ... */ + +struct atomsort_item *atomsort_add(struct atomsort_head *h, + struct atomsort_item *item, int (*cmpfn)( + const struct atomsort_item *, + const struct atomsort_item *)); + +void atomsort_del_hint(struct atomsort_head *h, + struct atomsort_item *item, _Atomic atomptr_t *hint); + +struct atomsort_item *atomsort_pop(struct atomsort_head *h); + +#endif /* _FRR_ATOMLIST_H */ diff --git a/lib/compiler.h b/lib/compiler.h index 02bdbd6afd..474adc7c8b 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -32,6 +32,7 @@ extern "C" { # define _FALLTHROUGH __attribute__((fallthrough)); #endif # define _CONSTRUCTOR(x) constructor(x) +# define _DEPRECATED(x) deprecated(x) #elif defined(__GNUC__) #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) # define _RET_NONNULL , returns_nonnull @@ -41,6 +42,9 @@ extern "C" { # define _DESTRUCTOR(x) destructor(x) # define _ALLOC_SIZE(x) alloc_size(x) #endif +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +# define _DEPRECATED(x) deprecated(x) +#endif #if __GNUC__ >= 7 # define _FALLTHROUGH __attribute__((fallthrough)); #endif @@ -68,6 +72,9 @@ extern "C" { #ifndef _FALLTHROUGH #define _FALLTHROUGH #endif +#ifndef _DEPRECATED +#define _DEPRECATED(x) deprecated +#endif /* for helper functions defined inside macros */ #define macro_inline static inline __attribute__((unused)) diff --git a/lib/subdir.am b/lib/subdir.am index 7e9ee90785..7efd3825ef 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -7,6 +7,7 @@ lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) lib_libfrr_la_SOURCES = \ lib/agg_table.c \ + lib/atomlist.c \ lib/bfd.c \ lib/buffer.c \ lib/checksum.c \ @@ -133,6 +134,7 @@ lib/northbound_cli.lo: lib/northbound_cli_clippy.c pkginclude_HEADERS += \ lib/agg_table.h \ + lib/atomlist.h \ lib/bfd.h \ lib/bitfield.h \ lib/buffer.h \ diff --git a/tests/.gitignore b/tests/.gitignore index c29a6c3820..78f70c3990 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -20,6 +20,7 @@ /lib/cli/test_commands_defun.c /lib/northbound/test_oper_data /lib/cxxcompat +/lib/test_atomlist /lib/test_buffer /lib/test_checksum /lib/test_graph diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c new file mode 100644 index 0000000000..078e05e336 --- /dev/null +++ b/tests/lib/test_atomlist.c @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "atomlist.h" +#include "seqlock.h" +#include "monotime.h" + +/* + * maybe test: + * - alist_del_hint + * - alist_next_safe + * - asort_del_hint + * - asort_next_safe + */ + +static struct seqlock sqlo; + +PREDECL_ATOMLIST(alist) +PREDECL_ATOMSORT_UNIQ(asort) +struct item { + uint64_t val1; + struct alist_item chain; + struct asort_item sortc; + uint64_t val2; +}; +DECLARE_ATOMLIST(alist, struct item, chain) + +static int icmp(const struct item *a, const struct item *b); +DECLARE_ATOMSORT_UNIQ(asort, struct item, sortc, icmp) + +static int icmp(const struct item *a, const struct item *b) +{ + if (a->val1 > b->val1) + return 1; + if (a->val1 < b->val1) + return -1; + return 0; +} + +#define NITEM 10000 +struct item itm[NITEM]; + +static struct alist_head ahead; +static struct asort_head shead; + +#define NTHREADS 4 +static struct testthread { + pthread_t pt; + struct seqlock sqlo; + size_t counter, nullops; +} thr[NTHREADS]; + +struct testrun { + struct testrun *next; + int lineno; + const char *desc; + ssize_t prefill; + bool sorted; + void (*func)(unsigned int offset); +}; +struct testrun *runs = NULL; + +#define NOCLEAR -1 + +#define deftestrun(name, _desc, _prefill, _sorted) \ +static void trfunc_##name(unsigned int offset); \ +struct testrun tr_##name = { \ + .desc = _desc, \ + .lineno = __LINE__, \ + .prefill = _prefill, \ + .func = &trfunc_##name, \ + .sorted = _sorted }; \ +static void __attribute__((constructor)) trsetup_##name(void) \ +{ \ + struct testrun **inspos = &runs; \ + while (*inspos && (*inspos)->lineno < tr_##name.lineno) \ + inspos = &(*inspos)->next; \ + tr_##name.next = *inspos; \ + *inspos = &tr_##name; \ +} \ +static void trfunc_##name(unsigned int offset) \ +{ \ + size_t i = 0, n = 0; + +#define endtestrun \ + thr[offset].counter = i; \ + thr[offset].nullops = n; \ +} + +deftestrun(add, "add vs. add", 0, false) + for (; i < NITEM / NTHREADS; i++) + alist_add_head(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(del, "del vs. del", NOCLEAR, false) + for (; i < NITEM / NTHREADS / 10; i++) + alist_del(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(addtail, "add_tail vs. add_tail", 0, false) + for (; i < NITEM / NTHREADS; i++) + alist_add_tail(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(pop, "pop vs. pop", NOCLEAR, false) + for (; i < NITEM / NTHREADS; ) + if (alist_pop(&ahead)) + i++; + else + n++; +endtestrun + +deftestrun(headN_vs_pop1, "add_head(N) vs. pop(1)", 1, false); + if (offset == 0) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(head1_vs_popN, "add_head(1) vs. pop(N)", 0, false); + if (offset < NTHREADS - 1) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = 0; i < NITEM; i++) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(headN_vs_popN, "add_head(N) vs. pop(N)", NTHREADS / 2, false) + if (offset < NTHREADS / 2) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM * 2 / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(tailN_vs_pop1, "add_tail(N) vs. pop(1)", 1, false) + if (offset == 0) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM - (NITEM / NTHREADS); ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_tail(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(tail1_vs_popN, "add_tail(1) vs. pop(N)", 0, false) + if (offset < NTHREADS - 1) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = 0; i < NITEM; i++) + alist_add_tail(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(sort_add, "add_sort vs. add_sort", 0, true) + for (; i < NITEM / NTHREADS / 10; i++) + asort_add(&shead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(sort_del, "del_sort vs. del_sort", NOCLEAR, true) + for (; i < NITEM / NTHREADS / 10; i++) + asort_del(&shead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(sort_add_del, "add_sort vs. del_sort", NTHREADS / 2, true) + if (offset < NTHREADS / 2) { + for (; i < NITEM / NTHREADS / 10; i++) + asort_del(&shead, &itm[i * NTHREADS + offset]); + } else { + for (; i < NITEM / NTHREADS / 10; i++) + asort_add(&shead, &itm[i * NTHREADS + offset]); + } +endtestrun + +static void *thr1func(void *arg) +{ + struct testthread *p = arg; + unsigned int offset = (unsigned int)(p - &thr[0]); + seqlock_val_t sv; + struct testrun *tr; + + for (tr = runs; tr; tr = tr->next) { + sv = seqlock_bump(&p->sqlo); + seqlock_wait(&sqlo, sv); + + tr->func(offset); + } + seqlock_bump(&p->sqlo); + + return NULL; +} + +static void clear_list(size_t prefill) +{ + size_t i; + + memset(&ahead, 0, sizeof(ahead)); + memset(&shead, 0, sizeof(shead)); + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) { + itm[i].val1 = itm[i].val2 = i; + if ((i % NTHREADS) < prefill) { + alist_add_tail(&ahead, &itm[i]); + asort_add(&shead, &itm[i]); + } + } +} + +static void run_tr(struct testrun *tr) +{ + const char *desc = tr->desc; + struct timeval tv; + int64_t delta; + seqlock_val_t sv; + size_t c = 0, s = 0, n = 0; + struct item *item, *prev, dummy; + + printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 1, "", desc); + fflush(stdout); + + if (tr->prefill != NOCLEAR) + clear_list(tr->prefill); + + monotime(&tv); + sv = seqlock_bump(&sqlo); + for (size_t i = 0; i < NTHREADS; i++) { + seqlock_wait(&thr[i].sqlo, seqlock_cur(&sqlo)); + s += thr[i].counter; + n += thr[i].nullops; + thr[i].counter = 0; + thr[i].nullops = 0; + } + + delta = monotime_since(&tv, NULL); + if (tr->sorted) { + uint64_t prevval = 0; + + for_each(asort, &shead, item) { + assert(item->val1 >= prevval); + prevval = item->val1; + c++; + } + assert(c == asort_count(&shead)); + } else { + prev = &dummy; + for_each(alist, &ahead, item) { + assert(item != prev); + prev = item; + c++; + assert(c <= NITEM); + } + assert(c == alist_count(&ahead)); + } + printf("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n", + sv >> 1, delta, c, s, n, desc); +} + +#ifdef BASIC_TESTS +static void dump(const char *lbl) +{ + struct item *item, *safe; + size_t ctr = 0; + + printf("dumping %s:\n", lbl); + for_each_safe(alist, &ahead, item) { + printf("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++, + (void *)item, item->val1, item->val2); + } +} + +static void basic_tests(void) +{ + size_t i; + + memset(&ahead, 0, sizeof(ahead)); + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) + itm[i].val1 = itm[i].val2 = i; + + assert(alist_first(&ahead) == NULL); + dump(""); + alist_add_head(&ahead, &itm[0]); + dump(""); + alist_add_head(&ahead, &itm[1]); + dump(""); + alist_add_tail(&ahead, &itm[2]); + dump(""); + alist_add_tail(&ahead, &itm[3]); + dump(""); + alist_del(&ahead, &itm[1]); + dump(""); + printf("POP: %p\n", alist_pop(&ahead)); + dump(""); + printf("POP: %p\n", alist_pop(&ahead)); + printf("POP: %p\n", alist_pop(&ahead)); + printf("POP: %p\n", alist_pop(&ahead)); + printf("POP: %p\n", alist_pop(&ahead)); + dump(""); +} +#else +#define basic_tests() do { } while (0) +#endif + +int main(int argc, char **argv) +{ + size_t i; + + basic_tests(); + + seqlock_init(&sqlo); + seqlock_acquire_val(&sqlo, 1); + + for (i = 0; i < NTHREADS; i++) { + seqlock_init(&thr[i].sqlo); + seqlock_acquire(&thr[i].sqlo, &sqlo); + thr[i].counter = 0; + thr[i].nullops = 0; + + pthread_create(&thr[i].pt, NULL, thr1func, &thr[i]); + } + + struct testrun *tr; + + for (tr = runs; tr; tr = tr->next) + run_tr(tr); + + for (i = 0; i < NTHREADS; i++) + pthread_join(thr[i].pt, NULL); + + return 0; +} diff --git a/tests/lib/test_atomlist.py b/tests/lib/test_atomlist.py new file mode 100644 index 0000000000..293d47f316 --- /dev/null +++ b/tests/lib/test_atomlist.py @@ -0,0 +1,6 @@ +import frrtest + +class TestAtomlist(frrtest.TestMultiOut): + program = './test_atomlist' + +TestAtomlist.exit_cleanly() diff --git a/tests/subdir.am b/tests/subdir.am index 167d8b43f1..fd471f757d 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -47,6 +47,7 @@ tests/ospf6d/test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c check_PROGRAMS = \ tests/lib/cxxcompat \ + tests/lib/test_atomlist \ tests/lib/test_buffer \ tests/lib/test_checksum \ tests/lib/test_heavy_thread \ @@ -190,6 +191,10 @@ tests_lib_northbound_test_oper_data_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD) tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c +tests_lib_test_atomlist_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_atomlist_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_atomlist_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_atomlist_SOURCES = tests/lib/test_atomlist.c tests_lib_test_buffer_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_buffer_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_buffer_LDADD = $(ALL_TESTS_LDADD) @@ -306,6 +311,7 @@ EXTRA_DIST += \ tests/lib/northbound/test_oper_data.in \ tests/lib/northbound/test_oper_data.py \ tests/lib/northbound/test_oper_data.refout \ + tests/lib/test_atomlist.py \ tests/lib/test_nexthop_iter.py \ tests/lib/test_ringbuf.py \ tests/lib/test_srcdest_table.py \ From 737d6e0956e707a94e9cd2b0ce3c371a992b0e45 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 13 Nov 2018 16:02:47 +0100 Subject: [PATCH 12/64] doc: add developer docs for type-safe lists Signed-off-by: David Lamparter --- doc/developer/library.rst | 3 +- doc/developer/lists.rst | 594 ++++++++++++++++++++++++++++++++++++++ doc/developer/subdir.am | 1 + 3 files changed, 597 insertions(+), 1 deletion(-) create mode 100644 doc/developer/lists.rst diff --git a/doc/developer/library.rst b/doc/developer/library.rst index 77b2f229b7..4ba0c0ebc6 100644 --- a/doc/developer/library.rst +++ b/doc/developer/library.rst @@ -7,8 +7,9 @@ Library Facilities (libfrr) .. toctree:: :maxdepth: 2 - logging memtypes + lists + logging hooks cli modules diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst new file mode 100644 index 0000000000..6d60420b2f --- /dev/null +++ b/doc/developer/lists.rst @@ -0,0 +1,594 @@ +List implementations +==================== + +.. note:: + + The term *list* is used generically for lists, skiplists, trees and hash + tables in this document. + +Common list interface +--------------------- + +FRR includes a set of list-like data structure implementations with abstracted +common APIs. The purpose of this is easily allow swapping out one +data structure for another while also making the code easier to read and write. +There is one API for unsorted lists and a similar but not identical API for +sorted lists. + +For unsorted lists, the following implementations exist: + +- single-linked list with tail pointer (e.g. STAILQ in BSD) + +- atomic single-linked list with tail pointer + + +For sorted lists, these data structures are implemented: + +- single-linked list + +- atomic single-linked list + +- skiplist + +- red-black tree (based on OpenBSD RB_TREE) + +- hash table (note below) + +Except for hash tables, each of the sorted data structures has a variant with +unique and non-unique list items. Hash tables always require unique items +and mostly follow the "sorted" API but use the hash value as sorting +key. Also, iterating while modifying does not work with hash tables. + + +The following sorted structures are likely to be implemented at some point +in the future: + +- atomic skiplist + +- atomic hash table (note below) + + +The APIs are all designed to be as type-safe as possible. This means that +there will be a compiler warning when an item doesn't match the list, or +the return value has a different type, or other similar situations. **You +should never use casts with these APIs.** If a cast is neccessary in relation +to these APIs, there is probably something wrong with the overall design. + +Only the following pieces use dynamically allocated memory: + +- the hash table itself is dynamically grown and shrunk + +- skiplists store up to 4 next pointers inline but will dynamically allocate + memory to hold an item's 5th up to 16th next pointer (if they exist) + +Cheat sheet +----------- + +Available types: + +:: + + DECLARE_LIST + DECLARE_ATOMLIST + + DECLARE_SORTLIST_UNIQ + DECLARE_SORTLIST_NONUNIQ + DECLARE_ATOMLIST_UNIQ + DECLARE_ATOMLIST_NONUNIQ + DECLARE_SKIPLIST_UNIQ + DECLARE_SKIPLIST_NONUNIQ + DECLARE_RBTREE_UNIQ + DECLARE_RBTREE_NONUNIQ + + DECLARE_HASH + +Functions provided: + ++------------------------------------+------+------+---------+------------+ +| Function | LIST | HASH | \*_UNIQ | \*_NONUNIQ | ++====================================+======+======+=========+============+ +| _init, _fini | yes | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ +| _first, _next, _next_safe | yes | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ +| _add_head, _add_tail, _add_after | yes | -- | -- | -- | ++------------------------------------+------+------+---------+------------+ +| _add | -- | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ +| _del, _pop | yes | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ +| _find | -- | yes | yes | -- | ++------------------------------------+------+------+---------+------------+ +| _find_lt, _find_gteq | -- | -- | yes | yes | ++------------------------------------+------+------+---------+------------+ +| use with for_each() macros | yes | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ + + +Datastructure type setup +------------------------ + +Each of the data structures has a ``PREDECL_*`` and a ``DECLARE_*`` macro to +set up an "instantiation" of the list. This works somewhat similar to C++ +templating, though much simpler. + +**In all following text, the Z prefix is replaced with a name choosen +for the instance of the datastructure.** + +The common setup pattern will look like this: + +.. code-block:: c + + PREDECL_XXX(Z) + struct item { + int otherdata; + struct Z_item mylistitem; + } + + struct Z_head mylisthead; + + /* unsorted: */ + DECLARE_XXX(Z, struct item, mylistitem) + + /* sorted, items that compare as equal cannot be added to list */ + int compare_func(const struct item *a, const struct item *b); + DECLARE_XXX_UNIQ(Z, struct item, mylistitem, compare_func) + + /* sorted, items that compare as equal can be added to list */ + int compare_func(const struct item *a, const struct item *b); + DECLARE_XXX_NONUNIQ(Z, struct item, mylistitem, compare_func) + + /* hash tables: */ + int compare_func(const struct item *a, const struct item *b); + uint32_t hash_func(const struct item *a); + DECLARE_XXX(Z, struct item, mylistitem, compare_func, hash_func) + +``XXX`` is replaced with the name of the data structure, e.g. ``SKIPLIST`` +or ``ATOMLIST``. The ``DECLARE_XXX`` invocation can either occur in a `.h` +file (if the list needs to be accessed from several C files) or it can be +placed in a `.c` file (if the list is only accessed from that file.) The +``PREDECL_XXX`` invocation defines the ``struct Z_item`` and ``struct +Z_head`` types and must therefore occur before these are used. + +To switch between compatible data structures, only these two lines need to be +changes. To switch to a data structure with a different API, some source +changes are necessary. + +Common iteration macros +----------------------- + +The following iteration macros work across all data structures: + +.. c:function:: for_each(Z, head, item) + + Equivalent to: + + .. code-block:: c + + for (item = Z_first(head); item; item = Z_next(head, item)) + + Note that this will fail if the list is modified while being iterated + over. + +.. c:function:: for_each_safe(Z, head, item) + + Same as the previous, but the next element is pre-loaded into a "hidden" + variable (named ``Z_safe``.) Equivalent to: + + .. code-block:: c + + for (item = Z_first(head); item; item = next) { + next = Z_next_safe(head, item); + ... + } + + .. warning:: + + Iterating over hash tables while adding or removing items is not + possible. The iteration position will be corrupted when the hash + tables is resized while iterating. This will cause items to be + skipped or iterated over twice. + +.. c:function:: for_each_from(Z, head, item, from) + + Iterates over the list, starting at item ``from``. This variant is "safe" + as in the previous macro. Equivalent to: + + .. code-block:: c + + for (item = from; item; item = from) { + from = Z_next_safe(head, item); + ... + } + + .. note:: + + The ``from`` variable is written to. This is intentional - you can + resume iteration after breaking out of the loop by keeping the ``from`` + value persistent and reusing it for the next loop. + +Common API +---------- + +The following documentation assumes that a list has been defined using +``Z`` as the name, and ``itemtype`` being the type of the list items (e.g. +``struct item``.) + +.. c:function:: void Z_init(struct Z_head *) + + Initializes the list for use. For most implementations, this just sets + some values. Hash tables are the only implementation that allocates + memory in this call. + +.. c:function:: void Z_fini(struct Z_head *) + + Reverse the effects of :c:func:`Z_init()`. The list must be empty + when this function is called. + + .. warning:: + + This function may ``assert()`` if the list is not empty. + +.. c:function:: size_t Z_count(struct Z_head *) + + Returns the number of items in a structure. All structures store a + counter in their `Z_head` so that calling this function completes + in O(1). + + .. note:: + + For atomic lists with concurrent access, the value will already be + outdated by the time this function returns and can therefore only be + used as an estimate. + +.. c:function:: itemtype *Z_first(struct Z_head *) + + Returns the first item in the structure, or ``NULL`` if the structure is + empty. This is O(1) for all data structures except red-black trees + where it is O(log n). + +.. c:function:: itemtype *Z_pop(struct Z_head *) + + Remove and return the first item in the structure, or ``NULL`` if the + structure is empty. Like :c:func:`Z_first`, this is O(1) for all + data structures except red-black trees where it is O(log n) again. + + This function can be used to build queues (with unsorted structures) or + priority queues (with sorted structures.) + + Another common pattern is deleting all list items: + + .. code-block:: c + + while ((item = Z_pop(head))) + item_free(item); + + .. note:: + + This function can - and should - be used with hash tables. It is not + affected by the "modification while iterating" problem. To remove + all items from a hash table, use the loop demonstrated above. + +.. c:function:: itemtype *Z_next(struct Z_head *, itemtype *prev) + + Return the item that follows after ``prev``, or ``NULL`` if ``prev`` is + the last item. + + .. warning:: + + ``prev`` must not be ``NULL``! Use :c:func:`Z_next_safe()` if + ``prev`` might be ``NULL``. + +.. c:function:: itemtype *Z_next_safe(struct Z_head *, itemtype *prev) + + Same as :c:func:`Z_next()`, except that ``NULL`` is returned if + ``prev`` is ``NULL``. + +.. c:function:: itemtype *Z_del(struct Z_head *, itemtype *item) + + Remove ``item`` from the list and return it. + + .. note:: + + This function's behaviour is undefined if ``item`` is not actually + on the list. Some structures return ``NULL`` in this case while others + return ``item``. The function may also call ``assert()`` (but most + don't.) + +.. todo:: + + ``Z_del_after()`` / ``Z_del_hint()``? + +API for unsorted structures +--------------------------- + +Since the insertion position is not pre-defined for unsorted data, there +are several functions exposed to insert data: + +.. note:: + + ``item`` must not be ``NULL`` for any of the following functions. + +.. c:function:: DECLARE_XXX(Z, type, field) + + :param listtype XXX: ``LIST`` or ``ATOMLIST`` to select a data structure + implementation. + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add_head()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + +.. c:function:: void Z_add_head(struct Z_head *, itemtype *item) + + Insert an item at the beginning of the structure, before the first item. + This is an O(1) operation for non-atomic lists. + +.. c:function:: void Z_add_tail(struct Z_head *, itemtype *item) + + Insert an item at the end of the structure, after the last item. + This is also an O(1) operation for non-atomic lists. + +.. c:function:: void Z_add_after(struct Z_head *, itemtype *after, itemtype *item) + + Insert ``item`` behind ``after``. If ``after`` is ``NULL``, the item is + inserted at the beginning of the list as with :c:func:`Z_add_head`. + This is also an O(1) operation for non-atomic lists. + + A common pattern is to keep a "previous" pointer around while iterating: + + .. code-block:: c + + itemtype *prev = NULL, *item; + + for_each_safe(Z, head, item) { + if (something) { + Z_add_after(head, prev, item); + break; + } + prev = item; + } + + .. todo:: + + maybe flip the order of ``item`` & ``after``? + ``Z_add_after(head, item, after)`` + +API for sorted structures +------------------------- + +Sorted data structures do not need to have an insertion position specified, +therefore the insertion calls are different from unsorted lists. Also, +sorted lists can be searched for a value. + +.. c:function:: DECLARE_XXX_UNIQ(Z, type, field, compare_func) + + :param listtype XXX: One of the following: + ``SORTLIST`` (single-linked sorted list), ``SKIPLIST`` (skiplist), + ``RBTREE`` (RB-tree) or ``ATOMSORT`` (atomic single-linked list). + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + :param funcptr compare_func: Item comparison function, must have the + following function signature: + ``int function(const itemtype *, const itemtype*)``. This function + may be static if the list is only used in one file. + +.. c:function:: DECLARE_XXX_NONUNIQ(Z, type, field, compare_func) + + Same as above, but allow adding multiple items to the list that compare + as equal in ``compare_func``. Ordering between these items is undefined + and depends on the list implementation. + +.. c:function:: itemtype *Z_add(struct Z_head *, itemtype *item) + + Insert an item at the appropriate sorted position. If another item exists + in the list that compares as equal (``compare_func()`` == 0), ``item`` is + not inserted into the list and the already-existing item in the list is + returned. Otherwise, on successful insertion, ``NULL`` is returned. + + For ``_NONUNIQ`` lists, this function always returns NULL since ``item`` + can always be successfully added to the list. + +.. c:function:: itemtype *Z_find(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares equal to ``ref``. If no equal + item is found, return ``NULL``. + + This function is likely used with a temporary stack-allocated value for + ``ref`` like so: + + .. code-block:: c + + itemtype searchfor = { .foo = 123 }; + + itemtype *item = Z_find(head, &searchfor); + + .. note:: + + The ``Z_find()`` function is only available for lists that contain + unique items (i.e. ``DECLARE_XXX_UNIQ``.) This is because on a list + containing non-unique items, more than one item may compare as equal to + the item that is searched for. + +.. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares greater or equal to + ``ref``. See :c:func:`Z_find()` above. + +.. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares less than + ``ref``. See :c:func:`Z_find()` above. + + +API for hash tables +------------------- + +.. c:function:: DECLARE_XXX(Z, type, field, compare_func, hash_func) + + :param listtype XXX: Only ``HASH`` is currently available. + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + :param funcptr compare_func: Item comparison function, must have the + following function signature: + ``int function(const itemtype *, const itemtype*)``. This function + may be static if the list is only used in one file. For hash tables, + this function is only used to check for equality, the ordering is + ignored. + :param funcptr hash_func: Hash calculation function, must have the + following function signature: + ``uint32_t function(const itemtype *)``. The hash value for items + stored in a hash table is cached in each item, so this value need not + be cached by the user code. + + .. warning:: + + Items that compare as equal cannot be inserted. Refer to the notes + about sorted structures in the previous section. + +.. c:function:: void Z_init_size(struct Z_head *, size_t size) + + Same as :c:func:`Z_init()` but preset the minimum hash table to + ``size``. + +Hash tables also support :c:func:`Z_add()` and :c:func:`Z_find()` with +the same semantics as noted above. :c:func:`Z_find_gteq()` and +:c:func:`Z_find_lt()` are **not** provided for hash tables. + + +Atomic lists +------------ + +`atomlist.h` provides an unsorted and a sorted atomic single-linked list. +Since atomic memory accesses can be considerably slower than plain memory +accessses (depending on the CPU type), these lists should only be used where +neccessary. + +The following guarantees are provided regarding concurrent access: + +- the operations are lock-free but not wait-free. + + Lock-free means that it is impossible for all threads to be blocked. Some + thread will always make progress, regardless of what other threads do. (This + even includes a random thread being stopped by a debugger in a random + location.) + + Wait-free implies that the time any single thread might spend in one of the + calls is bounded. This is not provided here since it is not normally + relevant to practical operations. What this means is that if some thread is + hammering a particular list with requests, it is possible that another + thread is blocked for an extended time. The lock-free guarantee still + applies since the hammering thread is making progress. + +- without a RCU mechanism in place, the point of contention for atomic lists + is memory deallocation. As it is, **a rwlock is required for correct + operation**. The *read* lock must be held for all accesses, including + reading the list, adding items to the list, and removing items from the + list. The *write* lock must be acquired and released before deallocating + any list element. If this is not followed, an use-after-free can occur + as a MT race condition when an element gets deallocated while another + thread is accessing the list. + + .. note:: + + The *write* lock does not need to be held for deleting items from the + list, and there should not be any instructions between the + ``pthread_rwlock_wrlock`` and ``pthread_rwlock_unlock``. The write lock + is used as a sequence point, not as an exclusion mechanism. + +- insertion operations are always safe to do with the read lock held. + Added items are immediately visible after the insertion call returns and + should not be touched anymore. + +- when removing a *particular* (pre-determined) item, the caller must ensure + that no other thread is attempting to remove that same item. If this cannot + be guaranteed by architecture, a separate lock might need to be added. + +- concurrent `pop` calls are always safe to do with only the read lock held. + This does not fall under the previous rule since the `pop` call will select + the next item if the first is already being removed by another thread. + + **Deallocation locking still applies.** Assume another thread starts + reading the list, but gets task-switched by the kernel while reading the + first item. `pop` will happily remove and return that item. If it is + deallocated without acquiring and releasing the write lock, the other thread + will later resume execution and try to access the now-deleted element. + +- the list count should be considered an estimate. Since there might be + concurrent insertions or removals in progress, it might already be outdated + by the time the call returns. No attempt is made to have it be correct even + for a nanosecond. + +Overall, atomic lists are well-suited for MT queues; concurrent insertion, +iteration and removal operations will work with the read lock held. + +Code snippets +^^^^^^^^^^^^^ + +Iteration: + +.. code-block:: c + + struct item *i; + + pthread_rwlock_rdlock(&itemhead_rwlock); + for_each(itemlist, &itemhead, i) { + /* lock must remain held while iterating */ + ... + } + pthread_rwlock_unlock(&itemhead_rwlock); + +Head removal (pop) and deallocation: + +.. code-block:: c + + struct item *i; + + pthread_rwlock_rdlock(&itemhead_rwlock); + i = itemlist_pop(&itemhead); + pthread_rwlock_unlock(&itemhead_rwlock); + + /* i might still be visible for another thread doing an + * for_each() (but won't be returned by another pop()) */ + ... + + pthread_rwlock_wrlock(&itemhead_rwlock); + pthread_rwlock_unlock(&itemhead_rwlock); + /* i now guaranteed to be gone from the list. + * note nothing between wrlock() and unlock() */ + XFREE(MTYPE_ITEM, i); + +FRR lists +--------- + +.. TODO:: + + document + +BSD lists +--------- + +.. TODO:: + + refer to external docs diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 7ae48881ab..479aa04d59 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -30,6 +30,7 @@ dev_RSTFILES = \ doc/developer/include-compile.rst \ doc/developer/index.rst \ doc/developer/library.rst \ + doc/developer/lists.rst \ doc/developer/logging.rst \ doc/developer/maintainer-release-build.rst \ doc/developer/memtypes.rst \ From 992f9967db14b18ee0eb5a4a104f17ab1dfb2d41 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 18 Feb 2019 21:17:22 +0100 Subject: [PATCH 13/64] tests: exercise the typesafe list wrappers Since all of these list implementations provide almost the same API, we can run and validate them against the same test code. 9 tests for the price of one! Signed-off-by: David Lamparter --- tests/.gitignore | 1 + tests/lib/test_typelist.c | 142 +++++++++++++++++++ tests/lib/test_typelist.h | 272 +++++++++++++++++++++++++++++++++++++ tests/lib/test_typelist.py | 14 ++ tests/subdir.am | 7 + 5 files changed, 436 insertions(+) create mode 100644 tests/lib/test_typelist.c create mode 100644 tests/lib/test_typelist.h create mode 100644 tests/lib/test_typelist.py diff --git a/tests/.gitignore b/tests/.gitignore index 78f70c3990..380172487d 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -41,6 +41,7 @@ /lib/test_timer_correctness /lib/test_timer_performance /lib/test_ttable +/lib/test_typelist /lib/test_zlog /lib/test_zmq /ospf6d/test_lsdb diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c new file mode 100644 index 0000000000..68ea82ea41 --- /dev/null +++ b/tests/lib/test_typelist.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define WNO_ATOMLIST_UNSAFE_FIND + +#include "typesafe.h" +#include "atomlist.h" +#include "memory.h" +#include "monotime.h" + +#include "tests/helpers/c/prng.h" + +/* note: these macros are layered 2-deep because that makes the C + * preprocessor expand the "type" argument. Otherwise, you get + * "PREDECL_type" instead of "PREDECL_LIST" + */ +#define _concat(a, b) a ## b +#define concat(a, b) _concat(a, b) +#define _str(x) #x +#define str(x) _str(x) + +#define _PREDECL(type, ...) PREDECL_##type(__VA_ARGS__) +#define PREDECL(type, ...) _PREDECL(type, __VA_ARGS__) +#define _DECLARE(type, ...) DECLARE_##type(__VA_ARGS__) +#define DECLARE(type, ...) _DECLARE(type, __VA_ARGS__) + +#define _U_SORTLIST_UNIQ 1 +#define _U_SORTLIST_NONUNIQ 0 +#define _U_HASH 1 +#define _U_SKIPLIST_UNIQ 1 +#define _U_SKIPLIST_NONUNIQ 0 +#define _U_RBTREE_UNIQ 1 +#define _U_RBTREE_NONUNIQ 0 +#define _U_ATOMSORT_UNIQ 1 +#define _U_ATOMSORT_NONUNIQ 0 + +#define _IS_UNIQ(type) _U_##type +#define IS_UNIQ(type) _IS_UNIQ(type) + +#define _H_SORTLIST_UNIQ 0 +#define _H_SORTLIST_NONUNIQ 0 +#define _H_HASH 1 +#define _H_SKIPLIST_UNIQ 0 +#define _H_SKIPLIST_NONUNIQ 0 +#define _H_RBTREE_UNIQ 0 +#define _H_RBTREE_NONUNIQ 0 +#define _H_ATOMSORT_UNIQ 0 +#define _H_ATOMSORT_NONUNIQ 0 + +#define _IS_HASH(type) _H_##type +#define IS_HASH(type) _IS_HASH(type) + +static struct timeval ref, ref0; + +static void ts_start(void) +{ + monotime(&ref0); + monotime(&ref); +} +static void ts_ref(const char *text) +{ + int64_t us; + us = monotime_since(&ref, NULL); + printf("%7"PRId64"us %s\n", us, text); + monotime(&ref); +} +static void ts_end(void) +{ + int64_t us; + us = monotime_since(&ref0, NULL); + printf("%7"PRId64"us total\n", us); +} + +#define TYPE SORTLIST_UNIQ +#include "test_typelist.h" + +#define TYPE SORTLIST_NONUNIQ +#include "test_typelist.h" + +#define TYPE HASH +#include "test_typelist.h" + +#define TYPE SKIPLIST_UNIQ +#include "test_typelist.h" + +#define TYPE SKIPLIST_NONUNIQ +#include "test_typelist.h" + +#define TYPE RBTREE_UNIQ +#include "test_typelist.h" + +#define TYPE RBTREE_NONUNIQ +#include "test_typelist.h" + +#define TYPE ATOMSORT_UNIQ +#include "test_typelist.h" + +#define TYPE ATOMSORT_NONUNIQ +#include "test_typelist.h" + +int main(int argc, char **argv) +{ + srandom(1); + + test_SORTLIST_UNIQ(); + test_SORTLIST_NONUNIQ(); + test_HASH(); + test_SKIPLIST_UNIQ(); + test_SKIPLIST_NONUNIQ(); + test_RBTREE_UNIQ(); + test_RBTREE_NONUNIQ(); + test_ATOMSORT_UNIQ(); + test_ATOMSORT_NONUNIQ(); + + log_memstats_stderr("test: "); + return 0; +} diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h new file mode 100644 index 0000000000..60a37e84ed --- /dev/null +++ b/tests/lib/test_typelist.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* C++ called, they want their templates back */ +#define item concat(item_, TYPE) +#define itm concat(itm_, TYPE) +#define head concat(head_, TYPE) +#define list concat(TYPE, ) +#define list_head concat(TYPE, _head) +#define list_item concat(TYPE, _item) +#define list_cmp concat(TYPE, _cmp) +#define list_hash concat(TYPE, _hash) +#define list_init concat(TYPE, _init) +#define list_fini concat(TYPE, _fini) +#define list_first concat(TYPE, _first) +#define list_next concat(TYPE, _next) +#define list_next_safe concat(TYPE, _next_safe) +#define list_count concat(TYPE, _count) +#define list_add concat(TYPE, _add) +#define list_find concat(TYPE, _find) +#define list_find_lt concat(TYPE, _find_lt) +#define list_find_gteq concat(TYPE, _find_gteq) +#define list_del concat(TYPE, _del) +#define list_pop concat(TYPE, _pop) + +PREDECL(TYPE, list) +struct item { + uint64_t val; + struct list_item itm; + int scratchpad; +}; + +static int list_cmp(const struct item *a, const struct item *b); + +#if IS_HASH(TYPE) +static uint32_t list_hash(const struct item *a); +DECLARE(TYPE, list, struct item, itm, list_cmp, list_hash) + +static uint32_t list_hash(const struct item *a) +{ + /* crappy hash to get some hash collisions */ + return a->val ^ (a->val << 29) ^ 0x55AA0000U; +} + +#else +DECLARE(TYPE, list, struct item, itm, list_cmp) +#endif + +static int list_cmp(const struct item *a, const struct item *b) +{ + if (a->val > b->val) + return 1; + if (a->val < b->val) + return -1; + return 0; +} + +#define NITEM 10000 +struct item itm[NITEM]; +static struct list_head head = concat(INIT_, TYPE)(head); + +static void concat(test_, TYPE)(void) +{ + size_t i, j, k, l; + struct prng *prng; + struct item *item, *prev; + struct item dummy; + + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) + itm[i].val = i; + + printf("%s start\n", str(TYPE)); + ts_start(); + + list_init(&head); + ts_ref("init"); + + assert(list_first(&head) == NULL); + + prng = prng_new(0); + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } else + assert(list_add(&head, &itm[j]) == &itm[j]); + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_ref("fill"); + + k = 0; + prev = NULL; + for_each(list, &head, item) { +#if IS_HASH(TYPE) + /* hash table doesn't give sorting */ + (void)prev; +#else + assert(!prev || prev->val < item->val); +#endif + prev = item; + k++; + } + assert(list_count(&head) == k); + ts_ref("walk"); + +#if IS_UNIQ(TYPE) + prng_free(prng); + prng = prng_new(0); + + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + dummy.val = j; + assert(list_find(&head, &dummy) == &itm[j]); + } + ts_ref("find"); + + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + memset(&dummy, 0, sizeof(dummy)); + dummy.val = j; + if (itm[j].scratchpad) + assert(list_add(&head, &dummy) == &itm[j]); + else { + assert(list_add(&head, &dummy) == NULL); + list_del(&head, &dummy); + } + } + ts_ref("add-dup"); +#else /* !IS_UNIQ(TYPE) */ + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + memset(&dummy, 0, sizeof(dummy)); + dummy.val = j; + + list_add(&head, &dummy); + if (itm[j].scratchpad) { + struct item *lt, *gteq, dummy2; + + assert(list_next(&head, &itm[j]) == &dummy || + list_next(&head, &dummy) == &itm[j]); + + memset(&dummy2, 0, sizeof(dummy)); + dummy2.val = j; + lt = list_find_lt(&head, &dummy2); + gteq = list_find_gteq(&head, &dummy2); + + assert(gteq == &itm[j] || gteq == &dummy); + if (lt) + assert(list_next(&head, lt) == &itm[j] || + list_next(&head, lt) == &dummy); + else + assert(list_first(&head) == &itm[j] || + list_first(&head) == &dummy); + } else if (list_next(&head, &dummy)) + assert(list_next(&head, &dummy)->val > j); + list_del(&head, &dummy); + } + ts_ref("add-dup+find_{lt,gteq}"); +#endif +#if !IS_HASH(TYPE) + prng_free(prng); + prng = prng_new(123456); + + l = 0; + for (i = 0; i < NITEM; i++) { + struct item *lt, *gteq, *tmp; + + j = prng_rand(prng) % NITEM; + dummy.val = j; + + lt = list_find_lt(&head, &dummy); + gteq = list_find_gteq(&head, &dummy); + + if (lt) { + assert(lt->val < j); + tmp = list_next(&head, lt); + assert(tmp == gteq); + assert(!tmp || tmp->val >= j); + } else + assert(gteq == list_first(&head)); + + if (gteq) + assert(gteq->val >= j); + } + ts_ref("find_{lt,gteq}"); +#endif /* !IS_HASH */ + + prng_free(prng); + prng = prng_new(0); + + l = 0; + for (i = 0; i < NITEM; i++) { + (void)prng_rand(prng); + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + list_del(&head, &itm[j]); + itm[j].scratchpad = 0; + l++; + } + } + assert(l + list_count(&head) == k); + ts_ref("del"); + + for_each_safe(list, &head, item) { + assert(item->scratchpad != 0); + + if (item->val & 1) { + list_del(&head, item); + item->scratchpad = 0; + l++; + } + } + assert(l + list_count(&head) == k); + ts_ref("for_each_safe+del"); + + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_ref("pop"); + + list_fini(&head); + ts_ref("fini"); + ts_end(); + printf("%s end\n", str(TYPE)); +} + +#undef item +#undef itm +#undef head +#undef list +#undef list_head +#undef list_item +#undef list_cmp +#undef list_hash +#undef list_init +#undef list_fini +#undef list_first +#undef list_next +#undef list_next_safe +#undef list_count +#undef list_add +#undef list_find +#undef list_find_lt +#undef list_find_gteq +#undef list_del +#undef list_pop + +#undef TYPE diff --git a/tests/lib/test_typelist.py b/tests/lib/test_typelist.py new file mode 100644 index 0000000000..3b7373ceb8 --- /dev/null +++ b/tests/lib/test_typelist.py @@ -0,0 +1,14 @@ +import frrtest + +class TestTypelist(frrtest.TestMultiOut): + program = './test_typelist' + +TestTypelist.onesimple('SORTLIST_UNIQ end') +TestTypelist.onesimple('SORTLIST_NONUNIQ end') +TestTypelist.onesimple('HASH end') +TestTypelist.onesimple('SKIPLIST_UNIQ end') +TestTypelist.onesimple('SKIPLIST_NONUNIQ end') +TestTypelist.onesimple('RBTREE_UNIQ end') +TestTypelist.onesimple('RBTREE_NONUNIQ end') +TestTypelist.onesimple('ATOMSORT_UNIQ end') +TestTypelist.onesimple('ATOMSORT_NONUNIQ end') diff --git a/tests/subdir.am b/tests/subdir.am index fd471f757d..ec5fea705e 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -67,6 +67,7 @@ check_PROGRAMS = \ tests/lib/test_timer_correctness \ tests/lib/test_timer_performance \ tests/lib/test_ttable \ + tests/lib/test_typelist \ tests/lib/test_zlog \ tests/lib/test_graph \ tests/lib/cli/test_cli \ @@ -105,6 +106,7 @@ noinst_HEADERS += \ tests/helpers/c/prng.h \ tests/helpers/c/tests.h \ tests/lib/cli/common_cli.h \ + tests/lib/test_typelist.h \ # end # @@ -274,6 +276,10 @@ tests_lib_test_ttable_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_ttable_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_ttable_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_ttable_SOURCES = tests/lib/test_ttable.c +tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) @@ -321,6 +327,7 @@ EXTRA_DIST += \ tests/lib/test_timer_correctness.py \ tests/lib/test_ttable.py \ tests/lib/test_ttable.refout \ + tests/lib/test_typelist.py \ tests/lib/test_zlog.py \ tests/lib/test_graph.py \ tests/lib/test_graph.refout \ From 679b1649cb98040e1b6ec9cf0089d005740a2b6d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Jan 2019 01:27:02 +0100 Subject: [PATCH 14/64] lib: use DECLARE_HASH for qobj hash This changes the qobj node ID hash to use the new typed hash instead of the old hash_* code. Signed-off-by: David Lamparter --- lib/qobj.c | 52 ++++++++++++++++++++++++++++------------------------ lib/qobj.h | 7 +++++++ 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/lib/qobj.c b/lib/qobj.c index 811645f3c3..3e3860a96a 100644 --- a/lib/qobj.c +++ b/lib/qobj.c @@ -27,20 +27,26 @@ #include "qobj.h" #include "jhash.h" +static uint32_t qobj_hash(const struct qobj_node *node) +{ + return (uint32_t)node->nid; +} + +static int qobj_cmp(const struct qobj_node *na, const struct qobj_node *nb) +{ + if (na->nid < nb->nid) + return -1; + if (na->nid > nb->nid) + return 1; + return 0; +} + +DECLARE_HASH(qobj_nodes, struct qobj_node, nodehash, + qobj_cmp, qobj_hash) + static pthread_rwlock_t nodes_lock; -static struct hash *nodes = NULL; +static struct qobj_nodes_head nodes = { }; -static unsigned int qobj_key(void *data) -{ - struct qobj_node *node = data; - return (unsigned int)node->nid; -} - -static bool qobj_cmp(const void *a, const void *b) -{ - const struct qobj_node *na = a, *nb = b; - return na->nid == nb->nid; -} void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type) { @@ -49,15 +55,15 @@ void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type) do { node->nid = (uint64_t)random(); node->nid ^= (uint64_t)random() << 32; - } while (!node->nid - || hash_get(nodes, node, hash_alloc_intern) != node); + } while (!node->nid || qobj_nodes_find(&nodes, node)); + qobj_nodes_add(&nodes, node); pthread_rwlock_unlock(&nodes_lock); } void qobj_unreg(struct qobj_node *node) { pthread_rwlock_wrlock(&nodes_lock); - hash_release(nodes, node); + qobj_nodes_del(&nodes, node); pthread_rwlock_unlock(&nodes_lock); } @@ -65,7 +71,7 @@ struct qobj_node *qobj_get(uint64_t id) { struct qobj_node dummy = {.nid = id}, *rv; pthread_rwlock_rdlock(&nodes_lock); - rv = hash_lookup(nodes, &dummy); + rv = qobj_nodes_find(&nodes, &dummy); pthread_rwlock_unlock(&nodes_lock); return rv; } @@ -77,7 +83,7 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type) void *rv; pthread_rwlock_rdlock(&nodes_lock); - node = hash_lookup(nodes, &dummy); + node = qobj_nodes_find(&nodes, &dummy); /* note: we explicitly hold the lock until after we have checked the * type. @@ -96,16 +102,14 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type) void qobj_init(void) { - if (!nodes) { - pthread_rwlock_init(&nodes_lock, NULL); - nodes = hash_create_size(16, qobj_key, qobj_cmp, "QOBJ Hash"); - } + pthread_rwlock_init(&nodes_lock, NULL); + qobj_nodes_init(&nodes); } void qobj_finish(void) { - hash_clean(nodes, NULL); - hash_free(nodes); - nodes = NULL; + struct qobj_node *node; + while ((node = qobj_nodes_pop(&nodes))) + qobj_nodes_del(&nodes, node); pthread_rwlock_destroy(&nodes_lock); } diff --git a/lib/qobj.h b/lib/qobj.h index d63988cbab..415eae02ef 100644 --- a/lib/qobj.h +++ b/lib/qobj.h @@ -21,6 +21,8 @@ #include #include +#include "typesafe.h" + #ifdef __cplusplus extern "C" { #endif @@ -69,6 +71,8 @@ struct qobj_nodetype_capnp { }; #endif +#include "typesafe.h" + /* each different kind of object will have a global variable of this type, * which can be used by various other pieces to store type-related bits. * type equality can be tested as pointer equality. (cf. QOBJ_GET_TYPESAFE) @@ -79,9 +83,12 @@ struct qobj_nodetype { RESERVED_SPACE_STRUCT(qobj_nodetype_capnp, capnp, 256) }; +PREDECL_HASH(qobj_nodes) + /* anchor to be embedded somewhere in the object's struct */ struct qobj_node { uint64_t nid; + struct qobj_nodes_item nodehash; struct qobj_nodetype *type; }; From c284542b669ffedca07c1a3addfcb3016724b591 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Jan 2019 02:12:38 +0100 Subject: [PATCH 15/64] lib: use DECLARE_LIST for thread_list Replaces the open-coded thread_list with a DECLARE_LIST instantiation. Some function prototypes are actually identical to what was previously open-coded. Signed-off-by: David Lamparter --- lib/thread.c | 131 +++++++++++++++------------------------------------ lib/thread.h | 15 ++---- 2 files changed, 42 insertions(+), 104 deletions(-) diff --git a/lib/thread.c b/lib/thread.c index 2760b83fb3..5ca859a74d 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -40,6 +40,8 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_MASTER, "Thread master") DEFINE_MTYPE_STATIC(LIB, THREAD_POLL, "Thread Poll Info") DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") +DECLARE_LIST(thread_list, struct thread, threaditem) + #if defined(__APPLE__) #include #include @@ -435,6 +437,9 @@ struct thread_master *thread_master_create(const char *name) (bool (*)(const void *, const void *))cpu_record_hash_cmp, "Thread Hash"); + thread_list_init(&rv->event); + thread_list_init(&rv->ready); + thread_list_init(&rv->unuse); /* Initialize the timer queues */ rv->timer = pqueue_create(); @@ -487,50 +492,6 @@ void thread_master_set_name(struct thread_master *master, const char *name) pthread_mutex_unlock(&master->mtx); } -/* Add a new thread to the list. */ -static void thread_list_add(struct thread_list *list, struct thread *thread) -{ - thread->next = NULL; - thread->prev = list->tail; - if (list->tail) - list->tail->next = thread; - else - list->head = thread; - list->tail = thread; - list->count++; -} - -/* Delete a thread from the list. */ -static struct thread *thread_list_delete(struct thread_list *list, - struct thread *thread) -{ - if (thread->next) - thread->next->prev = thread->prev; - else - list->tail = thread->prev; - if (thread->prev) - thread->prev->next = thread->next; - else - list->head = thread->next; - thread->next = thread->prev = NULL; - list->count--; - return thread; -} - -/* Thread list is empty or not. */ -static int thread_empty(struct thread_list *list) -{ - return list->head ? 0 : 1; -} - -/* Delete top of the list and return it. */ -static struct thread *thread_trim_head(struct thread_list *list) -{ - if (!thread_empty(list)) - return thread_list_delete(list, list->head); - return NULL; -} - #define THREAD_UNUSED_DEPTH 10 /* Move thread to unuse list. */ @@ -539,8 +500,6 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread) pthread_mutex_t mtxc = thread->mtx; assert(m != NULL && thread != NULL); - assert(thread->next == NULL); - assert(thread->prev == NULL); thread->hist->total_active--; memset(thread, 0, sizeof(struct thread)); @@ -549,8 +508,8 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread) /* Restore the thread mutex context. */ thread->mtx = mtxc; - if (m->unuse.count < THREAD_UNUSED_DEPTH) { - thread_list_add(&m->unuse, thread); + if (thread_list_count(&m->unuse) < THREAD_UNUSED_DEPTH) { + thread_list_add_tail(&m->unuse, thread); return; } @@ -558,16 +517,13 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread) } /* Free all unused thread. */ -static void thread_list_free(struct thread_master *m, struct thread_list *list) +static void thread_list_free(struct thread_master *m, + struct thread_list_head *list) { struct thread *t; - struct thread *next; - for (t = list->head; t; t = next) { - next = t->next; + while ((t = thread_list_pop(list))) thread_free(m, t); - list->count--; - } } static void thread_array_free(struct thread_master *m, @@ -609,9 +565,8 @@ void thread_master_free_unused(struct thread_master *m) pthread_mutex_lock(&m->mtx); { struct thread *t; - while ((t = thread_trim_head(&m->unuse)) != NULL) { + while ((t = thread_list_pop(&m->unuse))) thread_free(m, t); - } } pthread_mutex_unlock(&m->mtx); } @@ -690,7 +645,7 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, int (*func)(struct thread *), void *arg, debugargdef) { - struct thread *thread = thread_trim_head(&m->unuse); + struct thread *thread = thread_list_pop(&m->unuse); struct cpu_thread_history tmp; if (!thread) { @@ -971,7 +926,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m, pthread_mutex_lock(&thread->mtx); { thread->u.val = val; - thread_list_add(&m->event, thread); + thread_list_add_tail(&m->event, thread); } pthread_mutex_unlock(&thread->mtx); @@ -1063,7 +1018,7 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) */ static void do_thread_cancel(struct thread_master *master) { - struct thread_list *list = NULL; + struct thread_list_head *list = NULL; struct pqueue *queue = NULL; struct thread **thread_array = NULL; struct thread *thread; @@ -1078,31 +1033,23 @@ static void do_thread_cancel(struct thread_master *master) * need to check every thread in the ready queue. */ if (cr->eventobj) { struct thread *t; - thread = master->event.head; - while (thread) { - t = thread; - thread = t->next; - - if (t->arg == cr->eventobj) { - thread_list_delete(&master->event, t); - if (t->ref) - *t->ref = NULL; - thread_add_unuse(master, t); - } + for_each_safe(thread_list, &master->event, t) { + if (t->arg != cr->eventobj) + continue; + thread_list_del(&master->event, t); + if (t->ref) + *t->ref = NULL; + thread_add_unuse(master, t); } - thread = master->ready.head; - while (thread) { - t = thread; - thread = t->next; - - if (t->arg == cr->eventobj) { - thread_list_delete(&master->ready, t); - if (t->ref) - *t->ref = NULL; - thread_add_unuse(master, t); - } + for_each_safe(thread_list, &master->ready, t) { + if (t->arg != cr->eventobj) + continue; + thread_list_del(&master->ready, t); + if (t->ref) + *t->ref = NULL; + thread_add_unuse(master, t); } continue; } @@ -1146,7 +1093,7 @@ static void do_thread_cancel(struct thread_master *master) assert(thread == queue->array[thread->index]); pqueue_remove_at(thread->index, queue); } else if (list) { - thread_list_delete(list, thread); + thread_list_del(list, thread); } else if (thread_array) { thread_array[thread->u.fd] = NULL; } else { @@ -1301,7 +1248,7 @@ static int thread_process_io_helper(struct thread_master *m, thread_array = m->write; thread_array[thread->u.fd] = NULL; - thread_list_add(&m->ready, thread); + thread_list_add_tail(&m->ready, thread); thread->type = THREAD_READY; /* if another pthread scheduled this file descriptor for the event we're * responding to, no problem; we're getting to it now */ @@ -1380,24 +1327,21 @@ static unsigned int thread_process_timers(struct pqueue *queue, return ready; pqueue_dequeue(queue); thread->type = THREAD_READY; - thread_list_add(&thread->master->ready, thread); + thread_list_add_tail(&thread->master->ready, thread); ready++; } return ready; } /* process a list en masse, e.g. for event thread lists */ -static unsigned int thread_process(struct thread_list *list) +static unsigned int thread_process(struct thread_list_head *list) { struct thread *thread; - struct thread *next; unsigned int ready = 0; - for (thread = list->head; thread; thread = next) { - next = thread->next; - thread_list_delete(list, thread); + while ((thread = thread_list_pop(list))) { thread->type = THREAD_READY; - thread_list_add(&thread->master->ready, thread); + thread_list_add_tail(&thread->master->ready, thread); ready++; } return ready; @@ -1429,7 +1373,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * Attempt to flush ready queue before going into poll(). * This is performance-critical. Think twice before modifying. */ - if ((thread = thread_trim_head(&m->ready))) { + if ((thread = thread_list_pop(&m->ready))) { fetch = thread_run(m, thread, fetch); if (fetch->ref) *fetch->ref = NULL; @@ -1462,10 +1406,11 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * In every case except the last, we need to hit poll() at least * once per loop to avoid starvation by events */ - if (m->ready.count == 0) + if (!thread_list_count(&m->ready)) tw = thread_timer_wait(m->timer, &tv); - if (m->ready.count != 0 || (tw && !timercmp(tw, &zerotime, >))) + if (thread_list_count(&m->ready) || + (tw && !timercmp(tw, &zerotime, >))) tw = &zerotime; if (!tw && m->handler.pfdcount == 0) { /* die */ diff --git a/lib/thread.h b/lib/thread.h index ec774a6543..7897265120 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -26,6 +26,7 @@ #include #include "monotime.h" #include "frratomic.h" +#include "typesafe.h" #ifdef __cplusplus extern "C" { @@ -39,12 +40,7 @@ struct rusage_t { #define GETRUSAGE(X) thread_getrusage(X) -/* Linked list of thread. */ -struct thread_list { - struct thread *head; - struct thread *tail; - int count; -}; +PREDECL_LIST(thread_list) struct pqueue; @@ -78,9 +74,7 @@ struct thread_master { struct thread **read; struct thread **write; struct pqueue *timer; - struct thread_list event; - struct thread_list ready; - struct thread_list unuse; + struct thread_list_head event, ready, unuse; struct list *cancel_req; bool canceled; pthread_cond_t cancel_cond; @@ -100,8 +94,7 @@ struct thread_master { struct thread { uint8_t type; /* thread type */ uint8_t add_type; /* thread type */ - struct thread *next; /* next pointer of the thread */ - struct thread *prev; /* previous pointer of the thread */ + struct thread_list_item threaditem; struct thread **ref; /* external reference (if given) */ struct thread_master *master; /* pointer to the struct thread_master */ int (*func)(struct thread *); /* event function */ From 7c198e4e1ac07c043ecfc573aed9f1d107f87234 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Jan 2019 02:30:35 +0100 Subject: [PATCH 16/64] lib: use DECLARE_SKIPLIST for timers instead of pqueue Replaces the use of pqueue_* for the thread_master's timer list with an instance of DECLARE_SKIPLIST_*. Signed-off-by: David Lamparter --- lib/thread.c | 94 ++++++++++++++++++---------------------------------- lib/thread.h | 7 ++-- 2 files changed, 35 insertions(+), 66 deletions(-) diff --git a/lib/thread.c b/lib/thread.c index 5ca859a74d..12c65fbabd 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -27,7 +27,6 @@ #include "memory.h" #include "log.h" #include "hash.h" -#include "pqueue.h" #include "command.h" #include "sigevent.h" #include "network.h" @@ -42,6 +41,18 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") DECLARE_LIST(thread_list, struct thread, threaditem) +static int thread_timer_cmp(const struct thread *a, const struct thread *b) +{ + if (timercmp(&a->u.sands, &b->u.sands, <)) + return -1; + if (timercmp(&a->u.sands, &b->u.sands, >)) + return 1; + return 0; +} + +DECLARE_SKIPLIST_NONUNIQ(thread_timer_list, struct thread, timeritem, + thread_timer_cmp) + #if defined(__APPLE__) #include #include @@ -377,25 +388,6 @@ void thread_cmd_init(void) /* CLI end ------------------------------------------------------------------ */ -static int thread_timer_cmp(void *a, void *b) -{ - struct thread *thread_a = a; - struct thread *thread_b = b; - - if (timercmp(&thread_a->u.sands, &thread_b->u.sands, <)) - return -1; - if (timercmp(&thread_a->u.sands, &thread_b->u.sands, >)) - return 1; - return 0; -} - -static void thread_timer_update(void *node, int actual_position) -{ - struct thread *thread = node; - - thread->index = actual_position; -} - static void cancelreq_del(void *cr) { XFREE(MTYPE_TMP, cr); @@ -440,11 +432,7 @@ struct thread_master *thread_master_create(const char *name) thread_list_init(&rv->event); thread_list_init(&rv->ready); thread_list_init(&rv->unuse); - - /* Initialize the timer queues */ - rv->timer = pqueue_create(); - rv->timer->cmp = thread_timer_cmp; - rv->timer->update = thread_timer_update; + thread_timer_list_init(&rv->timer); /* Initialize thread_fetch() settings */ rv->spin = true; @@ -542,16 +530,6 @@ static void thread_array_free(struct thread_master *m, XFREE(MTYPE_THREAD_POLL, thread_array); } -static void thread_queue_free(struct thread_master *m, struct pqueue *queue) -{ - int i; - - for (i = 0; i < queue->size; i++) - thread_free(m, queue->array[i]); - - pqueue_delete(queue); -} - /* * thread_master_free_unused * @@ -574,6 +552,8 @@ void thread_master_free_unused(struct thread_master *m) /* Stop thread scheduler. */ void thread_master_free(struct thread_master *m) { + struct thread *t; + pthread_mutex_lock(&masters_mtx); { listnode_delete(masters, m); @@ -585,7 +565,8 @@ void thread_master_free(struct thread_master *m) thread_array_free(m, m->read); thread_array_free(m, m->write); - thread_queue_free(m, m->timer); + while ((t = thread_timer_list_pop(&m->timer))) + thread_free(m, t); thread_list_free(m, &m->event); thread_list_free(m, &m->ready); thread_list_free(m, &m->unuse); @@ -659,7 +640,6 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, thread->add_type = type; thread->master = m; thread->arg = arg; - thread->index = -1; thread->yield = THREAD_YIELD_TIME_SLOT; /* default */ thread->ref = NULL; @@ -818,7 +798,6 @@ funcname_thread_add_timer_timeval(struct thread_master *m, struct thread **t_ptr, debugargdef) { struct thread *thread; - struct pqueue *queue; assert(m != NULL); @@ -834,7 +813,6 @@ funcname_thread_add_timer_timeval(struct thread_master *m, return NULL; } - queue = m->timer; thread = thread_get(m, type, func, arg, debugargpass); pthread_mutex_lock(&thread->mtx); @@ -842,7 +820,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m, monotime(&thread->u.sands); timeradd(&thread->u.sands, time_relative, &thread->u.sands); - pqueue_enqueue(thread, queue); + thread_timer_list_add(&m->timer, thread); if (t_ptr) { *t_ptr = thread; thread->ref = t_ptr; @@ -1019,7 +997,6 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) static void do_thread_cancel(struct thread_master *master) { struct thread_list_head *list = NULL; - struct pqueue *queue = NULL; struct thread **thread_array = NULL; struct thread *thread; @@ -1075,7 +1052,7 @@ static void do_thread_cancel(struct thread_master *master) thread_array = master->write; break; case THREAD_TIMER: - queue = master->timer; + thread_timer_list_del(&master->timer, thread); break; case THREAD_EVENT: list = &master->event; @@ -1088,16 +1065,10 @@ static void do_thread_cancel(struct thread_master *master) break; } - if (queue) { - assert(thread->index >= 0); - assert(thread == queue->array[thread->index]); - pqueue_remove_at(thread->index, queue); - } else if (list) { + if (list) { thread_list_del(list, thread); } else if (thread_array) { thread_array[thread->u.fd] = NULL; - } else { - assert(!"Thread should be either in queue or list or array!"); } if (thread->ref) @@ -1215,15 +1186,15 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread, } /* ------------------------------------------------------------------------- */ -static struct timeval *thread_timer_wait(struct pqueue *queue, +static struct timeval *thread_timer_wait(struct thread_timer_list_head *timers, struct timeval *timer_val) { - if (queue->size) { - struct thread *next_timer = queue->array[0]; - monotime_until(&next_timer->u.sands, timer_val); - return timer_val; - } - return NULL; + if (!thread_timer_list_count(timers)) + return NULL; + + struct thread *next_timer = thread_timer_list_first(timers); + monotime_until(&next_timer->u.sands, timer_val); + return timer_val; } static struct thread *thread_run(struct thread_master *m, struct thread *thread, @@ -1315,17 +1286,16 @@ static void thread_process_io(struct thread_master *m, unsigned int num) } /* Add all timers that have popped to the ready list. */ -static unsigned int thread_process_timers(struct pqueue *queue, +static unsigned int thread_process_timers(struct thread_timer_list_head *timers, struct timeval *timenow) { struct thread *thread; unsigned int ready = 0; - while (queue->size) { - thread = queue->array[0]; + while ((thread = thread_timer_list_first(timers))) { if (timercmp(timenow, &thread->u.sands, <)) return ready; - pqueue_dequeue(queue); + thread_timer_list_pop(timers); thread->type = THREAD_READY; thread_list_add_tail(&thread->master->ready, thread); ready++; @@ -1407,7 +1377,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * once per loop to avoid starvation by events */ if (!thread_list_count(&m->ready)) - tw = thread_timer_wait(m->timer, &tv); + tw = thread_timer_wait(&m->timer, &tv); if (thread_list_count(&m->ready) || (tw && !timercmp(tw, &zerotime, >))) @@ -1452,7 +1422,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) /* Post timers to ready queue. */ monotime(&now); - thread_process_timers(m->timer, &now); + thread_process_timers(&m->timer, &now); /* Post I/O to ready queue. */ if (num > 0) diff --git a/lib/thread.h b/lib/thread.h index 7897265120..bbc953424d 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -41,8 +41,7 @@ struct rusage_t { #define GETRUSAGE(X) thread_getrusage(X) PREDECL_LIST(thread_list) - -struct pqueue; +PREDECL_SKIPLIST_NONUNIQ(thread_timer_list) struct fd_handler { /* number of pfd that fit in the allocated space of pfds. This is a @@ -73,7 +72,7 @@ struct thread_master { struct thread **read; struct thread **write; - struct pqueue *timer; + struct thread_timer_list_head timer; struct thread_list_head event, ready, unuse; struct list *cancel_req; bool canceled; @@ -95,6 +94,7 @@ struct thread { uint8_t type; /* thread type */ uint8_t add_type; /* thread type */ struct thread_list_item threaditem; + struct thread_timer_list_item timeritem; struct thread **ref; /* external reference (if given) */ struct thread_master *master; /* pointer to the struct thread_master */ int (*func)(struct thread *); /* event function */ @@ -104,7 +104,6 @@ struct thread { int fd; /* file descriptor in case of r/w */ struct timeval sands; /* rest of time sands value. */ } u; - int index; /* queue position for timers */ struct timeval real; struct cpu_thread_history *hist; /* cache pointer to cpu_history */ unsigned long yield; /* yield time in microseconds */ From 4ab0496e38f5e242648572efad300091a8e41d78 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Jan 2019 03:09:21 +0100 Subject: [PATCH 17/64] ospf6d: replace pqueue_* with DECLARE_SKIPLIST As the previous commit, this replaces ospf6d's pqueue_* usage in SPF calculations with a DECLARE_SKIPLIST_* skiplist. Signed-off-by: David Lamparter --- ospf6d/ospf6_spf.c | 30 +++++++++++++----------------- ospf6d/ospf6_spf.h | 4 ++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index f08426fb47..aa4a995173 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -27,7 +27,6 @@ #include "command.h" #include "vty.h" #include "prefix.h" -#include "pqueue.h" #include "linklist.h" #include "thread.h" #include "lib_errors.h" @@ -76,16 +75,18 @@ static unsigned int ospf6_spf_get_ifindex_from_nh(struct ospf6_vertex *v) return 0; } -static int ospf6_vertex_cmp(void *a, void *b) +static int ospf6_vertex_cmp(const struct ospf6_vertex *va, + const struct ospf6_vertex *vb) { - struct ospf6_vertex *va = (struct ospf6_vertex *)a; - struct ospf6_vertex *vb = (struct ospf6_vertex *)b; - /* ascending order */ if (va->cost != vb->cost) return (va->cost - vb->cost); - return (va->hops - vb->hops); + if (va->hops != vb->hops) + return (va->hops - vb->hops); + return 0; } +DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue, struct ospf6_vertex, pqi, + ospf6_vertex_cmp) static int ospf6_vertex_id_cmp(void *a, void *b) { @@ -461,7 +462,7 @@ void ospf6_spf_calculation(uint32_t router_id, struct ospf6_route_table *result_table, struct ospf6_area *oa) { - struct pqueue *candidate_list; + struct vertex_pqueue_head candidate_list; struct ospf6_vertex *root, *v, *w; int size; caddr_t lsdesc; @@ -481,8 +482,7 @@ void ospf6_spf_calculation(uint32_t router_id, } /* initialize */ - candidate_list = pqueue_create(); - candidate_list->cmp = ospf6_vertex_cmp; + vertex_pqueue_init(&candidate_list); root = ospf6_vertex_create(lsa); root->area = oa; @@ -492,13 +492,10 @@ void ospf6_spf_calculation(uint32_t router_id, inet_pton(AF_INET6, "::1", &address); /* Actually insert root to the candidate-list as the only candidate */ - pqueue_enqueue(root, candidate_list); + vertex_pqueue_add(&candidate_list, root); /* Iterate until candidate-list becomes empty */ - while (candidate_list->size) { - /* get closest candidate from priority queue */ - v = pqueue_dequeue(candidate_list); - + while ((v = vertex_pqueue_pop(&candidate_list))) { /* installing may result in merging or rejecting of the vertex */ if (ospf6_spf_install(v, result_table) < 0) @@ -557,12 +554,11 @@ void ospf6_spf_calculation(uint32_t router_id, zlog_debug( " New candidate: %s hops %d cost %d", w->name, w->hops, w->cost); - pqueue_enqueue(w, candidate_list); + vertex_pqueue_add(&candidate_list, w); } } - - pqueue_delete(candidate_list); + //vertex_pqueue_fini(&candidate_list); ospf6_remove_temp_router_lsa(oa); diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index da95ec80a3..a387d40a57 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -21,6 +21,7 @@ #ifndef OSPF6_SPF_H #define OSPF6_SPF_H +#include "typesafe.h" #include "ospf6_top.h" /* Debug option */ @@ -33,6 +34,7 @@ extern unsigned char conf_debug_ospf6_spf; #define IS_OSPF6_DEBUG_SPF(level) \ (conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_##level) +PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue) /* Transit Vertex */ struct ospf6_vertex { /* type of this vertex */ @@ -41,6 +43,8 @@ struct ospf6_vertex { /* Vertex Identifier */ struct prefix vertex_id; + struct vertex_pqueue_item pqi; + /* Identifier String */ char name[128]; From c971918aec0c86ecf934d87aee0a2af05ee9bd53 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Jan 2019 02:58:52 +0100 Subject: [PATCH 18/64] ospfd: replace pqueue_* with DECLARE_SKIPLIST This replaces the SPF pqueue_* with a DECLARE_SKIPLIST_* skiplist. Signed-off-by: David Lamparter --- ospfd/ospf_lsa.h | 7 ++-- ospfd/ospf_lsdb.c | 15 ------- ospfd/ospf_lsdb.h | 2 - ospfd/ospf_spf.c | 105 ++++++++++++++++++++++------------------------ ospfd/ospf_spf.h | 6 ++- 5 files changed, 58 insertions(+), 77 deletions(-) diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index a381cf7145..5e3dabc27a 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -69,6 +69,8 @@ struct lsa_header { uint16_t length; }; +struct vertex; + /* OSPF LSA. */ struct ospf_lsa { /* LSA origination flag. */ @@ -95,10 +97,7 @@ struct ospf_lsa { int lock; /* Flags for the SPF calculation. */ - int stat; -#define LSA_SPF_NOT_EXPLORED -1 -#define LSA_SPF_IN_SPFTREE -2 - /* If stat >= 0, stat is LSA position in candidates heap. */ + struct vertex *stat; /* References to this LSA in neighbor retransmission lists*/ int retransmit_counter; diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index 2e850c4e26..86eb141312 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -169,21 +169,6 @@ void ospf_lsdb_delete_all(struct ospf_lsdb *lsdb) } } -void ospf_lsdb_clean_stat(struct ospf_lsdb *lsdb) -{ - struct route_table *table; - struct route_node *rn; - struct ospf_lsa *lsa; - int i; - - for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { - table = lsdb->type[i].db; - for (rn = route_top(table); rn; rn = route_next(rn)) - if ((lsa = (rn->info)) != NULL) - lsa->stat = LSA_SPF_NOT_EXPLORED; - } -} - struct ospf_lsa *ospf_lsdb_lookup(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) { struct route_table *table; diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h index 65c7e28fed..5cf5d05449 100644 --- a/ospfd/ospf_lsdb.h +++ b/ospfd/ospf_lsdb.h @@ -67,8 +67,6 @@ extern void ls_prefix_set(struct prefix_ls *lp, struct ospf_lsa *lsa); extern void ospf_lsdb_add(struct ospf_lsdb *, struct ospf_lsa *); extern void ospf_lsdb_delete(struct ospf_lsdb *, struct ospf_lsa *); extern void ospf_lsdb_delete_all(struct ospf_lsdb *); -/* Set all stats to -1 (LSA_SPF_NOT_EXPLORED). */ -extern void ospf_lsdb_clean_stat(struct ospf_lsdb *lsdb); extern struct ospf_lsa *ospf_lsdb_lookup(struct ospf_lsdb *, struct ospf_lsa *); extern struct ospf_lsa *ospf_lsdb_lookup_by_id(struct ospf_lsdb *, uint8_t, struct in_addr, struct in_addr); diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 6e03fa9bde..296a05bdf1 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -30,7 +30,6 @@ #include "table.h" #include "log.h" #include "sockunion.h" /* for inet_ntop () */ -#include "pqueue.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -53,6 +52,11 @@ static unsigned int spf_reason_flags = 0; +/* dummy vertex to flag "in spftree" */ +static const struct vertex vertex_in_spftree = {}; +#define LSA_SPF_IN_SPFTREE (struct vertex *)&vertex_in_spftree +#define LSA_SPF_NOT_EXPLORED NULL + static void ospf_clear_spf_reason_flags(void) { spf_reason_flags = 0; @@ -72,35 +76,36 @@ static struct list vertex_list = {.del = ospf_vertex_free}; /* Heap related functions, for the managment of the candidates, to * be used with pqueue. */ -static int cmp(void *node1, void *node2) +static int vertex_cmp(const struct vertex *v1, const struct vertex *v2) { - struct vertex *v1 = (struct vertex *)node1; - struct vertex *v2 = (struct vertex *)node2; - if (v1 != NULL && v2 != NULL) { - /* network vertices must be chosen before router vertices of - * same - * cost in order to find all shortest paths - */ - if (((v1->distance - v2->distance) == 0) - && (v1->type != v2->type)) { - switch (v1->type) { - case OSPF_VERTEX_NETWORK: - return -1; - case OSPF_VERTEX_ROUTER: - return 1; - } - } else - return (v1->distance - v2->distance); + if (v1->distance != v2->distance) + return v1->distance - v2->distance; + + if (v1->type != v2->type) { + switch (v1->type) { + case OSPF_VERTEX_NETWORK: + return -1; + case OSPF_VERTEX_ROUTER: + return 1; + } } return 0; } +DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue, struct vertex, pqi, vertex_cmp) -static void update_stat(void *node, int position) +static void lsdb_clean_stat(struct ospf_lsdb *lsdb) { - struct vertex *v = node; + struct route_table *table; + struct route_node *rn; + struct ospf_lsa *lsa; + int i; - /* Set the status of the vertex, when its position changes. */ - *(v->stat) = position; + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + table = lsdb->type[i].db; + for (rn = route_top(table); rn; rn = route_next(rn)) + if ((lsa = (rn->info)) != NULL) + lsa->stat = LSA_SPF_NOT_EXPLORED; + } } static struct vertex_nexthop *vertex_nexthop_new(void) @@ -179,7 +184,6 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa) new = XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex)); new->flags = 0; - new->stat = &(lsa->stat); new->type = lsa->data->type; new->id = lsa->data->id; new->lsa = lsa->data; @@ -187,6 +191,9 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa) new->parents = list_new(); new->parents->del = vertex_parent_free; new->parents->cmp = vertex_parent_cmp; + new->lsa_p = lsa; + + lsa->stat = new; listnode_add(&vertex_list, new); @@ -786,7 +793,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, * path is found to a vertex already on the candidate list, store the new cost. */ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, - struct ospf_area *area, struct pqueue *candidate) + struct ospf_area *area, + struct vertex_pqueue_head *candidate) { struct ospf_lsa *w_lsa = NULL; uint8_t *p; @@ -935,13 +943,11 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, /* Calculate nexthop to W. */ if (ospf_nexthop_calculation(area, v, w, l, distance, lsa_pos)) - pqueue_enqueue(w, candidate); + vertex_pqueue_add(candidate, w); else if (IS_DEBUG_OSPF_EVENT) zlog_debug("Nexthop Calc failed"); - } else if (w_lsa->stat >= 0) { - /* Get the vertex from candidates. */ - w = candidate->array[w_lsa->stat]; - + } else if (w_lsa->stat != LSA_SPF_IN_SPFTREE) { + w = w_lsa->stat; /* if D is greater than. */ if (w->distance < distance) { continue; @@ -962,18 +968,10 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, * which * will flush the old parents */ - if (ospf_nexthop_calculation(area, v, w, l, - distance, lsa_pos)) - /* Decrease the key of the node in the - * heap. - * trickle-sort it up towards root, just - * in case this - * node should now be the new root due - * the cost change. - * (next pqueu_{de,en}queue will fully - * re-heap the queue). - */ - trickle_up(w_lsa->stat, candidate); + vertex_pqueue_del(candidate, w); + ospf_nexthop_calculation(area, v, w, l, + distance, lsa_pos); + vertex_pqueue_add(candidate, w); } } /* end W is already on the candidate list */ } /* end loop over the links in V's LSA */ @@ -1169,7 +1167,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, struct route_table *new_table, struct route_table *new_rtrs) { - struct pqueue *candidate; + struct vertex_pqueue_head candidate; struct vertex *v; if (IS_DEBUG_OSPF_EVENT) { @@ -1194,11 +1192,9 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, /* This function scans all the LSA database and set the stat field to * LSA_SPF_NOT_EXPLORED. */ - ospf_lsdb_clean_stat(area->lsdb); + lsdb_clean_stat(area->lsdb); /* Create a new heap for the candidates. */ - candidate = pqueue_create(); - candidate->cmp = cmp; - candidate->update = update_stat; + vertex_pqueue_init(&candidate); /* Initialize the shortest-path tree to only the root (which is the router doing the calculation). */ @@ -1207,7 +1203,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, /* Set LSA position to LSA_SPF_IN_SPFTREE. This vertex is the root of * the * spanning tree. */ - *(v->stat) = LSA_SPF_IN_SPFTREE; + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; /* Set Area A's TransitCapability to FALSE. */ area->transit = OSPF_TRANSIT_FALSE; @@ -1215,23 +1211,22 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, for (;;) { /* RFC2328 16.1. (2). */ - ospf_spf_next(v, ospf, area, candidate); + ospf_spf_next(v, ospf, area, &candidate); /* RFC2328 16.1. (3). */ /* If at this step the candidate list is empty, the shortest- path tree (of transit vertices) has been completely built and this stage of the procedure terminates. */ - if (candidate->size == 0) - break; - /* Otherwise, choose the vertex belonging to the candidate list that is closest to the root, and add it to the shortest-path tree (removing it from the candidate list in the process). */ /* Extract from the candidates the node with the lower key. */ - v = (struct vertex *)pqueue_dequeue(candidate); + v = vertex_pqueue_pop(&candidate); + if (!v) + break; /* Update stat field in vertex. */ - *(v->stat) = LSA_SPF_IN_SPFTREE; + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; ospf_vertex_add_parent(v); @@ -1255,7 +1250,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, ospf_spf_process_stubs(area, area->spf, new_table, 0); /* Free candidate queue. */ - pqueue_delete(candidate); + //vertex_pqueue_fini(&candidate); ospf_vertex_dump(__func__, area->spf, 0, 1); /* Free nexthop information, canonical versions of which are attached diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index 85f42bcd18..09a0b6f1b7 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -22,6 +22,8 @@ #ifndef _QUAGGA_OSPF_SPF_H #define _QUAGGA_OSPF_SPF_H +#include "typesafe.h" + /* values for vertex->type */ #define OSPF_VERTEX_ROUTER 1 /* for a Router-LSA */ #define OSPF_VERTEX_NETWORK 2 /* for a Network-LSA */ @@ -31,13 +33,15 @@ /* The "root" is the node running the SPF calculation */ +PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue) /* A router or network in an area */ struct vertex { + struct vertex_pqueue_item pqi; uint8_t flags; uint8_t type; /* copied from LSA header */ struct in_addr id; /* copied from LSA header */ + struct ospf_lsa *lsa_p; struct lsa_header *lsa; /* Router or Network LSA */ - int *stat; /* Link to LSA status. */ uint32_t distance; /* from root to this vertex */ struct list *parents; /* list of parents in SPF tree */ struct list *children; /* list of children in SPF tree*/ From 798ac49d06b6619adb4c5ac765b092397bc50a6c Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Jan 2019 03:09:45 +0100 Subject: [PATCH 19/64] lib: remove pqueue_* All users of the pqueue_* implementations have been migrated to use TYPEDSKIP_* skiplists. Remove. Signed-off-by: David Lamparter --- lib/pqueue.c | 185 ----------------------------- lib/pqueue.h | 54 --------- lib/subdir.am | 2 - tests/lib/cxxcompat.c | 1 - tests/lib/test_timer_correctness.c | 1 - tests/lib/test_timer_performance.c | 1 - 6 files changed, 244 deletions(-) delete mode 100644 lib/pqueue.c delete mode 100644 lib/pqueue.h diff --git a/lib/pqueue.c b/lib/pqueue.c deleted file mode 100644 index 87b54a681a..0000000000 --- a/lib/pqueue.c +++ /dev/null @@ -1,185 +0,0 @@ -/* Priority queue functions. - * Copyright (C) 2003 Yasuhiro Ohara - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "memory.h" -#include "pqueue.h" - -DEFINE_MTYPE_STATIC(LIB, PQUEUE, "Priority queue") -DEFINE_MTYPE_STATIC(LIB, PQUEUE_DATA, "Priority queue data") - -/* priority queue using heap sort */ - -/* pqueue->cmp() controls the order of sorting (i.e, ascending or - descending). If you want the left node to move upper of the heap - binary tree, make cmp() to return less than 0. for example, if cmp - (10, 20) returns -1, the sorting is ascending order. if cmp (10, - 20) returns 1, the sorting is descending order. if cmp (10, 20) - returns 0, this library does not do sorting (which will not be what - you want). To be brief, if the contents of cmp_func (left, right) - is left - right, dequeue () returns the smallest node. Otherwise - (if the contents is right - left), dequeue () returns the largest - node. */ - -#define DATA_SIZE (sizeof (void *)) -#define PARENT_OF(x) ((x - 1) / 2) -#define LEFT_OF(x) (2 * x + 1) -#define RIGHT_OF(x) (2 * x + 2) -#define HAVE_CHILD(x,q) (x < (q)->size / 2) - -void trickle_up(int index, struct pqueue *queue) -{ - void *tmp; - - /* Save current node as tmp node. */ - tmp = queue->array[index]; - - /* Continue until the node reaches top or the place where the parent - node should be upper than the tmp node. */ - while (index > 0 - && (*queue->cmp)(tmp, queue->array[PARENT_OF(index)]) < 0) { - /* actually trickle up */ - queue->array[index] = queue->array[PARENT_OF(index)]; - if (queue->update != NULL) - (*queue->update)(queue->array[index], index); - index = PARENT_OF(index); - } - - /* Restore the tmp node to appropriate place. */ - queue->array[index] = tmp; - if (queue->update != NULL) - (*queue->update)(tmp, index); -} - -void trickle_down(int index, struct pqueue *queue) -{ - void *tmp; - int which; - - /* Save current node as tmp node. */ - tmp = queue->array[index]; - - /* Continue until the node have at least one (left) child. */ - while (HAVE_CHILD(index, queue)) { - /* If right child exists, and if the right child is more proper - to be moved upper. */ - if (RIGHT_OF(index) < queue->size - && (*queue->cmp)(queue->array[LEFT_OF(index)], - queue->array[RIGHT_OF(index)]) - > 0) - which = RIGHT_OF(index); - else - which = LEFT_OF(index); - - /* If the tmp node should be upper than the child, break. */ - if ((*queue->cmp)(queue->array[which], tmp) > 0) - break; - - /* Actually trickle down the tmp node. */ - queue->array[index] = queue->array[which]; - if (queue->update != NULL) - (*queue->update)(queue->array[index], index); - index = which; - } - - /* Restore the tmp node to appropriate place. */ - queue->array[index] = tmp; - if (queue->update != NULL) - (*queue->update)(tmp, index); -} - -struct pqueue *pqueue_create(void) -{ - struct pqueue *queue; - - queue = XCALLOC(MTYPE_PQUEUE, sizeof(struct pqueue)); - - queue->array = - XCALLOC(MTYPE_PQUEUE_DATA, DATA_SIZE * PQUEUE_INIT_ARRAYSIZE); - queue->array_size = PQUEUE_INIT_ARRAYSIZE; - - /* By default we want nothing to happen when a node changes. */ - queue->update = NULL; - return queue; -} - -void pqueue_delete(struct pqueue *queue) -{ - XFREE(MTYPE_PQUEUE_DATA, queue->array); - XFREE(MTYPE_PQUEUE, queue); -} - -static int pqueue_expand(struct pqueue *queue) -{ - void **newarray; - - newarray = - XCALLOC(MTYPE_PQUEUE_DATA, queue->array_size * DATA_SIZE * 2); - - memcpy(newarray, queue->array, queue->array_size * DATA_SIZE); - - XFREE(MTYPE_PQUEUE_DATA, queue->array); - queue->array = newarray; - queue->array_size *= 2; - - return 1; -} - -void pqueue_enqueue(void *data, struct pqueue *queue) -{ - if (queue->size + 2 >= queue->array_size && !pqueue_expand(queue)) - return; - - queue->array[queue->size] = data; - if (queue->update != NULL) - (*queue->update)(data, queue->size); - trickle_up(queue->size, queue); - queue->size++; -} - -void *pqueue_dequeue(struct pqueue *queue) -{ - void *data = queue->array[0]; - queue->array[0] = queue->array[--queue->size]; - trickle_down(0, queue); - return data; -} - -void pqueue_remove_at(int index, struct pqueue *queue) -{ - queue->array[index] = queue->array[--queue->size]; - - if (index > 0 - && (*queue->cmp)(queue->array[index], - queue->array[PARENT_OF(index)]) - < 0) { - trickle_up(index, queue); - } else { - trickle_down(index, queue); - } -} - -void pqueue_remove(void *data, struct pqueue *queue) -{ - for (int i = 0; i < queue->size; i++) - if (queue->array[i] == data) - pqueue_remove_at(i, queue); -} diff --git a/lib/pqueue.h b/lib/pqueue.h deleted file mode 100644 index 032ee9db4c..0000000000 --- a/lib/pqueue.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Priority queue functions. - * Copyright (C) 2003 Yasuhiro Ohara - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _ZEBRA_PQUEUE_H -#define _ZEBRA_PQUEUE_H - -#ifdef __cplusplus -extern "C" { -#endif - -struct pqueue { - void **array; - int array_size; - int size; - - int (*cmp)(void *, void *); - void (*update)(void *node, int actual_position); -}; - -#define PQUEUE_INIT_ARRAYSIZE 32 - -extern struct pqueue *pqueue_create(void); -extern void pqueue_delete(struct pqueue *queue); - -extern void pqueue_enqueue(void *data, struct pqueue *queue); -extern void *pqueue_dequeue(struct pqueue *queue); -extern void pqueue_remove_at(int index, struct pqueue *queue); -extern void pqueue_remove(void *data, struct pqueue *queue); - -extern void trickle_down(int index, struct pqueue *queue); -extern void trickle_up(int index, struct pqueue *queue); - -#ifdef __cplusplus -} -#endif - -#endif /* _ZEBRA_PQUEUE_H */ diff --git a/lib/subdir.am b/lib/subdir.am index 7efd3825ef..9bf02be9c3 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -58,7 +58,6 @@ lib_libfrr_la_SOURCES = \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ - lib/pqueue.c \ lib/prefix.c \ lib/privs.c \ lib/ptm_lib.c \ @@ -188,7 +187,6 @@ pkginclude_HEADERS += \ lib/openbsd-queue.h \ lib/openbsd-tree.h \ lib/plist.h \ - lib/pqueue.h \ lib/prefix.h \ lib/privs.h \ lib/ptm_lib.h \ diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index d10962993d..b71361a23d 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -72,7 +72,6 @@ #include "lib/openbsd-tree.h" #include "lib/pbr.h" #include "lib/plist.h" -#include "lib/pqueue.h" #include "lib/prefix.h" #include "lib/privs.h" #include "lib/ptm_lib.h" diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c index 43e79ba9d0..cbf9b05546 100644 --- a/tests/lib/test_timer_correctness.c +++ b/tests/lib/test_timer_correctness.c @@ -28,7 +28,6 @@ #include #include "memory.h" -#include "pqueue.h" #include "prng.h" #include "thread.h" diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c index d5f4badc85..2960e0d81e 100644 --- a/tests/lib/test_timer_performance.c +++ b/tests/lib/test_timer_performance.c @@ -28,7 +28,6 @@ #include #include "thread.h" -#include "pqueue.h" #include "prng.h" #define SCHEDULE_TIMERS 1000000 From 4bef0ec4fbe97c7865f1de676d22832344167bab Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 4 Feb 2019 01:22:03 +0100 Subject: [PATCH 20/64] isisd: replace dict_* with DECLARE_RBTREE Historically, isisd has been carrying around its own red-black tree to manage its LSP DB in. This replaces that with the newly-added DECLARE_RBTREE_*. This allows completely removing the dict_* code. Signed-off-by: David Lamparter --- debian/copyright | 13 - isisd/dict.c | 1510 --------------------------------- isisd/dict.h | 121 --- isisd/isis_adjacency.c | 1 - isisd/isis_bpf.c | 1 - isisd/isis_circuit.c | 10 +- isisd/isis_csm.c | 1 - isisd/isis_dlpi.c | 1 - isisd/isis_dr.c | 1 - isisd/isis_dynhn.c | 1 - isisd/isis_events.c | 1 - isisd/isis_lsp.c | 295 +++---- isisd/isis_lsp.h | 31 +- isisd/isis_main.c | 1 - isisd/isis_misc.c | 1 - isisd/isis_northbound.c | 1 - isisd/isis_pdu.c | 40 +- isisd/isis_pfpacket.c | 1 - isisd/isis_redist.c | 1 - isisd/isis_route.c | 1 - isisd/isis_routemap.c | 1 - isisd/isis_spf.c | 13 +- isisd/isis_spf_private.h | 4 +- isisd/isis_te.c | 1 - isisd/isis_tlvs.c | 28 +- isisd/isis_tlvs.h | 5 +- isisd/isis_tx_queue.c | 1 - isisd/isis_vty_fabricd.c | 10 +- isisd/isis_zebra.c | 1 - isisd/isisd.c | 47 +- isisd/isisd.h | 6 +- isisd/subdir.am | 2 - tests/isisd/test_isis_lspdb.c | 17 +- 33 files changed, 221 insertions(+), 1948 deletions(-) delete mode 100644 isisd/dict.c delete mode 100644 isisd/dict.h diff --git a/debian/copyright b/debian/copyright index 61d87260d8..d1f28a65a2 100644 --- a/debian/copyright +++ b/debian/copyright @@ -324,19 +324,6 @@ Copyright: Copyright (c) 2006, 2007 Pierre-Yves Ritschard Copyright (c) 2006, 2007, 2008 Reyk Floeter -Files: isisd/dict.* -Copyright: Copyright (C) 1997 Kaz Kylheku -License: custom-BSD-like - All rights are reserved by the author, with the following exceptions: - Permission is granted to freely reproduce and distribute this software, - possibly in exchange for a fee, provided that this copyright notice appears - intact. Permission is also granted to adapt this software to produce - derivative works, as long as the modified versions carry this copyright - notice and additional notices stating that the work has been modified. - This source code may be translated into executable form and incorporated - into proprietary software; there is no requirement for such software to - contain a copyright notice related to this source. - Files: qpb/qpb.proto fpm/fpm.proto License: ISC Copyright: Copyright (C) 2016 Sproute Networks, Inc. diff --git a/isisd/dict.c b/isisd/dict.c deleted file mode 100644 index d91f05d254..0000000000 --- a/isisd/dict.c +++ /dev/null @@ -1,1510 +0,0 @@ -/* - * Dictionary Abstract Data Type - * Copyright (C) 1997 Kaz Kylheku - * - * Free Software License: - * - * All rights are reserved by the author, with the following exceptions: - * Permission is granted to freely reproduce and distribute this software, - * possibly in exchange for a fee, provided that this copyright notice appears - * intact. Permission is also granted to adapt this software to produce - * derivative works, as long as the modified versions carry this copyright - * notice and additional notices stating that the work has been modified. - * This source code may be translated into executable form and incorporated - * into proprietary software; there is no requirement for such software to - * contain a copyright notice related to this source. - */ - -#include "zebra.h" -#include "zassert.h" -#include "memory.h" -#include "isis_memory.h" -#include "dict.h" - -/* - * These macros provide short convenient names for structure members, - * which are embellished with dict_ prefixes so that they are - * properly confined to the documented namespace. It's legal for a - * program which uses dict to define, for instance, a macro called ``parent''. - * Such a macro would interfere with the dnode_t struct definition. - * In general, highly portable and reusable C modules which expose their - * structures need to confine structure member names to well-defined spaces. - * The resulting identifiers aren't necessarily convenient to use, nor - * readable, in the implementation, however! - */ - -#define left dict_left -#define right dict_right -#define parent dict_parent -#define color dict_color -#define key dict_key -#define data dict_data - -#define nilnode dict_nilnode -#define nodecount dict_nodecount -#define maxcount dict_maxcount -#define compare dict_compare -#define allocnode dict_allocnode -#define freenode dict_freenode -#define context dict_context -#define dupes dict_dupes - -#define dictptr dict_dictptr - -#define dict_root(D) ((D)->nilnode.left) -#define dict_nil(D) (&(D)->nilnode) -#define DICT_DEPTH_MAX 64 - -static dnode_t *dnode_alloc(void *context); -static void dnode_free(dnode_t *node, void *context); - -/* - * Perform a ``left rotation'' adjustment on the tree. The given node P and - * its right child C are rearranged so that the P instead becomes the left - * child of C. The left subtree of C is inherited as the new right subtree - * for P. The ordering of the keys within the tree is thus preserved. - */ - -static void rotate_left(dnode_t *upper) -{ - dnode_t *lower, *lowleft, *upparent; - - lower = upper->right; - upper->right = lowleft = lower->left; - lowleft->parent = upper; - - lower->parent = upparent = upper->parent; - - /* don't need to check for root node here because root->parent is - the sentinel nil node, and root->parent->left points back to root */ - - if (upper == upparent->left) { - upparent->left = lower; - } else { - assert(upper == upparent->right); - upparent->right = lower; - } - - lower->left = upper; - upper->parent = lower; -} - -/* - * This operation is the ``mirror'' image of rotate_left. It is - * the same procedure, but with left and right interchanged. - */ - -static void rotate_right(dnode_t *upper) -{ - dnode_t *lower, *lowright, *upparent; - - lower = upper->left; - upper->left = lowright = lower->right; - lowright->parent = upper; - - lower->parent = upparent = upper->parent; - - if (upper == upparent->right) { - upparent->right = lower; - } else { - assert(upper == upparent->left); - upparent->left = lower; - } - - lower->right = upper; - upper->parent = lower; -} - -/* - * Do a postorder traversal of the tree rooted at the specified - * node and free everything under it. Used by dict_free(). - */ - -static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) -{ - if (node == nil) - return; - free_nodes(dict, node->left, nil); - free_nodes(dict, node->right, nil); - dict->freenode(node, dict->context); -} - -/* - * This procedure performs a verification that the given subtree is a binary - * search tree. It performs an inorder traversal of the tree using the - * dict_next() successor function, verifying that the key of each node is - * strictly lower than that of its successor, if duplicates are not allowed, - * or lower or equal if duplicates are allowed. This function is used for - * debugging purposes. - */ - -static int verify_bintree(dict_t *dict) -{ - dnode_t *first, *next; - - first = dict_first(dict); - - if (dict->dupes) { - while (first && (next = dict_next(dict, first))) { - if (dict->compare(first->key, next->key) > 0) - return 0; - first = next; - } - } else { - while (first && (next = dict_next(dict, first))) { - if (dict->compare(first->key, next->key) >= 0) - return 0; - first = next; - } - } - return 1; -} - - -/* - * This function recursively verifies that the given binary subtree satisfies - * three of the red black properties. It checks that every red node has only - * black children. It makes sure that each node is either red or black. And it - * checks that every path has the same count of black nodes from root to leaf. - * It returns the blackheight of the given subtree; this allows blackheights to - * be computed recursively and compared for left and right siblings for - * mismatches. It does not check for every nil node being black, because there - * is only one sentinel nil node. The return value of this function is the - * black height of the subtree rooted at the node ``root'', or zero if the - * subtree is not red-black. - */ - -#ifdef EXTREME_DICT_DEBUG -static unsigned int verify_redblack(dnode_t *nil, dnode_t *root) -{ - unsigned height_left, height_right; - - if (root != nil) { - height_left = verify_redblack(nil, root->left); - height_right = verify_redblack(nil, root->right); - if (height_left == 0 || height_right == 0) - return 0; - if (height_left != height_right) - return 0; - if (root->color == dnode_red) { - if (root->left->color != dnode_black) - return 0; - if (root->right->color != dnode_black) - return 0; - return height_left; - } - if (root->color != dnode_black) - return 0; - return height_left + 1; - } - return 1; -} -#endif - -/* - * Compute the actual count of nodes by traversing the tree and - * return it. This could be compared against the stored count to - * detect a mismatch. - */ - -#ifdef EXTREME_DICT_DEBUG -static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root) -{ - if (root == nil) - return 0; - else - return 1 + verify_node_count(nil, root->left) - + verify_node_count(nil, root->right); -} -#endif - -/* - * Verify that the tree contains the given node. This is done by - * traversing all of the nodes and comparing their pointers to the - * given pointer. Returns 1 if the node is found, otherwise - * returns zero. It is intended for debugging purposes. - */ - -static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) -{ - if (root != nil) { - return root == node - || verify_dict_has_node(nil, root->left, node) - || verify_dict_has_node(nil, root->right, node); - } - return 0; -} - - -/* - * Dynamically allocate and initialize a dictionary object. - */ - -dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp) -{ - dict_t *new = XCALLOC(MTYPE_ISIS_DICT, sizeof(dict_t)); - - new->compare = comp; - new->allocnode = dnode_alloc; - new->freenode = dnode_free; - new->context = NULL; - new->nodecount = 0; - new->maxcount = maxcount; - new->nilnode.left = &new->nilnode; - new->nilnode.right = &new->nilnode; - new->nilnode.parent = &new->nilnode; - new->nilnode.color = dnode_black; - new->dupes = 0; - - return new; -} - -/* - * Select a different set of node allocator routines. - */ - -void dict_set_allocator(dict_t *dict, dnode_alloc_t al, dnode_free_t fr, - void *context) -{ - assert(dict_count(dict) == 0); - assert((al == NULL && fr == NULL) || (al != NULL && fr != NULL)); - - dict->allocnode = al ? al : dnode_alloc; - dict->freenode = fr ? fr : dnode_free; - dict->context = context; -} - -/* - * Free a dynamically allocated dictionary object. Removing the nodes - * from the tree before deleting it is required. - */ - -void dict_destroy(dict_t *dict) -{ - assert(dict_isempty(dict)); - XFREE(MTYPE_ISIS_DICT, dict); -} - -/* - * Free all the nodes in the dictionary by using the dictionary's - * installed free routine. The dictionary is emptied. - */ - -void dict_free_nodes(dict_t *dict) -{ - dnode_t *nil = dict_nil(dict), *root = dict_root(dict); - free_nodes(dict, root, nil); - dict->nodecount = 0; - dict->nilnode.left = &dict->nilnode; - dict->nilnode.right = &dict->nilnode; -} - -/* - * Obsolescent function, equivalent to dict_free_nodes - */ - -void dict_free(dict_t *dict) -{ - dict_free_nodes(dict); -} - -/* - * Initialize a user-supplied dictionary object. - */ - -dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) -{ - dict->compare = comp; - dict->allocnode = dnode_alloc; - dict->freenode = dnode_free; - dict->context = NULL; - dict->nodecount = 0; - dict->maxcount = maxcount; - dict->nilnode.left = &dict->nilnode; - dict->nilnode.right = &dict->nilnode; - dict->nilnode.parent = &dict->nilnode; - dict->nilnode.color = dnode_black; - dict->dupes = 0; - return dict; -} - -/* - * Initialize a dictionary in the likeness of another dictionary - */ - -void dict_init_like(dict_t *dict, const dict_t *template) -{ - dict->compare = template->compare; - dict->allocnode = template->allocnode; - dict->freenode = template->freenode; - dict->context = template->context; - dict->nodecount = 0; - dict->maxcount = template->maxcount; - dict->nilnode.left = &dict->nilnode; - dict->nilnode.right = &dict->nilnode; - dict->nilnode.parent = &dict->nilnode; - dict->nilnode.color = dnode_black; - dict->dupes = template->dupes; - - assert(dict_similar(dict, template)); -} - -/* - * Remove all nodes from the dictionary (without freeing them in any way). - */ - -static void dict_clear(dict_t *dict) -{ - dict->nodecount = 0; - dict->nilnode.left = &dict->nilnode; - dict->nilnode.right = &dict->nilnode; - dict->nilnode.parent = &dict->nilnode; - assert(dict->nilnode.color == dnode_black); -} - - -/* - * Verify the integrity of the dictionary structure. This is provided for - * debugging purposes, and should be placed in assert statements. Just because - * this function succeeds doesn't mean that the tree is not corrupt. Certain - * corruptions in the tree may simply cause undefined behavior. - */ - -int dict_verify(dict_t *dict) -{ -#ifdef EXTREME_DICT_DEBUG - dnode_t *nil = dict_nil(dict), *root = dict_root(dict); - - /* check that the sentinel node and root node are black */ - if (root->color != dnode_black) - return 0; - if (nil->color != dnode_black) - return 0; - if (nil->right != nil) - return 0; - /* nil->left is the root node; check that its parent pointer is nil */ - if (nil->left->parent != nil) - return 0; - /* perform a weak test that the tree is a binary search tree */ - if (!verify_bintree(dict)) - return 0; - /* verify that the tree is a red-black tree */ - if (!verify_redblack(nil, root)) - return 0; - if (verify_node_count(nil, root) != dict_count(dict)) - return 0; -#endif - return 1; -} - -/* - * Determine whether two dictionaries are similar: have the same comparison and - * allocator functions, and same status as to whether duplicates are allowed. - */ - -int dict_similar(const dict_t *left, const dict_t *right) -{ - if (left->compare != right->compare) - return 0; - - if (left->allocnode != right->allocnode) - return 0; - - if (left->freenode != right->freenode) - return 0; - - if (left->context != right->context) - return 0; - - if (left->dupes != right->dupes) - return 0; - - return 1; -} - -/* - * Locate a node in the dictionary having the given key. - * If the node is not found, a null a pointer is returned (rather than - * a pointer that dictionary's nil sentinel node), otherwise a pointer to the - * located node is returned. - */ - -dnode_t *dict_lookup(dict_t *dict, const void *key) -{ - dnode_t *root = dict_root(dict); - dnode_t *nil = dict_nil(dict); - dnode_t *saved; - int result; - - /* simple binary search adapted for trees that contain duplicate keys */ - - while (root != nil) { - result = dict->compare(key, root->key); - if (result < 0) - root = root->left; - else if (result > 0) - root = root->right; - else { - if (!dict->dupes) { /* no duplicates, return match - */ - return root; - } else { /* could be dupes, find leftmost one */ - do { - saved = root; - root = root->left; - while (root != nil - && dict->compare(key, root->key)) - root = root->right; - } while (root != nil); - return saved; - } - } - } - - return NULL; -} - -/* - * Look for the node corresponding to the lowest key that is equal to or - * greater than the given key. If there is no such node, return null. - */ - -dnode_t *dict_lower_bound(dict_t *dict, const void *key) -{ - dnode_t *root = dict_root(dict); - dnode_t *nil = dict_nil(dict); - dnode_t *tentative = 0; - - while (root != nil) { - int result = dict->compare(key, root->key); - - if (result > 0) { - root = root->right; - } else if (result < 0) { - tentative = root; - root = root->left; - } else { - if (!dict->dupes) { - return root; - } else { - tentative = root; - root = root->left; - } - } - } - - return tentative; -} - -/* - * Look for the node corresponding to the greatest key that is equal to or - * lower than the given key. If there is no such node, return null. - */ - -dnode_t *dict_upper_bound(dict_t *dict, const void *key) -{ - dnode_t *root = dict_root(dict); - dnode_t *nil = dict_nil(dict); - dnode_t *tentative = 0; - - while (root != nil) { - int result = dict->compare(key, root->key); - - if (result < 0) { - root = root->left; - } else if (result > 0) { - tentative = root; - root = root->right; - } else { - if (!dict->dupes) { - return root; - } else { - tentative = root; - root = root->right; - } - } - } - - return tentative; -} - -/* - * Insert a node into the dictionary. The node should have been - * initialized with a data field. All other fields are ignored. - * The behavior is undefined if the user attempts to insert into - * a dictionary that is already full (for which the dict_isfull() - * function returns true). - */ - -void dict_insert(dict_t *dict, dnode_t *node, const void *key) -{ - dnode_t *where = dict_root(dict), *nil = dict_nil(dict); - dnode_t *parent = nil, *uncle, *grandpa; - int result = -1; - - node->key = key; - - assert(!dict_isfull(dict)); - assert(!dict_contains(dict, node)); - assert(!dnode_is_in_a_dict(node)); - - /* basic binary tree insert */ - - while (where != nil) { - parent = where; - result = dict->compare(key, where->key); - /* trap attempts at duplicate key insertion unless it's - * explicitly allowed */ - assert(dict->dupes || result != 0); - if (result < 0) - where = where->left; - else - where = where->right; - } - - assert(where == nil); - - if (result < 0) - parent->left = node; - else - parent->right = node; - - node->parent = parent; - node->left = nil; - node->right = nil; - - dict->nodecount++; - - /* red black adjustments */ - - node->color = dnode_red; - - while (parent->color == dnode_red) { - grandpa = parent->parent; - if (parent == grandpa->left) { - uncle = grandpa->right; - if (uncle->color - == dnode_red) { /* red parent, red uncle */ - parent->color = dnode_black; - uncle->color = dnode_black; - grandpa->color = dnode_red; - node = grandpa; - parent = grandpa->parent; - } else { /* red parent, black uncle */ - if (node == parent->right) { - rotate_left(parent); - parent = node; - assert(grandpa == parent->parent); - /* rotation between parent and child - * preserves grandpa */ - } - parent->color = dnode_black; - grandpa->color = dnode_red; - rotate_right(grandpa); - break; - } - } else { /* symmetric cases: parent == parent->parent->right */ - uncle = grandpa->left; - if (uncle->color == dnode_red) { - parent->color = dnode_black; - uncle->color = dnode_black; - grandpa->color = dnode_red; - node = grandpa; - parent = grandpa->parent; - } else { - if (node == parent->left) { - rotate_right(parent); - parent = node; - assert(grandpa == parent->parent); - } - parent->color = dnode_black; - grandpa->color = dnode_red; - rotate_left(grandpa); - break; - } - } - } - - dict_root(dict)->color = dnode_black; - - assert(dict_verify(dict)); -} - -/* - * Delete the given node from the dictionary. If the given node does not belong - * to the given dictionary, undefined behavior results. A pointer to the - * deleted node is returned. - */ - -dnode_t *dict_delete(dict_t *dict, dnode_t *delete) -{ - dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent; - - /* basic deletion */ - - assert(!dict_isempty(dict)); - assert(dict_contains(dict, delete)); - - /* - * If the node being deleted has two children, then we replace it with - * its - * successor (i.e. the leftmost node in the right subtree.) By doing - * this, - * we avoid the traditional algorithm under which the successor's key - * and - * value *only* move to the deleted node and the successor is spliced - * out - * from the tree. We cannot use this approach because the user may hold - * pointers to the successor, or nodes may be inextricably tied to some - * other structures by way of embedding, etc. So we must splice out the - * node we are given, not some other node, and must not move contents - * from - * one node to another behind the user's back. - */ - - if (delete->left != nil && delete->right != nil) { - dnode_t *next = dict_next(dict, delete); - assert(next); - dnode_t *nextparent = next->parent; - dnode_color_t nextcolor = next->color; - - assert(next != nil); - assert(next->parent != nil); - assert(next->left == nil); - - /* - * First, splice out the successor from the tree completely, by - * moving up its right child into its place. - */ - - child = next->right; - child->parent = nextparent; - - if (nextparent->left == next) { - nextparent->left = child; - } else { - assert(nextparent->right == next); - nextparent->right = child; - } - - /* - * Now that the successor has been extricated from the tree, - * install it - * in place of the node that we want deleted. - */ - - next->parent = delparent; - next->left = delete->left; - next->right = delete->right; - next->left->parent = next; - next->right->parent = next; - next->color = delete->color; - delete->color = nextcolor; - - if (delparent->left == delete) { - delparent->left = next; - } else { - assert(delparent->right == delete); - delparent->right = next; - } - - } else { - assert(delete != nil); - assert(delete->left == nil || delete->right == nil); - - child = (delete->left != nil) ? delete->left : delete->right; - - child->parent = delparent = delete->parent; - - if (delete == delparent->left) { - delparent->left = child; - } else { - assert(delete == delparent->right); - delparent->right = child; - } - } - - delete->parent = NULL; - delete->right = NULL; - delete->left = NULL; - - dict->nodecount--; - - assert(verify_bintree(dict)); - - /* red-black adjustments */ - - if (delete->color == dnode_black) { - dnode_t *parent, *sister; - - dict_root(dict)->color = dnode_red; - - while (child->color == dnode_black) { - parent = child->parent; - if (child == parent->left) { - sister = parent->right; - assert(sister != nil); - if (sister->color == dnode_red) { - sister->color = dnode_black; - parent->color = dnode_red; - rotate_left(parent); - sister = parent->right; - assert(sister != nil); - } - if (sister->left->color == dnode_black - && sister->right->color == dnode_black) { - sister->color = dnode_red; - child = parent; - } else { - if (sister->right->color - == dnode_black) { - assert(sister->left->color - == dnode_red); - sister->left->color = - dnode_black; - sister->color = dnode_red; - rotate_right(sister); - sister = parent->right; - assert(sister != nil); - } - sister->color = parent->color; - sister->right->color = dnode_black; - parent->color = dnode_black; - rotate_left(parent); - break; - } - } else { /* symmetric case: child == - child->parent->right */ - assert(child == parent->right); - sister = parent->left; - assert(sister != nil); - if (sister->color == dnode_red) { - sister->color = dnode_black; - parent->color = dnode_red; - rotate_right(parent); - sister = parent->left; - assert(sister != nil); - } - if (sister->right->color == dnode_black - && sister->left->color == dnode_black) { - sister->color = dnode_red; - child = parent; - } else { - if (sister->left->color - == dnode_black) { - assert(sister->right->color - == dnode_red); - sister->right->color = - dnode_black; - sister->color = dnode_red; - rotate_left(sister); - sister = parent->left; - assert(sister != nil); - } - sister->color = parent->color; - sister->left->color = dnode_black; - parent->color = dnode_black; - rotate_right(parent); - break; - } - } - } - - child->color = dnode_black; - dict_root(dict)->color = dnode_black; - } - - assert(dict_verify(dict)); - - return delete; -} - -/* - * Allocate a node using the dictionary's allocator routine, give it - * the data item. - */ - -int dict_alloc_insert(dict_t *dict, const void *key, void *data) -{ - dnode_t *node = dict->allocnode(dict->context); - - if (node) { - dnode_init(node, data); - dict_insert(dict, node, key); - return 1; - } - return 0; -} - -void dict_delete_free(dict_t *dict, dnode_t *node) -{ - dict_delete(dict, node); - dict->freenode(node, dict->context); -} - -/* - * Return the node with the lowest (leftmost) key. If the dictionary is empty - * (that is, dict_isempty(dict) returns 1) a null pointer is returned. - */ - -dnode_t *dict_first(dict_t *dict) -{ - dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; - - if (root != nil) - while ((left = root->left) != nil) - root = left; - - return (root == nil) ? NULL : root; -} - -/* - * Return the node with the highest (rightmost) key. If the dictionary is empty - * (that is, dict_isempty(dict) returns 1) a null pointer is returned. - */ - -dnode_t *dict_last(dict_t *dict) -{ - dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right; - - if (root != nil) - while ((right = root->right) != nil) - root = right; - - return (root == nil) ? NULL : root; -} - -/* - * Return the given node's successor node---the node which has the - * next key in the the left to right ordering. If the node has - * no successor, a null pointer is returned rather than a pointer to - * the nil node. - */ - -dnode_t *dict_next(dict_t *dict, dnode_t *curr) -{ - dnode_t *nil = dict_nil(dict), *parent, *left; - - if (curr->right != nil) { - curr = curr->right; - while ((left = curr->left) != nil) - curr = left; - return curr; - } - - parent = curr->parent; - - while (parent != nil && curr == parent->right) { - curr = parent; - parent = curr->parent; - } - - return (parent == nil) ? NULL : parent; -} - -/* - * Return the given node's predecessor, in the key order. - * The nil sentinel node is returned if there is no predecessor. - */ - -dnode_t *dict_prev(dict_t *dict, dnode_t *curr) -{ - dnode_t *nil = dict_nil(dict), *parent, *right; - - if (curr->left != nil) { - curr = curr->left; - while ((right = curr->right) != nil) - curr = right; - return curr; - } - - parent = curr->parent; - - while (parent != nil && curr == parent->left) { - curr = parent; - parent = curr->parent; - } - - return (parent == nil) ? NULL : parent; -} - -void dict_allow_dupes(dict_t *dict) -{ - dict->dupes = 1; -} - -#undef dict_count -#undef dict_isempty -#undef dict_isfull -#undef dnode_get -#undef dnode_put -#undef dnode_getkey - -dictcount_t dict_count(dict_t *dict) -{ - return dict->nodecount; -} - -int dict_isempty(dict_t *dict) -{ - return dict->nodecount == 0; -} - -int dict_isfull(dict_t *dict) -{ - return dict->nodecount == dict->maxcount; -} - -int dict_contains(dict_t *dict, dnode_t *node) -{ - return verify_dict_has_node(dict_nil(dict), dict_root(dict), node); -} - -static dnode_t *dnode_alloc(void *context) -{ - return XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t)); -} - -static void dnode_free(dnode_t *node, void *context) -{ - XFREE(MTYPE_ISIS_DICT_NODE, node); -} - -dnode_t *dnode_create(void *data) -{ - dnode_t *new = XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t)); - - new->data = data; - new->parent = NULL; - new->left = NULL; - new->right = NULL; - - return new; -} - -dnode_t *dnode_init(dnode_t *dnode, void *data) -{ - dnode->data = data; - dnode->parent = NULL; - dnode->left = NULL; - dnode->right = NULL; - return dnode; -} - -void dnode_destroy(dnode_t *dnode) -{ - assert(!dnode_is_in_a_dict(dnode)); - XFREE(MTYPE_ISIS_DICT_NODE, dnode); -} - -void *dnode_get(dnode_t *dnode) -{ - return dnode->data; -} - -const void *dnode_getkey(dnode_t *dnode) -{ - return dnode->key; -} - -void dnode_put(dnode_t *dnode, void *data) -{ - dnode->data = data; -} - -int dnode_is_in_a_dict(dnode_t *dnode) -{ - return (dnode->parent && dnode->left && dnode->right); -} - -void dict_process(dict_t *dict, void *context, dnode_process_t function) -{ - dnode_t *node = dict_first(dict), *next; - - while (node != NULL) { - /* check for callback function deleting */ - /* the next node from under us */ - assert(dict_contains(dict, node)); - next = dict_next(dict, node); - function(dict, node, context); - node = next; - } -} - -static void load_begin_internal(dict_load_t *load, dict_t *dict) -{ - load->dictptr = dict; - load->nilnode.left = &load->nilnode; - load->nilnode.right = &load->nilnode; -} - -void dict_load_begin(dict_load_t *load, dict_t *dict) -{ - assert(dict_isempty(dict)); - load_begin_internal(load, dict); -} - -void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key) -{ - dict_t *dict = load->dictptr; - dnode_t *nil = &load->nilnode; - - assert(!dnode_is_in_a_dict(newnode)); - assert(dict->nodecount < dict->maxcount); - -#ifndef NDEBUG - if (dict->nodecount > 0) { - if (dict->dupes) - assert(dict->compare(nil->left->key, key) <= 0); - else - assert(dict->compare(nil->left->key, key) < 0); - } -#endif - - newnode->key = key; - nil->right->left = newnode; - nil->right = newnode; - newnode->left = nil; - dict->nodecount++; -} - -void dict_load_end(dict_load_t *load) -{ - dict_t *dict = load->dictptr; - dnode_t *tree[DICT_DEPTH_MAX] = {0}; - dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, - *next; - dnode_t *complete = 0; - dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount; - dictcount_t botrowcount; - unsigned baselevel = 0, level = 0, i; - - assert(dnode_red == 0 && dnode_black == 1); - - while (fullcount >= nodecount && fullcount) - fullcount >>= 1; - - botrowcount = nodecount - fullcount; - - for (curr = loadnil->left; curr != loadnil; curr = next) { - next = curr->left; - - if (complete == NULL && botrowcount-- == 0) { - assert(baselevel == 0); - assert(level == 0); - baselevel = level = 1; - complete = tree[0]; - - if (complete != NULL) { - tree[0] = 0; - complete->right = dictnil; - while (tree[level] != NULL) { - tree[level]->right = complete; - complete->parent = tree[level]; - complete = tree[level]; - tree[level++] = 0; - } - } - } - - if (complete == NULL) { - curr->left = dictnil; - curr->right = dictnil; - curr->color = level % 2; - complete = curr; - - assert(level == baselevel); - while (tree[level] != NULL) { - tree[level]->right = complete; - complete->parent = tree[level]; - complete = tree[level]; - tree[level++] = 0; - } - } else { - curr->left = complete; - curr->color = (level + 1) % 2; - complete->parent = curr; - tree[level] = curr; - complete = 0; - level = baselevel; - } - } - - if (complete == NULL) - complete = dictnil; - - for (i = 0; i < DICT_DEPTH_MAX; i++) { - if (tree[i] != NULL) { - tree[i]->right = complete; - complete->parent = tree[i]; - complete = tree[i]; - } - } - - dictnil->color = dnode_black; - dictnil->right = dictnil; - complete->parent = dictnil; - complete->color = dnode_black; - dict_root(dict) = complete; - - assert(dict_verify(dict)); -} - -void dict_merge(dict_t *dest, dict_t *source) -{ - dict_load_t load; - dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source); - - assert(dict_similar(dest, source)); - - if (source == dest) - return; - - dest->nodecount = 0; - load_begin_internal(&load, dest); - - for (;;) { - if (leftnode != NULL && rightnode != NULL) { - if (dest->compare(leftnode->key, rightnode->key) < 0) - goto copyleft; - else - goto copyright; - } else if (leftnode != NULL) { - goto copyleft; - } else if (rightnode != NULL) { - goto copyright; - } else { - assert(leftnode == NULL && rightnode == NULL); - break; - } - - copyleft : { - dnode_t *next = dict_next(dest, leftnode); -#ifndef NDEBUG - leftnode->left = - NULL; /* suppress assertion in dict_load_next */ -#endif - dict_load_next(&load, leftnode, leftnode->key); - leftnode = next; - continue; - } - - copyright : { - dnode_t *next = dict_next(source, rightnode); -#ifndef NDEBUG - rightnode->left = NULL; -#endif - dict_load_next(&load, rightnode, rightnode->key); - rightnode = next; - continue; - } - } - - dict_clear(source); - dict_load_end(&load); -} - -#ifdef KAZLIB_TEST_MAIN - -#include -#include -#include -#include - -typedef char input_t[256]; - -static int tokenize(char *string, ...) -{ - char **tokptr; - va_list arglist; - int tokcount = 0; - - va_start(arglist, string); - tokptr = va_arg(arglist, char **); - while (tokptr) { - while (*string && isspace((unsigned char)*string)) - string++; - if (!*string) - break; - *tokptr = string; - while (*string && !isspace((unsigned char)*string)) - string++; - tokptr = va_arg(arglist, char **); - tokcount++; - if (!*string) - break; - *string++ = 0; - } - va_end(arglist); - - return tokcount; -} - -static int comparef(const void *key1, const void *key2) -{ - return strcmp(key1, key2); -} - -static char *dupstring(char *str) -{ - int sz = strlen(str) + 1; - char *new = XCALLOC(MTYPE_ISIS_TMP, sz); - - memcpy(new, str, sz); - return new; -} - -static dnode_t *new_node(void *c) -{ - static dnode_t few[5]; - static int count; - - if (count < 5) - return few + count++; - - return NULL; -} - -static void del_node(dnode_t *n, void *c) -{ -} - -static int prompt = 0; - -static void construct(dict_t *d) -{ - input_t in; - int done = 0; - dict_load_t dl; - dnode_t *dn; - char *tok1, *tok2, *val; - const char *key; - char *help = - "p turn prompt on\n" - "q finish construction\n" - "a add new entry\n"; - - if (!dict_isempty(d)) - puts("warning: dictionary not empty!"); - - dict_load_begin(&dl, d); - - while (!done) { - if (prompt) - putchar('>'); - fflush(stdout); - - if (!fgets(in, sizeof(input_t), stdin)) - break; - - switch (in[0]) { - case '?': - puts(help); - break; - case 'p': - prompt = 1; - break; - case 'q': - done = 1; - break; - case 'a': - if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) { - puts("what?"); - break; - } - key = dupstring(tok1); - val = dupstring(tok2); - dn = dnode_create(val); - - if (!key || !val || !dn) { - puts("out of memory"); - free((void *)key); - free(val); - if (dn) - dnode_destroy(dn); - } else - dict_load_next(&dl, dn, key); - break; - default: - putchar('?'); - putchar('\n'); - break; - } - } - - dict_load_end(&dl); -} - -int main(void) -{ - input_t in; - dict_t darray[10]; - dict_t *d = &darray[0]; - dnode_t *dn; - int i; - char *tok1, *tok2, *val; - const char *key; - - char *help = - "a add value to dictionary\n" - "d delete value from dictionary\n" - "l lookup value in dictionary\n" - "( lookup lower bound\n" - ") lookup upper bound\n" - "# switch to alternate dictionary (0-9)\n" - "j merge two dictionaries\n" - "f free the whole dictionary\n" - "k allow duplicate keys\n" - "c show number of entries\n" - "t dump whole dictionary in sort order\n" - "m make dictionary out of sorted items\n" - "p turn prompt on\n" - "s switch to non-functioning allocator\n" - "q quit"; - - for (i = 0; i < 10; i++) - dict_init(&darray[i], DICTCOUNT_T_MAX, comparef); - - for (;;) { - if (prompt) - putchar('>'); - fflush(stdout); - - if (!fgets(in, sizeof(input_t), stdin)) - break; - - switch (in[0]) { - case '?': - puts(help); - break; - case 'a': - if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) { - puts("what?"); - break; - } - key = dupstring(tok1); - val = dupstring(tok2); - - if (!key || !val) { - puts("out of memory"); - free((void *)key); - free(val); - } - - if (!dict_alloc_insert(d, key, val)) { - puts("dict_alloc_insert failed"); - free((void *)key); - free(val); - break; - } - break; - case 'd': - if (tokenize(in + 1, &tok1, (char **)0) != 1) { - puts("what?"); - break; - } - dn = dict_lookup(d, tok1); - if (!dn) { - puts("dict_lookup failed"); - break; - } - val = dnode_get(dn); - key = dnode_getkey(dn); - dict_delete_free(d, dn); - - free(val); - free((void *)key); - break; - case 'f': - dict_free(d); - break; - case 'l': - case '(': - case ')': - if (tokenize(in + 1, &tok1, (char **)0) != 1) { - puts("what?"); - break; - } - dn = 0; - switch (in[0]) { - case 'l': - dn = dict_lookup(d, tok1); - break; - case '(': - dn = dict_lower_bound(d, tok1); - break; - case ')': - dn = dict_upper_bound(d, tok1); - break; - } - if (!dn) { - puts("lookup failed"); - break; - } - val = dnode_get(dn); - puts(val); - break; - case 'm': - construct(d); - break; - case 'k': - dict_allow_dupes(d); - break; - case 'c': - printf("%lu\n", (unsigned long)dict_count(d)); - break; - case 't': - for (dn = dict_first(d); dn; dn = dict_next(d, dn)) { - printf("%s\t%s\n", (char *)dnode_getkey(dn), - (char *)dnode_get(dn)); - } - break; - case 'q': - exit(0); - break; - case '\0': - break; - case 'p': - prompt = 1; - break; - case 's': - dict_set_allocator(d, new_node, del_node, NULL); - break; - case '#': - if (tokenize(in + 1, &tok1, (char **)0) != 1) { - puts("what?"); - break; - } else { - int dictnum = atoi(tok1); - if (dictnum < 0 || dictnum > 9) { - puts("invalid number"); - break; - } - d = &darray[dictnum]; - } - break; - case 'j': - if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) { - puts("what?"); - break; - } else { - int dict1 = atoi(tok1), dict2 = atoi(tok2); - if (dict1 < 0 || dict1 > 9 || dict2 < 0 - || dict2 > 9) { - puts("invalid number"); - break; - } - dict_merge(&darray[dict1], &darray[dict2]); - } - break; - default: - putchar('?'); - putchar('\n'); - break; - } - } - - return 0; -} - -#endif diff --git a/isisd/dict.h b/isisd/dict.h deleted file mode 100644 index 32683c57d5..0000000000 --- a/isisd/dict.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Dictionary Abstract Data Type - * Copyright (C) 1997 Kaz Kylheku - * - * Free Software License: - * - * All rights are reserved by the author, with the following exceptions: - * Permission is granted to freely reproduce and distribute this software, - * possibly in exchange for a fee, provided that this copyright notice appears - * intact. Permission is also granted to adapt this software to produce - * derivative works, as long as the modified versions carry this copyright - * notice and additional notices stating that the work has been modified. - * This source code may be translated into executable form and incorporated - * into proprietary software; there is no requirement for such software to - * contain a copyright notice related to this source. - * - */ - -#ifndef DICT_H -#define DICT_H - -#include - -/* - * Blurb for inclusion into C++ translation units - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned long dictcount_t; -#define DICTCOUNT_T_MAX ULONG_MAX - -/* - * The dictionary is implemented as a red-black tree - */ - -typedef enum { dnode_red, dnode_black } dnode_color_t; - -typedef struct dnode_t { - struct dnode_t *dict_left; - struct dnode_t *dict_right; - struct dnode_t *dict_parent; - dnode_color_t dict_color; - const void *dict_key; - void *dict_data; -} dnode_t; - -typedef int (*dict_comp_t)(const void *, const void *); -typedef dnode_t *(*dnode_alloc_t)(void *); -typedef void (*dnode_free_t)(dnode_t *, void *); - -typedef struct dict_t { - dnode_t dict_nilnode; - dictcount_t dict_nodecount; - dictcount_t dict_maxcount; - dict_comp_t dict_compare; - dnode_alloc_t dict_allocnode; - dnode_free_t dict_freenode; - void *dict_context; - int dict_dupes; -} dict_t; - -typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); - -typedef struct dict_load_t { - dict_t *dict_dictptr; - dnode_t dict_nilnode; -} dict_load_t; - -extern dict_t *dict_create(dictcount_t, dict_comp_t); -extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *); -extern void dict_destroy(dict_t *); -extern void dict_free_nodes(dict_t *); -extern void dict_free(dict_t *); -extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t); -extern void dict_init_like(dict_t *, const dict_t *); -extern int dict_verify(dict_t *); -extern int dict_similar(const dict_t *, const dict_t *); -extern dnode_t *dict_lookup(dict_t *, const void *); -extern dnode_t *dict_lower_bound(dict_t *, const void *); -extern dnode_t *dict_upper_bound(dict_t *, const void *); -extern void dict_insert(dict_t *, dnode_t *, const void *); -extern dnode_t *dict_delete(dict_t *, dnode_t *); -extern int dict_alloc_insert(dict_t *, const void *, void *); -extern void dict_delete_free(dict_t *, dnode_t *); -extern dnode_t *dict_first(dict_t *); -extern dnode_t *dict_last(dict_t *); -extern dnode_t *dict_next(dict_t *, dnode_t *); -extern dnode_t *dict_prev(dict_t *, dnode_t *); -extern dictcount_t dict_count(dict_t *); -extern int dict_isempty(dict_t *); -extern int dict_isfull(dict_t *); -extern int dict_contains(dict_t *, dnode_t *); -extern void dict_allow_dupes(dict_t *); -extern int dnode_is_in_a_dict(dnode_t *); -extern dnode_t *dnode_create(void *); -extern dnode_t *dnode_init(dnode_t *, void *); -extern void dnode_destroy(dnode_t *); -extern void *dnode_get(dnode_t *); -extern const void *dnode_getkey(dnode_t *); -extern void dnode_put(dnode_t *, void *); -extern void dict_process(dict_t *, void *, dnode_process_t); -extern void dict_load_begin(dict_load_t *, dict_t *); -extern void dict_load_next(dict_load_t *, dnode_t *, const void *); -extern void dict_load_end(dict_load_t *); -extern void dict_merge(dict_t *, dict_t *); - -#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) -#define dict_count(D) ((D)->dict_nodecount) -#define dict_isempty(D) ((D)->dict_nodecount == 0) -#define dnode_get(N) ((N)->dict_data) -#define dnode_getkey(N) ((N)->dict_key) -#define dnode_put(N, X) ((N)->dict_data = (X)) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 62814329ea..9b368cc404 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -32,7 +32,6 @@ #include "if.h" #include "stream.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 28750278b0..4e9aef47ad 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -34,7 +34,6 @@ #include "if.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_circuit.h" diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 8377638b92..f9a0285872 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -40,7 +40,6 @@ #include "qobj.h" #include "lib/northbound_cli.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -540,7 +539,6 @@ static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit, { struct isis_area *area; struct isis_lsp *lsp; - dnode_t *dnode; int level; assert(circuit); @@ -550,14 +548,10 @@ static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit, if (!(level & circuit->is_type)) continue; - if (!area->lspdb[level - 1] - || !dict_count(area->lspdb[level - 1])) + if (!lspdb_count(&area->lspdb[level - 1])) continue; - for (dnode = dict_first(area->lspdb[level - 1]); - dnode != NULL; - dnode = dict_next(area->lspdb[level - 1], dnode)) { - lsp = dnode_get(dnode); + for_each (lspdb, &area->lspdb[level - 1], lsp) { if (is_set) { isis_tx_queue_add(circuit->tx_queue, lsp, TX_LSP_NORMAL); diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index e50f57ae1d..88676dd990 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -32,7 +32,6 @@ #include "prefix.h" #include "stream.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c index 148b438661..a96dd93804 100644 --- a/isisd/isis_dlpi.c +++ b/isisd/isis_dlpi.c @@ -38,7 +38,6 @@ #include "if.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_circuit.h" diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c index 449648656a..7be5307500 100644 --- a/isisd/isis_dr.c +++ b/isisd/isis_dr.c @@ -32,7 +32,6 @@ #include "stream.h" #include "if.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_misc.h" diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 1d29d1086d..921e23d33a 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -31,7 +31,6 @@ #include "if.h" #include "thread.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 4da23c5912..6a5dcfb075 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -32,7 +32,6 @@ #include "stream.h" #include "table.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index b56a56fa3f..a0be2d82f2 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -40,7 +40,6 @@ #include "srcdest_table.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -63,41 +62,38 @@ static int lsp_refresh(struct thread *thread); static int lsp_l1_refresh_pseudo(struct thread *thread); static int lsp_l2_refresh_pseudo(struct thread *thread); +static void lsp_destroy(struct isis_lsp *lsp); + int lsp_id_cmp(uint8_t *id1, uint8_t *id2) { return memcmp(id1, id2, ISIS_SYS_ID_LEN + 2); } -dict_t *lsp_db_init(void) +int lspdb_compare(const struct isis_lsp *a, const struct isis_lsp *b) { - dict_t *dict; - - dict = dict_create(DICTCOUNT_T_MAX, (dict_comp_t)lsp_id_cmp); - - return dict; + return memcmp(a->hdr.lsp_id, b->hdr.lsp_id, sizeof(a->hdr.lsp_id)); } -struct isis_lsp *lsp_search(uint8_t *id, dict_t *lspdb) +void lsp_db_init(struct lspdb_head *head) { - dnode_t *node; + lspdb_init(head); +} -#ifdef EXTREME_DEBUG - dnode_t *dn; +void lsp_db_fini(struct lspdb_head *head) +{ + struct isis_lsp *lsp; - zlog_debug("searching db"); - for (dn = dict_first(lspdb); dn; dn = dict_next(lspdb, dn)) { - zlog_debug("%s\t%pX", - rawlspid_print((uint8_t *)dnode_getkey(dn)), - dnode_get(dn)); - } -#endif /* EXTREME DEBUG */ + while ((lsp = lspdb_pop(head))) + lsp_destroy(lsp); + lspdb_fini(head); +} - node = dict_lookup(lspdb, id); +struct isis_lsp *lsp_search(struct lspdb_head *head, const uint8_t *id) +{ + struct isis_lsp searchfor; + memcpy(searchfor.hdr.lsp_id, id, sizeof(searchfor.hdr.lsp_id)); - if (node) - return (struct isis_lsp *)dnode_get(node); - - return NULL; + return lspdb_find(head, &searchfor); } static void lsp_clear_data(struct isis_lsp *lsp) @@ -109,7 +105,7 @@ static void lsp_clear_data(struct isis_lsp *lsp) lsp->tlvs = NULL; } -static void lsp_remove_frags(struct list *frags, dict_t *lspdb); +static void lsp_remove_frags(struct lspdb_head *head, struct list *frags); static void lsp_destroy(struct isis_lsp *lsp) { @@ -128,8 +124,8 @@ static void lsp_destroy(struct isis_lsp *lsp) if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) { if (lsp->lspu.frags) { - lsp_remove_frags(lsp->lspu.frags, - lsp->area->lspdb[lsp->level - 1]); + lsp_remove_frags(&lsp->area->lspdb[lsp->level - 1], + lsp->lspu.frags); list_delete(&lsp->lspu.frags); } } else { @@ -148,56 +144,34 @@ static void lsp_destroy(struct isis_lsp *lsp) XFREE(MTYPE_ISIS_LSP, lsp); } -void lsp_db_destroy(dict_t *lspdb) -{ - dnode_t *dnode, *next; - struct isis_lsp *lsp; - - dnode = dict_first(lspdb); - while (dnode) { - next = dict_next(lspdb, dnode); - lsp = dnode_get(dnode); - lsp_destroy(lsp); - dict_delete_free(lspdb, dnode); - dnode = next; - } - - dict_free(lspdb); - - return; -} - /* * Remove all the frags belonging to the given lsp */ -static void lsp_remove_frags(struct list *frags, dict_t *lspdb) +static void lsp_remove_frags(struct lspdb_head *head, struct list *frags) { - dnode_t *dnode; struct listnode *lnode, *lnnode; struct isis_lsp *lsp; for (ALL_LIST_ELEMENTS(frags, lnode, lnnode, lsp)) { - dnode = dict_lookup(lspdb, lsp->hdr.lsp_id); + lsp = lsp_search(head, lsp->hdr.lsp_id); + lspdb_del(head, lsp); lsp_destroy(lsp); - dnode_destroy(dict_delete(lspdb, dnode)); } } -void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb) +void lsp_search_and_destroy(struct lspdb_head *head, const uint8_t *id) { - dnode_t *node; struct isis_lsp *lsp; - node = dict_lookup(lspdb, id); - if (node) { - node = dict_delete(lspdb, node); - lsp = dnode_get(node); + lsp = lsp_search(head, id); + if (lsp) { + lspdb_del(head, lsp); /* * If this is a zero lsp, remove all the frags now */ if (LSP_FRAGMENT(lsp->hdr.lsp_id) == 0) { if (lsp->lspu.frags) - lsp_remove_frags(lsp->lspu.frags, lspdb); + lsp_remove_frags(head, lsp->lspu.frags); } else { /* * else just remove this frag, from the zero lsps' frag @@ -209,7 +183,6 @@ void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb) lsp); } lsp_destroy(lsp); - dnode_destroy(node); } } @@ -514,7 +487,7 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, memcpy(lspid, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lspid) = 0; - lsp0 = lsp_search(lspid, area->lspdb[level - 1]); + lsp0 = lsp_search(&area->lspdb[level - 1], lspid); if (lsp0) lsp_link_fragment(lsp, lsp0); } @@ -582,9 +555,9 @@ struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id, return lsp; } -void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb) +void lsp_insert(struct lspdb_head *head, struct isis_lsp *lsp) { - dict_alloc_insert(lspdb, lsp->hdr.lsp_id, lsp); + lspdb_add(head, lsp); if (lsp->hdr.seqno) isis_spf_schedule(lsp->area, lsp->level); } @@ -592,13 +565,16 @@ void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb) /* * Build a list of LSPs with non-zero ht bounded by start and stop ids */ -void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id, - struct list *list, dict_t *lspdb) +void lsp_build_list_nonzero_ht(struct lspdb_head *head, const uint8_t *start_id, + const uint8_t *stop_id, struct list *list) { - for (dnode_t *curr = dict_lower_bound(lspdb, start_id); - curr; curr = dict_next(lspdb, curr)) { - struct isis_lsp *lsp = curr->dict_data; + struct isis_lsp searchfor; + struct isis_lsp *lsp, *start; + memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id)); + + start = lspdb_find_gteq(head, &searchfor); + for_each_from (lspdb, head, lsp, start) { if (memcmp(lsp->hdr.lsp_id, stop_id, ISIS_SYS_ID_LEN + 2) > 0) break; @@ -699,26 +675,20 @@ void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost) } /* print all the lsps info in the local lspdb */ -int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost) +int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, + char dynhost) { - - dnode_t *node = dict_first(lspdb), *next; + struct isis_lsp *lsp; int lsp_count = 0; if (detail == ISIS_UI_LEVEL_BRIEF) { - while (node != NULL) { - /* I think it is unnecessary, so I comment it out */ - /* dict_contains (lspdb, node); */ - next = dict_next(lspdb, node); - lsp_print(dnode_get(node), vty, dynhost); - node = next; + for_each (lspdb, head, lsp) { + lsp_print(lsp, vty, dynhost); lsp_count++; } } else if (detail == ISIS_UI_LEVEL_DETAIL) { - while (node != NULL) { - next = dict_next(lspdb, node); - lsp_print_detail(dnode_get(node), vty, dynhost); - node = next; + for_each (lspdb, head, lsp) { + lsp_print_detail(lsp, vty, dynhost); lsp_count++; } } @@ -847,7 +817,7 @@ static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0, memcpy(frag_id, lsp0->hdr.lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(frag_id) = frag_num; - lsp = lsp_search(frag_id, area->lspdb[level - 1]); + lsp = lsp_search(&area->lspdb[level - 1], frag_id); if (lsp) { lsp_clear_data(lsp); if (!lsp->lspu.zero_lsp) @@ -860,7 +830,7 @@ static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0, area->attached_bit), 0, lsp0, level); lsp->own_lsp = 1; - lsp_insert(lsp, area->lspdb[level - 1]); + lsp_insert(&area->lspdb[level - 1], lsp); return lsp; } @@ -1228,12 +1198,12 @@ int lsp_generate(struct isis_area *area, int level) memcpy(&lspid, isis->sysid, ISIS_SYS_ID_LEN); /* only builds the lsp if the area shares the level */ - oldlsp = lsp_search(lspid, area->lspdb[level - 1]); + oldlsp = lsp_search(&area->lspdb[level - 1], lspid); if (oldlsp) { /* FIXME: we should actually initiate a purge */ seq_num = oldlsp->hdr.seqno; - lsp_search_and_destroy(oldlsp->hdr.lsp_id, - area->lspdb[level - 1]); + lsp_search_and_destroy(&area->lspdb[level - 1], + oldlsp->hdr.lsp_id); } rem_lifetime = lsp_rem_lifetime(area, level); newlsp = @@ -1243,7 +1213,7 @@ int lsp_generate(struct isis_area *area, int level) newlsp->area = area; newlsp->own_lsp = 1; - lsp_insert(newlsp, area->lspdb[level - 1]); + lsp_insert(&area->lspdb[level - 1], newlsp); /* build_lsp_data (newlsp, area); */ lsp_build(newlsp, area); /* time to calculate our checksum */ @@ -1288,7 +1258,7 @@ int lsp_generate(struct isis_area *area, int level) */ static int lsp_regenerate(struct isis_area *area, int level) { - dict_t *lspdb; + struct lspdb_head *head; struct isis_lsp *lsp, *frag; struct listnode *node; uint8_t lspid[ISIS_SYS_ID_LEN + 2]; @@ -1297,12 +1267,12 @@ static int lsp_regenerate(struct isis_area *area, int level) if ((area == NULL) || (area->is_type & level) != level) return ISIS_ERROR; - lspdb = area->lspdb[level - 1]; + head = &area->lspdb[level - 1]; memset(lspid, 0, ISIS_SYS_ID_LEN + 2); memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN); - lsp = lsp_search(lspid, lspdb); + lsp = lsp_search(head, lspid); if (!lsp) { flog_err(EC_LIB_DEVELOPMENT, @@ -1445,7 +1415,7 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, continue; } - lsp = lsp_search(id, area->lspdb[lvl - 1]); + lsp = lsp_search(&area->lspdb[lvl - 1], id); if (!lsp) { sched_debug( "ISIS (%s): We do not have any LSPs to regenerate, nothing todo.", @@ -1597,7 +1567,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, int lsp_generate_pseudo(struct isis_circuit *circuit, int level) { - dict_t *lspdb = circuit->area->lspdb[level - 1]; + struct lspdb_head *head = &circuit->area->lspdb[level - 1]; struct isis_lsp *lsp; uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; uint16_t rem_lifetime, refresh_time; @@ -1615,7 +1585,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) /* * If for some reason have a pseudo LSP in the db already -> regenerate */ - if (lsp_search(lsp_id, lspdb)) + if (lsp_search(head, lsp_id)) return lsp_regenerate_schedule_pseudo(circuit, level); rem_lifetime = lsp_rem_lifetime(circuit->area, level); @@ -1628,7 +1598,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) lsp_build_pseudo(lsp, circuit, level); lsp_pack_pdu(lsp); lsp->own_lsp = 1; - lsp_insert(lsp, lspdb); + lsp_insert(head, lsp); lsp_flood(lsp, NULL); refresh_time = lsp_refresh_time(lsp, rem_lifetime); @@ -1659,7 +1629,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) { - dict_t *lspdb = circuit->area->lspdb[level - 1]; + struct lspdb_head *head = &circuit->area->lspdb[level - 1]; struct isis_lsp *lsp; uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; uint16_t rem_lifetime, refresh_time; @@ -1674,7 +1644,7 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id; LSP_FRAGMENT(lsp_id) = 0; - lsp = lsp_search(lsp_id, lspdb); + lsp = lsp_search(head, lsp_id); if (!lsp) { flog_err(EC_LIB_DEVELOPMENT, @@ -1813,7 +1783,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) continue; } - lsp = lsp_search(lsp_id, circuit->area->lspdb[lvl - 1]); + lsp = lsp_search(&circuit->area->lspdb[lvl - 1], lsp_id); if (!lsp) { sched_debug( "ISIS (%s): Pseudonode LSP does not exist yet, nothing to regenerate.", @@ -1869,7 +1839,6 @@ int lsp_tick(struct thread *thread) { struct isis_area *area; struct isis_lsp *lsp; - dnode_t *dnode, *dnode_next; int level; uint16_t rem_lifetime; bool fabricd_sync_incomplete = false; @@ -1885,83 +1854,69 @@ int lsp_tick(struct thread *thread) * Remove LSPs which have aged out */ for (level = 0; level < ISIS_LEVELS; level++) { - if (area->lspdb[level] && dict_count(area->lspdb[level]) > 0) { - for (dnode = dict_first(area->lspdb[level]); - dnode != NULL; dnode = dnode_next) { - dnode_next = - dict_next(area->lspdb[level], dnode); - lsp = dnode_get(dnode); + struct isis_lsp *next = lspdb_first(&area->lspdb[level]); + for_each_from (lspdb, &area->lspdb[level], lsp, next) { + /* + * The lsp rem_lifetime is kept at 0 for MaxAge + * or + * ZeroAgeLifetime depending on explicit purge + * or + * natural age out. So schedule spf only once + * when + * the first time rem_lifetime becomes 0. + */ + rem_lifetime = lsp->hdr.rem_lifetime; + lsp_set_time(lsp); - /* - * The lsp rem_lifetime is kept at 0 for MaxAge - * or - * ZeroAgeLifetime depending on explicit purge - * or - * natural age out. So schedule spf only once - * when - * the first time rem_lifetime becomes 0. + /* + * Schedule may run spf which should be done + * only after + * the lsp rem_lifetime becomes 0 for the first + * time. + * ISO 10589 - 7.3.16.4 first paragraph. + */ + if (rem_lifetime == 1 && lsp->hdr.seqno != 0) { + /* 7.3.16.4 a) set SRM flags on all */ + /* 7.3.16.4 b) retain only the header */ + if (lsp->area->purge_originator) + lsp_purge(lsp, lsp->level, NULL); + else + lsp_flood(lsp, NULL); + /* 7.3.16.4 c) record the time to purge + * FIXME */ + isis_spf_schedule(lsp->area, lsp->level); + } + + if (lsp->age_out == 0) { + zlog_debug( + "ISIS-Upd (%s): L%u LSP %s seq " + "0x%08" PRIx32 " aged out", + area->area_tag, lsp->level, + rawlspid_print(lsp->hdr.lsp_id), + lsp->hdr.seqno); + + /* if we're aging out fragment 0, lsp_destroy() + * below will delete all other fragments too, + * so we need to skip over those */ - rem_lifetime = lsp->hdr.rem_lifetime; - lsp_set_time(lsp); + if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) + while (next && + !memcmp(next->hdr.lsp_id, + lsp->hdr.lsp_id, + ISIS_SYS_ID_LEN + 1)) + next = lspdb_next( + &area->lspdb[level], + next); - /* - * Schedule may run spf which should be done - * only after - * the lsp rem_lifetime becomes 0 for the first - * time. - * ISO 10589 - 7.3.16.4 first paragraph. - */ - if (rem_lifetime == 1 && lsp->hdr.seqno != 0) { - /* 7.3.16.4 a) set SRM flags on all */ - /* 7.3.16.4 b) retain only the header */ - if (lsp->area->purge_originator) - lsp_purge(lsp, lsp->level, NULL); - else - lsp_flood(lsp, NULL); - /* 7.3.16.4 c) record the time to purge - * FIXME */ - isis_spf_schedule(lsp->area, lsp->level); - } + lspdb_del(&area->lspdb[level], lsp); + lsp_destroy(lsp); + lsp = NULL; + } - if (lsp->age_out == 0) { - zlog_debug( - "ISIS-Upd (%s): L%u LSP %s seq " - "0x%08" PRIx32 " aged out", - area->area_tag, lsp->level, - rawlspid_print(lsp->hdr.lsp_id), - lsp->hdr.seqno); - - /* if we're aging out fragment 0, - * lsp_destroy() below will delete all - * other fragments too, so we need to - * skip over those - */ - while (!LSP_FRAGMENT(lsp->hdr.lsp_id) - && dnode_next) { - struct isis_lsp *nextlsp; - - nextlsp = dnode_get(dnode_next); - if (memcmp(nextlsp->hdr.lsp_id, - lsp->hdr.lsp_id, - ISIS_SYS_ID_LEN + 1)) - break; - - dnode_next = dict_next( - area->lspdb[level], - dnode_next); - } - - lsp_destroy(lsp); - lsp = NULL; - dict_delete_free(area->lspdb[level], - dnode); - } - - if (fabricd_init_c && lsp) { - fabricd_sync_incomplete |= - ISIS_CHECK_FLAG(lsp->SSNflags, - fabricd_init_c); - } + if (fabricd_init_c && lsp) { + fabricd_sync_incomplete |= + ISIS_CHECK_FLAG(lsp->SSNflags, + fabricd_init_c); } } } @@ -1979,7 +1934,7 @@ void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level) { struct isis_lsp *lsp; - lsp = lsp_search(id, circuit->area->lspdb[level - 1]); + lsp = lsp_search(&circuit->area->lspdb[level - 1], id); if (!lsp) return; @@ -2012,7 +1967,7 @@ void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr, lsp_pack_pdu(lsp); - lsp_insert(lsp, area->lspdb[lsp->level - 1]); + lsp_insert(&area->lspdb[lsp->level - 1], lsp); lsp_flood(lsp, NULL); return; diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index e6ea0b4eda..4cbca5d517 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -24,13 +24,18 @@ #ifndef _ZEBRA_ISIS_LSP_H #define _ZEBRA_ISIS_LSP_H +#include "lib/typesafe.h" #include "isisd/isis_pdu.h" +PREDECL_RBTREE_UNIQ(lspdb) + /* Structure for isis_lsp, this structure will only support the fixed * System ID (Currently 6) (atleast for now). In order to support more * We will have to split the header into two parts, and for readability * sake it should better be avoided */ struct isis_lsp { + struct lspdb_item dbe; + struct isis_lsp_hdr hdr; struct stream *pdu; /* full pdu lsp */ union { @@ -54,8 +59,11 @@ struct isis_lsp { bool flooding_circuit_scoped; }; -dict_t *lsp_db_init(void); -void lsp_db_destroy(dict_t *lspdb); +extern int lspdb_compare(const struct isis_lsp *a, const struct isis_lsp *b); +DECLARE_RBTREE_UNIQ(lspdb, struct isis_lsp, dbe, lspdb_compare) + +void lsp_db_init(struct lspdb_head *head); +void lsp_db_fini(struct lspdb_head *head); int lsp_tick(struct thread *thread); int lsp_generate(struct isis_area *area, int level); @@ -76,14 +84,16 @@ struct isis_lsp *lsp_new_from_recv(struct isis_lsp_hdr *hdr, struct isis_tlvs *tlvs, struct stream *stream, struct isis_lsp *lsp0, struct isis_area *area, int level); -void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb); -struct isis_lsp *lsp_search(uint8_t *id, dict_t *lspdb); +void lsp_insert(struct lspdb_head *head, struct isis_lsp *lsp); +struct isis_lsp *lsp_search(struct lspdb_head *head, const uint8_t *id); -void lsp_build_list(uint8_t *start_id, uint8_t *stop_id, uint8_t num_lsps, - struct list *list, dict_t *lspdb); -void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id, - struct list *list, dict_t *lspdb); -void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb); +void lsp_build_list(struct lspdb_head *head, const uint8_t *start_id, + const uint8_t *stop_id, uint8_t num_lsps, + struct list *list); +void lsp_build_list_nonzero_ht(struct lspdb_head *head, + const uint8_t *start_id, + const uint8_t *stop_id, struct list *list); +void lsp_search_and_destroy(struct lspdb_head *head, const uint8_t *id); void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level); void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr, struct isis_area *area); @@ -108,7 +118,8 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno); void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag); void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost); void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost); -int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost); +int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, + char dynhost); /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set); diff --git a/isisd/isis_main.c b/isisd/isis_main.c index e74a9baadd..48ae760173 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -41,7 +41,6 @@ #include "qobj.h" #include "libfrr.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index c24917454a..ef0d77d6c5 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -30,7 +30,6 @@ #include "command.h" #include "log_int.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_northbound.c b/isisd/isis_northbound.c index 8b26a73975..cbe5ba7861 100644 --- a/isisd/isis_northbound.c +++ b/isisd/isis_northbound.c @@ -25,7 +25,6 @@ #include "libfrr.h" #include "linklist.h" #include "log.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 8e9302963d..afd5e2bf84 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -36,7 +36,6 @@ #include "md5.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -959,7 +958,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, /* Find the LSP in our database and compare it to this Link State header */ struct isis_lsp *lsp = - lsp_search(hdr.lsp_id, circuit->area->lspdb[level - 1]); + lsp_search(&circuit->area->lspdb[level - 1], hdr.lsp_id); int comp = 0; if (lsp) comp = lsp_compare(circuit->area->area_tag, lsp, hdr.seqno, @@ -1186,7 +1185,7 @@ dontcheckadj: memcpy(lspid, hdr.lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lspid) = 0; lsp0 = lsp_search( - lspid, circuit->area->lspdb[level - 1]); + &circuit->area->lspdb[level - 1], lspid); if (!lsp0) { zlog_debug( "Got lsp frag, while zero lsp not in database"); @@ -1199,8 +1198,8 @@ dontcheckadj: &hdr, tlvs, circuit->rcv_stream, lsp0, circuit->area, level); tlvs = NULL; - lsp_insert(lsp, - circuit->area->lspdb[level - 1]); + lsp_insert(&circuit->area->lspdb[level - 1], + lsp); } else /* exists, so we overwrite */ { lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream, @@ -1416,7 +1415,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, for (struct isis_lsp_entry *entry = entry_head; entry; entry = entry->next) { struct isis_lsp *lsp = - lsp_search(entry->id, circuit->area->lspdb[level - 1]); + lsp_search(&circuit->area->lspdb[level - 1], entry->id); bool own_lsp = !memcmp(entry->id, isis->sysid, ISIS_SYS_ID_LEN); if (lsp) { /* 7.3.15.2 b) 1) is this LSP newer */ @@ -1467,8 +1466,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lspid) = 0; lsp0 = lsp_search( - lspid, - circuit->area->lspdb[level - 1]); + &circuit->area->lspdb[level - 1], + lspid); if (!lsp0) { zlog_debug("Got lsp frag in snp, while zero not in database"); continue; @@ -1477,8 +1476,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, lsp = lsp_new(circuit->area, entry->id, entry->rem_lifetime, 0, 0, entry->checksum, lsp0, level); - lsp_insert(lsp, - circuit->area->lspdb[level - 1]); + lsp_insert(&circuit->area->lspdb[level - 1], + lsp); lsp_set_all_srmflags(lsp, false); ISIS_SET_FLAG(lsp->SSNflags, circuit); @@ -1495,8 +1494,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, * start_lsp_id and stop_lsp_id */ struct list *lsp_list = list_new(); - lsp_build_list_nonzero_ht(start_lsp_id, stop_lsp_id, lsp_list, - circuit->area->lspdb[level - 1]); + lsp_build_list_nonzero_ht(&circuit->area->lspdb[level - 1], + start_lsp_id, stop_lsp_id, lsp_list); /* Fixme: Find a better solution */ struct listnode *node, *nnode; @@ -2040,8 +2039,7 @@ static uint16_t get_max_lsp_count(uint16_t size) int send_csnp(struct isis_circuit *circuit, int level) { - if (circuit->area->lspdb[level - 1] == NULL - || dict_count(circuit->area->lspdb[level - 1]) == 0) + if (lspdb_count(&circuit->area->lspdb[level - 1]) == 0) return ISIS_OK; uint8_t pdu_type = (level == ISIS_LEVEL1) ? L1_COMPLETE_SEQ_NUM @@ -2094,7 +2092,7 @@ int send_csnp(struct isis_circuit *circuit, int level) struct isis_lsp *last_lsp; isis_tlvs_add_csnp_entries(tlvs, start, stop, num_lsps, - circuit->area->lspdb[level - 1], + &circuit->area->lspdb[level - 1], &last_lsp); /* * Update the stop lsp_id before encoding this CSNP. @@ -2215,8 +2213,7 @@ static int send_psnp(int level, struct isis_circuit *circuit) && circuit->u.bc.is_dr[level - 1]) return ISIS_OK; - if (circuit->area->lspdb[level - 1] == NULL - || dict_count(circuit->area->lspdb[level - 1]) == 0) + if (lspdb_count(&circuit->area->lspdb[level - 1]) == 0) return ISIS_OK; if (!circuit->snd_stream) @@ -2254,16 +2251,13 @@ static int send_psnp(int level, struct isis_circuit *circuit) get_max_lsp_count(STREAM_WRITEABLE(circuit->snd_stream)); while (1) { + struct isis_lsp *lsp; + tlvs = isis_alloc_tlvs(); if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) isis_tlvs_add_auth(tlvs, passwd); - for (dnode_t *dnode = - dict_first(circuit->area->lspdb[level - 1]); - dnode; dnode = dict_next(circuit->area->lspdb[level - 1], - dnode)) { - struct isis_lsp *lsp = dnode_get(dnode); - + for_each (lspdb, &circuit->area->lspdb[level - 1], lsp) { if (ISIS_CHECK_FLAG(lsp->SSNflags, circuit)) isis_tlvs_add_lsp_entry(tlvs, lsp); diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index 2f6526bc5e..824acd0ff8 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -33,7 +33,6 @@ #include "if.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_circuit.h" diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 9047707bf0..dc23e8ea49 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -32,7 +32,6 @@ #include "vty.h" #include "srcdest_table.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 1439a4229a..82005c911e 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -38,7 +38,6 @@ #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" -#include "dict.h" #include "isisd.h" #include "isis_misc.h" #include "isis_adjacency.h" diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index 3c2cf7b3fc..d63676256b 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -37,7 +37,6 @@ #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" -#include "dict.h" #include "isisd.h" #include "isis_misc.h" #include "isis_adjacency.h" diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 18eb857ec9..a28220eb26 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -39,7 +39,6 @@ #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" -#include "dict.h" #include "isisd.h" #include "isis_misc.h" #include "isis_adjacency.h" @@ -313,7 +312,7 @@ static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, memcpy(lspid, sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(lspid) = 0; LSP_FRAGMENT(lspid) = 0; - lsp = lsp_search(lspid, area->lspdb[level - 1]); + lsp = lsp_search(&area->lspdb[level - 1], lspid); if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; return NULL; @@ -870,10 +869,8 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, [spftree->level - 1], parent); lsp = lsp_search( - lsp_id, - spftree->area - ->lspdb[spftree->level - - 1]); + &spftree->area->lspdb[spftree->level- 1], + lsp_id); if (lsp == NULL || lsp->hdr.rem_lifetime == 0) zlog_warn( @@ -923,8 +920,8 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, continue; } lsp = lsp_search( - lsp_id, - spftree->area->lspdb[spftree->level - 1]); + &spftree->area->lspdb[spftree->level - 1], + lsp_id); if (lsp == NULL || lsp->hdr.rem_lifetime == 0) { zlog_warn( "ISIS-Spf: No lsp (%p) found from root " diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 3a05df3f14..3a2a52afac 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -347,8 +347,8 @@ static struct isis_lsp *lsp_for_vertex(struct isis_spftree *spftree, memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lsp_id) = 0; - dict_t *lspdb = spftree->area->lspdb[spftree->level - 1]; - struct isis_lsp *lsp = lsp_search(lsp_id, lspdb); + struct lspdb_head *lspdb = &spftree->area->lspdb[spftree->level - 1]; + struct isis_lsp *lsp = lsp_search(lspdb, lsp_id); if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 23a1f10a18..a9937b9551 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -43,7 +43,6 @@ #include "network.h" #include "sbuf.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index fbb1e5714c..bc9b514736 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -3522,26 +3522,24 @@ void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp) void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, uint8_t *stop_id, uint16_t num_lsps, - dict_t *lspdb, struct isis_lsp **last_lsp) + struct lspdb_head *head, + struct isis_lsp **last_lsp) { - dnode_t *first = dict_lower_bound(lspdb, start_id); + struct isis_lsp searchfor; + struct isis_lsp *first, *lsp; + + memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id)); + first = lspdb_find_gteq(head, &searchfor); if (!first) return; - dnode_t *last = dict_upper_bound(lspdb, stop_id); - dnode_t *curr = first; - - isis_tlvs_add_lsp_entry(tlvs, first->dict_data); - *last_lsp = first->dict_data; - - while (curr) { - curr = dict_next(lspdb, curr); - if (curr) { - isis_tlvs_add_lsp_entry(tlvs, curr->dict_data); - *last_lsp = curr->dict_data; - } - if (curr == last || tlvs->lsp_entries.count == num_lsps) + for_each_from (lspdb, head, lsp, first) { + if (memcmp(lsp->hdr.lsp_id, stop_id, sizeof(lsp->hdr.lsp_id)) + > 0 || tlvs->lsp_entries.count == num_lsps) break; + + isis_tlvs_add_lsp_entry(tlvs, lsp); + *last_lsp = lsp; } } diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index fce30d4ee7..4954d791d8 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -25,8 +25,8 @@ #include "openbsd-tree.h" #include "prefix.h" -#include "isisd/dict.h" +struct lspdb_head; struct isis_subtlvs; struct isis_area_address; @@ -355,7 +355,8 @@ bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa); void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp); void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, uint8_t *stop_id, uint16_t num_lsps, - dict_t *lspdb, struct isis_lsp **last_lsp); + struct lspdb_head *lspdb, + struct isis_lsp **last_lsp); void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs, const char *hostname); void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, diff --git a/isisd/isis_tx_queue.c b/isisd/isis_tx_queue.c index 270dcae7d0..6f46e6bec0 100644 --- a/isisd/isis_tx_queue.c +++ b/isisd/isis_tx_queue.c @@ -27,7 +27,6 @@ #include "isisd/isisd.h" #include "isisd/isis_memory.h" #include "isisd/isis_flags.h" -#include "dict.h" #include "isisd/isis_circuit.h" #include "isisd/isis_lsp.h" #include "isisd/isis_misc.h" diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index b2c0440de6..7f2061692f 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -161,13 +161,14 @@ DEFUN (show_lsp_flooding, struct isis_area *area; for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - dict_t *lspdb = area->lspdb[ISIS_LEVEL2 - 1]; + struct lspdb_head *head = &area->lspdb[ISIS_LEVEL2 - 1]; + struct isis_lsp *lsp; vty_out(vty, "Area %s:\n", area->area_tag ? area->area_tag : "null"); if (lspid) { - struct isis_lsp *lsp = lsp_for_arg(lspid, lspdb); + struct isis_lsp *lsp = lsp_for_arg(head, lspid); if (lsp) lsp_print_flooding(vty, lsp); @@ -175,9 +176,8 @@ DEFUN (show_lsp_flooding, continue; } - for (dnode_t *dnode = dict_first(lspdb); dnode; - dnode = dict_next(lspdb, dnode)) { - lsp_print_flooding(vty, dnode_get(dnode)); + for_each (lspdb, head, lsp) { + lsp_print_flooding(vty, lsp); vty_out(vty, "\n"); } } diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 79d79f8911..ad2cfe82dc 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -37,7 +37,6 @@ #include "vrf.h" #include "libfrr.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isisd.c b/isisd/isisd.c index ad02220438..172c00e268 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -38,7 +38,6 @@ #include "spf_backoff.h" #include "lib/northbound_cli.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -122,12 +121,10 @@ struct isis_area *isis_area_create(const char *area_tag) /* * intialize the databases */ - if (area->is_type & IS_LEVEL_1) { - area->lspdb[0] = lsp_db_init(); - } - if (area->is_type & IS_LEVEL_2) { - area->lspdb[1] = lsp_db_init(); - } + if (area->is_type & IS_LEVEL_1) + lsp_db_init(&area->lspdb[0]); + if (area->is_type & IS_LEVEL_2) + lsp_db_init(&area->lspdb[1]); spftree_area_init(area); @@ -268,14 +265,8 @@ int isis_area_destroy(const char *area_tag) list_delete(&area->circuit_list); } - if (area->lspdb[0] != NULL) { - lsp_db_destroy(area->lspdb[0]); - area->lspdb[0] = NULL; - } - if (area->lspdb[1] != NULL) { - lsp_db_destroy(area->lspdb[1]); - area->lspdb[1] = NULL; - } + lsp_db_fini(&area->lspdb[0]); + lsp_db_fini(&area->lspdb[1]); /* invalidate and verify to delete all routes from zebra */ isis_area_invalidate_routes(area, ISIS_LEVEL1 & ISIS_LEVEL2); @@ -1341,7 +1332,7 @@ DEFUN (show_isis_summary, return CMD_SUCCESS; } -struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb) +struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv) { char sysid[255] = {0}; uint8_t number[3]; @@ -1389,13 +1380,13 @@ struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb) * hostname.- */ if (sysid2buff(lspid, sysid)) { - lsp = lsp_search(lspid, lspdb); + lsp = lsp_search(head, lspid); } else if ((dynhn = dynhn_find_by_name(sysid))) { memcpy(lspid, dynhn->id, ISIS_SYS_ID_LEN); - lsp = lsp_search(lspid, lspdb); + lsp = lsp_search(head, lspid); } else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) { memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN); - lsp = lsp_search(lspid, lspdb); + lsp = lsp_search(head, lspid); } return lsp; @@ -1432,9 +1423,8 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level) area->area_tag ? area->area_tag : "null"); for (level = 0; level < ISIS_LEVELS; level++) { - if (area->lspdb[level] - && dict_count(area->lspdb[level]) > 0) { - lsp = lsp_for_arg(argv, area->lspdb[level]); + if (lspdb_count(&area->lspdb[level]) > 0) { + lsp = lsp_for_arg(&area->lspdb[level], argv); if (lsp != NULL || argv == NULL) { vty_out(vty, @@ -1456,7 +1446,7 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level) area->dynhostname); } else if (argv == NULL) { lsp_count = lsp_print_all( - vty, area->lspdb[level], + vty, &area->lspdb[level], ui_level, area->dynhostname); vty_out(vty, " %u LSPs\n\n", @@ -1696,10 +1686,7 @@ static void area_resign_level(struct isis_area *area, int level) isis_area_invalidate_routes(area, level); isis_area_verify_routes(area); - if (area->lspdb[level - 1]) { - lsp_db_destroy(area->lspdb[level - 1]); - area->lspdb[level - 1] = NULL; - } + lsp_db_fini(&area->lspdb[level - 1]); for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { if (area->spftree[tree][level - 1]) { @@ -1735,8 +1722,7 @@ void isis_area_is_type_set(struct isis_area *area, int is_type) if (is_type == IS_LEVEL_2) area_resign_level(area, IS_LEVEL_1); - if (area->lspdb[1] == NULL) - area->lspdb[1] = lsp_db_init(); + lsp_db_init(&area->lspdb[1]); break; case IS_LEVEL_1_AND_2: @@ -1750,8 +1736,7 @@ void isis_area_is_type_set(struct isis_area *area, int is_type) if (is_type == IS_LEVEL_1) area_resign_level(area, IS_LEVEL_2); - if (area->lspdb[0] == NULL) - area->lspdb[0] = lsp_db_init(); + lsp_db_init(&area->lspdb[0]); break; default: diff --git a/isisd/isisd.h b/isisd/isisd.h index fb879395c1..0237384137 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -31,7 +31,7 @@ #include "isisd/isis_pdu_counter.h" #include "isisd/isis_circuit.h" #include "isis_flags.h" -#include "dict.h" +#include "isis_lsp.h" #include "isis_memory.h" #include "qobj.h" @@ -107,7 +107,7 @@ enum isis_metric_style { struct isis_area { struct isis *isis; /* back pointer */ - dict_t *lspdb[ISIS_LEVELS]; /* link-state dbs */ + struct lspdb_head lspdb[ISIS_LEVELS]; /* link-state dbs */ struct isis_spftree *spftree[SPFTREE_COUNT][ISIS_LEVELS]; #define DEFAULT_LSP_MTU 1497 unsigned int lsp_mtu; /* Size of LSPs to generate */ @@ -195,7 +195,7 @@ struct isis_area *isis_area_lookup(const char *); int isis_area_get(struct vty *vty, const char *area_tag); int isis_area_destroy(const char *area_tag); void print_debug(struct vty *, int, int); -struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb); +struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv); void isis_area_invalidate_routes(struct isis_area *area, int levels); void isis_area_verify_routes(struct isis_area *area); diff --git a/isisd/subdir.am b/isisd/subdir.am index 4371d5993a..bae56309cf 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -25,7 +25,6 @@ dist_examples_DATA += isisd/fabricd.conf.sample endif noinst_HEADERS += \ - isisd/dict.h \ isisd/isis_adjacency.h \ isisd/isis_bfd.h \ isisd/isis_circuit.h \ @@ -61,7 +60,6 @@ noinst_HEADERS += \ # end LIBISIS_SOURCES = \ - isisd/dict.c \ isisd/isis_adjacency.c \ isisd/isis_bfd.c \ isisd/isis_circuit.c \ diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c index b9c6f2bbb2..f0baa482c7 100644 --- a/tests/isisd/test_isis_lspdb.c +++ b/tests/isisd/test_isis_lspdb.c @@ -28,21 +28,22 @@ static void test_lsp_build_list_nonzero_ht(void) area->lsp_mtu = 1500; - dict_t *lspdb = lsp_db_init(); + struct lspdb_head _lspdb, *lspdb = &_lspdb; + lsp_db_init(&_lspdb); struct isis_lsp *lsp1 = lsp_new(area, lsp_id1, 6000, 0, 0, 0, NULL, ISIS_LEVEL2); - lsp_insert(lsp1, lspdb); + lsp_insert(lspdb, lsp1); struct isis_lsp *lsp2 = lsp_new(area, lsp_id2, 6000, 0, 0, 0, NULL, ISIS_LEVEL2); - lsp_insert(lsp2, lspdb); + lsp_insert(lspdb, lsp2); struct list *list = list_new(); - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 1); assert(listgetdata(listhead(list)) == lsp1); list_delete_all_node(list); @@ -50,7 +51,7 @@ static void test_lsp_build_list_nonzero_ht(void) lsp_id_end[5] = 0x03; lsp_id_end[6] = 0x00; - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 2); assert(listgetdata(listhead(list)) == lsp1); assert(listgetdata(listtail(list)) == lsp2); @@ -58,7 +59,7 @@ static void test_lsp_build_list_nonzero_ht(void) memcpy(lsp_id1, lsp_id2, sizeof(lsp_id1)); - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 1); assert(listgetdata(listhead(list)) == lsp2); list_delete_all_node(list); @@ -66,13 +67,13 @@ static void test_lsp_build_list_nonzero_ht(void) lsp_id1[5] = 0x03; lsp_id_end[5] = 0x04; - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 0); list_delete_all_node(list); lsp_id1[5] = 0x00; - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 2); assert(listgetdata(listhead(list)) == lsp1); assert(listgetdata(listtail(list)) == lsp2); From a274fef86899e1907b81f3c28bf5e3f978eea6ad Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 21 Apr 2019 18:17:45 +0200 Subject: [PATCH 21/64] bgpd: replace ADV_FIFO with DECLARE_LIST The FIFO_* stuff in lib/fifo.h is no different from a simple unsorted list. Just use DECLARE_LIST here so we can get rid of FIFO_*. Signed-off-by: David Lamparter --- bgpd/bgp_advertise.c | 6 ++--- bgpd/bgp_advertise.h | 51 +++++++--------------------------------- bgpd/bgp_updgrp.c | 6 ++--- bgpd/bgp_updgrp.h | 6 ++--- bgpd/bgp_updgrp_adv.c | 12 +++++----- bgpd/bgp_updgrp_packet.c | 8 +++---- 6 files changed, 28 insertions(+), 61 deletions(-) diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 05eeeca156..c9f68d037b 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -241,9 +241,9 @@ void bgp_sync_init(struct peer *peer) FOREACH_AFI_SAFI (afi, safi) { sync = XCALLOC(MTYPE_BGP_SYNCHRONISE, sizeof(struct bgp_synchronize)); - BGP_ADV_FIFO_INIT(&sync->update); - BGP_ADV_FIFO_INIT(&sync->withdraw); - BGP_ADV_FIFO_INIT(&sync->withdraw_low); + bgp_adv_fifo_init(&sync->update); + bgp_adv_fifo_init(&sync->withdraw); + bgp_adv_fifo_init(&sync->withdraw_low); peer->sync[afi][safi] = sync; } } diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index 9aa5a0eaff..cc845b93e7 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -21,17 +21,12 @@ #ifndef _QUAGGA_BGP_ADVERTISE_H #define _QUAGGA_BGP_ADVERTISE_H -#include +#include "lib/typesafe.h" + +PREDECL_LIST(bgp_adv_fifo) struct update_subgroup; -/* BGP advertise FIFO. */ -struct bgp_advertise_fifo { - struct bgp_advertise *next; - struct bgp_advertise *prev; - uint32_t count; -}; - /* BGP advertise attribute. */ struct bgp_advertise_attr { /* Head of advertisement pointer. */ @@ -46,7 +41,7 @@ struct bgp_advertise_attr { struct bgp_advertise { /* FIFO for advertisement. */ - struct bgp_advertise_fifo fifo; + struct bgp_adv_fifo_item fifo; /* Link list for same attribute advertise. */ struct bgp_advertise *next; @@ -65,6 +60,8 @@ struct bgp_advertise { struct bgp_path_info *pathi; }; +DECLARE_LIST(bgp_adv_fifo, struct bgp_advertise, fifo) + /* BGP adjacency out. */ struct bgp_adj_out { /* RB Tree of adjacency entries */ @@ -110,9 +107,9 @@ struct bgp_adj_in { /* BGP advertisement list. */ struct bgp_synchronize { - struct bgp_advertise_fifo update; - struct bgp_advertise_fifo withdraw; - struct bgp_advertise_fifo withdraw_low; + struct bgp_adv_fifo_head update; + struct bgp_adv_fifo_head withdraw; + struct bgp_adv_fifo_head withdraw_low; }; /* BGP adjacency linked list. */ @@ -138,36 +135,6 @@ struct bgp_synchronize { #define BGP_ADJ_IN_ADD(N, A) BGP_PATH_INFO_ADD(N, A, adj_in) #define BGP_ADJ_IN_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_in) -#define BGP_ADV_FIFO_ADD(F, N) \ - do { \ - FIFO_ADD((F), (N)); \ - (F)->count++; \ - } while (0) - -#define BGP_ADV_FIFO_DEL(F, N) \ - do { \ - FIFO_DEL((N)); \ - (F)->count--; \ - } while (0) - -#define BGP_ADV_FIFO_INIT(F) \ - do { \ - FIFO_INIT((F)); \ - (F)->count = 0; \ - } while (0) - -#define BGP_ADV_FIFO_COUNT(F) (F)->count - -#define BGP_ADV_FIFO_EMPTY(F) \ - (((struct bgp_advertise_fifo *)(F))->next \ - == (struct bgp_advertise *)(F)) - -#define BGP_ADV_FIFO_HEAD(F) \ - ((((struct bgp_advertise_fifo *)(F))->next \ - == (struct bgp_advertise *)(F)) \ - ? NULL \ - : (F)->next) - /* Prototypes. */ extern int bgp_adj_out_lookup(struct peer *, struct bgp_node *, uint32_t); extern void bgp_adj_in_set(struct bgp_node *, struct peer *, struct attr *, diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 49a435120d..57717bf59b 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -83,9 +83,9 @@ static void sync_init(struct update_subgroup *subgrp) { subgrp->sync = XCALLOC(MTYPE_BGP_SYNCHRONISE, sizeof(struct bgp_synchronize)); - BGP_ADV_FIFO_INIT(&subgrp->sync->update); - BGP_ADV_FIFO_INIT(&subgrp->sync->withdraw); - BGP_ADV_FIFO_INIT(&subgrp->sync->withdraw_low); + bgp_adv_fifo_init(&subgrp->sync->update); + bgp_adv_fifo_init(&subgrp->sync->withdraw); + bgp_adv_fifo_init(&subgrp->sync->withdraw_low); subgrp->hash = hash_create(baa_hash_key, baa_hash_cmp, "BGP SubGroup Hash"); diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h index 6b3bf9d1f7..39e67bf608 100644 --- a/bgpd/bgp_updgrp.h +++ b/bgpd/bgp_updgrp.h @@ -590,9 +590,9 @@ static inline void bgp_announce_peer(struct peer *peer) */ static inline int advertise_list_is_empty(struct update_subgroup *subgrp) { - if (!BGP_ADV_FIFO_EMPTY(&subgrp->sync->update) - || !BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw) - || !BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw_low)) { + if (bgp_adv_fifo_count(&subgrp->sync->update) + || bgp_adv_fifo_count(&subgrp->sync->withdraw) + || bgp_adv_fifo_count(&subgrp->sync->withdraw_low)) { return 0; } diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 3870df593f..b64c51f341 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -422,7 +422,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp, struct bgp_advertise *adv; struct bgp_advertise_attr *baa; struct bgp_advertise *next; - struct bgp_advertise_fifo *fhead; + struct bgp_adv_fifo_head *fhead; adv = adj->adv; baa = adv->baa; @@ -444,7 +444,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp, /* Unlink myself from advertisement FIFO. */ - BGP_ADV_FIFO_DEL(fhead, adv); + bgp_adv_fifo_del(fhead, adv); /* Free memory. */ bgp_advertise_free(adj->adv); @@ -507,7 +507,7 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn, * If the update adv list is empty, trigger the member peers' * mrai timers so the socket writes can happen. */ - if (BGP_ADV_FIFO_EMPTY(&subgrp->sync->update)) { + if (!bgp_adv_fifo_count(&subgrp->sync->update)) { struct peer_af *paf; SUBGRP_FOREACH_PEER (subgrp, paf) { @@ -515,7 +515,7 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn, } } - BGP_ADV_FIFO_ADD(&subgrp->sync->update, &adv->fifo); + bgp_adv_fifo_add_tail(&subgrp->sync->update, adv); subgrp->version = max(subgrp->version, rn->version); } @@ -550,11 +550,11 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn, /* Note if we need to trigger a packet write */ trigger_write = - BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw); + !bgp_adv_fifo_count(&subgrp->sync->withdraw); /* Add to synchronization entry for withdraw * announcement. */ - BGP_ADV_FIFO_ADD(&subgrp->sync->withdraw, &adv->fifo); + bgp_adv_fifo_add_tail(&subgrp->sync->withdraw, adv); if (trigger_write) subgroup_trigger_write(subgrp); diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 66e306cba2..3556b236a7 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -664,11 +664,11 @@ int subgroup_packets_to_build(struct update_subgroup *subgrp) if (!subgrp) return 0; - adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->withdraw); + adv = bgp_adv_fifo_first(&subgrp->sync->withdraw); if (adv) return 1; - adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->update); + adv = bgp_adv_fifo_first(&subgrp->sync->update); if (adv) return 1; @@ -725,7 +725,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) addpath_encode = bgp_addpath_encode_tx(peer, afi, safi); addpath_overhead = addpath_encode ? BGP_ADDPATH_ID_LEN : 0; - adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->update); + adv = bgp_adv_fifo_first(&subgrp->sync->update); while (adv) { assert(adv->rn); rn = adv->rn; @@ -966,7 +966,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) addpath_encode = bgp_addpath_encode_tx(peer, afi, safi); addpath_overhead = addpath_encode ? BGP_ADDPATH_ID_LEN : 0; - while ((adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->withdraw)) != NULL) { + while ((adv = bgp_adv_fifo_first(&subgrp->sync->withdraw)) != NULL) { assert(adv->rn); adj = adv->adj; rn = adv->rn; From 41397f2e6223217c38b273890b3674c750cda67a Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 21 Apr 2019 18:27:08 +0200 Subject: [PATCH 22/64] bgpd: replace label pool fifo with DECLARE_LIST Again, the FIFO_* stuff in lib/fifo.h is no different from a simple unsorted list. Just use DECLARE_LIST here so we can get rid of FIFO_*. Signed-off-by: David Lamparter --- bgpd/bgp_labelpool.c | 62 +++++++++++--------------------------------- bgpd/bgp_labelpool.h | 4 ++- 2 files changed, 18 insertions(+), 48 deletions(-) diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c index 69dd0f9dac..71c0c8c7c6 100644 --- a/bgpd/bgp_labelpool.c +++ b/bgpd/bgp_labelpool.c @@ -25,7 +25,6 @@ #include "stream.h" #include "mpls.h" #include "vty.h" -#include "fifo.h" #include "linklist.h" #include "skiplist.h" #include "workqueue.h" @@ -50,34 +49,10 @@ static struct labelpool *lp; #define LP_CHUNK_SIZE 50 DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk") -DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO") +DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO item") DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment") DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback") -#define LABEL_FIFO_ADD(F, N) \ - do { \ - FIFO_ADD((F), (N)); \ - (F)->count++; \ - } while (0) - -#define LABEL_FIFO_DEL(F, N) \ - do { \ - FIFO_DEL((N)); \ - (F)->count--; \ - } while (0) - -#define LABEL_FIFO_INIT(F) \ - do { \ - FIFO_INIT((F)); \ - (F)->count = 0; \ - } while (0) - -#define LABEL_FIFO_COUNT(F) ((F)->count) - -#define LABEL_FIFO_EMPTY(F) FIFO_EMPTY(F) - -#define LABEL_FIFO_HEAD(F) ((F)->next == (F) ? NULL : (F)->next) - struct lp_chunk { uint32_t first; uint32_t last; @@ -98,15 +73,13 @@ struct lp_lcb { int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); }; -/* XXX same first elements as "struct fifo" */ struct lp_fifo { - struct lp_fifo *next; - struct lp_fifo *prev; - - uint32_t count; + struct lp_fifo_item fifo; struct lp_lcb lcb; }; +DECLARE_LIST(lp_fifo, struct lp_fifo, fifo) + struct lp_cbq_item { int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); int type; @@ -199,8 +172,7 @@ void bgp_lp_init(struct thread_master *master, struct labelpool *pool) lp->inuse = skiplist_new(0, NULL, NULL); lp->chunks = list_new(); lp->chunks->del = lp_chunk_free; - lp->requests = XCALLOC(MTYPE_BGP_LABEL_FIFO, sizeof(struct lp_fifo)); - LABEL_FIFO_INIT(lp->requests); + lp_fifo_init(&lp->requests); lp->callback_q = work_queue_new(master, "label callbacks"); lp->callback_q->spec.workfunc = lp_cbq_docallback; @@ -223,13 +195,9 @@ void bgp_lp_finish(void) list_delete(&lp->chunks); - while ((lf = LABEL_FIFO_HEAD(lp->requests))) { - - LABEL_FIFO_DEL(lp->requests, lf); + while ((lf = lp_fifo_pop(&lp->requests))) XFREE(MTYPE_BGP_LABEL_FIFO, lf); - } - XFREE(MTYPE_BGP_LABEL_FIFO, lp->requests); - lp->requests = NULL; + lp_fifo_fini(&lp->requests); work_queue_free_and_null(&lp->callback_q); @@ -385,9 +353,9 @@ void bgp_lp_get( sizeof(struct lp_fifo)); lf->lcb = *lcb; - LABEL_FIFO_ADD(lp->requests, lf); + lp_fifo_add_tail(&lp->requests, lf); - if (LABEL_FIFO_COUNT(lp->requests) > lp->pending_count) { + if (lp_fifo_count(&lp->requests) > lp->pending_count) { if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) { lp->pending_count += LP_CHUNK_SIZE; return; @@ -441,11 +409,11 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) lp->pending_count -= (last - first + 1); if (debug) { - zlog_debug("%s: %u pending requests", __func__, - LABEL_FIFO_COUNT(lp->requests)); + zlog_debug("%s: %zu pending requests", __func__, + lp_fifo_count(&lp->requests)); } - while ((lf = LABEL_FIFO_HEAD(lp->requests))) { + while ((lf = lp_fifo_first(&lp->requests))) { struct lp_lcb *lcb; void *labelid = lf->lcb.labelid; @@ -504,7 +472,7 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) work_queue_add(lp->callback_q, q); finishedrequest: - LABEL_FIFO_DEL(lp->requests, lf); + lp_fifo_del(&lp->requests, lf); XFREE(MTYPE_BGP_LABEL_FIFO, lf); } } @@ -533,7 +501,7 @@ void bgp_lp_event_zebra_up(void) /* * Get label chunk allocation request dispatched to zebra */ - labels_needed = LABEL_FIFO_COUNT(lp->requests) + + labels_needed = lp_fifo_count(&lp->requests) + skiplist_count(lp->inuse); /* round up */ @@ -588,7 +556,7 @@ void bgp_lp_event_zebra_up(void) sizeof(struct lp_fifo)); lf->lcb = *lcb; - LABEL_FIFO_ADD(lp->requests, lf); + lp_fifo_add_tail(&lp->requests, lf); } skiplist_delete_first(lp->inuse); diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h index 0507e65489..eaa3fce20b 100644 --- a/bgpd/bgp_labelpool.h +++ b/bgpd/bgp_labelpool.h @@ -31,11 +31,13 @@ #define LP_TYPE_VRF 0x00000001 #define LP_TYPE_BGP_LU 0x00000002 +PREDECL_LIST(lp_fifo) + struct labelpool { struct skiplist *ledger; /* all requests */ struct skiplist *inuse; /* individual labels */ struct list *chunks; /* granted by zebra */ - struct lp_fifo *requests; /* blocked on zebra */ + struct lp_fifo_head requests; /* blocked on zebra */ struct work_queue *callback_q; uint32_t pending_count; /* requested from zebra */ }; From a297301e89f49fd36f7651ff4c262923e731a46e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 21 Apr 2019 18:28:01 +0200 Subject: [PATCH 23/64] lib: remove fifo implementation --- lib/fifo.h | 66 ------------------------------------------- lib/subdir.am | 1 - tests/lib/cxxcompat.c | 1 - 3 files changed, 68 deletions(-) delete mode 100644 lib/fifo.h diff --git a/lib/fifo.h b/lib/fifo.h deleted file mode 100644 index 6f9c59b5c1..0000000000 --- a/lib/fifo.h +++ /dev/null @@ -1,66 +0,0 @@ -/* FIFO common header. - * Copyright (C) 2015 Kunihiro Ishiguro - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __LIB_FIFO_H__ -#define __LIB_FIFO_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -/* FIFO -- first in first out structure and macros. */ -struct fifo { - struct fifo *next; - struct fifo *prev; -}; - -#define FIFO_INIT(F) \ - do { \ - struct fifo *Xfifo = (struct fifo *)(F); \ - Xfifo->next = Xfifo->prev = Xfifo; \ - } while (0) - -#define FIFO_ADD(F, N) \ - do { \ - struct fifo *Xfifo = (struct fifo *)(F); \ - struct fifo *Xnode = (struct fifo *)(N); \ - Xnode->next = Xfifo; \ - Xnode->prev = Xfifo->prev; \ - Xfifo->prev = Xfifo->prev->next = Xnode; \ - } while (0) - -#define FIFO_DEL(N) \ - do { \ - struct fifo *Xnode = (struct fifo *)(N); \ - Xnode->prev->next = Xnode->next; \ - Xnode->next->prev = Xnode->prev; \ - } while (0) - -#define FIFO_HEAD(F) \ - ((((struct fifo *)(F))->next == (struct fifo *)(F)) ? NULL : (F)->next) - -#define FIFO_EMPTY(F) (((struct fifo *)(F))->next == (struct fifo *)(F)) - -#define FIFO_TOP(F) (FIFO_EMPTY(F) ? NULL : ((struct fifo *)(F))->next) - -#ifdef __cplusplus -} -#endif - -#endif /* __LIB_FIFO_H__ */ diff --git a/lib/subdir.am b/lib/subdir.am index 9bf02be9c3..38688a2470 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -148,7 +148,6 @@ pkginclude_HEADERS += \ lib/debug.h \ lib/distribute.h \ lib/ferr.h \ - lib/fifo.h \ lib/filter.h \ lib/freebsd-queue.h \ lib/frr_pthread.h \ diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index b71361a23d..d1278cef22 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -32,7 +32,6 @@ #include "lib/debug.h" #include "lib/distribute.h" #include "lib/ferr.h" -#include "lib/fifo.h" #include "lib/filter.h" #include "lib/frr_pthread.h" #include "lib/frratomic.h" From faf6559a00e148c47a091eaec80314b3b83be001 Mon Sep 17 00:00:00 2001 From: Faicker Mo Date: Mon, 29 Apr 2019 17:28:42 +0800 Subject: [PATCH 24/64] bpgd: Add the end of newline of show bgp table json output Signed-off-by: Faicker Mo --- bgpd/bgp_route.c | 1 + 1 file changed, 1 insertion(+) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 063cc24dc1..fc6798fdfc 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -9012,6 +9012,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, unsigned long i; for (i = 0; i < *json_header_depth; ++i) vty_out(vty, " } "); + vty_out(vty, "\n"); } } else { if (is_last) { From 7629b6b79c7a2de2e83633b87ec420b30ed4173b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 29 Apr 2019 21:18:48 +0200 Subject: [PATCH 25/64] Revert "lib: remove pqueue_*" This reverts commit 798ac49d06b6619adb4c5ac765b092397bc50a6c. --- lib/pqueue.c | 185 +++++++++++++++++++++++++++++ lib/pqueue.h | 54 +++++++++ lib/subdir.am | 2 + tests/lib/cxxcompat.c | 1 + tests/lib/test_timer_correctness.c | 1 + tests/lib/test_timer_performance.c | 1 + 6 files changed, 244 insertions(+) create mode 100644 lib/pqueue.c create mode 100644 lib/pqueue.h diff --git a/lib/pqueue.c b/lib/pqueue.c new file mode 100644 index 0000000000..87b54a681a --- /dev/null +++ b/lib/pqueue.c @@ -0,0 +1,185 @@ +/* Priority queue functions. + * Copyright (C) 2003 Yasuhiro Ohara + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "memory.h" +#include "pqueue.h" + +DEFINE_MTYPE_STATIC(LIB, PQUEUE, "Priority queue") +DEFINE_MTYPE_STATIC(LIB, PQUEUE_DATA, "Priority queue data") + +/* priority queue using heap sort */ + +/* pqueue->cmp() controls the order of sorting (i.e, ascending or + descending). If you want the left node to move upper of the heap + binary tree, make cmp() to return less than 0. for example, if cmp + (10, 20) returns -1, the sorting is ascending order. if cmp (10, + 20) returns 1, the sorting is descending order. if cmp (10, 20) + returns 0, this library does not do sorting (which will not be what + you want). To be brief, if the contents of cmp_func (left, right) + is left - right, dequeue () returns the smallest node. Otherwise + (if the contents is right - left), dequeue () returns the largest + node. */ + +#define DATA_SIZE (sizeof (void *)) +#define PARENT_OF(x) ((x - 1) / 2) +#define LEFT_OF(x) (2 * x + 1) +#define RIGHT_OF(x) (2 * x + 2) +#define HAVE_CHILD(x,q) (x < (q)->size / 2) + +void trickle_up(int index, struct pqueue *queue) +{ + void *tmp; + + /* Save current node as tmp node. */ + tmp = queue->array[index]; + + /* Continue until the node reaches top or the place where the parent + node should be upper than the tmp node. */ + while (index > 0 + && (*queue->cmp)(tmp, queue->array[PARENT_OF(index)]) < 0) { + /* actually trickle up */ + queue->array[index] = queue->array[PARENT_OF(index)]; + if (queue->update != NULL) + (*queue->update)(queue->array[index], index); + index = PARENT_OF(index); + } + + /* Restore the tmp node to appropriate place. */ + queue->array[index] = tmp; + if (queue->update != NULL) + (*queue->update)(tmp, index); +} + +void trickle_down(int index, struct pqueue *queue) +{ + void *tmp; + int which; + + /* Save current node as tmp node. */ + tmp = queue->array[index]; + + /* Continue until the node have at least one (left) child. */ + while (HAVE_CHILD(index, queue)) { + /* If right child exists, and if the right child is more proper + to be moved upper. */ + if (RIGHT_OF(index) < queue->size + && (*queue->cmp)(queue->array[LEFT_OF(index)], + queue->array[RIGHT_OF(index)]) + > 0) + which = RIGHT_OF(index); + else + which = LEFT_OF(index); + + /* If the tmp node should be upper than the child, break. */ + if ((*queue->cmp)(queue->array[which], tmp) > 0) + break; + + /* Actually trickle down the tmp node. */ + queue->array[index] = queue->array[which]; + if (queue->update != NULL) + (*queue->update)(queue->array[index], index); + index = which; + } + + /* Restore the tmp node to appropriate place. */ + queue->array[index] = tmp; + if (queue->update != NULL) + (*queue->update)(tmp, index); +} + +struct pqueue *pqueue_create(void) +{ + struct pqueue *queue; + + queue = XCALLOC(MTYPE_PQUEUE, sizeof(struct pqueue)); + + queue->array = + XCALLOC(MTYPE_PQUEUE_DATA, DATA_SIZE * PQUEUE_INIT_ARRAYSIZE); + queue->array_size = PQUEUE_INIT_ARRAYSIZE; + + /* By default we want nothing to happen when a node changes. */ + queue->update = NULL; + return queue; +} + +void pqueue_delete(struct pqueue *queue) +{ + XFREE(MTYPE_PQUEUE_DATA, queue->array); + XFREE(MTYPE_PQUEUE, queue); +} + +static int pqueue_expand(struct pqueue *queue) +{ + void **newarray; + + newarray = + XCALLOC(MTYPE_PQUEUE_DATA, queue->array_size * DATA_SIZE * 2); + + memcpy(newarray, queue->array, queue->array_size * DATA_SIZE); + + XFREE(MTYPE_PQUEUE_DATA, queue->array); + queue->array = newarray; + queue->array_size *= 2; + + return 1; +} + +void pqueue_enqueue(void *data, struct pqueue *queue) +{ + if (queue->size + 2 >= queue->array_size && !pqueue_expand(queue)) + return; + + queue->array[queue->size] = data; + if (queue->update != NULL) + (*queue->update)(data, queue->size); + trickle_up(queue->size, queue); + queue->size++; +} + +void *pqueue_dequeue(struct pqueue *queue) +{ + void *data = queue->array[0]; + queue->array[0] = queue->array[--queue->size]; + trickle_down(0, queue); + return data; +} + +void pqueue_remove_at(int index, struct pqueue *queue) +{ + queue->array[index] = queue->array[--queue->size]; + + if (index > 0 + && (*queue->cmp)(queue->array[index], + queue->array[PARENT_OF(index)]) + < 0) { + trickle_up(index, queue); + } else { + trickle_down(index, queue); + } +} + +void pqueue_remove(void *data, struct pqueue *queue) +{ + for (int i = 0; i < queue->size; i++) + if (queue->array[i] == data) + pqueue_remove_at(i, queue); +} diff --git a/lib/pqueue.h b/lib/pqueue.h new file mode 100644 index 0000000000..032ee9db4c --- /dev/null +++ b/lib/pqueue.h @@ -0,0 +1,54 @@ +/* Priority queue functions. + * Copyright (C) 2003 Yasuhiro Ohara + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_PQUEUE_H +#define _ZEBRA_PQUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct pqueue { + void **array; + int array_size; + int size; + + int (*cmp)(void *, void *); + void (*update)(void *node, int actual_position); +}; + +#define PQUEUE_INIT_ARRAYSIZE 32 + +extern struct pqueue *pqueue_create(void); +extern void pqueue_delete(struct pqueue *queue); + +extern void pqueue_enqueue(void *data, struct pqueue *queue); +extern void *pqueue_dequeue(struct pqueue *queue); +extern void pqueue_remove_at(int index, struct pqueue *queue); +extern void pqueue_remove(void *data, struct pqueue *queue); + +extern void trickle_down(int index, struct pqueue *queue); +extern void trickle_up(int index, struct pqueue *queue); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_PQUEUE_H */ diff --git a/lib/subdir.am b/lib/subdir.am index 38688a2470..7027f3f0da 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -58,6 +58,7 @@ lib_libfrr_la_SOURCES = \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ + lib/pqueue.c \ lib/prefix.c \ lib/privs.c \ lib/ptm_lib.c \ @@ -186,6 +187,7 @@ pkginclude_HEADERS += \ lib/openbsd-queue.h \ lib/openbsd-tree.h \ lib/plist.h \ + lib/pqueue.h \ lib/prefix.h \ lib/privs.h \ lib/ptm_lib.h \ diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index d1278cef22..12c333c8b6 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -71,6 +71,7 @@ #include "lib/openbsd-tree.h" #include "lib/pbr.h" #include "lib/plist.h" +#include "lib/pqueue.h" #include "lib/prefix.h" #include "lib/privs.h" #include "lib/ptm_lib.h" diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c index cbf9b05546..43e79ba9d0 100644 --- a/tests/lib/test_timer_correctness.c +++ b/tests/lib/test_timer_correctness.c @@ -28,6 +28,7 @@ #include #include "memory.h" +#include "pqueue.h" #include "prng.h" #include "thread.h" diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c index 2960e0d81e..d5f4badc85 100644 --- a/tests/lib/test_timer_performance.c +++ b/tests/lib/test_timer_performance.c @@ -28,6 +28,7 @@ #include #include "thread.h" +#include "pqueue.h" #include "prng.h" #define SCHEDULE_TIMERS 1000000 From 8390828dacec2befdd8f71c7d08b17963a8a340a Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 29 Apr 2019 21:18:55 +0200 Subject: [PATCH 26/64] Revert "lib: use DECLARE_SKIPLIST for timers instead of pqueue" This reverts commit 7c198e4e1ac07c043ecfc573aed9f1d107f87234. --- lib/thread.c | 94 ++++++++++++++++++++++++++++++++++------------------ lib/thread.h | 7 ++-- 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/lib/thread.c b/lib/thread.c index 12c65fbabd..5ca859a74d 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -27,6 +27,7 @@ #include "memory.h" #include "log.h" #include "hash.h" +#include "pqueue.h" #include "command.h" #include "sigevent.h" #include "network.h" @@ -41,18 +42,6 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") DECLARE_LIST(thread_list, struct thread, threaditem) -static int thread_timer_cmp(const struct thread *a, const struct thread *b) -{ - if (timercmp(&a->u.sands, &b->u.sands, <)) - return -1; - if (timercmp(&a->u.sands, &b->u.sands, >)) - return 1; - return 0; -} - -DECLARE_SKIPLIST_NONUNIQ(thread_timer_list, struct thread, timeritem, - thread_timer_cmp) - #if defined(__APPLE__) #include #include @@ -388,6 +377,25 @@ void thread_cmd_init(void) /* CLI end ------------------------------------------------------------------ */ +static int thread_timer_cmp(void *a, void *b) +{ + struct thread *thread_a = a; + struct thread *thread_b = b; + + if (timercmp(&thread_a->u.sands, &thread_b->u.sands, <)) + return -1; + if (timercmp(&thread_a->u.sands, &thread_b->u.sands, >)) + return 1; + return 0; +} + +static void thread_timer_update(void *node, int actual_position) +{ + struct thread *thread = node; + + thread->index = actual_position; +} + static void cancelreq_del(void *cr) { XFREE(MTYPE_TMP, cr); @@ -432,7 +440,11 @@ struct thread_master *thread_master_create(const char *name) thread_list_init(&rv->event); thread_list_init(&rv->ready); thread_list_init(&rv->unuse); - thread_timer_list_init(&rv->timer); + + /* Initialize the timer queues */ + rv->timer = pqueue_create(); + rv->timer->cmp = thread_timer_cmp; + rv->timer->update = thread_timer_update; /* Initialize thread_fetch() settings */ rv->spin = true; @@ -530,6 +542,16 @@ static void thread_array_free(struct thread_master *m, XFREE(MTYPE_THREAD_POLL, thread_array); } +static void thread_queue_free(struct thread_master *m, struct pqueue *queue) +{ + int i; + + for (i = 0; i < queue->size; i++) + thread_free(m, queue->array[i]); + + pqueue_delete(queue); +} + /* * thread_master_free_unused * @@ -552,8 +574,6 @@ void thread_master_free_unused(struct thread_master *m) /* Stop thread scheduler. */ void thread_master_free(struct thread_master *m) { - struct thread *t; - pthread_mutex_lock(&masters_mtx); { listnode_delete(masters, m); @@ -565,8 +585,7 @@ void thread_master_free(struct thread_master *m) thread_array_free(m, m->read); thread_array_free(m, m->write); - while ((t = thread_timer_list_pop(&m->timer))) - thread_free(m, t); + thread_queue_free(m, m->timer); thread_list_free(m, &m->event); thread_list_free(m, &m->ready); thread_list_free(m, &m->unuse); @@ -640,6 +659,7 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, thread->add_type = type; thread->master = m; thread->arg = arg; + thread->index = -1; thread->yield = THREAD_YIELD_TIME_SLOT; /* default */ thread->ref = NULL; @@ -798,6 +818,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m, struct thread **t_ptr, debugargdef) { struct thread *thread; + struct pqueue *queue; assert(m != NULL); @@ -813,6 +834,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m, return NULL; } + queue = m->timer; thread = thread_get(m, type, func, arg, debugargpass); pthread_mutex_lock(&thread->mtx); @@ -820,7 +842,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m, monotime(&thread->u.sands); timeradd(&thread->u.sands, time_relative, &thread->u.sands); - thread_timer_list_add(&m->timer, thread); + pqueue_enqueue(thread, queue); if (t_ptr) { *t_ptr = thread; thread->ref = t_ptr; @@ -997,6 +1019,7 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) static void do_thread_cancel(struct thread_master *master) { struct thread_list_head *list = NULL; + struct pqueue *queue = NULL; struct thread **thread_array = NULL; struct thread *thread; @@ -1052,7 +1075,7 @@ static void do_thread_cancel(struct thread_master *master) thread_array = master->write; break; case THREAD_TIMER: - thread_timer_list_del(&master->timer, thread); + queue = master->timer; break; case THREAD_EVENT: list = &master->event; @@ -1065,10 +1088,16 @@ static void do_thread_cancel(struct thread_master *master) break; } - if (list) { + if (queue) { + assert(thread->index >= 0); + assert(thread == queue->array[thread->index]); + pqueue_remove_at(thread->index, queue); + } else if (list) { thread_list_del(list, thread); } else if (thread_array) { thread_array[thread->u.fd] = NULL; + } else { + assert(!"Thread should be either in queue or list or array!"); } if (thread->ref) @@ -1186,15 +1215,15 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread, } /* ------------------------------------------------------------------------- */ -static struct timeval *thread_timer_wait(struct thread_timer_list_head *timers, +static struct timeval *thread_timer_wait(struct pqueue *queue, struct timeval *timer_val) { - if (!thread_timer_list_count(timers)) - return NULL; - - struct thread *next_timer = thread_timer_list_first(timers); - monotime_until(&next_timer->u.sands, timer_val); - return timer_val; + if (queue->size) { + struct thread *next_timer = queue->array[0]; + monotime_until(&next_timer->u.sands, timer_val); + return timer_val; + } + return NULL; } static struct thread *thread_run(struct thread_master *m, struct thread *thread, @@ -1286,16 +1315,17 @@ static void thread_process_io(struct thread_master *m, unsigned int num) } /* Add all timers that have popped to the ready list. */ -static unsigned int thread_process_timers(struct thread_timer_list_head *timers, +static unsigned int thread_process_timers(struct pqueue *queue, struct timeval *timenow) { struct thread *thread; unsigned int ready = 0; - while ((thread = thread_timer_list_first(timers))) { + while (queue->size) { + thread = queue->array[0]; if (timercmp(timenow, &thread->u.sands, <)) return ready; - thread_timer_list_pop(timers); + pqueue_dequeue(queue); thread->type = THREAD_READY; thread_list_add_tail(&thread->master->ready, thread); ready++; @@ -1377,7 +1407,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * once per loop to avoid starvation by events */ if (!thread_list_count(&m->ready)) - tw = thread_timer_wait(&m->timer, &tv); + tw = thread_timer_wait(m->timer, &tv); if (thread_list_count(&m->ready) || (tw && !timercmp(tw, &zerotime, >))) @@ -1422,7 +1452,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) /* Post timers to ready queue. */ monotime(&now); - thread_process_timers(&m->timer, &now); + thread_process_timers(m->timer, &now); /* Post I/O to ready queue. */ if (num > 0) diff --git a/lib/thread.h b/lib/thread.h index bbc953424d..7897265120 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -41,7 +41,8 @@ struct rusage_t { #define GETRUSAGE(X) thread_getrusage(X) PREDECL_LIST(thread_list) -PREDECL_SKIPLIST_NONUNIQ(thread_timer_list) + +struct pqueue; struct fd_handler { /* number of pfd that fit in the allocated space of pfds. This is a @@ -72,7 +73,7 @@ struct thread_master { struct thread **read; struct thread **write; - struct thread_timer_list_head timer; + struct pqueue *timer; struct thread_list_head event, ready, unuse; struct list *cancel_req; bool canceled; @@ -94,7 +95,6 @@ struct thread { uint8_t type; /* thread type */ uint8_t add_type; /* thread type */ struct thread_list_item threaditem; - struct thread_timer_list_item timeritem; struct thread **ref; /* external reference (if given) */ struct thread_master *master; /* pointer to the struct thread_master */ int (*func)(struct thread *); /* event function */ @@ -104,6 +104,7 @@ struct thread { int fd; /* file descriptor in case of r/w */ struct timeval sands; /* rest of time sands value. */ } u; + int index; /* queue position for timers */ struct timeval real; struct cpu_thread_history *hist; /* cache pointer to cpu_history */ unsigned long yield; /* yield time in microseconds */ From 981566521462dbd750790a72ec735398d6e687e3 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Mon, 29 Apr 2019 19:24:26 -0400 Subject: [PATCH 27/64] zebra: Add PBR to route_info array Add PBR to the route_info array for handling processing of the PBR route type. Signed-off-by: Stephen Worley --- zebra/zebra_rib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 4cf4c24060..231a49751c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -98,6 +98,7 @@ static const struct { [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 3}, [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 2}, [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 4}, + [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4}, /* no entry/default: 150 */ }; From 42d96b73cb1187a839e16fcdc2c1e76b46c6dd1f Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Mon, 29 Apr 2019 19:26:11 -0400 Subject: [PATCH 28/64] zebra: Add BFD to route_info array Add BFD to the route_info array for handling processing of the BFD route type. Signed-off-by: Stephen Worley --- zebra/zebra_rib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 231a49751c..291833e6a4 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -99,6 +99,7 @@ static const struct { [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 2}, [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 4}, [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4}, + [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4}, /* no entry/default: 150 */ }; From eab7b6e371638e34b8c477de8c47628f12721c9d Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Mon, 29 Apr 2019 19:28:15 -0400 Subject: [PATCH 29/64] zebra: Add OpenFabric to route_info array Add OpenFabric to the route_info array for handling processing of the OpenFabric route type. Signed-off-by: Stephen Worley --- zebra/zebra_rib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 291833e6a4..6d1be08f02 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -100,6 +100,7 @@ static const struct { [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 4}, [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4}, [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4}, + [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 2}, /* no entry/default: 150 */ }; From d6abd8b070fd717c4b7acda6d0fe5bd9ee6e0921 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Tue, 30 Apr 2019 10:07:45 -0400 Subject: [PATCH 30/64] zebra: Comment to ensure types added to route_info Add a comment to indicate that route types added to Zebra, should also be present in the route_info array. Signed-off-by: Stephen Worley --- zebra/zebra_rib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 6d1be08f02..16b5f555fc 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -101,6 +101,7 @@ static const struct { [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4}, [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4}, [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 2}, + /* Any new route type added to zebra, should be mirrored here */ /* no entry/default: 150 */ }; From a07e17d5e78bb4d16c12e5b2b8767daafe2fc47e Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Tue, 30 Apr 2019 11:31:02 -0400 Subject: [PATCH 31/64] topotest: fixes to support python3 Make some small changes to support both python 2 and 3. Signed-off-by: Mark Stapp --- tests/topotests/lib/topogen.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index d989240a16..0bc1596eb2 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -42,7 +42,12 @@ import os import sys import logging import json -import ConfigParser + +if sys.version_info[0] > 2: + import configparser +else: + import ConfigParser as configparser + import glob import grp import platform @@ -153,7 +158,7 @@ class Topogen(object): Loads the configuration file `pytest.ini` located at the root dir of topotests. """ - self.config = ConfigParser.ConfigParser(tgen_defaults) + self.config = configparser.ConfigParser(tgen_defaults) pytestini_path = os.path.join(CWD, '../pytest.ini') self.config.read(pytestini_path) @@ -599,7 +604,7 @@ class TopoRouter(TopoGear): def _prepare_tmpfiles(self): # Create directories if they don't exist try: - os.makedirs(self.logdir, 0755) + os.makedirs(self.logdir, 0o755) except OSError: pass @@ -608,10 +613,10 @@ class TopoRouter(TopoGear): # Only allow group, if it exist. gid = grp.getgrnam(self.routertype)[2] os.chown(self.logdir, 0, gid) - os.chmod(self.logdir, 0775) + os.chmod(self.logdir, 0o775) except KeyError: # Allow anyone, but set the sticky bit to avoid file deletions - os.chmod(self.logdir, 01777) + os.chmod(self.logdir, 0o1777) # Try to find relevant old logfiles in /tmp and delete them map(os.remove, glob.glob('{}/{}/*.log'.format(self.logdir, self.name))) @@ -931,7 +936,7 @@ def diagnose_env_linux(): logger.info('Running environment diagnostics') # Load configuration - config = ConfigParser.ConfigParser(tgen_defaults) + config = configparser.ConfigParser(tgen_defaults) pytestini_path = os.path.join(CWD, '../pytest.ini') config.read(pytestini_path) From 8a64de72fff55b7ffcbedcbbbaf37847756734e8 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 30 Apr 2019 21:29:03 -0400 Subject: [PATCH 32/64] zebra: Fix incorrect reading of REMOTE_VTEP_[ADD|DEL] With flooding control added recently we were not properly handling the new flood control parameter in zebra_vxlan.c handler functions. The error message that was being repeatedly seen: 2019/05/01 00:47:32 ZEBRA: [EC 100663311] stream_get2: Attempt to get out of bounds 2019/05/01 00:47:32 ZEBRA: [EC 100663311] &(struct stream): 0x7f0f04001740, size: 22, getp: 22, endp: 22 The fix was to ensure that both the _add and _del functions kept proper sizing of amount of data read *and* the _del function was not reading the flood_control data from the stream. Signed-off-by: Donald Sharp --- zebra/zebra_vxlan.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index a2e2171304..525ead5a0d 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -7858,12 +7858,18 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { + int flood_control; + /* Obtain each remote VTEP and process. */ STREAM_GETL(s, vni); l += 4; STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); l += IPV4_MAX_BYTELEN; + /* Flood control is intentionally ignored right now */ + STREAM_GETL(s, flood_control); + l += 4; + if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Recv VTEP_DEL %s VNI %u from %s", inet_ntoa(vtep_ip), vni, @@ -7949,7 +7955,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) l += 4; STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); STREAM_GETL(s, flood_control); - l += IPV4_MAX_BYTELEN; + l += IPV4_MAX_BYTELEN + 4; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Recv VTEP_ADD %s VNI %u flood %d from %s", From 694bd4ce201caa71fec4e82847cd90dcd5140c01 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 1 May 2019 19:29:24 +0000 Subject: [PATCH 33/64] zebra: suppress unused variable warning Signed-off-by: Quentin Young --- zebra/zebra_vxlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 525ead5a0d..baa050c9b9 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -7858,7 +7858,7 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { - int flood_control; + int flood_control __attribute__((unused)); /* Obtain each remote VTEP and process. */ STREAM_GETL(s, vni); From eaa2716dfb30f713375b62652afc640591f65872 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Tue, 30 Apr 2019 10:43:16 -0400 Subject: [PATCH 34/64] zebra: Check on startup route_info has all types Add a function to check if the route_info array has all types specified with data in it. Specifically, test the 'key' attribute for non-zero data. Ignore ZEBRA_ROUTE_SYSTEM as it should be zero key anyway. Signed-off-by: Stephen Worley --- zebra/zebra_rib.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8eb309b4b2..2994911165 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3390,9 +3390,32 @@ static int rib_dplane_results(struct dplane_ctx_q *ctxlist) return 0; } +/* + * Ensure there are no empty slots in the route_info array. + * Every route type in zebra should be present there. + */ +static void check_route_info(void) +{ + int len = array_size(route_info); + + /* + * ZEBRA_ROUTE_SYSTEM is special cased since + * its key is 0 anyway. + * + * ZEBRA_ROUTE_ALL is also ignored. + */ + for (int i = 0; i < len; i++) { + if (i == ZEBRA_ROUTE_SYSTEM || i == ZEBRA_ROUTE_ALL) + continue; + assert(route_info[i].key); + } +} + /* Routing information base initialize. */ void rib_init(void) { + check_route_info(); + rib_queue_init(); /* Init dataplane, and register for results */ From 4cd0f3fed60dc14da09210e4e2e694a1dc856291 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Wed, 1 May 2019 21:16:14 +0000 Subject: [PATCH 35/64] topotests/bgp_rfapi_basic_sanity: cleanup rfapi using non-debug command Signed-off-by: Lou Berger --- tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py index 096e97fa94..e9c1916f75 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py @@ -10,7 +10,8 @@ luCommand('r3','vtysh -c "debug rfapi-dev close vn 10.0.0.2 un 2.2.2.2"','status luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"','', 'none', 'Prefix removed') luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"','', 'none', 'MP prefix removed') luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 0 ','wait','Local registration removed') -luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +#luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +luCommand('r4','vtysh -c "clear vnc nve *"','.', 'pass', 'Cleared NVEs') luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') From 820b3b6596b3aa973378dda64a838e338279ee06 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 30 Apr 2019 17:56:05 -0400 Subject: [PATCH 36/64] doc: Some minor doc cleanup for new data structures Noticed during attempts at usage that the documentation needed a couple small updates: 1) Tell the user which header to include 2) Some functions want the address of the data structure Signed-off-by: Donald Sharp --- doc/developer/lists.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst index 6d60420b2f..987b3b7f4f 100644 --- a/doc/developer/lists.rst +++ b/doc/developer/lists.rst @@ -119,6 +119,8 @@ The common setup pattern will look like this: .. code-block:: c + #include + PREDECL_XXX(Z) struct item { int otherdata; @@ -159,26 +161,26 @@ Common iteration macros The following iteration macros work across all data structures: -.. c:function:: for_each(Z, head, item) +.. c:function:: for_each(Z, &head, item) Equivalent to: .. code-block:: c - for (item = Z_first(head); item; item = Z_next(head, item)) + for (item = Z_first(&head); item; item = Z_next(&head, item)) Note that this will fail if the list is modified while being iterated over. -.. c:function:: for_each_safe(Z, head, item) +.. c:function:: for_each_safe(Z, &head, item) Same as the previous, but the next element is pre-loaded into a "hidden" variable (named ``Z_safe``.) Equivalent to: .. code-block:: c - for (item = Z_first(head); item; item = next) { - next = Z_next_safe(head, item); + for (item = Z_first(&head); item; item = next) { + next = Z_next_safe(&head, item); ... } @@ -189,7 +191,7 @@ The following iteration macros work across all data structures: tables is resized while iterating. This will cause items to be skipped or iterated over twice. -.. c:function:: for_each_from(Z, head, item, from) +.. c:function:: for_each_from(Z, &head, item, from) Iterates over the list, starting at item ``from``. This variant is "safe" as in the previous macro. Equivalent to: @@ -197,7 +199,7 @@ The following iteration macros work across all data structures: .. code-block:: c for (item = from; item; item = from) { - from = Z_next_safe(head, item); + from = Z_next_safe(&head, item); ... } From 0a45d974729e1495626783212b1bbaa829eb6b1e Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 30 Apr 2019 18:04:57 -0400 Subject: [PATCH 37/64] zebra: Remove linked list and replace with new LIST The `struct rib_dest_t` was being used to store the linked list of rnh's associated with the node. This was taking up a bunch of memory. Replace with new data structure supplied by David and see the memory reductions associated with 1 million routes in the zebra rib: Old: Memory statistics for zebra: System allocator statistics: Total heap allocated: 675 MiB Holding block headers: 0 bytes Used small blocks: 0 bytes Used ordinary blocks: 567 MiB Free small blocks: 39 MiB Free ordinary blocks: 69 MiB Ordinary blocks: 0 Small blocks: 0 Holding blocks: 0 New: Memory statistics for zebra: System allocator statistics: Total heap allocated: 574 MiB Holding block headers: 0 bytes Used small blocks: 0 bytes Used ordinary blocks: 536 MiB Free small blocks: 33 MiB Free ordinary blocks: 4600 KiB Ordinary blocks: 0 Small blocks: 0 Holding blocks: 0 `struct rnh` was moved to rib.h because of the tangled web of structure dependancies. This data structure is used in numerous places so it should be ok for the moment. Future work might be needed to do a better job of splitting up data structures and function definitions. Signed-off-by: Donald Sharp --- zebra/rib.h | 43 ++++++++++++++++++++++++++++++++++++++++++- zebra/zebra_rib.c | 7 +++---- zebra/zebra_rnh.c | 6 +++--- zebra/zebra_rnh.h | 34 ---------------------------------- zebra/zebra_vrf.c | 2 +- 5 files changed, 49 insertions(+), 43 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index e26831e1a6..fd2aeabf57 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -24,6 +24,7 @@ #include "zebra.h" #include "hook.h" +#include "typesafe.h" #include "linklist.h" #include "prefix.h" #include "table.h" @@ -39,6 +40,44 @@ extern "C" { #endif +typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; + +PREDECL_LIST(rnh_list) + +/* Nexthop structure. */ +struct rnh { + uint8_t flags; + +#define ZEBRA_NHT_CONNECTED 0x1 +#define ZEBRA_NHT_DELETED 0x2 +#define ZEBRA_NHT_EXACT_MATCH 0x4 + + /* VRF identifier. */ + vrf_id_t vrf_id; + + afi_t afi; + + rnh_type_t type; + + uint32_t seqno; + + struct route_entry *state; + struct prefix resolved_route; + struct list *client_list; + + /* pseudowires dependent on this nh */ + struct list *zebra_pseudowire_list; + + struct route_node *node; + + /* + * if this has been filtered for the client + */ + int filtered[ZEBRA_ROUTE_MAX]; + + struct rnh_list_item rnh_list_item; +}; + #define DISTANCE_INFINITY 255 #define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */ @@ -151,7 +190,7 @@ typedef struct rib_dest_t_ { * the data plane we will run evaluate_rnh * on these prefixes. */ - struct list *nht; + struct rnh_list_head nht; /* * Linkage to put dest on the FPM processing queue. @@ -160,6 +199,8 @@ typedef struct rib_dest_t_ { } rib_dest_t; +DECLARE_LIST(rnh_list, struct rnh, rnh_list_item); + #define RIB_ROUTE_QUEUED(x) (1 << (x)) // If MQ_SIZE is modified this value needs to be updated. #define RIB_ROUTE_ANY_QUEUED 0x1F diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 2994911165..f5ba619afa 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1204,7 +1204,6 @@ static int rib_can_delete_dest(rib_dest_t *dest) void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) { rib_dest_t *dest = rib_dest_from_rnode(rn); - struct listnode *node, *nnode; struct rnh *rnh; /* @@ -1236,7 +1235,7 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) * nht resolution and as such we need to call the * nexthop tracking evaluation code */ - for (ALL_LIST_ELEMENTS(dest->nht, node, nnode, rnh)) { + for_each (rnh_list, &dest->nht, rnh) { struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id); struct prefix *p = &rnh->node->p; @@ -1312,7 +1311,7 @@ int rib_gc_dest(struct route_node *rn) zebra_rib_evaluate_rn_nexthops(rn, zebra_router_get_next_sequence()); dest->rnode = NULL; - list_delete(&dest->nht); + rnh_list_fini(&dest->nht); XFREE(MTYPE_RIB_DEST, dest); rn->info = NULL; @@ -2357,7 +2356,7 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn) rib_dest_t *dest; dest = XCALLOC(MTYPE_RIB_DEST, sizeof(rib_dest_t)); - dest->nht = list_new(); + rnh_list_init(&dest->nht); route_lock_node(rn); /* rn route table reference */ rn->info = dest; dest->rnode = rn; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 220a8006d0..2917d0e7a8 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -119,7 +119,7 @@ static void zebra_rnh_remove_from_routing_table(struct rnh *rnh) } dest = rib_dest_from_rnode(rn); - listnode_delete(dest->nht, rnh); + rnh_list_del(&dest->nht, rnh); route_unlock_node(rn); } @@ -145,7 +145,7 @@ static void zebra_rnh_store_in_routing_table(struct rnh *rnh) } dest = rib_dest_from_rnode(rn); - listnode_add(dest->nht, rnh); + rnh_list_add_tail(&dest->nht, rnh); route_unlock_node(rn); } @@ -251,7 +251,7 @@ void zebra_free_rnh(struct rnh *rnh) route_unlock_node(rern); dest = rib_dest_from_rnode(rern); - listnode_delete(dest->nht, rnh); + rnh_list_del(&dest->nht, rnh); } } free_state(rnh->vrf_id, rnh->state, rnh->node); diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 9cd9116eed..95a3941181 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -29,40 +29,6 @@ extern "C" { #endif -typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; - -/* Nexthop structure. */ -struct rnh { - uint8_t flags; - -#define ZEBRA_NHT_CONNECTED 0x1 -#define ZEBRA_NHT_DELETED 0x2 -#define ZEBRA_NHT_EXACT_MATCH 0x4 - - /* VRF identifier. */ - vrf_id_t vrf_id; - - afi_t afi; - - rnh_type_t type; - - uint32_t seqno; - - struct route_entry *state; - struct prefix resolved_route; - struct list *client_list; - - /* pseudowires dependent on this nh */ - struct list *zebra_pseudowire_list; - - struct route_node *node; - - /* - * if this has been filtered for the client - */ - int filtered[ZEBRA_ROUTE_MAX]; -}; - extern int zebra_rnh_ip_default_route; extern int zebra_rnh_ipv6_default_route; diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 2d721ec8a1..f0b99f41fc 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -357,7 +357,7 @@ void zebra_rtable_node_cleanup(struct route_table *table, if (node->info) { rib_dest_t *dest = node->info; - list_delete(&dest->nht); + rnh_list_fini(&dest->nht); XFREE(MTYPE_RIB_DEST, node->info); } } From a07f484614e0e9cc92afaf4bf58e6f6507fdbed4 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 30 Apr 2019 19:38:15 -0400 Subject: [PATCH 38/64] lib: Make _find functions treat the head as const The head of a list should not change for find functions. Probably are others that should be considered but these changes can come in as needed I believe. Signed-off-by: Donald Sharp --- lib/typesafe.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/typesafe.h b/lib/typesafe.h index bbf3ce8f1c..94002da599 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -275,7 +275,7 @@ macro_pure size_t prefix ## _count(struct prefix##_head *h) \ #define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \ _DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn) \ \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline type *prefix ## _find(const struct prefix##_head *h, const type *item) \ { \ struct ssort_item *sitem = h->sh.first; \ int cmpval = 0; \ @@ -383,7 +383,7 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ *np = &item->field.hi; \ return NULL; \ } \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline type *prefix ## _find(const struct prefix##_head *h, const type *item) \ { \ if (!h->hh.tabshift) \ return NULL; \ @@ -576,7 +576,7 @@ macro_inline int prefix ## __cmp(const struct sskip_item *a, \ return cmpfn(container_of(a, type, field.si), \ container_of(b, type, field.si)); \ } \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline type *prefix ## _find(const struct prefix##_head *h, const type *item) \ { \ struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \ &item->field.si, &prefix ## __cmp); \ From 6701b1b7e749af853f9b9363ef507189d10ef6d2 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 30 Apr 2019 19:40:11 -0400 Subject: [PATCH 39/64] lib: Make prefix_hash_key accept a const We should not be modifying the pointer for the prefix_hash_key function, make it a const so that we can use it elsewhere. Signed-off-by: Donald Sharp --- lib/prefix.c | 2 +- lib/prefix.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/prefix.c b/lib/prefix.c index 6b91969218..d2a4c3a432 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1543,7 +1543,7 @@ char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size) return ptr; } -unsigned prefix_hash_key(void *pp) +unsigned prefix_hash_key(const void *pp) { struct prefix copy; diff --git a/lib/prefix.h b/lib/prefix.h index d3c387e102..d57b43dac6 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -466,7 +466,7 @@ extern int is_zero_mac(struct ethaddr *mac); extern int prefix_str2mac(const char *str, struct ethaddr *mac); extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); -extern unsigned prefix_hash_key(void *pp); +extern unsigned prefix_hash_key(const void *pp); extern int str_to_esi(const char *str, esi_t *esi); extern char *esi_to_str(const esi_t *esi, char *buf, int size); From 0103534f9c48fb2c00a3833a5acb7405af12b55a Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 30 Apr 2019 20:23:52 -0400 Subject: [PATCH 40/64] lib: Convert table code to use new hash type This converts the new table code to use the new hash type provided by David. The following test is 1 million routes installed and how much memory we are using: Old mem usage: Memory statistics for zebra: System allocator statistics: Total heap allocated: 574 MiB Holding block headers: 0 bytes Used small blocks: 0 bytes Used ordinary blocks: 536 MiB Free small blocks: 33 MiB Free ordinary blocks: 4600 KiB Ordinary blocks: 0 Small blocks: 0 Holding blocks: 0 New Memory usage: Memory statistics for zebra: System allocator statistics: Total heap allocated: 542 MiB Holding block headers: 0 bytes Used small blocks: 0 bytes Used ordinary blocks: 506 MiB Free small blocks: 3374 KiB Free ordinary blocks: 33 MiB Ordinary blocks: 0 Small blocks: 0 Holding blocks: 0 Signed-off-by: Donald Sharp --- lib/table.c | 31 ++++++++++++++----------------- lib/table.h | 6 +++++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/table.c b/lib/table.c index edba7f1932..2d42e2d55c 100644 --- a/lib/table.c +++ b/lib/table.c @@ -33,12 +33,14 @@ DEFINE_MTYPE(LIB, ROUTE_NODE, "Route node") static void route_table_free(struct route_table *); -static bool route_table_hash_cmp(const void *a, const void *b) +static int route_table_hash_cmp(const void *a, const void *b) { const struct prefix *pa = a, *pb = b; - return prefix_cmp(pa, pb) == 0; + return prefix_cmp(pa, pb); } +DECLARE_HASH(rn_hash_node, struct route_node, nodehash, route_table_hash_cmp, + prefix_hash_key) /* * route_table_init_with_delegate */ @@ -49,8 +51,7 @@ route_table_init_with_delegate(route_table_delegate_t *delegate) rt = XCALLOC(MTYPE_ROUTE_TABLE, sizeof(struct route_table)); rt->delegate = delegate; - rt->hash = hash_create(prefix_hash_key, route_table_hash_cmp, - "route table hash"); + rn_hash_node_init(&rt->hash); return rt; } @@ -69,15 +70,14 @@ static struct route_node *route_node_new(struct route_table *table) static struct route_node *route_node_set(struct route_table *table, const struct prefix *prefix) { - struct route_node *node, *inserted; + struct route_node *node; node = route_node_new(table); prefix_copy(&node->p, prefix); node->table = table; - inserted = hash_get(node->table->hash, node, hash_alloc_intern); - assert(inserted == node); + rn_hash_node_add(&node->table->hash, node); return node; } @@ -99,9 +99,6 @@ static void route_table_free(struct route_table *rt) if (rt == NULL) return; - hash_clean(rt->hash, NULL); - hash_free(rt->hash); - node = rt->top; /* Bulk deletion of nodes remaining in this table. This function is not @@ -123,6 +120,7 @@ static void route_table_free(struct route_table *rt) tmp_node->table->count--; tmp_node->lock = 0; /* to cause assert if unlocked after this */ + rn_hash_node_del(&rt->hash, tmp_node); route_node_free(rt, tmp_node); if (node != NULL) { @@ -137,6 +135,7 @@ static void route_table_free(struct route_table *rt) assert(rt->count == 0); + rn_hash_node_fini(&rt->hash); XFREE(MTYPE_ROUTE_TABLE, rt); return; } @@ -257,7 +256,7 @@ struct route_node *route_node_lookup(const struct route_table *table, prefix_copy(&p, pu.p); apply_mask(&p); - node = hash_get(table->hash, (void *)&p, NULL); + node = rn_hash_node_find(&table->hash, (void *)&p); return (node && node->info) ? route_lock_node(node) : NULL; } @@ -270,7 +269,7 @@ struct route_node *route_node_lookup_maynull(const struct route_table *table, prefix_copy(&p, pu.p); apply_mask(&p); - node = hash_get(table->hash, (void *)&p, NULL); + node = rn_hash_node_find(&table->hash, (void *)&p); return node ? route_lock_node(node) : NULL; } @@ -282,12 +281,11 @@ struct route_node *route_node_get(struct route_table *const table, struct route_node *new; struct route_node *node; struct route_node *match; - struct route_node *inserted; uint16_t prefixlen = p->prefixlen; const uint8_t *prefix = &p->u.prefix; apply_mask((struct prefix *)p); - node = hash_get(table->hash, (void *)p, NULL); + node = rn_hash_node_find(&table->hash, (void *)p); if (node && node->info) return route_lock_node(node); @@ -314,8 +312,7 @@ struct route_node *route_node_get(struct route_table *const table, new->p.family = p->family; new->table = table; set_link(new, node); - inserted = hash_get(node->table->hash, new, hash_alloc_intern); - assert(inserted == new); + rn_hash_node_add(&table->hash, new); if (match) set_link(match, new); @@ -367,7 +364,7 @@ void route_node_delete(struct route_node *node) node->table->count--; - hash_release(node->table->hash, node); + rn_hash_node_del(&node->table->hash, node); /* WARNING: FRAGILE CODE! * route_node_free may have the side effect of free'ing the entire diff --git a/lib/table.h b/lib/table.h index ce578e795c..3e3fb658ae 100644 --- a/lib/table.h +++ b/lib/table.h @@ -25,6 +25,7 @@ #include "memory.h" #include "hash.h" #include "prefix.h" +#include "typesafe.h" #ifdef __cplusplus extern "C" { @@ -59,10 +60,12 @@ struct route_table_delegate_t_ { route_table_destroy_node_func_t destroy_node; }; +PREDECL_HASH(rn_hash_node) + /* Routing table top structure. */ struct route_table { struct route_node *top; - struct hash *hash; + struct rn_hash_node_head hash; /* * Delegate that performs certain functions for this table. @@ -129,6 +132,7 @@ struct route_table { /* Lock of this radix */ \ unsigned int table_rdonly(lock); \ \ + struct rn_hash_node_item nodehash; \ /* Each node of route. */ \ void *info; \ From be6f84af73d0f9cc28c485512cba200aecded424 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Wed, 1 May 2019 21:16:14 +0000 Subject: [PATCH 41/64] topotests/bgp_rfapi_basic_sanity: cleanup rfapi using non-debug command Signed-off-by: Lou Berger --- tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py index 096e97fa94..e9c1916f75 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py @@ -10,7 +10,8 @@ luCommand('r3','vtysh -c "debug rfapi-dev close vn 10.0.0.2 un 2.2.2.2"','status luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"','', 'none', 'Prefix removed') luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"','', 'none', 'MP prefix removed') luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 0 ','wait','Local registration removed') -luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +#luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +luCommand('r4','vtysh -c "clear vnc nve *"','.', 'pass', 'Cleared NVEs') luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') From e8b9ad5cdda832ea5ff8dbe21f6078fe15badd33 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Thu, 2 May 2019 06:54:59 -0400 Subject: [PATCH 42/64] Revert "Zebra diet" --- doc/developer/lists.rst | 16 +++---- lib/prefix.c | 2 +- lib/prefix.h | 2 +- lib/table.c | 31 +++++++------ lib/table.h | 6 +-- lib/typesafe.h | 6 +-- .../scripts/cleanup_all.py | 3 +- zebra/rib.h | 43 +------------------ zebra/zebra_rib.c | 7 +-- zebra/zebra_rnh.c | 6 +-- zebra/zebra_rnh.h | 34 +++++++++++++++ zebra/zebra_vrf.c | 2 +- 12 files changed, 74 insertions(+), 84 deletions(-) diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst index 987b3b7f4f..6d60420b2f 100644 --- a/doc/developer/lists.rst +++ b/doc/developer/lists.rst @@ -119,8 +119,6 @@ The common setup pattern will look like this: .. code-block:: c - #include - PREDECL_XXX(Z) struct item { int otherdata; @@ -161,26 +159,26 @@ Common iteration macros The following iteration macros work across all data structures: -.. c:function:: for_each(Z, &head, item) +.. c:function:: for_each(Z, head, item) Equivalent to: .. code-block:: c - for (item = Z_first(&head); item; item = Z_next(&head, item)) + for (item = Z_first(head); item; item = Z_next(head, item)) Note that this will fail if the list is modified while being iterated over. -.. c:function:: for_each_safe(Z, &head, item) +.. c:function:: for_each_safe(Z, head, item) Same as the previous, but the next element is pre-loaded into a "hidden" variable (named ``Z_safe``.) Equivalent to: .. code-block:: c - for (item = Z_first(&head); item; item = next) { - next = Z_next_safe(&head, item); + for (item = Z_first(head); item; item = next) { + next = Z_next_safe(head, item); ... } @@ -191,7 +189,7 @@ The following iteration macros work across all data structures: tables is resized while iterating. This will cause items to be skipped or iterated over twice. -.. c:function:: for_each_from(Z, &head, item, from) +.. c:function:: for_each_from(Z, head, item, from) Iterates over the list, starting at item ``from``. This variant is "safe" as in the previous macro. Equivalent to: @@ -199,7 +197,7 @@ The following iteration macros work across all data structures: .. code-block:: c for (item = from; item; item = from) { - from = Z_next_safe(&head, item); + from = Z_next_safe(head, item); ... } diff --git a/lib/prefix.c b/lib/prefix.c index d2a4c3a432..6b91969218 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1543,7 +1543,7 @@ char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size) return ptr; } -unsigned prefix_hash_key(const void *pp) +unsigned prefix_hash_key(void *pp) { struct prefix copy; diff --git a/lib/prefix.h b/lib/prefix.h index d57b43dac6..d3c387e102 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -466,7 +466,7 @@ extern int is_zero_mac(struct ethaddr *mac); extern int prefix_str2mac(const char *str, struct ethaddr *mac); extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); -extern unsigned prefix_hash_key(const void *pp); +extern unsigned prefix_hash_key(void *pp); extern int str_to_esi(const char *str, esi_t *esi); extern char *esi_to_str(const esi_t *esi, char *buf, int size); diff --git a/lib/table.c b/lib/table.c index 2d42e2d55c..edba7f1932 100644 --- a/lib/table.c +++ b/lib/table.c @@ -33,14 +33,12 @@ DEFINE_MTYPE(LIB, ROUTE_NODE, "Route node") static void route_table_free(struct route_table *); -static int route_table_hash_cmp(const void *a, const void *b) +static bool route_table_hash_cmp(const void *a, const void *b) { const struct prefix *pa = a, *pb = b; - return prefix_cmp(pa, pb); + return prefix_cmp(pa, pb) == 0; } -DECLARE_HASH(rn_hash_node, struct route_node, nodehash, route_table_hash_cmp, - prefix_hash_key) /* * route_table_init_with_delegate */ @@ -51,7 +49,8 @@ route_table_init_with_delegate(route_table_delegate_t *delegate) rt = XCALLOC(MTYPE_ROUTE_TABLE, sizeof(struct route_table)); rt->delegate = delegate; - rn_hash_node_init(&rt->hash); + rt->hash = hash_create(prefix_hash_key, route_table_hash_cmp, + "route table hash"); return rt; } @@ -70,14 +69,15 @@ static struct route_node *route_node_new(struct route_table *table) static struct route_node *route_node_set(struct route_table *table, const struct prefix *prefix) { - struct route_node *node; + struct route_node *node, *inserted; node = route_node_new(table); prefix_copy(&node->p, prefix); node->table = table; - rn_hash_node_add(&node->table->hash, node); + inserted = hash_get(node->table->hash, node, hash_alloc_intern); + assert(inserted == node); return node; } @@ -99,6 +99,9 @@ static void route_table_free(struct route_table *rt) if (rt == NULL) return; + hash_clean(rt->hash, NULL); + hash_free(rt->hash); + node = rt->top; /* Bulk deletion of nodes remaining in this table. This function is not @@ -120,7 +123,6 @@ static void route_table_free(struct route_table *rt) tmp_node->table->count--; tmp_node->lock = 0; /* to cause assert if unlocked after this */ - rn_hash_node_del(&rt->hash, tmp_node); route_node_free(rt, tmp_node); if (node != NULL) { @@ -135,7 +137,6 @@ static void route_table_free(struct route_table *rt) assert(rt->count == 0); - rn_hash_node_fini(&rt->hash); XFREE(MTYPE_ROUTE_TABLE, rt); return; } @@ -256,7 +257,7 @@ struct route_node *route_node_lookup(const struct route_table *table, prefix_copy(&p, pu.p); apply_mask(&p); - node = rn_hash_node_find(&table->hash, (void *)&p); + node = hash_get(table->hash, (void *)&p, NULL); return (node && node->info) ? route_lock_node(node) : NULL; } @@ -269,7 +270,7 @@ struct route_node *route_node_lookup_maynull(const struct route_table *table, prefix_copy(&p, pu.p); apply_mask(&p); - node = rn_hash_node_find(&table->hash, (void *)&p); + node = hash_get(table->hash, (void *)&p, NULL); return node ? route_lock_node(node) : NULL; } @@ -281,11 +282,12 @@ struct route_node *route_node_get(struct route_table *const table, struct route_node *new; struct route_node *node; struct route_node *match; + struct route_node *inserted; uint16_t prefixlen = p->prefixlen; const uint8_t *prefix = &p->u.prefix; apply_mask((struct prefix *)p); - node = rn_hash_node_find(&table->hash, (void *)p); + node = hash_get(table->hash, (void *)p, NULL); if (node && node->info) return route_lock_node(node); @@ -312,7 +314,8 @@ struct route_node *route_node_get(struct route_table *const table, new->p.family = p->family; new->table = table; set_link(new, node); - rn_hash_node_add(&table->hash, new); + inserted = hash_get(node->table->hash, new, hash_alloc_intern); + assert(inserted == new); if (match) set_link(match, new); @@ -364,7 +367,7 @@ void route_node_delete(struct route_node *node) node->table->count--; - rn_hash_node_del(&node->table->hash, node); + hash_release(node->table->hash, node); /* WARNING: FRAGILE CODE! * route_node_free may have the side effect of free'ing the entire diff --git a/lib/table.h b/lib/table.h index 3e3fb658ae..ce578e795c 100644 --- a/lib/table.h +++ b/lib/table.h @@ -25,7 +25,6 @@ #include "memory.h" #include "hash.h" #include "prefix.h" -#include "typesafe.h" #ifdef __cplusplus extern "C" { @@ -60,12 +59,10 @@ struct route_table_delegate_t_ { route_table_destroy_node_func_t destroy_node; }; -PREDECL_HASH(rn_hash_node) - /* Routing table top structure. */ struct route_table { struct route_node *top; - struct rn_hash_node_head hash; + struct hash *hash; /* * Delegate that performs certain functions for this table. @@ -132,7 +129,6 @@ struct route_table { /* Lock of this radix */ \ unsigned int table_rdonly(lock); \ \ - struct rn_hash_node_item nodehash; \ /* Each node of route. */ \ void *info; \ diff --git a/lib/typesafe.h b/lib/typesafe.h index 94002da599..bbf3ce8f1c 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -275,7 +275,7 @@ macro_pure size_t prefix ## _count(struct prefix##_head *h) \ #define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \ _DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn) \ \ -macro_inline type *prefix ## _find(const struct prefix##_head *h, const type *item) \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ { \ struct ssort_item *sitem = h->sh.first; \ int cmpval = 0; \ @@ -383,7 +383,7 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ *np = &item->field.hi; \ return NULL; \ } \ -macro_inline type *prefix ## _find(const struct prefix##_head *h, const type *item) \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ { \ if (!h->hh.tabshift) \ return NULL; \ @@ -576,7 +576,7 @@ macro_inline int prefix ## __cmp(const struct sskip_item *a, \ return cmpfn(container_of(a, type, field.si), \ container_of(b, type, field.si)); \ } \ -macro_inline type *prefix ## _find(const struct prefix##_head *h, const type *item) \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ { \ struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \ &item->field.si, &prefix ## __cmp); \ diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py index e9c1916f75..096e97fa94 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py @@ -10,8 +10,7 @@ luCommand('r3','vtysh -c "debug rfapi-dev close vn 10.0.0.2 un 2.2.2.2"','status luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"','', 'none', 'Prefix removed') luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"','', 'none', 'MP prefix removed') luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 0 ','wait','Local registration removed') -#luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') -luCommand('r4','vtysh -c "clear vnc nve *"','.', 'pass', 'Cleared NVEs') +luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') diff --git a/zebra/rib.h b/zebra/rib.h index fd2aeabf57..e26831e1a6 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -24,7 +24,6 @@ #include "zebra.h" #include "hook.h" -#include "typesafe.h" #include "linklist.h" #include "prefix.h" #include "table.h" @@ -40,44 +39,6 @@ extern "C" { #endif -typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; - -PREDECL_LIST(rnh_list) - -/* Nexthop structure. */ -struct rnh { - uint8_t flags; - -#define ZEBRA_NHT_CONNECTED 0x1 -#define ZEBRA_NHT_DELETED 0x2 -#define ZEBRA_NHT_EXACT_MATCH 0x4 - - /* VRF identifier. */ - vrf_id_t vrf_id; - - afi_t afi; - - rnh_type_t type; - - uint32_t seqno; - - struct route_entry *state; - struct prefix resolved_route; - struct list *client_list; - - /* pseudowires dependent on this nh */ - struct list *zebra_pseudowire_list; - - struct route_node *node; - - /* - * if this has been filtered for the client - */ - int filtered[ZEBRA_ROUTE_MAX]; - - struct rnh_list_item rnh_list_item; -}; - #define DISTANCE_INFINITY 255 #define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */ @@ -190,7 +151,7 @@ typedef struct rib_dest_t_ { * the data plane we will run evaluate_rnh * on these prefixes. */ - struct rnh_list_head nht; + struct list *nht; /* * Linkage to put dest on the FPM processing queue. @@ -199,8 +160,6 @@ typedef struct rib_dest_t_ { } rib_dest_t; -DECLARE_LIST(rnh_list, struct rnh, rnh_list_item); - #define RIB_ROUTE_QUEUED(x) (1 << (x)) // If MQ_SIZE is modified this value needs to be updated. #define RIB_ROUTE_ANY_QUEUED 0x1F diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f5ba619afa..2994911165 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1204,6 +1204,7 @@ static int rib_can_delete_dest(rib_dest_t *dest) void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) { rib_dest_t *dest = rib_dest_from_rnode(rn); + struct listnode *node, *nnode; struct rnh *rnh; /* @@ -1235,7 +1236,7 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) * nht resolution and as such we need to call the * nexthop tracking evaluation code */ - for_each (rnh_list, &dest->nht, rnh) { + for (ALL_LIST_ELEMENTS(dest->nht, node, nnode, rnh)) { struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id); struct prefix *p = &rnh->node->p; @@ -1311,7 +1312,7 @@ int rib_gc_dest(struct route_node *rn) zebra_rib_evaluate_rn_nexthops(rn, zebra_router_get_next_sequence()); dest->rnode = NULL; - rnh_list_fini(&dest->nht); + list_delete(&dest->nht); XFREE(MTYPE_RIB_DEST, dest); rn->info = NULL; @@ -2356,7 +2357,7 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn) rib_dest_t *dest; dest = XCALLOC(MTYPE_RIB_DEST, sizeof(rib_dest_t)); - rnh_list_init(&dest->nht); + dest->nht = list_new(); route_lock_node(rn); /* rn route table reference */ rn->info = dest; dest->rnode = rn; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 2917d0e7a8..220a8006d0 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -119,7 +119,7 @@ static void zebra_rnh_remove_from_routing_table(struct rnh *rnh) } dest = rib_dest_from_rnode(rn); - rnh_list_del(&dest->nht, rnh); + listnode_delete(dest->nht, rnh); route_unlock_node(rn); } @@ -145,7 +145,7 @@ static void zebra_rnh_store_in_routing_table(struct rnh *rnh) } dest = rib_dest_from_rnode(rn); - rnh_list_add_tail(&dest->nht, rnh); + listnode_add(dest->nht, rnh); route_unlock_node(rn); } @@ -251,7 +251,7 @@ void zebra_free_rnh(struct rnh *rnh) route_unlock_node(rern); dest = rib_dest_from_rnode(rern); - rnh_list_del(&dest->nht, rnh); + listnode_delete(dest->nht, rnh); } } free_state(rnh->vrf_id, rnh->state, rnh->node); diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 95a3941181..9cd9116eed 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -29,6 +29,40 @@ extern "C" { #endif +typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; + +/* Nexthop structure. */ +struct rnh { + uint8_t flags; + +#define ZEBRA_NHT_CONNECTED 0x1 +#define ZEBRA_NHT_DELETED 0x2 +#define ZEBRA_NHT_EXACT_MATCH 0x4 + + /* VRF identifier. */ + vrf_id_t vrf_id; + + afi_t afi; + + rnh_type_t type; + + uint32_t seqno; + + struct route_entry *state; + struct prefix resolved_route; + struct list *client_list; + + /* pseudowires dependent on this nh */ + struct list *zebra_pseudowire_list; + + struct route_node *node; + + /* + * if this has been filtered for the client + */ + int filtered[ZEBRA_ROUTE_MAX]; +}; + extern int zebra_rnh_ip_default_route; extern int zebra_rnh_ipv6_default_route; diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index f0b99f41fc..2d721ec8a1 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -357,7 +357,7 @@ void zebra_rtable_node_cleanup(struct route_table *table, if (node->info) { rib_dest_t *dest = node->info; - rnh_list_fini(&dest->nht); + list_delete(&dest->nht); XFREE(MTYPE_RIB_DEST, node->info); } } From 5e76ce5069b4fd1e385b5b2bcbe05d01bfa1ebaa Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 2 May 2019 07:15:39 -0400 Subject: [PATCH 43/64] Revert "bgpd: Prevent IPv6 routes received via a ibgp session with own ip as nexthop " --- bgpd/bgp_nexthop.c | 133 --------------------------------------------- bgpd/bgp_nexthop.h | 10 ---- bgpd/bgp_route.c | 4 +- bgpd/bgpd.c | 1 - bgpd/bgpd.h | 3 - lib/jhash.c | 15 ----- lib/jhash.h | 2 - 7 files changed, 1 insertion(+), 167 deletions(-) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index cf67a36238..de97b73c72 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -47,11 +47,6 @@ DEFINE_MTYPE_STATIC(BGPD, MARTIAN_STRING, "BGP Martian Address Intf String"); -static void bgp_ipv6_address_add(struct bgp *bgp, struct connected *ifc, - struct prefix *p); -static void bgp_ipv6_address_del(struct bgp *bgp, struct connected *ifc, - struct prefix *p); - char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size) { prefix2str(&(bnc->node->p), buf, size); @@ -384,8 +379,6 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc) if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) return; - bgp_ipv6_address_add(bgp, ifc, addr); - rn = bgp_node_get(bgp->connected_table[AFI_IP6], (struct prefix *)&p); @@ -426,8 +419,6 @@ void bgp_connected_delete(struct bgp *bgp, struct connected *ifc) if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) return; - bgp_ipv6_address_del(bgp, ifc, addr); - rn = bgp_node_lookup(bgp->connected_table[AFI_IP6], (struct prefix *)&p); } @@ -781,127 +772,3 @@ void bgp_scan_finish(struct bgp *bgp) bgp->import_check_table[afi] = NULL; } } - -static void *bgp_ipv6_address_hash_alloc(void *p) -{ - const struct in6_addr *v6addr = (const struct in6_addr *)p; - struct bgp_addrv6 *addr; - - addr = XMALLOC(MTYPE_BGP_ADDR, sizeof(struct bgp_addrv6)); - addr->addrv6 = *v6addr; - - addr->ifp_name_list = list_new(); - addr->ifp_name_list->del = bgp_address_hash_string_del; - - return addr; -} - -static void bgp_ipv6_address_hash_free(void *data) -{ - struct bgp_addrv6 *v6addr = data; - - list_delete(&v6addr->ifp_name_list); - XFREE(MTYPE_BGP_ADDR, v6addr); -} - -static unsigned int bgp_ipv6_address_hash_key_make(void *p) -{ - const struct bgp_addrv6 *v6 = p; - - return __ipv6_addr_jhash(&v6->addrv6, 0); -} - -static bool bgp_ipv6_address_hash_cmp(const void *p1, - const void *p2) -{ - const struct bgp_addrv6 *addr1 = p1; - const struct bgp_addrv6 *addr2 = p2; - - return(!memcmp(&addr1->addrv6, &addr2->addrv6, - sizeof(struct in6_addr))); -} - -void bgp_ipv6_address_init(struct bgp *bgp) -{ - bgp->ipv6_address_hash = hash_create(bgp_ipv6_address_hash_key_make, - bgp_ipv6_address_hash_cmp, - "BGP IPV6 Address Hash"); -} - -void bgp_ipv6_address_destroy(struct bgp *bgp) -{ - if (bgp->ipv6_address_hash == NULL) - return; - hash_clean(bgp->ipv6_address_hash, - bgp_ipv6_address_hash_free); - - hash_free(bgp->ipv6_address_hash); - bgp->ipv6_address_hash = NULL; -} - -static void bgp_ipv6_address_add(struct bgp *bgp, struct connected *ifc, - struct prefix *p) -{ - struct bgp_addrv6 tmp = {0}; - struct bgp_addrv6 *addr = NULL; - struct listnode *node = NULL; - char *name = 0; - - tmp.addrv6 = p->u.prefix6; - - addr = hash_get(bgp->ipv6_address_hash, &tmp, - bgp_ipv6_address_hash_alloc); - if (!addr) - return; - - for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) { - if (strcmp(ifc->ifp->name, name) == 0) - break; - } - - if (!node) { - name = XSTRDUP(MTYPE_MARTIAN_STRING, ifc->ifp->name); - listnode_add(addr->ifp_name_list, name); - } -} - - -static void bgp_ipv6_address_del(struct bgp *bgp, struct connected *ifc, - struct prefix *p) -{ - struct bgp_addrv6 tmp; - struct bgp_addrv6 *addr; - struct listnode *node; - char *name; - - - tmp.addrv6 = p->u.prefix6; - - addr = hash_lookup(bgp->ipv6_address_hash, &tmp); - /* may have been deleted earlier by bgp_interface_down() */ - if (addr == NULL) - return; - - for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) { - if (strcmp(ifc->ifp->name, name) == 0) - break; - } - - if (node) { - list_delete_node(addr->ifp_name_list, node); - XFREE(MTYPE_MARTIAN_STRING, name); - } - - if (addr->ifp_name_list->count == 0) { - hash_release(bgp->ipv6_address_hash, addr); - list_delete(&addr->ifp_name_list); - XFREE(MTYPE_BGP_ADDR, addr); - } -} -int bgp_nexthop_self_ipv6(struct bgp *bgp, struct in6_addr *addr) -{ - struct bgp_addrv6 tmp; - - tmp.addrv6 = *addr; - return (!!hash_lookup(bgp->ipv6_address_hash, &tmp)); -} diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index c26295f810..f06fae5706 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -74,12 +74,6 @@ struct tip_addr { int refcnt; }; -/* ipv6 connected address info structure */ -struct bgp_addrv6 { - struct in6_addr addrv6; - struct list *ifp_name_list; -}; - extern void bgp_connected_add(struct bgp *bgp, struct connected *c); extern void bgp_connected_delete(struct bgp *bgp, struct connected *c); extern int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, @@ -102,8 +96,4 @@ extern void bgp_tip_hash_init(struct bgp *bgp); extern void bgp_tip_hash_destroy(struct bgp *bgp); extern void bgp_nexthop_show_address_hash(struct vty *vty, struct bgp *bgp); -extern void bgp_ipv6_address_init(struct bgp *bgp); -extern void bgp_ipv6_address_destroy(struct bgp *bgp); -extern int bgp_nexthop_self_ipv6(struct bgp *bgp, - struct in6_addr *addr); #endif /* _QUAGGA_BGP_NEXTHOP_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 0481830bde..fc6798fdfc 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2932,9 +2932,7 @@ static int bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, ret = (IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) || IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) || IN6_IS_ADDR_MULTICAST( - &attr->mp_nexthop_global) - || bgp_nexthop_self_ipv6(bgp, - &attr->mp_nexthop_global)); + &attr->mp_nexthop_global)); break; default: diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 730cc8bde7..b2925cd512 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3213,7 +3213,6 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp->vrf_id = vrf_generate_id(); bgp_router_id_set(bgp, &bgp->router_id_zebra); bgp_address_init(bgp); - bgp_ipv6_address_init(bgp); bgp_tip_hash_init(bgp); bgp_scan_init(bgp); *bgp_val = bgp; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index cf03328f8c..b0f6567534 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -393,9 +393,6 @@ struct bgp { struct hash *address_hash; - /* ipv6 connected address hash table pointer */ - struct hash *ipv6_address_hash; - /* DB for all local tunnel-ips - used mainly for martian checks Currently it only has all VxLan tunnel IPs*/ struct hash *tip_hash; diff --git a/lib/jhash.c b/lib/jhash.c index d0aff57d87..0d561ef3a4 100644 --- a/lib/jhash.c +++ b/lib/jhash.c @@ -185,18 +185,3 @@ uint32_t jhash_1word(uint32_t a, uint32_t initval) { return jhash_3words(a, 0, 0, initval); } - -/* ipv6 hash function */ -uint32_t __ipv6_addr_jhash(const struct in6_addr *a, const uint32_t initval) -{ - uint32_t v = 0; - uint32_t y[4] = {0}; - - /* Since s6_addr32 is not available is few os like FreeBSD, NetBDS, - * OMIBDS & OpenBDS. So recreating in uint32_t format. - */ - memcpy(y, a->s6_addr, sizeof(struct in6_addr)); - v = y[0] ^ y[1]; - - return jhash_3words(v, y[2], y[3], initval); -} diff --git a/lib/jhash.h b/lib/jhash.h index 1b6f879369..977421495c 100644 --- a/lib/jhash.h +++ b/lib/jhash.h @@ -45,8 +45,6 @@ extern uint32_t jhash_3words(uint32_t a, uint32_t b, uint32_t c, uint32_t initval); extern uint32_t jhash_2words(uint32_t a, uint32_t b, uint32_t initval); extern uint32_t jhash_1word(uint32_t a, uint32_t initval); -extern uint32_t __ipv6_addr_jhash(const struct in6_addr *a, - const uint32_t initval); #ifdef __cplusplus } From b7b7bf31da5ae8e3a13827b3ab14e4e113ce6fe6 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Thu, 2 May 2019 09:52:48 -0400 Subject: [PATCH 44/64] zebra: replace strncpy with strlcpy in dplane The dataplane module picked up a couple of strncpys; replace them. Signed-off-by: Mark Stapp --- zebra/zebra_dplane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index d1b28227c3..e6671bd87c 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -1706,7 +1706,7 @@ static enum zebra_dplane_result intf_addr_update_internal( /* Init the interface-addr-specific area */ memset(&ctx->u.intf, 0, sizeof(ctx->u.intf)); - strncpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname)); + strlcpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname)); ctx->u.intf.ifindex = ifp->ifindex; ctx->u.intf.prefix = *(ifc->address); @@ -1734,7 +1734,7 @@ static enum zebra_dplane_result intf_addr_update_internal( len = strlen(ifc->label); if (len < sizeof(ctx->u.intf.label_buf)) { - strncpy(ctx->u.intf.label_buf, ifc->label, + strlcpy(ctx->u.intf.label_buf, ifc->label, sizeof(ctx->u.intf.label_buf)); ctx->u.intf.label = ctx->u.intf.label_buf; } else { From 859ea2dea0d46c6a67897b019e5aa6c9d5dcd83d Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 2 May 2019 10:37:04 -0400 Subject: [PATCH 45/64] pimd: Stop crash in show of single interface There exists a possiblity that we have upstream data but at this point in time the rpf failed because there is no path. As such the rpf interface will be NULL and we should not necessarily trust it. Prevent a crash Ticket: CM-24857 Signed-off-by: Donald Sharp --- pimd/pim_cmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 9c1cb38012..cb2ba87ec6 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1207,6 +1207,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim, print_header = 1; for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + if (!up->rpf.source_nexthop.interface) + continue; if (strcmp(ifp->name, up->rpf.source_nexthop From 80cb48d2f47d72e497c2c9430ddb28febff259cc Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Thu, 2 May 2019 14:43:18 -0400 Subject: [PATCH 46/64] topotest: fix pytest deprecation warning As of pytest 4.something, a pattern we were using in conftest.py was deprecated. Also make a new-ish test script executable (all the rest appear to be?) Signed-off-by: Mark Stapp --- .../topotests/bgp-vrf-route-leak-basic/test_bgp.py | 0 tests/topotests/conftest.py | 14 +++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) mode change 100644 => 100755 tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py diff --git a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py old mode 100644 new mode 100755 diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 327e4625f2..49e48ba927 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -7,6 +7,8 @@ from lib.topotest import json_cmp_result from lib.topolog import logger import pytest +topology_only = False + def pytest_addoption(parser): """ Add topology-only option to the topology tester. This option makes pytest @@ -20,9 +22,9 @@ def pytest_runtest_call(): This function must be run after setup_module(), it does standarized post setup routines. It is only being used for the 'topology-only' option. """ - # pylint: disable=E1101 - # Trust me, 'config' exists. - if pytest.config.getoption('--topology-only'): + global topology_only + + if topology_only: tgen = get_topogen() if tgen is not None: # Allow user to play with the setup. @@ -44,9 +46,15 @@ def pytest_assertrepr_compare(op, left, right): def pytest_configure(config): "Assert that the environment is correctly configured." + + global topology_only + if not diagnose_env(): pytest.exit('enviroment has errors, please read the logs') + if config.getoption('--topology-only'): + topology_only = True + def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" # Nothing happened From 964c3dba62d7532957f7ea20a82c0afb4c8b3de2 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:02:53 -0400 Subject: [PATCH 47/64] pbrd: Prevent usage of c if it is null It is possible, that a connected lookup from zebra_interface_address_read is null. Protect and Serve Signed-off-by: Donald Sharp --- pbrd/pbr_zebra.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 4f8f50556b..1396d83d65 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -111,8 +111,9 @@ static int interface_address_add(int command, struct zclient *zclient, c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); DEBUGD(&pbr_dbg_zebra, - "%s: %s added %s", __PRETTY_FUNCTION__, c->ifp->name, - prefix2str(c->address, buf, sizeof(buf))); + "%s: %s added %s", __PRETTY_FUNCTION__, + c ? c->ifp->name : "Unknown", + c ? prefix2str(c->address, buf, sizeof(buf)) : "Unknown"); return 0; } From 4bb55bbecc67b33c4f72bf88f6f9c73330a1f44a Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:06:55 -0400 Subject: [PATCH 48/64] zebra: ifp must be a real pointer sometimes The ifp pointer must be pointing at a real location in memory since right above us in this loop we return if it is. Signed-off-by: Donald Sharp --- zebra/zebra_rib.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 2994911165..026fa41af5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -474,8 +474,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( "\t%s: Interface %s is not unnumbered", - __PRETTY_FUNCTION__, - ifp ? ifp->name : "Unknown"); + __PRETTY_FUNCTION__, ifp->name); return 0; } } From b705b4578e4d7e26d3397444e4c9cd3c6f6afd45 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:09:54 -0400 Subject: [PATCH 49/64] bgpd: Remove logically dead code assignment The label value is set to MPLS_LABEL_NONE at the start of the function and we never modify it, testing it for BGP_PREVENT_VRF_2_VRF_LEAK equality will never be true Signed-off-by: Donald Sharp --- bgpd/bgp_mplsvpn.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index d7cb84c323..6eddd0e1e3 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -319,9 +319,6 @@ void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi) bgp->name_pretty, bgp->vrf_id); } - if (label == BGP_PREVENT_VRF_2_VRF_LEAK) - label = MPLS_LABEL_NONE; - zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; } From 8dc7a759180b05a8270378f7f8d87e4338d99f05 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:14:39 -0400 Subject: [PATCH 50/64] zebra: Add some extra safety for route_info The route_info[X].meta_q_map *must* be less than MQ_SIZE or we will do some strange stuff, so assert on it at startup. The distance in route_info is a uint8_t so let's keep the data structure the same. Signed-off-by: Donald Sharp --- zebra/zebra_rib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 026fa41af5..10be5d8fa5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -71,7 +71,7 @@ extern int allow_delete; /* Each route type's string and default distance value. */ static const struct { int key; - int distance; + uint8_t distance; uint8_t meta_q_map; } route_info[ZEBRA_ROUTE_MAX] = { [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 4}, @@ -3407,6 +3407,7 @@ static void check_route_info(void) if (i == ZEBRA_ROUTE_SYSTEM || i == ZEBRA_ROUTE_ALL) continue; assert(route_info[i].key); + assert(route_info[i].meta_q_map < MQ_SIZE); } } From 2613754b2ed966d990c982eb79f34405e15d50e7 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:18:58 -0400 Subject: [PATCH 51/64] bgpd: The vpn variable must be non-null The vpn variable in bgp_evpn_advertise_svi_ip_vni must be non-null as such it is impossible to ever need the !vpn test case. Signed-off-by: Donald Sharp --- bgpd/bgp_evpn_vty.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 4dccc89f52..63b3145b53 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -3259,9 +3259,6 @@ DEFPY(bgp_evpn_advertise_svi_ip_vni, if (!bgp) return CMD_WARNING; - if (!vpn) - return CMD_WARNING; - if (no) evpn_set_advertise_svi_macip(bgp, vpn, 0); else From 24d9575d5372c5b8da1586624252994d1812da3c Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:21:14 -0400 Subject: [PATCH 52/64] ospfd: continue statement is redundant The continue statement is redundant because DISCARD_LSA has one in it already. Signed-off-by: Donald Sharp --- ospfd/ospf_packet.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 43c5e338b0..6bc8c25153 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -2107,7 +2107,6 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, dump_lsa_key(lsa)); DISCARD_LSA(lsa, 4); - continue; } /* Actual flooding procedure. */ From 48e5512c01d3b3484fda64546003f9a723451b35 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:28:38 -0400 Subject: [PATCH 53/64] lib: Remove functionally dead code. The vrf_with_default_name vrf variable is set to NULL and then tested to see if it is valid. Removing the dead code. Signed-off-by: Donald Sharp --- lib/vrf.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/vrf.c b/lib/vrf.c index de50e6a517..6d9ac2e1e4 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -860,7 +860,6 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty), void vrf_set_default_name(const char *default_name, bool force) { struct vrf *def_vrf; - struct vrf *vrf_with_default_name = NULL; static bool def_vrf_forced; def_vrf = vrf_lookup_by_id(VRF_DEFAULT); @@ -871,13 +870,7 @@ void vrf_set_default_name(const char *default_name, bool force) def_vrf->vrf_id); return; } - if (vrf_with_default_name && vrf_with_default_name != def_vrf) { - /* vrf name already used by an other VRF */ - zlog_debug("VRF: %s, avoid changing name to %s, same name exists (%u)", - vrf_with_default_name->name, default_name, - vrf_with_default_name->vrf_id); - return; - } + snprintf(vrf_default_name, VRF_NAMSIZ, "%s", default_name); if (def_vrf) { if (force) From eb6967279c5effc48252e70694061aa20b4e26f3 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:32:02 -0400 Subject: [PATCH 54/64] zebra: Memory allocations do not fail If a memory allocation fails then we *know* we assert and core the program. Signed-off-by: Donald Sharp --- zebra/zebra_dplane.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index e6671bd87c..ae52762c5a 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -1344,10 +1344,6 @@ dplane_route_update_internal(struct route_node *rn, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } /* Init context with info from zebra data structs */ ret = dplane_ctx_route_init(ctx, op, rn, re); @@ -1382,7 +1378,6 @@ dplane_route_update_internal(struct route_node *rn, ret = dplane_route_enqueue(ctx); } -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, memory_order_relaxed); @@ -1562,10 +1557,6 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_lsp_init(ctx, op, lsp); if (ret != AOK) @@ -1601,10 +1592,6 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, struct zebra_dplane_ctx *ctx = NULL; ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_pw_init(ctx, op, pw); if (ret != AOK) @@ -1691,10 +1678,6 @@ static enum zebra_dplane_result intf_addr_update_internal( } ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; @@ -1744,8 +1727,6 @@ static enum zebra_dplane_result intf_addr_update_internal( ret = dplane_route_enqueue(ctx); -done: - /* Increment counter */ atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1, memory_order_relaxed); From 54e53978fcaf8634886aa05e9a89e6316f86b43d Mon Sep 17 00:00:00 2001 From: Juergen Werner Date: Fri, 3 May 2019 20:17:08 +0200 Subject: [PATCH 55/64] doc: Fix failing `make doc` This build system bug was introduced with 9251d1f596. Signed-off-by: Juergen Werner --- doc/developer/subdir.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 479aa04d59..996f12d47f 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -40,7 +40,7 @@ dev_RSTFILES = \ doc/developer/ospf-sr.rst \ doc/developer/ospf.rst \ doc/developer/packaging-debian.rst \ - doc/developer/packaging-redhat.rst + doc/developer/packaging-redhat.rst \ doc/developer/packaging.rst \ doc/developer/testing.rst \ doc/developer/topotests-snippets.rst \ From 6c33ca975ab61a869cc70fa64c0a10bcc6bc1e0d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 3 May 2019 18:41:00 +0000 Subject: [PATCH 56/64] lib: define ZAPI_CALLBACK_ARGS macro Signed-off-by: Quentin Young --- lib/zclient.h | 95 +++++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 56 deletions(-) diff --git a/lib/zclient.h b/lib/zclient.h index 0926281f2e..2744a47f69 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -221,66 +221,49 @@ struct zclient { /* Redistribute defauilt. */ vrf_bitmap_t default_information[AFI_MAX]; +#define ZAPI_CALLBACK_ARGS \ + int cmd, struct zclient *zclient, uint16_t length, vrf_id_t vrf_id + /* Pointer to the callback functions. */ void (*zebra_connected)(struct zclient *); void (*zebra_capabilities)(struct zclient_capabilities *cap); - int (*router_id_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_delete)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_up)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_down)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_address_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_address_delete)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_link_params)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_bfd_dest_update)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_nbr_address_add)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_nbr_address_delete)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_vrf_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*nexthop_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*import_check_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*bfd_dest_replay)(int, struct zclient *, uint16_t, vrf_id_t); - int (*redistribute_route_add)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*redistribute_route_del)(int, struct zclient *, uint16_t, - vrf_id_t); + int (*router_id_update)(ZAPI_CALLBACK_ARGS); + int (*interface_add)(ZAPI_CALLBACK_ARGS); + int (*interface_delete)(ZAPI_CALLBACK_ARGS); + int (*interface_up)(ZAPI_CALLBACK_ARGS); + int (*interface_down)(ZAPI_CALLBACK_ARGS); + int (*interface_address_add)(ZAPI_CALLBACK_ARGS); + int (*interface_address_delete)(ZAPI_CALLBACK_ARGS); + int (*interface_link_params)(ZAPI_CALLBACK_ARGS); + int (*interface_bfd_dest_update)(ZAPI_CALLBACK_ARGS); + int (*interface_nbr_address_add)(ZAPI_CALLBACK_ARGS); + int (*interface_nbr_address_delete)(ZAPI_CALLBACK_ARGS); + int (*interface_vrf_update)(ZAPI_CALLBACK_ARGS); + int (*nexthop_update)(ZAPI_CALLBACK_ARGS); + int (*import_check_update)(ZAPI_CALLBACK_ARGS); + int (*bfd_dest_replay)(ZAPI_CALLBACK_ARGS); + int (*redistribute_route_add)(ZAPI_CALLBACK_ARGS); + int (*redistribute_route_del)(ZAPI_CALLBACK_ARGS); int (*fec_update)(int, struct zclient *, uint16_t); - int (*local_es_add)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*local_es_del)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t); - void (*local_ip_prefix_add)(int, struct zclient *, uint16_t, vrf_id_t); - void (*local_ip_prefix_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*route_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*rule_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - void (*label_chunk)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*ipset_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*ipset_entry_notify_owner)(int command, - struct zclient *zclient, - uint16_t length, - vrf_id_t vrf_id); - int (*iptable_notify_owner)(int command, - struct zclient *zclient, - uint16_t length, - vrf_id_t vrf_id); - int (*vxlan_sg_add)(int command, struct zclient *client, - uint16_t length, vrf_id_t vrf_id); - int (*vxlan_sg_del)(int command, struct zclient *client, - uint16_t length, vrf_id_t vrf_id_t); + int (*local_es_add)(ZAPI_CALLBACK_ARGS); + int (*local_es_del)(ZAPI_CALLBACK_ARGS); + int (*local_vni_add)(ZAPI_CALLBACK_ARGS); + int (*local_vni_del)(ZAPI_CALLBACK_ARGS); + int (*local_l3vni_add)(ZAPI_CALLBACK_ARGS); + int (*local_l3vni_del)(ZAPI_CALLBACK_ARGS); + void (*local_ip_prefix_add)(ZAPI_CALLBACK_ARGS); + void (*local_ip_prefix_del)(ZAPI_CALLBACK_ARGS); + int (*local_macip_add)(ZAPI_CALLBACK_ARGS); + int (*local_macip_del)(ZAPI_CALLBACK_ARGS); + int (*pw_status_update)(ZAPI_CALLBACK_ARGS); + int (*route_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*rule_notify_owner)(ZAPI_CALLBACK_ARGS); + void (*label_chunk)(ZAPI_CALLBACK_ARGS); + int (*ipset_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*ipset_entry_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*iptable_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*vxlan_sg_add)(ZAPI_CALLBACK_ARGS); + int (*vxlan_sg_del)(ZAPI_CALLBACK_ARGS); }; /* Zebra API message flag. */ From 49b3b01f4cd6265380dd804279c2ced73a3a8220 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 1 May 2019 00:49:13 +0000 Subject: [PATCH 57/64] zebra: fix zapi msg debugging dumps When we switched to a pthread per client, we lost the ability to correlate zapi message debugs with their handlers in zlog, because the message was logged when it was read off the zapi socket and not right before it was processed. Move the zapi msg hexdump to happen right before we call the message handler. Signed-off-by: Quentin Young --- zebra/zapi_msg.c | 3 +++ zebra/zserv.c | 7 ++----- zebra/zserv.h | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index f31fb53a34..7f6af82018 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2507,6 +2507,9 @@ void zserv_handle_commands(struct zserv *client, struct stream *msg) zapi_parse_header(msg, &hdr); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zserv_log_message(NULL, msg, &hdr); + #if defined(HANDLE_ZAPI_FUZZING) zserv_write_incoming(msg, hdr.command); #endif diff --git a/zebra/zserv.c b/zebra/zserv.c index df5f236c04..e7b0a2302d 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -149,8 +149,8 @@ static void zserv_event(struct zserv *client, enum zserv_event event); * hdr (optional) * The message header */ -static void zserv_log_message(const char *errmsg, struct stream *msg, - struct zmsghdr *hdr) +void zserv_log_message(const char *errmsg, struct stream *msg, + struct zmsghdr *hdr) { zlog_debug("Rx'd ZAPI message"); if (errmsg) @@ -411,9 +411,6 @@ static int zserv_read(struct thread *thread) hdr.vrf_id, hdr.length, sock); - if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) - zserv_log_message(NULL, client->ibuf_work, &hdr); - stream_set_getp(client->ibuf_work, 0); struct stream *msg = stream_dup(client->ibuf_work); diff --git a/zebra/zserv.h b/zebra/zserv.h index 90fd195712..380f23916e 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -240,6 +240,22 @@ extern struct zserv *zserv_find_client(uint8_t proto, unsigned short instance); */ extern void zserv_close_client(struct zserv *client); + +/* + * Log a ZAPI message hexdump. + * + * errmsg + * Error message to include with packet hexdump + * + * msg + * Message to log + * + * hdr + * Message header + */ +void zserv_log_message(const char *errmsg, struct stream *msg, + struct zmsghdr *hdr); + #if defined(HANDLE_ZAPI_FUZZING) extern void zserv_read_file(char *input); #endif From 121f9dee7ce9f1b1b8a81f8bd97eed39ed87b477 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 3 May 2019 19:42:59 +0000 Subject: [PATCH 58/64] *: use ZAPI_CALLBACK_ARGS macro for zapi handlers This macro: - Marks ZAPI callbacks for readability - Standardizes argument names - Makes it simple to add ZAPI arguments in the future - Ensures proper types - Looks better - Shortens function declarations Signed-off-by: Quentin Young --- babeld/babel_interface.c | 26 +++++----- babeld/babel_zebra.c | 5 +- bfdd/ptm_adapter.c | 27 ++++------ bgpd/bgp_bfd.c | 6 +-- bgpd/bgp_zebra.c | 105 ++++++++++++++------------------------- bgpd/rfapi/vnc_zebra.c | 5 +- eigrpd/eigrp_zebra.c | 55 ++++++++------------ isisd/isis_bfd.c | 6 +-- isisd/isis_zebra.c | 31 ++++-------- ldpd/ldp_zebra.c | 56 ++++++++------------- lib/zclient.c | 7 +-- lib/zclient.h | 4 +- nhrpd/nhrp_interface.c | 30 +++++------ nhrpd/nhrp_route.c | 3 +- nhrpd/nhrpd.h | 21 +++----- ospf6d/ospf6_bfd.c | 6 +-- ospf6d/ospf6_zebra.c | 31 ++++-------- ospfd/ospf_bfd.c | 6 +-- ospfd/ospf_zebra.c | 38 ++++++-------- pbrd/pbr_zebra.c | 31 ++++-------- pimd/pim_bfd.c | 6 +-- pimd/pim_nht.c | 5 +- pimd/pim_nht.h | 3 +- pimd/pim_zebra.c | 35 +++++-------- ripd/rip_interface.c | 21 +++----- ripd/rip_interface.h | 3 +- ripd/rip_zebra.c | 7 ++- ripngd/ripng_interface.c | 21 +++----- ripngd/ripng_zebra.c | 5 +- ripngd/ripngd.h | 21 +++----- sharpd/sharp_zebra.c | 28 ++++------- staticd/static_zebra.c | 28 ++++------- 32 files changed, 245 insertions(+), 437 deletions(-) diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c index 0ff89abc49..b84bc39cd8 100644 --- a/babeld/babel_interface.c +++ b/babeld/babel_interface.c @@ -66,7 +66,7 @@ static struct cmd_node babel_interface_node = /* babeld's interface node. */ int -babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_up (ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; struct interface *ifp = NULL; @@ -74,7 +74,7 @@ babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id debugf(BABEL_DEBUG_IF, "receive a 'interface up'"); s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ + ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */ if (ifp == NULL) { return 0; @@ -85,7 +85,7 @@ babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id } int -babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_down (ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; struct interface *ifp = NULL; @@ -93,7 +93,7 @@ babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_ debugf(BABEL_DEBUG_IF, "receive a 'interface down'"); s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ + ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */ if (ifp == NULL) { return 0; @@ -104,14 +104,14 @@ babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_ } int -babel_interface_add (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_add (ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; debugf(BABEL_DEBUG_IF, "receive a 'interface add'"); /* read and add the interface in the iflist. */ - ifp = zebra_interface_add_read (zclient->ibuf, vrf); + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); if (ifp == NULL) { return 0; @@ -122,7 +122,7 @@ babel_interface_add (int cmd, struct zclient *client, zebra_size_t length, vrf_i } int -babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_delete (ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -130,7 +130,7 @@ babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vr debugf(BABEL_DEBUG_IF, "receive a 'interface delete'"); s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ + ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */ if (ifp == NULL) return 0; @@ -146,8 +146,7 @@ babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vr } int -babel_interface_address_add (int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf) +babel_interface_address_add (ZAPI_CALLBACK_ARGS) { babel_interface_nfo *babel_ifp; struct connected *ifc; @@ -156,7 +155,7 @@ babel_interface_address_add (int cmd, struct zclient *client, debugf(BABEL_DEBUG_IF, "receive a 'interface address add'"); ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, - zclient->ibuf, vrf); + zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -183,8 +182,7 @@ babel_interface_address_add (int cmd, struct zclient *client, } int -babel_interface_address_delete (int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf) +babel_interface_address_delete (ZAPI_CALLBACK_ARGS) { babel_interface_nfo *babel_ifp; struct connected *ifc; @@ -193,7 +191,7 @@ babel_interface_address_delete (int cmd, struct zclient *client, debugf(BABEL_DEBUG_IF, "receive a 'interface address delete'"); ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, - zclient->ibuf, vrf); + zclient->ibuf, vrf_id); if (ifc == NULL) return 0; diff --git a/babeld/babel_zebra.c b/babeld/babel_zebra.c index e909f8ea7a..d70823544a 100644 --- a/babeld/babel_zebra.c +++ b/babeld/babel_zebra.c @@ -56,8 +56,7 @@ static struct { /* Zebra route add and delete treatment. */ static int -babel_zebra_read_route (int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf) +babel_zebra_read_route (ZAPI_CALLBACK_ARGS) { struct zapi_route api; @@ -68,7 +67,7 @@ babel_zebra_read_route (int command, struct zclient *zclient, if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { babel_route_add(&api); } else { babel_route_delete(&api); diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index e92500cd80..6f76a15a57 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -487,9 +487,9 @@ stream_failure: log_error("ptm-del-client: failed to deregister client"); } -static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid) +static int bfdd_replay(ZAPI_CALLBACK_ARGS) { - struct stream *msg = zc->ibuf; + struct stream *msg = zclient->ibuf; uint32_t rcmd; STREAM_GETL(msg, rcmd); @@ -590,9 +590,7 @@ static void bfdd_sessions_disable_interface(struct interface *ifp) } } -static int bfdd_interface_update(int cmd, struct zclient *zc, - uint16_t len __attribute__((__unused__)), - vrf_id_t vrfid) +static int bfdd_interface_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -602,7 +600,7 @@ static int bfdd_interface_update(int cmd, struct zclient *zc, * rolling our own. */ if (cmd == ZEBRA_INTERFACE_ADD) { - ifp = zebra_interface_add_read(zc->ibuf, vrfid); + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -611,7 +609,7 @@ static int bfdd_interface_update(int cmd, struct zclient *zc, } /* Update interface information. */ - ifp = zebra_interface_state_read(zc->ibuf, vrfid); + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -622,16 +620,12 @@ static int bfdd_interface_update(int cmd, struct zclient *zc, return 0; } -static int bfdd_interface_vrf_update(int command __attribute__((__unused__)), - struct zclient *zclient, - zebra_size_t length - __attribute__((__unused__)), - vrf_id_t vrfid) +static int bfdd_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t nvrfid; - ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrfid, &nvrfid); + ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, &nvrfid); if (ifp == NULL) return 0; @@ -666,14 +660,11 @@ static void bfdd_sessions_enable_address(struct connected *ifc) } } -static int bfdd_interface_address_update(int cmd, struct zclient *zc, - zebra_size_t len - __attribute__((__unused__)), - vrf_id_t vrfid) +static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS) { struct connected *ifc; - ifc = zebra_interface_address_read(cmd, zc->ibuf, vrfid); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index dadf124eec..3b6499d36f 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -234,8 +234,7 @@ static void bgp_bfd_update_type(struct peer *peer) * bgp_bfd_dest_replay - Replay all the peers that have BFD enabled * to zebra */ -static int bgp_bfd_dest_replay(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_bfd_dest_replay(ZAPI_CALLBACK_ARGS) { struct listnode *mnode, *node, *nnode; struct bgp *bgp; @@ -294,8 +293,7 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status) * has changed and bring down the peer * connectivity if the BFD session went down. */ -static int bgp_bfd_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_bfd_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct prefix dp; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index e42d6ee260..d47c1f9638 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -83,8 +83,7 @@ static inline int bgp_install_info_to_zebra(struct bgp *bgp) int zclient_num_connects; /* Router-id update message from zebra. */ -static int bgp_router_id_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_router_id_update(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -101,17 +100,15 @@ static int bgp_router_id_update(int command, struct zclient *zclient, } /* Nexthop update message from zebra. */ -static int bgp_read_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_read_nexthop_update(ZAPI_CALLBACK_ARGS) { - bgp_parse_nexthop_update(command, vrf_id); + bgp_parse_nexthop_update(cmd, vrf_id); return 0; } -static int bgp_read_import_check_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_read_import_check_update(ZAPI_CALLBACK_ARGS) { - bgp_parse_nexthop_update(command, vrf_id); + bgp_parse_nexthop_update(cmd, vrf_id); return 0; } @@ -206,8 +203,7 @@ static void bgp_nbr_connected_delete(struct bgp *bgp, struct nbr_connected *ifc, } /* Inteface addition message from zebra. */ -static int bgp_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct bgp *bgp; @@ -229,8 +225,7 @@ static int bgp_interface_add(int command, struct zclient *zclient, return 0; } -static int bgp_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_delete(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -255,8 +250,7 @@ static int bgp_interface_delete(int command, struct zclient *zclient, return 0; } -static int bgp_interface_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_up(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -290,8 +284,7 @@ static int bgp_interface_up(int command, struct zclient *zclient, return 0; } -static int bgp_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_down(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -350,15 +343,14 @@ static int bgp_interface_down(int command, struct zclient *zclient, return 0; } -static int bgp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct bgp *bgp; bgp = bgp_lookup_by_vrf_id(vrf_id); - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -388,15 +380,14 @@ static int bgp_interface_address_add(int command, struct zclient *zclient, return 0; } -static int bgp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct bgp *bgp; bgp = bgp_lookup_by_vrf_id(vrf_id); - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -417,13 +408,12 @@ static int bgp_interface_address_delete(int command, struct zclient *zclient, return 0; } -static int bgp_interface_nbr_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_nbr_address_add(ZAPI_CALLBACK_ARGS) { struct nbr_connected *ifc = NULL; struct bgp *bgp; - ifc = zebra_interface_nbr_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -444,15 +434,12 @@ static int bgp_interface_nbr_address_add(int command, struct zclient *zclient, return 0; } -static int bgp_interface_nbr_address_delete(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int bgp_interface_nbr_address_delete(ZAPI_CALLBACK_ARGS) { struct nbr_connected *ifc = NULL; struct bgp *bgp; - ifc = zebra_interface_nbr_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -476,8 +463,7 @@ static int bgp_interface_nbr_address_delete(int command, } /* VRF update for an interface. */ -static int bgp_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -532,8 +518,7 @@ static int bgp_interface_vrf_update(int command, struct zclient *zclient, } /* Zebra route add and delete treatment. */ -static int zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int zebra_read_route(ZAPI_CALLBACK_ARGS) { enum nexthop_types_t nhtype; struct zapi_route api; @@ -562,7 +547,7 @@ static int zebra_read_route(int command, struct zclient *zclient, ifindex = api.nexthops[0].ifindex; nhtype = api.nexthops[0].type; - add = (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD); + add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) { /* * The ADD message is actually an UPDATE and there is no @@ -2101,8 +2086,7 @@ int bgp_zebra_dup_addr_detection(struct bgp *bgp) return zclient_send_message(zclient); } -static int rule_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rule_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t seqno, priority, unique; enum zapi_rule_notify_owner note; @@ -2171,8 +2155,7 @@ static int rule_notify_owner(int command, struct zclient *zclient, return 0; } -static int ipset_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ipset_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; enum zapi_ipset_notify_owner note; @@ -2217,8 +2200,7 @@ static int ipset_notify_owner(int command, struct zclient *zclient, return 0; } -static int ipset_entry_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ipset_entry_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; char ipset_name[ZEBRA_IPSET_NAME_SIZE]; @@ -2275,8 +2257,7 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient, return 0; } -static int iptable_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int iptable_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; enum zapi_iptable_notify_owner note; @@ -2322,7 +2303,7 @@ static int iptable_notify_owner(int command, struct zclient *zclient, /* this function is used to forge ip rule, * - either for iptable/ipset using fwmark id - * - or for sample ip rule command + * - or for sample ip rule cmd */ static void bgp_encode_pbr_rule_action(struct stream *s, struct bgp_pbr_action *pbra, @@ -2470,8 +2451,7 @@ static void bgp_zebra_connected(struct zclient *zclient) */ } -static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_es(ZAPI_CALLBACK_ARGS) { esi_t esi; struct bgp *bgp = NULL; @@ -2504,8 +2484,7 @@ static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient, return 0; } -static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS) { int filter = 0; char buf[ETHER_ADDR_STRLEN]; @@ -2545,8 +2524,7 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, return 0; } -static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) { struct stream *s; vni_t vni; @@ -2557,7 +2535,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, s = zclient->ibuf; vni = stream_getl(s); - if (command == ZEBRA_VNI_ADD) { + if (cmd == ZEBRA_VNI_ADD) { vtep_ip.s_addr = stream_get_ipv4(s); stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); mcast_grp.s_addr = stream_get_ipv4(s); @@ -2569,11 +2547,11 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s", - (command == ZEBRA_VNI_ADD) ? "add" : "del", + (cmd == ZEBRA_VNI_ADD) ? "add" : "del", vrf_id_to_name(vrf_id), vni, vrf_id_to_name(tenant_vrf_id)); - if (command == ZEBRA_VNI_ADD) + if (cmd == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id, tenant_vrf_id, mcast_grp); @@ -2581,8 +2559,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, return bgp_evpn_local_vni_del(bgp, vni); } -static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS) { struct stream *s; vni_t vni; @@ -2605,7 +2582,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, && ipa_len != IPV6_MAX_BYTELEN) { flog_err(EC_BGP_MACIP_LEN, "%u:Recv MACIP %s with invalid IP addr length %d", - vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del", + vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", ipa_len); return -1; } @@ -2615,7 +2592,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6; stream_get(&ip.ip.addr, s, ipa_len); } - if (command == ZEBRA_MACIP_ADD) { + if (cmd == ZEBRA_MACIP_ADD) { flags = stream_getc(s); seqnum = stream_getl(s); } else { @@ -2628,21 +2605,19 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u seq %u state %d", - vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del", + vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, prefix_mac2str(&mac, buf, sizeof(buf)), ipaddr2str(&ip, buf1, sizeof(buf1)), vni, seqnum, state); - if (command == ZEBRA_MACIP_ADD) + if (cmd == ZEBRA_MACIP_ADD) return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, flags, seqnum); else return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip, state); } -static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static void bgp_zebra_process_local_ip_prefix(ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; struct bgp *bgp_vrf = NULL; @@ -2682,11 +2657,7 @@ static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient, } } -static void bgp_zebra_process_label_chunk( - int cmd, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static void bgp_zebra_process_label_chunk(ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; uint8_t response_keep; diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index b08e922962..481500dfb4 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -344,8 +344,7 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) * * Assumes 1 nexthop */ -static int vnc_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int vnc_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; int add; @@ -357,7 +356,7 @@ static int vnc_zebra_read_route(int command, struct zclient *zclient, if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; - add = (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD); + add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) vnc_redistribute_add(&api.prefix, api.metric, api.type); else diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index dc1ae675b0..0a74e86263 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -53,21 +53,15 @@ #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -static int eigrp_interface_add(int, struct zclient *, zebra_size_t, vrf_id_t); -static int eigrp_interface_delete(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int eigrp_interface_address_add(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_address_delete(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_state_up(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_state_down(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); +static int eigrp_interface_add(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_delete(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_state_up(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_state_down(ZAPI_CALLBACK_ARGS); static struct interface *zebra_interface_if_lookup(struct stream *); -static int eigrp_zebra_read_route(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); +static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS); /* Zebra structure to hold current status. */ struct zclient *zclient = NULL; @@ -77,8 +71,7 @@ extern struct thread_master *master; struct in_addr router_id_zebra; /* Router-id update message from zebra. */ -static int eigrp_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct eigrp *eigrp; struct prefix router_id; @@ -94,8 +87,7 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_zebra_route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -134,8 +126,7 @@ void eigrp_zebra_init(void) /* Zebra route add and delete treatment. */ -static int eigrp_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct eigrp *eigrp; @@ -150,9 +141,9 @@ static int eigrp_zebra_read_route(int command, struct zclient *zclient, if (eigrp == NULL) return 0; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { - } else /* if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ + } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ { } @@ -160,8 +151,7 @@ static int eigrp_zebra_read_route(int command, struct zclient *zclient, } /* Inteface addition message from zebra. */ -static int eigrp_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct eigrp_interface *ei; @@ -180,8 +170,7 @@ static int eigrp_interface_add(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -211,12 +200,11 @@ static int eigrp_interface_delete(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -233,14 +221,13 @@ static int eigrp_interface_address_add(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; struct interface *ifp; struct eigrp_interface *ei; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -266,8 +253,7 @@ static int eigrp_interface_address_delete(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -323,8 +309,7 @@ static int eigrp_interface_state_up(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index 81976f8dd2..a98c44eeb0 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -92,8 +92,7 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, isis_adj_state_change(adj, ISIS_ADJ_DOWN, "bfd session went down"); } -static int isis_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct prefix dst_ip; @@ -138,8 +137,7 @@ static int isis_bfd_interface_dest_update(int command, struct zclient *zclient, return 0; } -static int isis_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 904ab99c79..e2ef934696 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -53,8 +53,7 @@ struct zclient *zclient = NULL; /* Router-id update message from zebra. */ -static int isis_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct isis_area *area; struct listnode *node; @@ -72,8 +71,7 @@ static int isis_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -86,8 +84,7 @@ static int isis_zebra_if_add(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_del(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -113,8 +110,7 @@ static int isis_zebra_if_del(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -128,8 +124,7 @@ static int isis_zebra_if_state_up(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct isis_circuit *circuit; @@ -147,8 +142,7 @@ static int isis_zebra_if_state_down(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -175,8 +169,7 @@ static int isis_zebra_if_address_add(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_address_del(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_address_del(ZAPI_CALLBACK_ARGS) { struct connected *c; struct interface *ifp; @@ -210,8 +203,7 @@ static int isis_zebra_if_address_del(int command, struct zclient *client, return 0; } -static int isis_zebra_link_params(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_link_params(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -346,8 +338,7 @@ void isis_zebra_route_update(struct prefix *prefix, isis_zebra_route_del_route(prefix, src_p, route_info); } -static int isis_zebra_read(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_read(ZAPI_CALLBACK_ARGS) { struct zapi_route api; @@ -367,10 +358,10 @@ static int isis_zebra_read(int command, struct zclient *zclient, if (api.prefix.prefixlen == 0 && api.src_prefix.prefixlen == 0 && api.type == PROTO_TYPE) { - command = ZEBRA_REDISTRIBUTE_ROUTE_DEL; + cmd = ZEBRA_REDISTRIBUTE_ROUTE_DEL; } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) isis_redist_add(api.type, &api.prefix, &api.src_prefix, api.distance, api.metric); else diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 9dc5677358..35a7d944d3 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -38,22 +38,14 @@ static void ifp2kif(struct interface *, struct kif *); static void ifc2kaddr(struct interface *, struct connected *, struct kaddr *); static int zebra_send_mpls_labels(int, struct kroute *); -static int ldp_router_id_update(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_add(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_delete(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_status_change(int command, struct zclient *, - zebra_size_t, vrf_id_t); -static int ldp_interface_address_add(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_address_delete(int, struct zclient *, - zebra_size_t, vrf_id_t); -static int ldp_zebra_read_route(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_zebra_read_pw_status_update(int, struct zclient *, - zebra_size_t, vrf_id_t); +static int ldp_router_id_update(ZAPI_CALLBACK_ARGS); +static int ldp_interface_add(ZAPI_CALLBACK_ARGS); +static int ldp_interface_delete(ZAPI_CALLBACK_ARGS); +static int ldp_interface_status_change(ZAPI_CALLBACK_ARGS); +static int ldp_interface_address_add(ZAPI_CALLBACK_ARGS); +static int ldp_interface_address_delete(ZAPI_CALLBACK_ARGS); +static int ldp_zebra_read_route(ZAPI_CALLBACK_ARGS); +static int ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS); static void ldp_zebra_connected(struct zclient *); static struct zclient *zclient; @@ -235,8 +227,7 @@ kif_redistribute(const char *ifname) } static int -ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_router_id_update(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -255,8 +246,7 @@ ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length, } static int -ldp_interface_add(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct kif kif; @@ -272,8 +262,7 @@ ldp_interface_add(int command, struct zclient *zclient, zebra_size_t length, } static int -ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct kif kif; @@ -297,8 +286,7 @@ ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length, } static int -ldp_interface_status_change(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_status_change(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct listnode *node; @@ -337,14 +325,13 @@ ldp_interface_status_change(int command, struct zclient *zclient, } static int -ldp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct interface *ifp; struct kaddr ka; - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return (0); @@ -365,14 +352,13 @@ ldp_interface_address_add(int command, struct zclient *zclient, } static int -ldp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct interface *ifp; struct kaddr ka; - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return (0); @@ -394,8 +380,7 @@ ldp_interface_address_delete(int command, struct zclient *zclient, } static int -ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -439,7 +424,7 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, (kr.af == AF_INET6 && IN6_IS_SCOPE_EMBED(&kr.prefix.v6))) return (0); - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) add = 1; if (api.nexthop_num == 0) @@ -502,12 +487,11 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, * Receive PW status update from Zebra and send it to LDE process. */ static int -ldp_zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS) { struct zapi_pw_status zpw; - zebra_read_pw_status_update(command, zclient, length, vrf_id, &zpw); + zebra_read_pw_status_update(cmd, zclient, length, vrf_id, &zpw); debug_zebra_in("pseudowire %s status %s", zpw.ifname, (zpw.status == PW_STATUS_UP) ? "up" : "down"); diff --git a/lib/zclient.c b/lib/zclient.c index 4901c92743..96a78efad6 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2371,9 +2371,7 @@ int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw) /* * Receive PW status update from Zebra and send it to LDE process. */ -void zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id, - struct zapi_pw_status *pw) +void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw) { struct stream *s; @@ -2386,8 +2384,7 @@ void zebra_read_pw_status_update(int command, struct zclient *zclient, pw->status = stream_getl(s); } -static void zclient_capability_decode(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static void zclient_capability_decode(ZAPI_CALLBACK_ARGS) { struct zclient_capabilities cap; struct stream *s = zclient->ibuf; diff --git a/lib/zclient.h b/lib/zclient.h index 2744a47f69..c46d63bfab 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -580,9 +580,7 @@ extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start, extern int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw); -extern void zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id, - struct zapi_pw_status *pw); +extern void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw); extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *); extern int zclient_send_rnh(struct zclient *zclient, int command, diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index b33eaa0478..8f1ba14fe4 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -296,13 +296,12 @@ void nhrp_interface_update(struct interface *ifp) } } -int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; /* read and add the interface in the iflist. */ - ifp = zebra_interface_add_read(client->ibuf, vrf_id); + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -315,13 +314,12 @@ int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; - s = client->ibuf; + s = zclient->ibuf; ifp = zebra_interface_state_read(s, vrf_id); if (ifp == NULL) return 0; @@ -335,12 +333,11 @@ int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_interface_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; - ifp = zebra_interface_state_read(client->ibuf, vrf_id); + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -350,12 +347,11 @@ int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_interface_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; - ifp = zebra_interface_state_read(client->ibuf, vrf_id); + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -364,13 +360,12 @@ int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_address_add(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; char buf[PREFIX_STRLEN]; - ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -383,13 +378,12 @@ int nhrp_interface_address_add(int cmd, struct zclient *client, return 0; } -int nhrp_interface_address_delete(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; char buf[PREFIX_STRLEN]; - ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index dae00bbcea..a788eb2efb 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -184,8 +184,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, &api); } -int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_route_read(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct zapi_nexthop *api_nh; diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index 8f1c63457a..89de145e65 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -314,18 +314,12 @@ void nhrp_interface_init(void); void nhrp_interface_update(struct interface *ifp); void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi); -int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_address_add(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id); -int nhrp_interface_address_delete(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id); +int nhrp_interface_add(ZAPI_CALLBACK_ARGS); +int nhrp_interface_delete(ZAPI_CALLBACK_ARGS); +int nhrp_interface_up(ZAPI_CALLBACK_ARGS); +int nhrp_interface_down(ZAPI_CALLBACK_ARGS); +int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS); +int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS); void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn); @@ -349,8 +343,7 @@ void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp); void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu); -int nhrp_route_read(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id); +int nhrp_route_read(ZAPI_CALLBACK_ARGS); int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp); enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c index e7284a6659..3394bd75db 100644 --- a/ospf6d/ospf6_bfd.c +++ b/ospf6d/ospf6_bfd.c @@ -138,8 +138,7 @@ static void ospf6_bfd_reg_dereg_all_nbr(struct ospf6_interface *oi, int command) * ospf6_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); struct listnode *node; @@ -182,8 +181,7 @@ static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient, * has changed and bring down the neighbor * connectivity if BFD down is received. */ -static int ospf6_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf6_interface *oi; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index abdc82a738..8db4ffef18 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -48,8 +48,7 @@ unsigned char conf_debug_ospf6_zebra = 0; struct zclient *zclient = NULL; /* Router-id update message from zebra. */ -static int ospf6_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct prefix router_id; struct ospf6 *o = ospf6; @@ -99,8 +98,7 @@ void ospf6_zebra_no_redistribute(int type) } /* Inteface addition message from zebra. */ -static int ospf6_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_if_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -112,8 +110,7 @@ static int ospf6_zebra_if_add(int command, struct zclient *zclient, return 0; } -static int ospf6_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_if_del(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -132,8 +129,7 @@ static int ospf6_zebra_if_del(int command, struct zclient *zclient, return 0; } -static int ospf6_zebra_if_state_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_if_state_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -152,10 +148,7 @@ static int ospf6_zebra_if_state_update(int command, struct zclient *zclient, return 0; } -static int ospf6_zebra_if_address_update_add(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[128]; @@ -179,10 +172,7 @@ static int ospf6_zebra_if_address_update_add(int command, return 0; } -static int ospf6_zebra_if_address_update_delete(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[128]; @@ -209,8 +199,7 @@ static int ospf6_zebra_if_address_update_delete(int command, return 0; } -static int ospf6_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; unsigned long ifindex; @@ -240,13 +229,13 @@ static int ospf6_zebra_read_route(int command, struct zclient *zclient, zlog_debug( "Zebra Receive route %s: %s %s nexthop %s ifindex %ld tag %" ROUTE_TAG_PRI, - (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add" - : "delete"), + (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add" + : "delete"), zebra_route_string(api.type), prefixstr, nexthopstr, ifindex, api.tag); } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) ospf6_asbr_redistribute_add(api.type, ifindex, &api.prefix, api.nexthop_num, nexthop, api.tag); else diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c index 594735a08f..42696b85db 100644 --- a/ospfd/ospf_bfd.c +++ b/ospfd/ospf_bfd.c @@ -141,8 +141,7 @@ static int ospf_bfd_reg_dereg_all_nbr(struct interface *ifp, int command) * ospf_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int ospf_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { struct listnode *inode, *node, *onode; struct ospf *ospf; @@ -195,8 +194,7 @@ static int ospf_bfd_nbr_replay(int command, struct zclient *zclient, * connectivity if the BFD status changed to * down. */ -static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf_interface *oi; diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 4cbd817ad8..16796e68fb 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -65,8 +65,7 @@ struct zclient *zclient = NULL; extern struct thread_master *master; /* Router-id update message from zebra. */ -static int ospf_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct ospf *ospf = NULL; struct prefix router_id; @@ -99,8 +98,7 @@ static int ospf_router_id_update_zebra(int command, struct zclient *zclient, } /* Inteface addition message from zebra. */ -static int ospf_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; struct ospf *ospf = NULL; @@ -138,8 +136,7 @@ static int ospf_interface_add(int command, struct zclient *zclient, return 0; } -static int ospf_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -181,8 +178,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s, return if_lookup_by_name(ifname_tmp, vrf_id); } -static int ospf_interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf_interface *oi; @@ -238,8 +234,7 @@ static int ospf_interface_state_up(int command, struct zclient *zclient, return 0; } -static int ospf_interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf_interface *oi; @@ -263,14 +258,13 @@ static int ospf_interface_state_down(int command, struct zclient *zclient, return 0; } -static int ospf_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct ospf *ospf = NULL; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -294,8 +288,7 @@ static int ospf_interface_address_add(int command, struct zclient *zclient, return 0; } -static int ospf_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; struct interface *ifp; @@ -303,7 +296,7 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient, struct route_node *rn; struct prefix p; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -339,8 +332,7 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient, return 0; } -static int ospf_interface_link_params(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_link_params(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -356,8 +348,7 @@ static int ospf_interface_link_params(int command, struct zclient *zclient, } /* VRF update for an interface. */ -static int ospf_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; vrf_id_t new_vrf_id; @@ -1003,8 +994,7 @@ void ospf_routemap_unset(struct ospf_redist *red) } /* Zebra route add and delete treatment. */ -static int ospf_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct prefix_ipv4 p; @@ -1047,7 +1037,7 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, zebra_route_string(api.type), vrf_id, buf_prefix); } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { /* XXX|HACK|TODO|FIXME: * Maybe we should ignore reject/blackhole routes? Testing * shows that there is no problems though and this is only way @@ -1108,7 +1098,7 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, } } } - } else /* if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ + } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ { ospf_external_info_delete(ospf, rt_type, api.instance, p); if (is_prefix_default(&p)) diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 4f8f50556b..19e7a1ec17 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -59,8 +59,7 @@ struct pbr_interface *pbr_if_new(struct interface *ifp) } /* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -80,8 +79,7 @@ static int interface_add(int command, struct zclient *zclient, return 0; } -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -102,13 +100,12 @@ static int interface_delete(int command, struct zclient *zclient, return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[PREFIX_STRLEN]; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); DEBUGD(&pbr_dbg_zebra, "%s: %s added %s", __PRETTY_FUNCTION__, c->ifp->name, @@ -117,13 +114,12 @@ static int interface_address_add(int command, struct zclient *zclient, return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[PREFIX_STRLEN]; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -136,8 +132,7 @@ static int interface_address_delete(int command, struct zclient *zclient, return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -151,8 +146,7 @@ static int interface_state_up(int command, struct zclient *zclient, return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -166,8 +160,7 @@ static int interface_state_down(int command, struct zclient *zclient, return 0; } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -212,8 +205,7 @@ static int route_notify_owner(int command, struct zclient *zclient, return 0; } -static int rule_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rule_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t seqno, priority, unique; enum zapi_rule_notify_owner note; @@ -401,8 +393,7 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi) } } -static int pbr_zebra_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) { struct zapi_route nhr; char buf[PREFIX2STR_BUFFER]; diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c index 466cc60643..82087c4c56 100644 --- a/pimd/pim_bfd.c +++ b/pimd/pim_bfd.c @@ -208,8 +208,7 @@ void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx, * connectivity if the BFD status changed to * down. */ -static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; struct pim_interface *pim_ifp = NULL; @@ -288,8 +287,7 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, * pim_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int pim_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; struct pim_interface *pim_ifp = NULL; diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 8a459fe86e..48b9f1f284 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -553,8 +553,7 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, /* This API is used to parse Registered address nexthop update coming from Zebra */ -int pim_parse_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) { struct nexthop *nexthop; struct nexthop *nhlist_head = NULL; @@ -581,7 +580,7 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, return 0; } - if (command == ZEBRA_NEXTHOP_UPDATE) { + if (cmd == ZEBRA_NEXTHOP_UPDATE) { prefix_copy(&rpf.rpf_addr, &nhr.prefix); pnc = pim_nexthop_cache_find(pim, &rpf); if (!pnc) { diff --git a/pimd/pim_nht.h b/pimd/pim_nht.h index 13bb0fcb55..e7a5fa7720 100644 --- a/pimd/pim_nht.h +++ b/pimd/pim_nht.h @@ -47,8 +47,7 @@ struct pim_nexthop_cache { struct hash *upstream_hash; }; -int pim_parse_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS); int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, struct pim_upstream *up, struct rp_info *rp, struct pim_nexthop_cache *out_pnc); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index aeaea7d69f..11d39bb84f 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -54,8 +54,7 @@ static struct zclient *zclient = NULL; /* Router-id update message from zebra. */ -static int pim_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -64,8 +63,7 @@ static int pim_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct pim_instance *pim; @@ -126,8 +124,7 @@ static int pim_zebra_if_add(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_del(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct pim_instance *pim; @@ -166,8 +163,7 @@ static int pim_zebra_if_del(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_state_up(ZAPI_CALLBACK_ARGS) { struct pim_instance *pim; struct interface *ifp; @@ -235,8 +231,7 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -280,8 +275,7 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, return 0; } -static int pim_zebra_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -326,8 +320,7 @@ static void dump_if_address(struct interface *ifp) } #endif -static int pim_zebra_if_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -342,7 +335,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, will add address to interface list by calling connected_add_by_prefix() */ - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -406,8 +399,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_address_del(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -426,7 +418,7 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, will remove address from interface list by calling connected_delete_by_prefix() */ - c = zebra_interface_address_read(command, client->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -554,8 +546,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, pim_upstream_update_join_desired(pim, up); } -static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS) { struct stream *s; struct pim_instance *pim; @@ -577,11 +568,11 @@ static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient, pim_str_sg_set(&sg, sg_str); zlog_debug("%u:recv SG %s %s", vrf_id, - (command == ZEBRA_VXLAN_SG_ADD)?"add":"del", + (cmd == ZEBRA_VXLAN_SG_ADD)?"add":"del", sg_str); } - if (command == ZEBRA_VXLAN_SG_ADD) + if (cmd == ZEBRA_VXLAN_SG_ADD) pim_vxlan_sg_add(pim, &sg); else pim_vxlan_sg_del(pim, &sg); diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index b909cbcb2b..634fee0b30 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -344,8 +344,7 @@ int if_check_address(struct rip *rip, struct in_addr addr) } /* Inteface link down message processing. */ -int rip_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -372,8 +371,7 @@ int rip_interface_down(int command, struct zclient *zclient, } /* Inteface link up message processing */ -int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +int rip_interface_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -405,8 +403,7 @@ int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length, } /* Inteface addition message from zebra. */ -int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +int rip_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -436,8 +433,7 @@ int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length, return 0; } -int rip_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -468,8 +464,7 @@ int rip_interface_delete(int command, struct zclient *zclient, } /* VRF update for an interface. */ -int rip_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -615,8 +610,7 @@ static void rip_apply_address_add(struct connected *ifc) 0); } -int rip_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; @@ -669,8 +663,7 @@ static void rip_apply_address_del(struct connected *ifc) &address, ifc->ifp->ifindex); } -int rip_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h index 303be0315d..6befda0e28 100644 --- a/ripd/rip_interface.h +++ b/ripd/rip_interface.h @@ -30,8 +30,7 @@ extern int rip_interface_address_add(int, struct zclient *, zebra_size_t, vrf_id_t); extern int rip_interface_address_delete(int, struct zclient *, zebra_size_t, vrf_id_t); -extern int rip_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +extern int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS); extern void rip_interface_sync(struct interface *ifp); #endif /* _QUAGGA_RIP_INTERFACE_H */ diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 4f0df12232..0c88cb202b 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -118,8 +118,7 @@ void rip_zebra_ipv4_delete(struct rip *rip, struct route_node *rp) } /* Zebra route add and delete treatment. */ -static int rip_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rip_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct rip *rip; struct zapi_route api; @@ -138,11 +137,11 @@ static int rip_zebra_read_route(int command, struct zclient *zclient, nh.ifindex = api.nexthops[0].ifindex; /* Then fetch IPv4 prefixes. */ - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) rip_redistribute_add(rip, api.type, RIP_ROUTE_REDISTRIBUTE, (struct prefix_ipv4 *)&api.prefix, &nh, api.metric, api.distance, api.tag); - else if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) + else if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) rip_redistribute_delete(rip, api.type, RIP_ROUTE_REDISTRIBUTE, (struct prefix_ipv4 *)&api.prefix, nh.ifindex); diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index e35652b1ac..5a4087b177 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -193,8 +193,7 @@ static int ripng_if_down(struct interface *ifp) } /* Inteface link up message processing. */ -int ripng_interface_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_up(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -228,8 +227,7 @@ int ripng_interface_up(int command, struct zclient *zclient, } /* Inteface link down message processing. */ -int ripng_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_down(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -255,8 +253,7 @@ int ripng_interface_down(int command, struct zclient *zclient, } /* Inteface addition message from zebra. */ -int ripng_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -281,8 +278,7 @@ int ripng_interface_add(int command, struct zclient *zclient, return 0; } -int ripng_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -313,8 +309,7 @@ int ripng_interface_delete(int command, struct zclient *zclient, } /* VRF update for an interface. */ -int ripng_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -383,8 +378,7 @@ static void ripng_apply_address_add(struct connected *ifc) ifc->ifp->ifindex, NULL, 0); } -int ripng_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -450,8 +444,7 @@ static void ripng_apply_address_del(struct connected *ifc) ifc->ifp->ifindex); } -int ripng_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index cf60de2de9..a557a90c82 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -113,8 +113,7 @@ void ripng_zebra_ipv6_delete(struct ripng *ripng, struct agg_node *rp) } /* Zebra route add and delete treatment. */ -static int ripng_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ripng_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct ripng *ripng; struct zapi_route api; @@ -138,7 +137,7 @@ static int ripng_zebra_read_route(int command, struct zclient *zclient, nexthop = api.nexthops[0].gate.ipv6; ifindex = api.nexthops[0].ifindex; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) ripng_redistribute_add(ripng, api.type, RIPNG_ROUTE_REDISTRIBUTE, (struct prefix_ipv6 *)&api.prefix, diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index dc425b6958..a2686304fc 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -468,20 +468,13 @@ extern int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to, extern void ripng_packet_dump(struct ripng_packet *packet, int size, const char *sndrcv); -extern int ripng_interface_up(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_down(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_add(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_delete(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_address_add(int command, struct zclient *, - zebra_size_t, vrf_id_t); -extern int ripng_interface_address_delete(int command, struct zclient *, - zebra_size_t, vrf_id_t); -extern int ripng_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +extern int ripng_interface_up(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_down(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_add(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_delete(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_address_add(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS); extern void ripng_interface_sync(struct interface *ifp); extern struct ripng *ripng_lookup_by_vrf_id(vrf_id_t vrf_id); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index f1e83628c2..19c7e556ca 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -58,8 +58,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s) } /* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -71,8 +70,7 @@ static int interface_add(int command, struct zclient *zclient, return 0; } -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -90,21 +88,19 @@ static int interface_delete(int command, struct zclient *zclient, return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_add(ZAPI_CALLBACK_ARGS) { - zebra_interface_address_read(command, zclient->ibuf, vrf_id); + zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -113,8 +109,7 @@ static int interface_address_delete(int command, struct zclient *zclient, return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_up(ZAPI_CALLBACK_ARGS) { zebra_interface_if_lookup(zclient->ibuf); @@ -122,8 +117,7 @@ static int interface_state_up(int command, struct zclient *zclient, return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_down(ZAPI_CALLBACK_ARGS) { zebra_interface_state_read(zclient->ibuf, vrf_id); @@ -202,8 +196,7 @@ static void handle_repeated(bool installed) } } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct timeval r; struct prefix p; @@ -345,8 +338,7 @@ void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, __PRETTY_FUNCTION__); } -static int sharp_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS) { struct sharp_nh_tracker *nht; struct zapi_route nhr; diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 3f31177524..c03a371d88 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -59,8 +59,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s) } /* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -73,8 +72,7 @@ static int interface_add(int command, struct zclient *zclient, return 0; } -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -93,20 +91,18 @@ static int interface_delete(int command, struct zclient *zclient, return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_add(ZAPI_CALLBACK_ARGS) { - zebra_interface_address_read(command, zclient->ibuf, vrf_id); + zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -115,8 +111,7 @@ static int interface_address_delete(int command, struct zclient *zclient, return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -138,16 +133,14 @@ static int interface_state_up(int command, struct zclient *zclient, return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_down(ZAPI_CALLBACK_ARGS) { zebra_interface_state_read(zclient->ibuf, vrf_id); return 0; } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -194,8 +187,7 @@ struct static_nht_data { uint8_t nh_num; }; -static int static_zebra_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) { struct static_nht_data *nhtd, lookup; struct zapi_route nhr; From 37bb7aca30eb0997820e888dcd171f7f3c86bffa Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 2 May 2019 09:35:29 +0200 Subject: [PATCH 59/64] bgpd: add bfd event trace that bfd event trace is visible when 'debug bgp neighbor-event' is enabled. Signed-off-by: Philippe Guibert --- bgpd/bgp_bfd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index dadf124eec..f1737ff9f0 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -276,6 +276,11 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status) bfd_info->status = status; bfd_info->last_update = bgp_clock(); + if (status != old_status) { + if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) + zlog_debug("[%s]: BFD %s", peer->host, + bfd_get_status_str(status)); + } if ((status == BFD_STATUS_DOWN) && (old_status == BFD_STATUS_UP)) { peer->last_reset = PEER_DOWN_BFD_DOWN; BGP_EVENT_ADD(peer, BGP_Stop); From 4828ea7749f62d503ff0b322a39d0ecd452db601 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 2 May 2019 09:36:06 +0200 Subject: [PATCH 60/64] ospfd: add bfd up event trace that bfd trace is visible when using 'debug ospf nsm event' command. Signed-off-by: Philippe Guibert --- ospfd/ospf_bfd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c index 594735a08f..a4f66ab70a 100644 --- a/ospfd/ospf_bfd.c +++ b/ospfd/ospf_bfd.c @@ -251,6 +251,13 @@ static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); } + if ((status == BFD_STATUS_UP) + && (old_status == BFD_STATUS_DOWN)) { + if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) + zlog_debug("NSM[%s:%s]: BFD Up", + IF_NAME(nbr->oi), + inet_ntoa(nbr->address.u.prefix4)); + } } return 0; From 35f6f850efe9cc33b23d467a47d74681a7d48699 Mon Sep 17 00:00:00 2001 From: vishaldhingra Date: Mon, 6 May 2019 05:32:58 -0700 Subject: [PATCH 61/64] bgpd : dynamic modification in lcomm-list is not taking effect. lcomm-list is configured and attached to route-map via match clause. If you modify the lcomm-list then it is not taking into effect via routemap. Signed-off-by: vishaldhingra --- bgpd/bgp_clist.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 7b64f349d2..e308e963b5 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -1049,8 +1049,10 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, /* Do not put duplicated community entry. */ if (community_list_dup_check(list, entry)) community_entry_free(entry); - else + else { community_list_entry_add(list, entry); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED); + } return 0; } @@ -1075,6 +1077,7 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, /* Delete all of entry belongs to this community-list. */ if (!str) { community_list_delete(cm, list); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED); return 0; } @@ -1100,6 +1103,7 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, return COMMUNITY_LIST_ERR_CANT_FIND_LIST; community_list_entry_delete(cm, list, entry); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED); return 0; } From 56e7825479d12a9a9cf5e108c02432ea17e5b236 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 6 May 2019 10:41:40 -0400 Subject: [PATCH 62/64] zebra: Be consistent in how we call rib_add( and rib_delete( with tableid The rib_add( and rib_delete( functions are there to allow kernel interactions with the creation of routes. Fixup the code to be consistent in the passup of the tableid. Signed-off-by: Donald Sharp --- zebra/connected.c | 51 ++++++++++++++++++++++++++++--------------- zebra/kernel_socket.c | 8 ++++--- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/zebra/connected.c b/zebra/connected.c index 7114a3286b..bba221c2cf 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -209,8 +209,16 @@ void connected_up(struct interface *ifp, struct connected *ifc) .ifindex = ifp->ifindex, .vrf_id = ifp->vrf_id, }; + struct zebra_vrf *zvrf; uint32_t metric; + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) { + flog_err(EC_ZEBRA_VRF_NOT_FOUND, + "%s: Received Up for interface but no associated zvrf: %d", + __PRETTY_FUNCTION__, ifp->vrf_id); + return; + } if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -246,11 +254,11 @@ void connected_up(struct interface *ifp, struct connected *ifc) metric = (ifc->metric < (uint32_t)METRIC_MAX) ? ifc->metric : ifp->metric; - rib_add(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p, - NULL, &nh, RT_TABLE_MAIN, metric, 0, 0, 0); + rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, zvrf->table_id, metric, 0, 0, 0); - rib_add(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p, - NULL, &nh, RT_TABLE_MAIN, metric, 0, 0, 0); + rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, zvrf->table_id, metric, 0, 0, 0); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; @@ -260,19 +268,19 @@ void connected_up(struct interface *ifp, struct connected *ifc) ifp->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + rib_update(zvrf->vrf->vrf_id, RIB_UPDATE_IF_CHANGE); /* Schedule LSP forwarding entries for processing, if appropriate. */ - if (ifp->vrf_id == VRF_DEFAULT) { + if (zvrf->vrf->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS) { char buf[PREFIX_STRLEN]; zlog_debug( "%u: IF %s IP %s address add/up, scheduling MPLS processing", - ifp->vrf_id, ifp->name, + zvrf->vrf->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), &p); + mpls_mark_lsps_for_processing(zvrf, &p); } } @@ -377,6 +385,15 @@ void connected_down(struct interface *ifp, struct connected *ifc) .ifindex = ifp->ifindex, .vrf_id = ifp->vrf_id, }; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) { + flog_err(EC_ZEBRA_VRF_NOT_FOUND, + "%s: Received Up for interface but no associated zvrf: %d", + __PRETTY_FUNCTION__, ifp->vrf_id); + return; + } if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -410,34 +427,34 @@ void connected_down(struct interface *ifp, struct connected *ifc) * Same logic as for connected_up(): push the changes into the * head. */ - rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, 0, false); + rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, zvrf->table_id, 0, 0, false); - rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, 0, false); + rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, zvrf->table_id, 0, 0, false); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; zlog_debug( "%u: IF %s IP %s address down, scheduling RIB processing", - ifp->vrf_id, ifp->name, + zvrf->vrf->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + rib_update(zvrf->vrf->vrf_id, RIB_UPDATE_IF_CHANGE); /* Schedule LSP forwarding entries for processing, if appropriate. */ - if (ifp->vrf_id == VRF_DEFAULT) { + if (zvrf->vrf->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS) { char buf[PREFIX_STRLEN]; zlog_debug( "%u: IF %s IP %s address down, scheduling MPLS processing", - ifp->vrf_id, ifp->name, + zvrf->vrf->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), &p); + mpls_mark_lsps_for_processing(zvrf, &p); } } diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 13d2185b0f..5f4bd3bbc6 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1138,14 +1138,16 @@ void rtm_read(struct rt_msghdr *rtm) */ if (rtm->rtm_type == RTM_CHANGE) rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, - 0, zebra_flags, &p, NULL, NULL, 0, 0, 0, true); + 0, zebra_flags, &p, NULL, NULL, RT_TABLE_MAIN, + 0, 0, true); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, - zebra_flags, &p, NULL, &nh, 0, 0, 0, 0, 0); + zebra_flags, &p, NULL, &nh, RT_TABLE_MAIN, 0, 0, 0, 0); else rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, - 0, zebra_flags, &p, NULL, &nh, 0, 0, 0, true); + 0, zebra_flags, &p, NULL, &nh, RT_TABLE_MAIN, + 0, 0, true); } /* Interface function for the kernel routing table updates. Support From c447ad08b2c880d5f3ef35af2e4055fcbc970961 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 3 May 2019 20:54:20 -0400 Subject: [PATCH 63/64] doc, zebra: Remove "table X" command This command is broken and has been broken since the introduction of vrf's. Since no-one has complained it is safe to assume that there is no call for this specialized linux command. Remove from the system with extreme prejudice. Signed-off-by: Donald Sharp --- doc/user/zebra.rst | 8 -------- zebra/redistribute.c | 9 ++++----- zebra/zebra_rib.c | 3 +-- zebra/zebra_router.c | 1 - zebra/zebra_router.h | 3 --- zebra/zebra_vrf.c | 12 ++++-------- zebra/zebra_vty.c | 42 ------------------------------------------ zebra/zserv.c | 4 ---- zebra/zserv.h | 3 --- 9 files changed, 9 insertions(+), 76 deletions(-) diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index a7bf0c74da..bb3d6e491c 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -268,14 +268,6 @@ Link Parameters Commands for InterASv2 link in OSPF (RFC5392). Note that this option is not yet supported for ISIS (RFC5316). -.. index:: table TABLENO -.. clicmd:: table TABLENO - - Select the primary kernel routing table to be used. This only works for - kernels supporting multiple routing tables (like GNU/Linux 2.2.x and later). - After setting TABLENO with this command, static routes defined after this - are added to the specified table. - .. index:: ip nht resolve-via-default .. clicmd:: ip nht resolve-via-default diff --git a/zebra/redistribute.c b/zebra/redistribute.c index f98a4c02c3..0071e001c6 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -612,7 +612,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, newre->flags = re->flags; newre->metric = re->metric; newre->mtu = re->mtu; - newre->table = zrouter.rtm_table_default; + newre->table = 0; newre->nexthop_num = 0; newre->uptime = time(NULL); newre->instance = re->table; @@ -632,8 +632,8 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) prefix_copy(&p, &rn->p); rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table, - re->flags, &p, NULL, re->ng.nexthop, - zrouter.rtm_table_default, re->metric, re->distance, false); + re->flags, &p, NULL, re->ng.nexthop, 0, re->metric, + re->distance, false); return 0; } @@ -647,8 +647,7 @@ int zebra_import_table(afi_t afi, uint32_t table_id, uint32_t distance, struct route_node *rn; if (!is_zebra_valid_kernel_table(table_id) - || ((table_id == RT_TABLE_MAIN) - || (table_id == zrouter.rtm_table_default))) + || (table_id == RT_TABLE_MAIN)) return (-1); if (afi >= AFI_MAX) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 2994911165..13d6c1a4d5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -165,8 +165,7 @@ int is_zebra_valid_kernel_table(uint32_t table_id) int is_zebra_main_routing_table(uint32_t table_id) { - if ((table_id == RT_TABLE_MAIN) - || (table_id == zrouter.rtm_table_default)) + if (table_id == RT_TABLE_MAIN) return 1; return 0; } diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index a81752d205..63724fc350 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -226,7 +226,6 @@ void zebra_router_init(void) { zrouter.sequence_num = 0; - zrouter.rtm_table_default = 0; zrouter.packets_to_process = ZEBRA_ZAPI_PACKETS_TO_PROCESS; zebra_vxlan_init(); diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 72b5e9b9b1..b316b91d0d 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -89,9 +89,6 @@ struct zebra_router { /* A sequence number used for tracking routes */ _Atomic uint32_t sequence_num; - /* The default table used for this router */ - uint32_t rtm_table_default; - /* rib work queue */ #define ZEBRA_RIB_PROCESS_HOLD_TIME 10 #define ZEBRA_RIB_PROCESS_RETRY_TIME 1 diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 2d721ec8a1..315d5b4905 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -326,15 +326,13 @@ struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, return NULL; if (vrf_id == VRF_DEFAULT) { - if (table_id == RT_TABLE_MAIN - || table_id == zrouter.rtm_table_default) + if (table_id == RT_TABLE_MAIN) table = zebra_vrf_table(afi, safi, vrf_id); else table = zebra_vrf_other_route_table(afi, table_id, vrf_id); } else if (vrf_is_backend_netns()) { - if (table_id == RT_TABLE_MAIN - || table_id == zrouter.rtm_table_default) + if (table_id == RT_TABLE_MAIN) table = zebra_vrf_table(afi, safi, vrf_id); else table = zebra_vrf_other_route_table(afi, table_id, @@ -452,10 +450,8 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, uint32_t table_id, if (afi >= AFI_MAX) return NULL; - if ((table_id != RT_TABLE_MAIN) - && (table_id != zrouter.rtm_table_default)) { - if (zvrf->table_id == RT_TABLE_MAIN || - zvrf->table_id == zrouter.rtm_table_default) { + if (table_id != RT_TABLE_MAIN) { + if (zvrf->table_id == RT_TABLE_MAIN) { /* this VRF use default table * so in all cases, it does not use specific table * so it is possible to configure tables in this VRF diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 8cde07a109..77795c6152 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2556,40 +2556,6 @@ static int config_write_protocol(struct vty *vty) return 1; } -#ifdef HAVE_NETLINK -/* Display default rtm_table for all clients. */ -DEFUN (show_table, - show_table_cmd, - "show table", - SHOW_STR - "default routing table to use for all clients\n") -{ - vty_out(vty, "table %d\n", zrouter.rtm_table_default); - return CMD_SUCCESS; -} - -DEFUN (config_table, - config_table_cmd, - "table TABLENO", - "Configure target kernel routing table\n" - "TABLE integer\n") -{ - zrouter.rtm_table_default = strtol(argv[1]->arg, (char **)0, 10); - return CMD_SUCCESS; -} - -DEFUN (no_config_table, - no_config_table_cmd, - "no table [TABLENO]", - NO_STR - "Configure target kernel routing table\n" - "TABLE integer\n") -{ - zrouter.rtm_table_default = 0; - return CMD_SUCCESS; -} -#endif - DEFUN (show_zebra, show_zebra_cmd, "show zebra", @@ -2833,8 +2799,6 @@ DEFUN (zebra_show_routing_tables_summary, /* Table configuration write function. */ static int config_write_table(struct vty *vty) { - if (zrouter.rtm_table_default) - vty_out(vty, "table %d\n", zrouter.rtm_table_default); return 0; } @@ -2938,12 +2902,6 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_ip_forwarding_cmd); install_element(ENABLE_NODE, &show_zebra_cmd); -#ifdef HAVE_NETLINK - install_element(VIEW_NODE, &show_table_cmd); - install_element(CONFIG_NODE, &config_table_cmd); - install_element(CONFIG_NODE, &no_config_table_cmd); -#endif /* HAVE_NETLINK */ - install_element(VIEW_NODE, &show_ipv6_forwarding_cmd); install_element(CONFIG_NODE, &ipv6_forwarding_cmd); install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd); diff --git a/zebra/zserv.c b/zebra/zserv.c index e7b0a2302d..fbb5af875d 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -697,9 +697,6 @@ static struct zserv *zserv_client_create(int sock) pthread_mutex_init(&client->obuf_mtx, NULL); client->wb = buffer_new(0); - /* Set table number. */ - client->rtm_table = zrouter.rtm_table_default; - atomic_store_explicit(&client->connect_time, (uint32_t) monotime(NULL), memory_order_relaxed); @@ -907,7 +904,6 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "------------------------ \n"); vty_out(vty, "FD: %d \n", client->sock); - vty_out(vty, "Route Table ID: %d \n", client->rtm_table); connect_time = (time_t) atomic_load_explicit(&client->connect_time, memory_order_relaxed); diff --git a/zebra/zserv.h b/zebra/zserv.h index 380f23916e..d6fdc05374 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -83,9 +83,6 @@ struct zserv { /* Threads for the main pthread */ struct thread *t_cleanup; - /* default routing table this client munges */ - int rtm_table; - /* This client's redistribute flag. */ struct redist_proto mi_redist[AFI_MAX][ZEBRA_ROUTE_MAX]; vrf_bitmap_t redist[AFI_MAX][ZEBRA_ROUTE_MAX]; From ed1809c9258b65b40bb3a498ea3b142f7025464f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 6 May 2019 22:38:10 +0000 Subject: [PATCH 64/64] lib: add string replace function Signed-off-by: Quentin Young --- lib/frrstr.c | 26 ++++++++++++++++++++++++++ lib/frrstr.h | 22 ++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/lib/frrstr.c b/lib/frrstr.c index fd337073f8..fbbc890ec6 100644 --- a/lib/frrstr.c +++ b/lib/frrstr.c @@ -152,6 +152,32 @@ void frrstr_strvec_free(vector v) vector_free(v); } +char *frrstr_replace(const char *str, const char *find, const char *replace) +{ + char *ch; + char *nustr = XSTRDUP(MTYPE_TMP, str); + + size_t findlen = strlen(find); + size_t repllen = strlen(replace); + + while ((ch = strstr(nustr, find))) { + if (repllen > findlen) { + size_t nusz = strlen(nustr) + repllen - findlen + 1; + nustr = XREALLOC(MTYPE_TMP, nustr, nusz); + ch = strstr(nustr, find); + } + + size_t nustrlen = strlen(nustr); + size_t taillen = (nustr + nustrlen) - (ch + findlen); + + memmove(ch + findlen + (repllen - findlen), ch + findlen, + taillen + 1); + memcpy(ch, replace, repllen); + } + + return nustr; +} + bool begins_with(const char *str, const char *prefix) { if (!str || !prefix) diff --git a/lib/frrstr.h b/lib/frrstr.h index 8b591849a3..d40ca14ba5 100644 --- a/lib/frrstr.h +++ b/lib/frrstr.h @@ -87,6 +87,28 @@ void frrstr_filter_vec(vector v, regex_t *filter); */ void frrstr_strvec_free(vector v); +/* + * Given a string, replaces all occurrences of a substring with a different + * string. The result is a new string. The original string is not modified. + * + * If 'replace' is longer than 'find', this function performs N+1 allocations, + * where N is the number of times 'find' occurs in 'str'. If 'replace' is equal + * in length or shorter than 'find', only 1 allocation is performed. + * + * str + * String to perform replacement on. + * + * find + * Substring to replace. + * + * replace + * String to replace 'find' with. + * + * Returns: + * A new string, allocated with MTYPE_TMP, that is the result of performing + * the replacement on 'str'. This must be freed by the caller. + */ +char *frrstr_replace(const char *str, const char *find, const char *replace); /* * Prefix match for string. *