diff options
Diffstat (limited to 'src/vnet/dev/process.c')
-rw-r--r-- | src/vnet/dev/process.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/src/vnet/dev/process.c b/src/vnet/dev/process.c new file mode 100644 index 00000000000..3c1f0b8d2d8 --- /dev/null +++ b/src/vnet/dev/process.c @@ -0,0 +1,474 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include "vppinfra/error.h" +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/log.h> + +VLIB_REGISTER_LOG_CLASS (dev_log, static) = { + .class_name = "dev", + .subclass_name = "process", +}; + +typedef enum +{ + VNET_DEV_EVENT_PERIODIC_STOP, + VNET_DEV_EVENT_PERIODIC_START, + VNET_DEV_EVENT_PORT_CONFIG_CHANGE_REQ, + VNET_DEV_EVENT_PROCESS_QUIT, + VNET_DEV_EVENT_CALL_OP, + VNET_DEV_EVENT_CALL_OP_NO_RV, + VNET_DEV_EVENT_CALL_OP_NO_WAIT, + VNET_DEV_EVENT_CALL_PORT_OP, + VNET_DEV_EVENT_CALL_PORT_OP_NO_RV, + VNET_DEV_EVENT_CALL_PORT_OP_NO_WAIT, + VNET_DEV_EVENT_CLOCK = ~0 +} __clib_packed vnet_dev_event_t; + +typedef struct +{ + vnet_dev_event_t event; + u8 reply_needed : 1; + u32 calling_process_index; + union + { + struct + { + vnet_dev_port_t *port; + vnet_dev_port_cfg_change_req_t *change_req; + } port_cfg_change; + struct + { + vnet_dev_op_t *op; + } call_op; + struct + { + vnet_dev_op_no_rv_t *op; + } call_op_no_rv; + struct + { + vnet_dev_op_no_rv_t *op; + } call_op_no_wait; + struct + { + vnet_dev_port_op_t *op; + vnet_dev_port_t *port; + } call_port_op; + struct + { + vnet_dev_port_op_no_rv_t *op; + vnet_dev_port_t *port; + } call_port_op_no_rv; + struct + { + vnet_dev_port_op_no_rv_t *op; + vnet_dev_port_t *port; + } call_port_op_no_wait; + }; +} vnet_dev_event_data_t; + +static vnet_dev_rv_t +vnet_dev_process_one_event (vlib_main_t *vm, vnet_dev_t *dev, + vnet_dev_event_data_t *ed) +{ + vnet_dev_port_t *p; + vnet_dev_rv_t rv = VNET_DEV_OK; + + switch (ed->event) + { + case VNET_DEV_EVENT_CLOCK: + break; + case VNET_DEV_EVENT_PROCESS_QUIT: + log_debug (dev, "quit requested"); + dev->process_node_quit = 1; + break; + case VNET_DEV_EVENT_PERIODIC_START: + log_debug (dev, "periodic start"); + dev->process_node_periodic = 1; + break; + case VNET_DEV_EVENT_PERIODIC_STOP: + log_debug (dev, "periodic stop"); + dev->process_node_periodic = 0; + break; + case VNET_DEV_EVENT_PORT_CONFIG_CHANGE_REQ: + log_debug (dev, "port config change"); + p = ed->port_cfg_change.port; + rv = vnet_dev_port_cfg_change (vm, p, ed->port_cfg_change.change_req); + break; + case VNET_DEV_EVENT_CALL_OP: + log_debug (dev, "call op"); + rv = ed->call_op.op (vm, dev); + break; + case VNET_DEV_EVENT_CALL_OP_NO_RV: + log_debug (dev, "call op no rv"); + ed->call_op_no_rv.op (vm, dev); + break; + case VNET_DEV_EVENT_CALL_OP_NO_WAIT: + log_debug (dev, "call op no wait"); + ed->call_op_no_wait.op (vm, dev); + break; + case VNET_DEV_EVENT_CALL_PORT_OP: + log_debug (dev, "call port op"); + rv = ed->call_port_op.op (vm, ed->call_port_op.port); + break; + case VNET_DEV_EVENT_CALL_PORT_OP_NO_RV: + log_debug (dev, "call port op no rv"); + ed->call_port_op_no_rv.op (vm, ed->call_port_op_no_rv.port); + break; + case VNET_DEV_EVENT_CALL_PORT_OP_NO_WAIT: + log_debug (dev, "call port op no wait"); + ed->call_port_op_no_wait.op (vm, ed->call_port_op_no_wait.port); + break; + default: + ASSERT (0); + } + return rv; +} + +static uword +vnet_dev_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f) +{ + vnet_dev_main_t *dm = &vnet_dev_main; + vnet_dev_periodic_op_t *pop, *pops = 0; + f64 next = CLIB_F64_MAX; + vnet_dev_event_data_t *event_data = 0, *new_event_data, *ed; + + vnet_dev_t *dev = + *((vnet_dev_t **) vlib_node_get_runtime_data (vm, rt->node_index)); + + log_debug (dev, "process '%U' started", format_vlib_node_name, vm, + rt->node_index); + + while (dev->process_node_quit == 0) + { + uword event_type; + f64 now = vlib_time_now (vm); + + if (dev->process_node_periodic) + vlib_process_wait_for_event_or_clock (vm, next > now ? next - now : 0); + else + vlib_process_wait_for_event (vm); + + new_event_data = vlib_process_get_event_data (vm, &event_type); + + if (new_event_data) + { + vec_append (event_data, new_event_data); + vlib_process_put_event_data (vm, new_event_data); + + ASSERT (event_type == 0); + + vec_foreach (ed, event_data) + { + vnet_dev_rv_t rv; + rv = vnet_dev_process_one_event (vm, dev, ed); + if (ed->reply_needed) + vlib_process_signal_event (vm, ed->calling_process_index, + ed->event, rv); + } + vec_reset_length (event_data); + } + + next = CLIB_F64_MAX; + pool_foreach (pop, dev->periodic_ops) + { + if (pop->last_run + pop->interval < now) + { + vec_add1 (pops, *pop); + pop->last_run = now; + } + if (pop->last_run + pop->interval < next) + next = pop->last_run + pop->interval; + } + + vec_foreach (pop, pops) + { + switch (pop->type) + { + case VNET_DEV_PERIODIC_OP_TYPE_DEV: + pop->dev_op (vm, pop->dev); + break; + case VNET_DEV_PERIODIC_OP_TYPE_PORT: + pop->port_op (vm, pop->port); + break; + default: + ASSERT (0); + } + } + vec_reset_length (pops); + } + + log_debug (dev, "process '%U' quit", format_vlib_node_name, vm, + rt->node_index); + vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED); + vlib_node_rename (vm, rt->node_index, "deleted-%u", rt->node_index); + + /* add node index to the freelist */ + vec_add1 (dm->free_process_node_indices, rt->node_index); + vec_free (pops); + vec_free (event_data); + return 0; +} + +vnet_dev_rv_t +vnet_dev_process_create (vlib_main_t *vm, vnet_dev_t *dev) +{ + vnet_dev_main_t *dm = &vnet_dev_main; + vlib_node_t *n; + uword l; + + l = vec_len (dm->free_process_node_indices); + if (l > 0) + { + n = vlib_get_node (vm, dm->free_process_node_indices[l - 1]); + if (n->function != vnet_dev_process) + { + vlib_node_runtime_t *rt = vlib_node_get_runtime (vm, n->index); + n->function = vnet_dev_process; + rt->function = vnet_dev_process; + } + vlib_node_rename (vm, n->index, "%s-process", dev->device_id); + vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING); + vec_set_len (dm->free_process_node_indices, l - 1); + log_debug (dev, "process node '%U' (%u) reused", format_vlib_node_name, + vm, n->index, n->index); + } + else + { + vlib_node_registration_t r = { + .function = vnet_dev_process, + .type = VLIB_NODE_TYPE_PROCESS, + .process_log2_n_stack_bytes = 16, + .runtime_data_bytes = sizeof (void *), + }; + + vlib_register_node (vm, &r, "%s-process", dev->device_id); + + n = vlib_get_node (vm, r.index); + log_debug (dev, "process node '%U' (%u) created", format_vlib_node_name, + vm, r.index, r.index); + } + + dev->process_node_index = n->index; + *(vnet_dev_t **) vlib_node_get_runtime_data (vm, n->index) = dev; + vlib_start_process (vm, n->runtime_index); + + return VNET_DEV_OK; +} + +static void +vnet_dev_process_event_send (vlib_main_t *vm, vnet_dev_t *dev, + vnet_dev_event_data_t ed) +{ + vnet_dev_event_data_t *edp = vlib_process_signal_event_data ( + vm, dev->process_node_index, 0, 1, sizeof (ed)); + *edp = ed; +} + +static vnet_dev_rv_t +vnet_dev_process_event_send_and_wait (vlib_main_t *vm, vnet_dev_t *dev, + vnet_dev_event_data_t ed) +{ + uword event, *event_data = 0; + vnet_dev_rv_t rv; + + ed.calling_process_index = vlib_get_current_process_node_index (vm); + + if (ed.calling_process_index == dev->process_node_index) + return vnet_dev_process_one_event (vm, dev, &ed); + + ed.reply_needed = 1; + vnet_dev_process_event_send (vm, dev, ed); + vlib_process_wait_for_event_or_clock (vm, 5.0); + event = vlib_process_get_events (vm, &event_data); + if (event != ed.event) + { + log_err (dev, "%s", + event == VNET_DEV_EVENT_CLOCK ? + "timeout waiting for process node to respond" : + "unexpected event received"); + rv = VNET_DEV_ERR_PROCESS_REPLY; + } + else + rv = event_data[0]; + vec_free (event_data); + return rv; +} + +void +vnet_dev_process_quit (vlib_main_t *vm, vnet_dev_t *dev) +{ + vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PROCESS_QUIT }; + vnet_dev_process_event_send_and_wait (vm, dev, ed); +} + +static int +_vnet_dev_poll_add (vlib_main_t *vm, vnet_dev_t *dev, + vnet_dev_periodic_op_t pop) +{ + const vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PERIODIC_START }; + vnet_dev_periodic_op_t *p; + + pool_foreach (p, dev->periodic_ops) + if (p->op == pop.op && p->arg == pop.arg) + return 0; + + pool_get_zero (dev->periodic_ops, p); + *p = pop; + if (pool_elts (dev->periodic_ops) == 1) + vnet_dev_process_event_send (vm, dev, ed); + return 1; +} + +static int +_vnet_dev_poll_remove (vlib_main_t *vm, vnet_dev_t *dev, void *op, void *arg) +{ + const vnet_dev_event_data_t ed = { .event = VNET_DEV_EVENT_PERIODIC_STOP }; + vnet_dev_periodic_op_t *pop; + + pool_foreach (pop, dev->periodic_ops) + if (pop->op == op && pop->arg == arg) + { + pool_put (dev->periodic_ops, pop); + if (pool_elts (dev->periodic_ops) == 0) + vnet_dev_process_event_send (vm, dev, ed); + return 1; + } + return 0; +} + +void +vnet_dev_poll_dev_add (vlib_main_t *vm, vnet_dev_t *dev, f64 interval, + vnet_dev_op_no_rv_t *dev_op) +{ + vnet_dev_periodic_op_t pop = { + .interval = interval, + .type = VNET_DEV_PERIODIC_OP_TYPE_DEV, + .dev_op = dev_op, + .dev = dev, + }; + + if (_vnet_dev_poll_add (vm, dev, pop) == 0) + log_warn (dev, "poll_dev_add: op already exists, not added"); +} + +void +vnet_dev_poll_dev_remove (vlib_main_t *vm, vnet_dev_t *dev, + vnet_dev_op_no_rv_t *dev_op) +{ + if (_vnet_dev_poll_remove (vm, dev, (void *) dev_op, (void *) dev) == 0) + log_warn (dev, "poll_dev_remove: op not found, not removed"); +} + +void +vnet_dev_poll_port_add (vlib_main_t *vm, vnet_dev_port_t *port, f64 interval, + vnet_dev_port_op_no_rv_t *port_op) +{ + vnet_dev_t *dev = port->dev; + vnet_dev_periodic_op_t pop = { + .interval = interval, + .type = VNET_DEV_PERIODIC_OP_TYPE_PORT, + .port_op = port_op, + .port = port, + }; + + if (_vnet_dev_poll_add (vm, dev, pop) == 0) + log_warn (dev, "poll_port_add: op already exists, not added"); +} + +void +vnet_dev_poll_port_remove (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_op_no_rv_t *port_op) +{ + vnet_dev_t *dev = port->dev; + if (_vnet_dev_poll_remove (vm, dev, (void *) port_op, (void *) port) == 0) + log_warn (dev, "poll_port_remove: op not found, not removed"); +} + +vnet_dev_rv_t +vnet_dev_process_port_cfg_change_req (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_cfg_change_req_t *pccr) +{ + const vnet_dev_event_data_t ed = { + .event = VNET_DEV_EVENT_PORT_CONFIG_CHANGE_REQ, + .port_cfg_change = { + .port = port, + .change_req = pccr, + }, + }; + + return vnet_dev_process_event_send_and_wait (vm, port->dev, ed); +} + +vnet_dev_rv_t +vnet_dev_process_call_op (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_op_t *op) +{ + const vnet_dev_event_data_t ed = { + .event = VNET_DEV_EVENT_CALL_OP, + .call_op.op = op, + }; + + return vnet_dev_process_event_send_and_wait (vm, dev, ed); +} + +vnet_dev_rv_t +vnet_dev_process_call_op_no_rv (vlib_main_t *vm, vnet_dev_t *dev, + vnet_dev_op_no_rv_t *op) +{ + const vnet_dev_event_data_t ed = { + .event = VNET_DEV_EVENT_CALL_OP_NO_RV, + .call_op_no_rv.op = op, + }; + + return vnet_dev_process_event_send_and_wait (vm, dev, ed); +} + +void +vnet_dev_process_call_op_no_wait (vlib_main_t *vm, vnet_dev_t *dev, + vnet_dev_op_no_rv_t *op) +{ + const vnet_dev_event_data_t ed = { + .event = VNET_DEV_EVENT_CALL_OP_NO_WAIT, + .call_op_no_rv.op = op, + }; + + vnet_dev_process_event_send (vm, dev, ed); +} + +vnet_dev_rv_t +vnet_dev_process_call_port_op (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_op_t *op) +{ + const vnet_dev_event_data_t ed = { + .event = VNET_DEV_EVENT_CALL_PORT_OP, + .call_port_op = { .op = op, .port = port }, + }; + + return vnet_dev_process_event_send_and_wait (vm, port->dev, ed); +} + +vnet_dev_rv_t +vnet_dev_process_call_port_op_no_rv (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_op_no_rv_t *op) +{ + const vnet_dev_event_data_t ed = { + .event = VNET_DEV_EVENT_CALL_PORT_OP_NO_RV, + .call_port_op_no_rv = { .op = op, .port = port }, + }; + + return vnet_dev_process_event_send_and_wait (vm, port->dev, ed); +} + +void +vnet_dev_process_call_port_op_no_wait (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_op_no_rv_t *op) +{ + const vnet_dev_event_data_t ed = { + .event = VNET_DEV_EVENT_CALL_PORT_OP_NO_WAIT, + .call_port_op_no_wait = { .op = op, .port = port }, + }; + + vnet_dev_process_event_send (vm, port->dev, ed); +} |