zebra: design changes in netlink batching code

Signed-off-by: Jakub Urbańczyk <xthaid@gmail.com>
This commit is contained in:
Jakub Urbańczyk 2020-08-03 16:51:56 +02:00
parent 18f60fe999
commit f6feb48b48
3 changed files with 103 additions and 99 deletions

View File

@ -175,10 +175,6 @@ extern struct zebra_privs_t zserv_privs;
char nl_batch_tx_buf[NL_BATCH_TX_BUFSIZE];
char nl_batch_rx_buf[NL_BATCH_RX_BUFSIZE];
DEFINE_MTYPE_STATIC(ZEBRA, NL_BATCH_ITEM, "Netlink batch items")
PREDECL_LIST(nl_batch_list)
struct nl_batch {
void *buf;
size_t bufsiz;
@ -189,20 +185,16 @@ struct nl_batch {
size_t msgcnt;
const struct zebra_dplane_info *zns;
struct nl_batch_list_head items;
struct dplane_ctx_q ctx_list;
/*
* Pointer to the queue of completed contexts outbound back
* towards the dataplane module.
*/
struct dplane_ctx_q *ctx_out_q;
};
struct nl_msg_batch_item {
int seq;
struct zebra_dplane_ctx *ctx;
bool ignore_res;
bool failure;
struct nl_batch_list_item item;
};
DECLARE_LIST(nl_batch_list, struct nl_msg_batch_item, item)
int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
/*
@ -1137,17 +1129,16 @@ static int nl_batch_read_resp(struct nl_batch *bth)
struct nlmsghdr *h;
struct sockaddr_nl snl;
struct msghdr msg;
int status;
int status, seq;
const struct nlsock *nl;
struct nl_msg_batch_item *item, *from_item;
struct zebra_dplane_ctx *ctx;
bool ignore_msg;
nl = &(bth->zns->nls);
msg.msg_name = (void *)&snl;
msg.msg_namelen = sizeof(snl);
from_item = nl_batch_list_first(&(bth->items));
status = netlink_recv_msg(nl, msg, nl_batch_rx_buf,
sizeof(nl_batch_rx_buf));
if (status == -1 || status == 0)
@ -1156,27 +1147,51 @@ static int nl_batch_read_resp(struct nl_batch *bth)
for (h = (struct nlmsghdr *)nl_batch_rx_buf;
(status >= 0 && NLMSG_OK(h, (unsigned int)status));
h = NLMSG_NEXT(h, status)) {
ignore_msg = false;
seq = h->nlmsg_seq;
/*
* Find the corresponding batch item. Received responses are in
* the same order as requests we sent, so we can simply iterate
* over the batch item list and match responses with requests
* at same time.
* Find the corresponding context object. Received responses are
* in the same order as requests we sent, so we can simply
* iterate over the context list and match responses with
* requests at same time.
*/
frr_each_from(nl_batch_list, &(bth->items), item, from_item) {
if (item->seq == (int)h->nlmsg_seq)
while (true) {
ctx = dplane_ctx_dequeue(&(bth->ctx_list));
if (ctx == NULL)
break;
dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx);
/* We have found corresponding context object. */
if (dplane_ctx_get_ns(ctx)->nls.seq == seq)
break;
/*
* 'update' context objects take two consecutive
* sequence numbers.
*/
if (dplane_ctx_is_update(ctx)
&& dplane_ctx_get_ns(ctx)->nls.seq + 1 == seq) {
/*
* This is the situation where we get a response
* to a message that should be ignored.
*/
ignore_msg = true;
break;
}
}
if (ignore_msg)
continue;
/*
* We received a message with the sequence number that isn't
* associated with any dplane context object.
*/
if (item == NULL) {
if (ctx == NULL) {
zlog_debug(
"%s: skipping unassociated response, seq number %d NS %u",
__func__, h->nlmsg_seq, bth->zns->ns_id);
from_item = nl_batch_list_first(&(bth->items));
continue;
}
@ -1184,7 +1199,8 @@ static int nl_batch_read_resp(struct nl_batch *bth)
int err = netlink_parse_error(nl, h, bth->zns, 0);
if (err == -1)
item->failure = true;
dplane_ctx_set_status(
ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
zlog_debug("%s: netlink error message seq=%d ",
__func__, h->nlmsg_seq);
@ -1206,85 +1222,71 @@ static int nl_batch_read_resp(struct nl_batch *bth)
static void nl_batch_reset(struct nl_batch *bth)
{
bth->buf = nl_batch_tx_buf;
bth->bufsiz = sizeof(nl_batch_tx_buf);
bth->limit = NL_BATCH_SEND_THRESHOLD;
bth->buf_head = bth->buf;
bth->curlen = 0;
bth->msgcnt = 0;
bth->zns = NULL;
nl_batch_list_init(&(bth->items));
TAILQ_INIT(&(bth->ctx_list));
}
static void nl_batch_init(struct nl_batch *bth, struct dplane_ctx_q *ctx_out_q)
{
bth->buf = nl_batch_tx_buf;
bth->bufsiz = sizeof(nl_batch_tx_buf);
bth->limit = NL_BATCH_SEND_THRESHOLD;
bth->ctx_out_q = ctx_out_q;
nl_batch_reset(bth);
}
static void nl_batch_send(struct nl_batch *bth)
{
struct nl_msg_batch_item *item;
struct zebra_dplane_ctx *ctx;
bool err = false;
if (bth->curlen == 0 || bth->zns == NULL)
return;
if (bth->curlen != 0 && bth->zns != NULL) {
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("%s: %s, batch size=%zu, msg cnt=%zu",
__func__, bth->zns->nls.name, bth->curlen,
bth->msgcnt);
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("%s: %s, batch size=%zu, msg cnt=%zu", __func__,
bth->zns->nls.name, bth->curlen, bth->msgcnt);
if (netlink_send_msg(&(bth->zns->nls), bth->buf, bth->curlen) == -1)
err = true;
if (!err) {
if (nl_batch_read_resp(bth) == -1)
if (netlink_send_msg(&(bth->zns->nls), bth->buf, bth->curlen)
== -1)
err = true;
if (!err) {
if (nl_batch_read_resp(bth) == -1)
err = true;
}
}
frr_each_safe(nl_batch_list, &(bth->items), item) {
enum zebra_dplane_result res = ZEBRA_DPLANE_REQUEST_SUCCESS;
/* Move remaining contexts to the outbound queue. */
while (true) {
ctx = dplane_ctx_dequeue(&(bth->ctx_list));
if (ctx == NULL)
break;
/*
* If either sending or receiving a message batch has ended with
* the error, mark all dplane requests as failed.
*/
if (item->failure || err)
res = ZEBRA_DPLANE_REQUEST_FAILURE;
if (err)
dplane_ctx_set_status(ctx,
ZEBRA_DPLANE_REQUEST_FAILURE);
if (!item->ignore_res)
dplane_ctx_set_status(item->ctx, res);
nl_batch_list_del(&(bth->items), item);
XFREE(MTYPE_NL_BATCH_ITEM, item);
dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx);
}
nl_batch_reset(bth);
}
static void nl_batch_add_item(struct nl_batch *bth, int seq,
struct zebra_dplane_ctx *ctx, bool ignore_res)
{
struct nl_msg_batch_item *item =
XCALLOC(MTYPE_NL_BATCH_ITEM, sizeof(*item));
item->seq = seq;
item->ctx = ctx;
item->ignore_res = ignore_res;
item->failure = false;
nl_batch_list_add_tail(&(bth->items), item);
}
enum netlink_msg_status netlink_batch_add_msg(
struct nl_batch *bth, struct zebra_dplane_ctx *ctx,
ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t),
bool extra_msg)
bool ignore_res)
{
int seq;
ssize_t size;
struct nlmsghdr *msgh;
if (bth->zns != NULL
&& bth->zns->ns_id != dplane_ctx_get_ns(ctx)->ns_id)
nl_batch_send(bth);
size = (*msg_encoder)(ctx, bth->buf_head, bth->bufsiz - bth->curlen);
/*
@ -1311,23 +1313,18 @@ enum netlink_msg_status netlink_batch_add_msg(
}
seq = dplane_ctx_get_ns(ctx)->nls.seq;
if (extra_msg)
if (ignore_res)
seq++;
msgh = (struct nlmsghdr *)bth->buf_head;
msgh->nlmsg_seq = seq;
msgh->nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid;
nl_batch_add_item(bth, seq, ctx, extra_msg);
bth->zns = dplane_ctx_get_ns(ctx);
bth->buf_head = ((char *)bth->buf_head) + size;
bth->curlen += size;
bth->msgcnt++;
if (bth->curlen > bth->limit)
nl_batch_send(bth);
return FRR_NETLINK_QUEUED;
}
@ -1398,29 +1395,33 @@ void kernel_update_multi(struct dplane_ctx_q *ctx_list)
struct dplane_ctx_q handled_list;
enum netlink_msg_status res;
nl_batch_reset(&batch);
TAILQ_INIT(&handled_list);
nl_batch_init(&batch, &handled_list);
while (true) {
ctx = dplane_ctx_dequeue(ctx_list);
if (ctx == NULL)
break;
res = nl_put_msg(&batch, ctx);
if (batch.zns != NULL
&& batch.zns->ns_id != dplane_ctx_get_ns(ctx)->ns_id)
nl_batch_send(&batch);
/*
* If we already know the result, we set the status of the
* dataplane context object. Otherwise, it will be set after we
* send the batch.
* Assume all messages will succeed and then mark only the ones
* that failed.
*/
if (res == FRR_NETLINK_SUCCESS)
dplane_ctx_set_status(ctx,
ZEBRA_DPLANE_REQUEST_SUCCESS);
else if (res == FRR_NETLINK_ERROR)
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
res = nl_put_msg(&batch, ctx);
dplane_ctx_enqueue_tail(&(batch.ctx_list), ctx);
if (res == FRR_NETLINK_ERROR)
dplane_ctx_set_status(ctx,
ZEBRA_DPLANE_REQUEST_FAILURE);
dplane_ctx_enqueue_tail(&handled_list, ctx);
if (batch.curlen > batch.limit)
nl_batch_send(&batch);
}
nl_batch_send(&batch);

View File

@ -118,16 +118,16 @@ struct nl_batch;
* pointer to a buffer and buffer's length as parameters
* and should return -1 on error, 0 on buffer overflow or
* size of the encoded message.
* @extra_msg: In some cases there are two netlink messages for single
* context object that need to be handled differently. This flag
* distinguishes those.
* @ignore_res: Whether the result of this message should be ignored.
* This should be used in some 'update' cases where we
* need to send two messages for one context object.
*
* Return: Status of the message.
*/
extern enum netlink_msg_status netlink_batch_add_msg(
struct nl_batch *bth, struct zebra_dplane_ctx *ctx,
ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t),
bool extra_msg);
bool ignore_res);
#endif /* HAVE_NETLINK */

View File

@ -1977,6 +1977,7 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
* it probably won't require two messages
*/
dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE));
ctx->zd_is_update = (op == DPLANE_OP_NH_UPDATE);
ret = AOK;
@ -1999,6 +2000,7 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
/* Capture namespace info */
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
(op == DPLANE_OP_LSP_UPDATE));
ctx->zd_is_update = (op == DPLANE_OP_LSP_UPDATE);
memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp));
@ -2220,6 +2222,7 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx,
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
op == DPLANE_OP_RULE_UPDATE);
ctx->zd_is_update = (op == DPLANE_OP_RULE_UPDATE);
ctx->zd_vrf_id = new_rule->vrf_id;
memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname));