From: Christian Franke Date: Mon, 2 Oct 2017 23:42:22 +0000 (+0200) Subject: isisd: optimize per interface lsp send-queue creation X-Git-Tag: frr-4.0-dev~242^2 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=58e1623702c828d286773da0c188af9ba176c030;p=mirror_frr.git isisd: optimize per interface lsp send-queue creation Signed-off-by: Christian Franke --- diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 0afa65d72..c8b9a66e2 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -214,13 +214,11 @@ void isis_adj_state_change(struct isis_adjacency *adj, } else if (new_state == ISIS_ADJ_DOWN) { listnode_delete(circuit->u.bc.adjdb[level - 1], adj); + circuit->upadjcount[level - 1]--; - if (circuit->upadjcount[level - 1] == 0) { - /* Clean lsp_queue when no adj is up. */ - if (circuit->lsp_queue) - list_delete_all_node( - circuit->lsp_queue); - } + if (circuit->upadjcount[level - 1] == 0) + isis_circuit_lsp_queue_clean(circuit); + isis_event_adjacency_state_change(adj, new_state); del = true; @@ -270,12 +268,9 @@ void isis_adj_state_change(struct isis_adjacency *adj, if (adj->circuit->u.p2p.neighbor == adj) adj->circuit->u.p2p.neighbor = NULL; circuit->upadjcount[level - 1]--; - if (circuit->upadjcount[level - 1] == 0) { - /* Clean lsp_queue when no adj is up. */ - if (circuit->lsp_queue) - list_delete_all_node( - circuit->lsp_queue); - } + if (circuit->upadjcount[level - 1] == 0) + isis_circuit_lsp_queue_clean(circuit); + isis_event_adjacency_state_change(adj, new_state); del = true; diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 1a978ee04..1c1385ab7 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -45,6 +45,7 @@ #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isis_lsp.h" +#include "isisd/isis_lsp_hash.h" #include "isisd/isis_pdu.h" #include "isisd/isis_network.h" #include "isisd/isis_misc.h" @@ -674,7 +675,8 @@ int isis_circuit_up(struct isis_circuit *circuit) isis_circuit_prepare(circuit); circuit->lsp_queue = list_new(); - circuit->lsp_queue_last_cleared = time(NULL); + circuit->lsp_hash = isis_lsp_hash_new(); + monotime(&circuit->lsp_queue_last_cleared); return ISIS_OK; } @@ -739,14 +741,19 @@ void isis_circuit_down(struct isis_circuit *circuit) THREAD_TIMER_OFF(circuit->t_send_csnp[1]); THREAD_TIMER_OFF(circuit->t_send_psnp[0]); THREAD_TIMER_OFF(circuit->t_send_psnp[1]); + THREAD_OFF(circuit->t_send_lsp); THREAD_OFF(circuit->t_read); if (circuit->lsp_queue) { - circuit->lsp_queue->del = NULL; list_delete(circuit->lsp_queue); circuit->lsp_queue = NULL; } + if (circuit->lsp_hash) { + isis_lsp_hash_free(circuit->lsp_hash); + circuit->lsp_hash = NULL; + } + /* send one gratuitous hello to spead up convergence */ if (circuit->is_type & IS_LEVEL_1) send_hello(circuit, IS_LEVEL_1); @@ -1339,3 +1346,56 @@ void isis_circuit_init() isis_vty_init(); } + +void isis_circuit_schedule_lsp_send(struct isis_circuit *circuit) +{ + if (circuit->t_send_lsp) + return; + circuit->t_send_lsp = thread_add_event(master, send_lsp, circuit, 0, NULL); +} + +void isis_circuit_queue_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp) +{ + if (isis_lsp_hash_lookup(circuit->lsp_hash, lsp)) + return; + + listnode_add(circuit->lsp_queue, lsp); + isis_lsp_hash_add(circuit->lsp_hash, lsp); + isis_circuit_schedule_lsp_send(circuit); +} + +void isis_circuit_lsp_queue_clean(struct isis_circuit *circuit) +{ + if (!circuit->lsp_queue) + return; + + list_delete_all_node(circuit->lsp_queue); + isis_lsp_hash_clean(circuit->lsp_hash); +} + +void isis_circuit_cancel_queued_lsp(struct isis_circuit *circuit, + struct isis_lsp *lsp) +{ + if (!circuit->lsp_queue) + return; + + listnode_delete(circuit->lsp_queue, lsp); + isis_lsp_hash_release(circuit->lsp_hash, lsp); +} + +struct isis_lsp *isis_circuit_lsp_queue_pop(struct isis_circuit *circuit) +{ + if (!circuit->lsp_queue) + return NULL; + + struct listnode *node = listhead(circuit->lsp_queue); + if (!node) + return NULL; + + struct isis_lsp *rv = listgetdata(node); + + list_delete_node(circuit->lsp_queue, node); + isis_lsp_hash_release(circuit->lsp_hash, rv); + + return rv; +} diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 5906efd2b..29694deb3 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -34,6 +34,8 @@ #define CIRCUIT_MAX 255 +struct isis_lsp; + struct password { struct password *next; int len; @@ -79,8 +81,10 @@ struct isis_circuit { struct thread *t_read; struct thread *t_send_csnp[2]; struct thread *t_send_psnp[2]; + struct thread *t_send_lsp; struct list *lsp_queue; /* LSPs to be txed (both levels) */ - time_t lsp_queue_last_cleared; /* timestamp used to enforce transmit + struct isis_lsp_hash *lsp_hash; /* Hashtable synchronized with lsp_queue */ + struct timeval lsp_queue_last_cleared; /* timestamp used to enforce transmit * interval; * for scalability, use one timestamp per * circuit, instead of one per lsp per @@ -195,4 +199,10 @@ ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid, bool enabled); +void isis_circuit_schedule_lsp_send(struct isis_circuit *circuit); +void isis_circuit_queue_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp); +void isis_circuit_lsp_queue_clean(struct isis_circuit *circuit); +void isis_circuit_cancel_queued_lsp(struct isis_circuit *circuit, + struct isis_lsp *lsp); +struct isis_lsp *isis_circuit_lsp_queue_pop(struct isis_circuit *circuit); #endif /* _ZEBRA_ISIS_CIRCUIT_H */ diff --git a/isisd/isis_constants.h b/isisd/isis_constants.h index f3a5a24dd..b7b5d35c2 100644 --- a/isisd/isis_constants.h +++ b/isisd/isis_constants.h @@ -73,7 +73,7 @@ #define MAX_MIN_LSP_GEN_INTERVAL 120 /* RFC 4444 says 65535 */ #define DEFAULT_MIN_LSP_GEN_INTERVAL 30 -#define MIN_LSP_TRANS_INTERVAL 5 +#define MIN_LSP_TRANS_INTERVAL 20000 /* Microseconds */ #define MIN_CSNP_INTERVAL 1 #define MAX_CSNP_INTERVAL 600 diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 33c52804e..bee6abfda 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -111,25 +111,15 @@ static void lsp_clear_data(struct isis_lsp *lsp) static void lsp_destroy(struct isis_lsp *lsp) { - struct listnode *cnode, *lnode, *lnnode; - struct isis_lsp *lsp_in_list; + struct listnode *cnode; struct isis_circuit *circuit; if (!lsp) return; - if (lsp->area->circuit_list) { - for (ALL_LIST_ELEMENTS_RO(lsp->area->circuit_list, cnode, - circuit)) { - if (circuit->lsp_queue == NULL) - continue; - for (ALL_LIST_ELEMENTS(circuit->lsp_queue, lnode, - lnnode, lsp_in_list)) - if (lsp_in_list == lsp) - list_delete_node(circuit->lsp_queue, - lnode); - } - } + for (ALL_LIST_ELEMENTS_RO(lsp->area->circuit_list, cnode, circuit)) + isis_circuit_cancel_queued_lsp(circuit, lsp); + ISIS_FLAGS_CLEAR_ALL(lsp->SSNflags); ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags); @@ -1890,12 +1880,15 @@ int lsp_tick(struct thread *thread) if (listcount(lsp_list) > 0) { for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) { - int diff = - time(NULL) - - circuit->lsp_queue_last_cleared; - if (circuit->lsp_queue == NULL - || diff < MIN_LSP_TRANS_INTERVAL) + if (!circuit->lsp_queue) continue; + + if (monotime_since( + &circuit->lsp_queue_last_cleared, + NULL) < MIN_LSP_TRANS_INTERVAL) { + continue; + } + for (ALL_LIST_ELEMENTS_RO( lsp_list, lspnode, lsp)) { if (circuit->upadjcount @@ -1903,23 +1896,7 @@ int lsp_tick(struct thread *thread) && ISIS_CHECK_FLAG( lsp->SRMflags, circuit)) { - /* Add the lsp only if - * it is not already in - * lsp - * queue */ - if (!listnode_lookup( - circuit->lsp_queue, - lsp)) { - listnode_add( - circuit->lsp_queue, - lsp); - thread_add_event( - master, - send_lsp, - circuit, - 0, - NULL); - } + isis_circuit_queue_lsp(circuit, lsp); } } } diff --git a/isisd/isis_lsp_hash.c b/isisd/isis_lsp_hash.c new file mode 100644 index 000000000..919612882 --- /dev/null +++ b/isisd/isis_lsp_hash.c @@ -0,0 +1,89 @@ +/* + * IS-IS Rout(e)ing protocol - LSP Hash + * + * Copyright (C) 2017 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR 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. + * + * FRR 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 "hash.h" +#include "jhash.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_lsp_hash.h" + +DEFINE_MTYPE_STATIC(ISISD, LSP_HASH, "ISIS LSP Hash") + +struct isis_lsp_hash { + struct hash *h; +}; + +static unsigned lsp_hash_key(void *lp) +{ + struct isis_lsp *lsp = lp; + + return jhash(lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2, 0x55aa5a5a); +} + +static int lsp_hash_cmp(const void *a, const void *b) +{ + const struct isis_lsp *la = a, *lb = b; + + return 0 == memcmp(la->hdr.lsp_id, lb->hdr.lsp_id, ISIS_SYS_ID_LEN + 2); +} + +struct isis_lsp_hash *isis_lsp_hash_new(void) +{ + struct isis_lsp_hash *rv = XCALLOC(MTYPE_LSP_HASH, sizeof(*rv)); + + rv->h = hash_create(lsp_hash_key, lsp_hash_cmp, NULL); + return rv; +} + +void isis_lsp_hash_clean(struct isis_lsp_hash *ih) +{ + hash_clean(ih->h, NULL); +} + +void isis_lsp_hash_free(struct isis_lsp_hash *ih) +{ + isis_lsp_hash_clean(ih); + hash_free(ih->h); +} + +struct isis_lsp *isis_lsp_hash_lookup(struct isis_lsp_hash *ih, + struct isis_lsp *lsp) +{ + return hash_lookup(ih->h, lsp); +} + +void isis_lsp_hash_add(struct isis_lsp_hash *ih, struct isis_lsp *lsp) +{ + struct isis_lsp *inserted; + inserted = hash_get(ih->h, lsp, hash_alloc_intern); + assert(inserted == lsp); +} + +void isis_lsp_hash_release(struct isis_lsp_hash *ih, struct isis_lsp *lsp) +{ + hash_release(ih->h, lsp); +} diff --git a/isisd/isis_lsp_hash.h b/isisd/isis_lsp_hash.h new file mode 100644 index 000000000..b50aa09dc --- /dev/null +++ b/isisd/isis_lsp_hash.h @@ -0,0 +1,34 @@ +/* + * IS-IS Rout(e)ing protocol - LSP Hash + * + * Copyright (C) 2017 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR 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. + * + * FRR 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 ISIS_LSP_HASH_H +#define ISIS_LSP_HASH_H + +struct isis_lsp_hash; + +struct isis_lsp_hash *isis_lsp_hash_new(void); +void isis_lsp_hash_clean(struct isis_lsp_hash *ih); +void isis_lsp_hash_free(struct isis_lsp_hash *ih); +struct isis_lsp *isis_lsp_hash_lookup(struct isis_lsp_hash *ih, + struct isis_lsp *lsp); +void isis_lsp_hash_add(struct isis_lsp_hash *ih, struct isis_lsp *lsp); +void isis_lsp_hash_release(struct isis_lsp_hash *ih, struct isis_lsp *lsp); +#endif diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index a20051f8c..2f9e3caf1 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -2046,39 +2046,24 @@ int send_lsp(struct thread *thread) { struct isis_circuit *circuit; struct isis_lsp *lsp; - struct listnode *node; int clear_srm = 1; int retval = ISIS_OK; circuit = THREAD_ARG(thread); assert(circuit); + circuit->t_send_lsp = NULL; - if (!circuit->lsp_queue) + lsp = isis_circuit_lsp_queue_pop(circuit); + if (!lsp) return ISIS_OK; - - node = listhead(circuit->lsp_queue); - - /* - * Handle case where there are no LSPs on the queue. This can - * happen, for instance, if an adjacency goes down before this - * thread gets a chance to run. - */ - if (!node) - return ISIS_OK; - - /* - * Delete LSP from lsp_queue. If it's still in queue, it is assumed - * as 'transmit pending', but send_lsp may never be called again. - * Retry will happen because SRM flag will not be cleared. - */ - lsp = listgetdata(node); - list_delete_node(circuit->lsp_queue, node); - /* Set the last-cleared time if the queue is empty. */ /* TODO: Is is possible that new lsps keep being added to the queue * that the queue is never empty? */ - if (list_isempty(circuit->lsp_queue)) - circuit->lsp_queue_last_cleared = time(NULL); + if (list_isempty(circuit->lsp_queue)) { + monotime(&circuit->lsp_queue_last_cleared); + } else { + isis_circuit_schedule_lsp_send(circuit); + } if (circuit->state != C_STATE_UP || circuit->is_passive == 1) goto out; diff --git a/isisd/subdir.am b/isisd/subdir.am index 6e49d4ec8..7b56715fa 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -18,6 +18,7 @@ isisd_libisis_a_SOURCES = \ isisd/isis_events.c \ isisd/isis_flags.c \ isisd/isis_lsp.c \ + isisd/isis_lsp_hash.c \ isisd/isis_memory.c \ isisd/isis_misc.c \ isisd/isis_mt.c \ @@ -46,6 +47,7 @@ noinst_HEADERS += \ isisd/isis_events.h \ isisd/isis_flags.h \ isisd/isis_lsp.h \ + isisd/isis_lsp_hash.h \ isisd/isis_memory.h \ isisd/isis_misc.h \ isisd/isis_mt.h \