Merge pull request #1286 from opensourcerouting/isis-lsp-queue-improv

isisd: optimize per interface lsp send-queue creation
This commit is contained in:
Donald Sharp 2017-10-05 08:06:57 -04:00 committed by GitHub
commit c1c41b0458
9 changed files with 227 additions and 75 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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 */

View File

@ -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

View File

@ -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);
}
}
}

89
isisd/isis_lsp_hash.c Normal file
View File

@ -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 <zebra.h>
#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);
}

34
isisd/isis_lsp_hash.h Normal file
View File

@ -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

View File

@ -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;

View File

@ -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 \