blob: 79c55cfbd53abe80a2e83496467447ee67735569 [file] [log] [blame]
/* SPDX-License-Identifier: Apache-2.0
* Copyright (c) 2023 Cisco Systems, Inc.
*/
#include "vppinfra/bitmap.h"
#include "vppinfra/lock.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 = "runtime",
};
static vnet_dev_rt_op_t *rt_ops;
static void
_vnet_dev_rt_exec_op (vlib_main_t *vm, vnet_dev_rt_op_t *op)
{
vnet_dev_port_t *port = op->port;
vnet_dev_rx_queue_t *previous = 0, *first = 0;
vnet_dev_rx_node_runtime_t *rtd;
vlib_node_state_t state = VLIB_NODE_STATE_DISABLED;
u32 node_index = port->intf.rx_node_index;
rtd = vlib_node_get_runtime_data (vm, node_index);
foreach_vnet_dev_port_rx_queue (q, port)
{
if (q->rx_thread_index != vm->thread_index)
continue;
if (q->interrupt_mode == 0)
state = VLIB_NODE_STATE_POLLING;
else if (state != VLIB_NODE_STATE_POLLING)
state = VLIB_NODE_STATE_INTERRUPT;
q->next_on_thread = 0;
if (previous == 0)
first = q;
else
previous->next_on_thread = q;
previous = q;
}
rtd->first_rx_queue = first;
vlib_node_set_state (vm, port->intf.rx_node_index, state);
__atomic_store_n (&op->completed, 1, __ATOMIC_RELEASE);
}
static uword
vnet_dev_rt_mgmt_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_frame_t *frame)
{
u16 thread_index = vm->thread_index;
vnet_dev_rt_op_t *op, *ops = __atomic_load_n (&rt_ops, __ATOMIC_ACQUIRE);
u32 n_pending = 0;
uword rv = 0;
vec_foreach (op, ops)
{
if (!op->completed && op->thread_index == thread_index)
{
if (op->in_order == 1 && n_pending)
{
vlib_node_set_interrupt_pending (vm, node->node_index);
return rv;
}
_vnet_dev_rt_exec_op (vm, op);
rv++;
}
if (op->completed == 0)
n_pending++;
}
return rv;
}
VLIB_REGISTER_NODE (vnet_dev_rt_mgmt_node, static) = {
.function = vnet_dev_rt_mgmt_node_fn,
.name = "dev-rt-mgmt",
.type = VLIB_NODE_TYPE_PRE_INPUT,
.state = VLIB_NODE_STATE_INTERRUPT,
};
vnet_dev_rv_t
vnet_dev_rt_exec_ops (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_rt_op_t *ops,
u32 n_ops)
{
vnet_dev_rt_op_t *op = ops;
vnet_dev_rt_op_t *remote_ops = 0;
clib_bitmap_t *remote_bmp = 0;
u32 i;
ASSERT (rt_ops == 0);
if (vlib_worker_thread_barrier_held ())
{
for (op = ops; op < (ops + n_ops); op++)
{
vlib_main_t *tvm = vlib_get_main_by_index (op->thread_index);
_vnet_dev_rt_exec_op (tvm, op);
log_debug (
dev,
"port %u rx node runtime update on thread %u executed locally",
op->port->port_id, op->thread_index);
}
return VNET_DEV_OK;
}
while (n_ops)
{
if (op->thread_index != vm->thread_index)
break;
_vnet_dev_rt_exec_op (vm, op);
log_debug (
dev, "port %u rx node runtime update on thread %u executed locally",
op->port->port_id, op->thread_index);
op++;
n_ops--;
}
if (n_ops == 0)
return VNET_DEV_OK;
for (op = ops; op < (ops + n_ops); op++)
{
if (op->thread_index == vm->thread_index &&
(op->in_order == 0 || vec_len (remote_ops) == 0))
{
_vnet_dev_rt_exec_op (vm, op);
log_debug (dev,
"port %u rx node runtime update on thread "
"%u executed locally",
op->port->port_id, op->thread_index);
}
else
{
vec_add1 (remote_ops, *op);
log_debug (dev,
"port %u rx node runtime update on thread %u "
"enqueued for remote execution",
op->port->port_id, op->thread_index);
remote_bmp = clib_bitmap_set (remote_bmp, op->thread_index, 1);
}
}
if (remote_ops == 0)
return VNET_DEV_OK;
__atomic_store_n (&rt_ops, remote_ops, __ATOMIC_RELEASE);
clib_bitmap_foreach (i, remote_bmp)
{
vlib_node_set_interrupt_pending (vlib_get_main_by_index (i),
vnet_dev_rt_mgmt_node.index);
log_debug (dev, "interrupt sent to %s node on thread %u",
vnet_dev_rt_mgmt_node.name, i);
}
vec_foreach (op, remote_ops)
{
while (op->completed == 0)
vlib_process_suspend (vm, 5e-5);
log_debug (
dev, "port %u rx node runtime update on thread %u executed locally",
op->port->port_id, op->thread_index);
}
__atomic_store_n (&rt_ops, 0, __ATOMIC_RELAXED);
vec_free (remote_ops);
clib_bitmap_free (remote_bmp);
return VNET_DEV_OK;
}