diff options
| author | Mark Stapp <mjs@voltanet.io> | 2020-04-07 09:50:53 -0400 |
|---|---|---|
| committer | Mark Stapp <mjs@voltanet.io> | 2020-06-02 08:20:54 -0400 |
| commit | aa9002a5f5ef55e4c99c99601b4aa5a164d08bbf (patch) | |
| tree | 31cf9479538c3c3bdaca25a4bb8320391b812eb6 /zebra/zserv.c | |
| parent | 9bb02389d0423941e328c88b38e241aa722229d0 (diff) | |
zebra: add lock and busy counter for zclients
Add a mutex used to manage the list of zclients. Add a busy
counter to the zapi client session, so that we can use a
client session from another pthread.
Signed-off-by: Mark Stapp <mjs@voltanet.io>
Diffstat (limited to 'zebra/zserv.c')
| -rw-r--r-- | zebra/zserv.c | 183 |
1 files changed, 162 insertions, 21 deletions
diff --git a/zebra/zserv.c b/zebra/zserv.c index 9d6ae2ec2e..474c69a29f 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -71,6 +71,14 @@ extern struct zebra_privs_t zserv_privs; /* The listener socket for clients connecting to us */ static int zsock; +/* The lock that protects access to zapi client objects */ +static pthread_mutex_t client_mutex; + +static struct zserv *find_client_internal(uint8_t proto, + unsigned short instance, + uint32_t session_id); + + /* * Client thread events. * @@ -629,26 +637,47 @@ static void zserv_client_free(struct zserv *client) void zserv_close_client(struct zserv *client) { - /* synchronously stop and join pthread */ - frr_pthread_stop(client->pthread, NULL); + bool free_p = true; - if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("Closing client '%s'", - zebra_route_string(client->proto)); + if (client->pthread) { + /* synchronously stop and join pthread */ + frr_pthread_stop(client->pthread, NULL); - thread_cancel_event(zrouter.master, client); - THREAD_OFF(client->t_cleanup); - THREAD_OFF(client->t_process); + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("Closing client '%s'", + zebra_route_string(client->proto)); - /* destroy pthread */ - frr_pthread_destroy(client->pthread); - client->pthread = NULL; + thread_cancel_event(zrouter.master, client); + THREAD_OFF(client->t_cleanup); + THREAD_OFF(client->t_process); - /* remove from client list */ - listnode_delete(zrouter.client_list, client); + /* destroy pthread */ + frr_pthread_destroy(client->pthread); + client->pthread = NULL; + } + + /* + * Final check in case the client struct is in use in another + * pthread: if not in-use, continue and free the client + */ + frr_with_mutex(&client_mutex) { + if (client->busy_count <= 0) { + /* remove from client list */ + listnode_delete(zrouter.client_list, client); + } else { + /* + * The client session object may be in use, although + * the associated pthread is gone. Defer final + * cleanup. + */ + client->is_closed = true; + free_p = false; + } + } /* delete client */ - zserv_client_free(client); + if (free_p) + zserv_client_free(client); } /* @@ -708,7 +737,9 @@ static struct zserv *zserv_client_create(int sock) client->ridinfo = vrf_bitmap_init(); /* Add this client to linked list. */ - listnode_add(zrouter.client_list, client); + frr_with_mutex(&client_mutex) { + listnode_add(zrouter.client_list, client); + } struct frr_pthread_attr zclient_pthr_attrs = { .start = frr_pthread_attr_default.start, @@ -731,6 +762,81 @@ static struct zserv *zserv_client_create(int sock) } /* + * Retrieve a client object by the complete tuple of + * {protocol, instance, session}. This version supports use + * from a different pthread: the object will be returned marked + * in-use. The caller *must* release the client object with the + * release_client() api, to ensure that the in-use marker is cleared properly. + */ +struct zserv *zserv_acquire_client(uint8_t proto, unsigned short instance, + uint32_t session_id) +{ + struct zserv *client = NULL; + + frr_with_mutex(&client_mutex) { + client = find_client_internal(proto, instance, session_id); + if (client) { + /* Don't return a dead/closed client object */ + if (client->is_closed) + client = NULL; + else + client->busy_count++; + } + } + + return client; +} + +/* + * Release a client object that was acquired with the acquire_client() api. + * After this has been called, the caller must not use the client pointer - + * it may be freed if the client has closed. + */ +void zserv_release_client(struct zserv *client) +{ + bool cleanup_p = false; + const char *proto_str; + uint16_t instance; + + /* Capture some info for debugging */ + proto_str = zebra_route_string(client->proto); + instance = client->instance; + + /* + * Once we've decremented the client object's refcount, it's possible + * for it to be deleted as soon as we release the lock, so we won't + * touch the object again. + */ + frr_with_mutex(&client_mutex) { + client->busy_count--; + + if (client->busy_count <= 0) { + /* + * No more users of the client object. If the client + * session is closed, schedule cleanup on the zebra + * main pthread. + */ + if (client->is_closed) { + thread_add_event(zrouter.master, + zserv_handle_client_fail, + client, 0, &client->t_cleanup); + + cleanup_p = true; + } + } + } + + /* + * Cleanup must take place on the zebra main pthread, so we've + * scheduled an event. + */ + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: %s clean-up for client '%s'[%u]", + __func__, (cleanup_p ? "scheduled" : "NO"), + proto_str, instance); +} + +/* * Accept socket connection. */ static int zserv_accept(struct thread *thread) @@ -773,6 +879,9 @@ void zserv_close(void) */ close(zsock); zsock = -1; + + /* Free client list's mutex */ + pthread_mutex_destroy(&client_mutex); } void zserv_start(char *path) @@ -1077,24 +1186,55 @@ static void zebra_show_client_brief(struct vty *vty, struct zserv *client) client->v6_route_del_cnt); } -struct zserv *zserv_find_client_session(uint8_t proto, unsigned short instance, - uint32_t session_id) +/* + * Common logic that searches the client list for a zapi client; this + * MUST be called holding the client list mutex. + */ +static struct zserv *find_client_internal(uint8_t proto, + unsigned short instance, + uint32_t session_id) { struct listnode *node, *nnode; - struct zserv *client; + struct zserv *client = NULL; for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { if (client->proto == proto && client->instance == instance && client->session_id == session_id) - return client; + break; } - return NULL; + return client; } +/* + * Public api that searches for a client session; this version is + * used from the zebra main pthread. + */ struct zserv *zserv_find_client(uint8_t proto, unsigned short instance) { - return zserv_find_client_session(proto, instance, 0); + struct zserv *client; + + frr_with_mutex(&client_mutex) { + client = find_client_internal(proto, instance, 0); + } + + return client; +} + +/* + * Retrieve a client by its protocol, instance number, and session id. + */ +struct zserv *zserv_find_client_session(uint8_t proto, unsigned short instance, + uint32_t session_id) +{ + struct zserv *client; + + frr_with_mutex(&client_mutex) { + client = find_client_internal(proto, instance, session_id); + } + + return client; + } /* This command is for debugging purpose. */ @@ -1161,6 +1301,7 @@ void zserv_init(void) /* Misc init. */ zsock = -1; + pthread_mutex_init(&client_mutex, NULL); install_element(ENABLE_NODE, &show_zebra_client_cmd); install_element(ENABLE_NODE, &show_zebra_client_summary_cmd); |
