dev: interrupt mode support
[vpp.git] / src / vnet / dev / runtime.c
1
2 /* SPDX-License-Identifier: Apache-2.0
3  * Copyright (c) 2023 Cisco Systems, Inc.
4  */
5
6 #include "vppinfra/bitmap.h"
7 #include "vppinfra/lock.h"
8 #include <vnet/vnet.h>
9 #include <vnet/dev/dev.h>
10 #include <vnet/dev/log.h>
11
12 VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
13   .class_name = "dev",
14   .subclass_name = "runtime",
15 };
16
17 static vnet_dev_rt_op_t *rt_ops;
18
19 static void
20 _vnet_dev_rt_exec_op (vlib_main_t *vm, vnet_dev_rt_op_t *op)
21 {
22   vnet_dev_port_t *port = op->port;
23   vnet_dev_rx_queue_t *previous = 0, *first = 0;
24   vnet_dev_rx_node_runtime_t *rtd;
25   vlib_node_state_t state = VLIB_NODE_STATE_DISABLED;
26   u32 node_index = port->intf.rx_node_index;
27
28   rtd = vlib_node_get_runtime_data (vm, node_index);
29
30   foreach_vnet_dev_port_rx_queue (q, port)
31     {
32       if (q->rx_thread_index != vm->thread_index)
33         continue;
34
35       if (q->interrupt_mode == 0)
36         state = VLIB_NODE_STATE_POLLING;
37       else if (state != VLIB_NODE_STATE_POLLING)
38         state = VLIB_NODE_STATE_INTERRUPT;
39
40       q->next_on_thread = 0;
41       if (previous == 0)
42         first = q;
43       else
44         previous->next_on_thread = q;
45
46       previous = q;
47     }
48
49   rtd->first_rx_queue = first;
50   vlib_node_set_state (vm, port->intf.rx_node_index, state);
51   __atomic_store_n (&op->completed, 1, __ATOMIC_RELEASE);
52 }
53
54 static uword
55 vnet_dev_rt_mgmt_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
56                           vlib_frame_t *frame)
57 {
58   u16 thread_index = vm->thread_index;
59   vnet_dev_rt_op_t *op, *ops = __atomic_load_n (&rt_ops, __ATOMIC_ACQUIRE);
60   u32 n_pending = 0;
61   uword rv = 0;
62
63   vec_foreach (op, ops)
64     {
65       if (!op->completed && op->thread_index == thread_index)
66         {
67           if (op->in_order == 1 && n_pending)
68             {
69               vlib_node_set_interrupt_pending (vm, node->node_index);
70               return rv;
71             }
72           _vnet_dev_rt_exec_op (vm, op);
73           rv++;
74         }
75
76       if (op->completed == 0)
77         n_pending++;
78     }
79
80   return rv;
81 }
82
83 VLIB_REGISTER_NODE (vnet_dev_rt_mgmt_node, static) = {
84   .function = vnet_dev_rt_mgmt_node_fn,
85   .name = "dev-rt-mgmt",
86   .type = VLIB_NODE_TYPE_PRE_INPUT,
87   .state = VLIB_NODE_STATE_INTERRUPT,
88 };
89
90 vnet_dev_rv_t
91 vnet_dev_rt_exec_ops (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_rt_op_t *ops,
92                       u32 n_ops)
93 {
94   vnet_dev_rt_op_t *op = ops;
95   vnet_dev_rt_op_t *remote_ops = 0;
96   clib_bitmap_t *remote_bmp = 0;
97   u32 i;
98
99   ASSERT (rt_ops == 0);
100
101   if (vlib_worker_thread_barrier_held ())
102     {
103       for (op = ops; op < (ops + n_ops); op++)
104         {
105           vlib_main_t *tvm = vlib_get_main_by_index (op->thread_index);
106           _vnet_dev_rt_exec_op (tvm, op);
107           log_debug (
108             dev,
109             "port %u rx node runtime update on thread %u executed locally",
110             op->port->port_id, op->thread_index);
111         }
112       return VNET_DEV_OK;
113     }
114
115   while (n_ops)
116     {
117       if (op->thread_index != vm->thread_index)
118         break;
119
120       _vnet_dev_rt_exec_op (vm, op);
121       log_debug (
122         dev, "port %u rx node runtime update on thread %u executed locally",
123         op->port->port_id, op->thread_index);
124       op++;
125       n_ops--;
126     }
127
128   if (n_ops == 0)
129     return VNET_DEV_OK;
130
131   for (op = ops; op < (ops + n_ops); op++)
132     {
133       if (op->thread_index == vm->thread_index &&
134           (op->in_order == 0 || vec_len (remote_ops) == 0))
135         {
136           _vnet_dev_rt_exec_op (vm, op);
137           log_debug (dev,
138                      "port %u rx node runtime update on thread "
139                      "%u executed locally",
140                      op->port->port_id, op->thread_index);
141         }
142       else
143         {
144           vec_add1 (remote_ops, *op);
145           log_debug (dev,
146                      "port %u rx node runtime update on thread %u "
147                      "enqueued for remote execution",
148                      op->port->port_id, op->thread_index);
149           remote_bmp = clib_bitmap_set (remote_bmp, op->thread_index, 1);
150         }
151     }
152
153   if (remote_ops == 0)
154     return VNET_DEV_OK;
155
156   __atomic_store_n (&rt_ops, remote_ops, __ATOMIC_RELEASE);
157
158   clib_bitmap_foreach (i, remote_bmp)
159     {
160       vlib_node_set_interrupt_pending (vlib_get_main_by_index (i),
161                                        vnet_dev_rt_mgmt_node.index);
162       log_debug (dev, "interrupt sent to %s node on thread %u",
163                  vnet_dev_rt_mgmt_node.name, i);
164     }
165
166   vec_foreach (op, remote_ops)
167     {
168       while (op->completed == 0)
169         vlib_process_suspend (vm, 5e-5);
170
171       log_debug (
172         dev, "port %u rx node runtime update on thread %u executed locally",
173         op->port->port_id, op->thread_index);
174     }
175
176   __atomic_store_n (&rt_ops, 0, __ATOMIC_RELAXED);
177   vec_free (remote_ops);
178   clib_bitmap_free (remote_bmp);
179   return VNET_DEV_OK;
180 }