Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2025 NVIDIA Corporation & Affiliates
3 : : */
4 : :
5 : : #include "mlx5_driver_event.h"
6 : :
7 : : #include <sys/queue.h>
8 : :
9 : : #include <eal_export.h>
10 : :
11 : : #include "mlx5.h"
12 : : #include "mlx5_rx.h"
13 : : #include "mlx5_tx.h"
14 : : #include "rte_pmd_mlx5.h"
15 : :
16 : : /*
17 : : * Macro serving as a longest possible "queue_info" string generated as part of driver event
18 : : * callback. Used to derive the correct size of the static buffer, so the dynamic allocation
19 : : * can be skipped during callback.
20 : : */
21 : : #define MAX_QUEUE_INFO ( \
22 : : "lro_timeout=" RTE_STR(UINT32_MAX) "," \
23 : : "max_lro_msg_size=" RTE_STR(UINT32_MAX) "," \
24 : : "td=" RTE_STR(UINT32_MAX) "," \
25 : : "lpbk=1")
26 : :
27 : : static char queue_info_buf[sizeof(MAX_QUEUE_INFO)];
28 : :
29 : : struct registered_cb {
30 : : LIST_ENTRY(registered_cb) list;
31 : : rte_pmd_mlx5_driver_event_callback_t cb;
32 : : const void *opaque;
33 : : };
34 : :
35 : : LIST_HEAD(, registered_cb) cb_list_head = LIST_HEAD_INITIALIZER(cb_list_head);
36 : :
37 : : static const char *
38 : 0 : generate_rx_queue_info(struct mlx5_rxq_priv *rxq)
39 : : {
40 : 0 : struct mlx5_priv *priv = rxq->priv;
41 : : uint32_t max_lro_msg_size = 0;
42 : : uint32_t lro_timeout = 0;
43 : : uint32_t lpbk = 0;
44 : : uint32_t td = 0;
45 : : int ret __rte_unused;
46 : :
47 [ # # ]: 0 : if (rxq->ctrl->rxq.lro) {
48 : 0 : lro_timeout = priv->config.lro_timeout;
49 : 0 : max_lro_msg_size = priv->max_lro_msg_size / MLX5_LRO_SEG_CHUNK_SIZE;
50 : : }
51 : :
52 [ # # ]: 0 : if (rxq->ctrl->is_hairpin)
53 : 0 : td = priv->sh->td->id;
54 : : else
55 : 0 : td = priv->sh->tdn;
56 : :
57 : 0 : lpbk = !!priv->dev_data->dev_conf.lpbk_mode;
58 : :
59 : : ret = snprintf(queue_info_buf, sizeof(queue_info_buf),
60 : : "lro_timeout=%u,max_lro_msg_size=%u,td=%u,lpbk=%u",
61 : : lro_timeout, max_lro_msg_size, td, lpbk);
62 : : /*
63 : : * queue_info_buf is set up to accommodate maximum possible values.
64 : : * As a result, snprintf should always succeed here.
65 : : */
66 : : MLX5_ASSERT(ret >= 0);
67 : :
68 : 0 : return queue_info_buf;
69 : : }
70 : :
71 : : static void
72 : 0 : fill_rxq_info(struct mlx5_rxq_priv *rxq,
73 : : struct rte_pmd_mlx5_driver_event_cb_queue_info *queue,
74 : : enum rte_pmd_mlx5_driver_event_cb_type event)
75 : : {
76 : : /* It is assumed that port is started so all control structs should be initialized. */
77 : : MLX5_ASSERT(rxq != NULL);
78 : : MLX5_ASSERT(rxq->ctrl != NULL);
79 : :
80 : 0 : queue->dpdk_queue_id = rxq->idx;
81 [ # # ]: 0 : if (rxq->ctrl->is_hairpin) {
82 : : MLX5_ASSERT(rxq->ctrl->obj != NULL && rxq->ctrl->obj->rq != NULL);
83 : 0 : queue->hw_queue_id = rxq->ctrl->obj->rq->id;
84 : : } else {
85 : : MLX5_ASSERT(rxq->devx_rq.rq != NULL);
86 : 0 : queue->hw_queue_id = rxq->devx_rq.rq->id;
87 : : }
88 [ # # ]: 0 : if (event == RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_RXQ_CREATE)
89 : 0 : queue->queue_info = generate_rx_queue_info(rxq);
90 : 0 : }
91 : :
92 : : static void
93 : 0 : notify_rxq_event(struct mlx5_rxq_priv *rxq,
94 : : enum rte_pmd_mlx5_driver_event_cb_type event)
95 : : {
96 : 0 : struct rte_pmd_mlx5_driver_event_cb_info cb_info = {
97 : : .event = event,
98 : : };
99 : : struct registered_cb *r;
100 : : uint16_t port_id;
101 : :
102 : : MLX5_ASSERT(rxq != NULL);
103 : : MLX5_ASSERT(event == RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_RXQ_CREATE ||
104 : : event == RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_RXQ_DESTROY);
105 : :
106 [ # # ]: 0 : if (LIST_EMPTY(&cb_list_head))
107 : 0 : return;
108 : :
109 : 0 : port_id = rxq->priv->dev_data->port_id;
110 : 0 : fill_rxq_info(rxq, &cb_info.queue, event);
111 : :
112 [ # # ]: 0 : LIST_FOREACH(r, &cb_list_head, list)
113 : 0 : r->cb(port_id, &cb_info, r->opaque);
114 : : }
115 : :
116 : : void
117 : 0 : mlx5_driver_event_notify_rxq_create(struct mlx5_rxq_priv *rxq)
118 : : {
119 : 0 : notify_rxq_event(rxq, RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_RXQ_CREATE);
120 : 0 : }
121 : :
122 : : void
123 : 0 : mlx5_driver_event_notify_rxq_destroy(struct mlx5_rxq_priv *rxq)
124 : : {
125 : 0 : notify_rxq_event(rxq, RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_RXQ_DESTROY);
126 : 0 : }
127 : :
128 : : static void
129 : : fill_txq_info(struct mlx5_txq_ctrl *txq_ctrl,
130 : : struct rte_pmd_mlx5_driver_event_cb_queue_info *queue)
131 : : {
132 : : /* It is assumed that port is started so all control structs should be initialized. */
133 : : MLX5_ASSERT(txq_ctrl != NULL);
134 : : MLX5_ASSERT(txq_ctrl->obj != NULL);
135 : :
136 : 0 : queue->dpdk_queue_id = txq_ctrl->txq.idx;
137 : 0 : if (txq_ctrl->is_hairpin) {
138 : : MLX5_ASSERT(txq_ctrl->obj->sq != NULL);
139 : 0 : queue->hw_queue_id = txq_ctrl->obj->sq->id;
140 : : } else {
141 : : MLX5_ASSERT(txq_ctrl->obj->sq_obj.sq != NULL);
142 : 0 : queue->hw_queue_id = txq_ctrl->obj->sq_obj.sq->id;
143 : : }
144 : : }
145 : :
146 : : static void
147 : 0 : notify_txq_event(struct mlx5_txq_ctrl *txq_ctrl,
148 : : enum rte_pmd_mlx5_driver_event_cb_type event)
149 : : {
150 : 0 : struct rte_pmd_mlx5_driver_event_cb_info cb_info = {
151 : : .event = event,
152 : : };
153 : : struct registered_cb *r;
154 : : uint16_t port_id;
155 : :
156 : : MLX5_ASSERT(txq_ctrl != NULL);
157 : : MLX5_ASSERT(event == RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_TXQ_CREATE ||
158 : : event == RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_TXQ_DESTROY);
159 : :
160 [ # # ]: 0 : if (LIST_EMPTY(&cb_list_head))
161 : 0 : return;
162 : :
163 [ # # ]: 0 : port_id = txq_ctrl->priv->dev_data->port_id;
164 : : fill_txq_info(txq_ctrl, &cb_info.queue);
165 : :
166 [ # # ]: 0 : LIST_FOREACH(r, &cb_list_head, list)
167 : 0 : r->cb(port_id, &cb_info, r->opaque);
168 : : }
169 : :
170 : : void
171 : 0 : mlx5_driver_event_notify_txq_create(struct mlx5_txq_ctrl *txq_ctrl)
172 : : {
173 : 0 : notify_txq_event(txq_ctrl, RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_TXQ_CREATE);
174 : 0 : }
175 : :
176 : : void
177 : 0 : mlx5_driver_event_notify_txq_destroy(struct mlx5_txq_ctrl *txq_ctrl)
178 : : {
179 : 0 : notify_txq_event(txq_ctrl, RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_TXQ_DESTROY);
180 : 0 : }
181 : :
182 : : static void
183 : 0 : notify_existing_queues(uint16_t port_id,
184 : : rte_pmd_mlx5_driver_event_callback_t cb,
185 : : void *opaque)
186 : : {
187 : 0 : struct rte_eth_dev *dev = &rte_eth_devices[port_id];
188 : 0 : struct mlx5_priv *priv = (struct mlx5_priv *)dev->data->dev_private;
189 : : unsigned int i;
190 : :
191 : : /* Stopped port does not have any queues. */
192 [ # # ]: 0 : if (!dev->data->dev_started)
193 : : return;
194 : :
195 [ # # ]: 0 : for (i = 0; i < priv->rxqs_n; ++i) {
196 : 0 : struct mlx5_rxq_priv *rxq = mlx5_rxq_get(dev, i);
197 : 0 : struct rte_pmd_mlx5_driver_event_cb_info cb_info = {
198 : : .event = RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_RXQ_CREATE,
199 : : };
200 : :
201 : : /* Port is started and only known queues are iterated on. All should be there. */
202 : : MLX5_ASSERT(rxq != NULL);
203 : :
204 : 0 : fill_rxq_info(rxq, &cb_info.queue, cb_info.event);
205 : 0 : cb(port_id, &cb_info, opaque);
206 : : }
207 : :
208 [ # # ]: 0 : for (i = 0; i < priv->txqs_n; ++i) {
209 : 0 : struct mlx5_txq_ctrl *txq_ctrl = mlx5_txq_get(dev, i);
210 [ # # ]: 0 : struct rte_pmd_mlx5_driver_event_cb_info cb_info = {
211 : : .event = RTE_PMD_MLX5_DRIVER_EVENT_CB_TYPE_TXQ_CREATE,
212 : : };
213 : :
214 : : /* Port is started and only known queues are iterated on. All should be there. */
215 : : MLX5_ASSERT(txq_ctrl != NULL);
216 : :
217 : : fill_txq_info(txq_ctrl, &cb_info.queue);
218 : 0 : cb(port_id, &cb_info, opaque);
219 : :
220 : : /* mlx5_txq_get() increments a ref count on Tx queue. Need to decrement. */
221 : 0 : mlx5_txq_release(dev, i);
222 : : }
223 : : }
224 : :
225 : : static void
226 : 0 : notify_existing_devices(rte_pmd_mlx5_driver_event_callback_t cb, void *opaque)
227 : : {
228 : : uint16_t port_id;
229 : :
230 : : /*
231 : : * Whenever there is at least one available port,
232 : : * it means that EAL was initialized and ports were probed.
233 : : * Logging library should be available, so it is safe to use DRV_LOG.
234 : : */
235 [ # # ]: 0 : MLX5_ETH_FOREACH_DEV(port_id, NULL)
236 : 0 : notify_existing_queues(port_id, cb, opaque);
237 : 0 : }
238 : :
239 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pmd_mlx5_driver_event_cb_register, 25.11)
240 : : int
241 : 0 : rte_pmd_mlx5_driver_event_cb_register(rte_pmd_mlx5_driver_event_callback_t cb, void *opaque)
242 : : {
243 : : struct registered_cb *r;
244 : :
245 [ # # ]: 0 : if (cb == NULL)
246 : : return -EINVAL;
247 : :
248 [ # # ]: 0 : LIST_FOREACH(r, &cb_list_head, list) {
249 [ # # ]: 0 : if (r->cb == cb)
250 : : return -EEXIST;
251 : : }
252 : :
253 : 0 : r = calloc(1, sizeof(*r));
254 [ # # ]: 0 : if (r == NULL)
255 : : return -ENOMEM;
256 : :
257 : 0 : r->cb = cb;
258 : 0 : r->opaque = opaque;
259 : :
260 : 0 : notify_existing_devices(cb, opaque);
261 : :
262 [ # # ]: 0 : LIST_INSERT_HEAD(&cb_list_head, r, list);
263 : :
264 : 0 : return 0;
265 : : }
266 : :
267 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pmd_mlx5_driver_event_cb_unregister, 25.11)
268 : : int
269 : 0 : rte_pmd_mlx5_driver_event_cb_unregister(rte_pmd_mlx5_driver_event_callback_t cb)
270 : : {
271 : : struct registered_cb *r;
272 : : bool found = false;
273 : :
274 [ # # ]: 0 : if (cb == NULL)
275 : : return -EINVAL;
276 : :
277 [ # # ]: 0 : LIST_FOREACH(r, &cb_list_head, list) {
278 [ # # ]: 0 : if (r->cb == cb) {
279 : : found = true;
280 : : break;
281 : : }
282 : : }
283 [ # # ]: 0 : if (!found)
284 : : return 0;
285 : :
286 [ # # ]: 0 : LIST_REMOVE(r, list);
287 : 0 : free(r);
288 : :
289 : 0 : return 0;
290 : : }
291 : :
292 : 253 : RTE_FINI(rte_pmd_mlx5_driver_event_cb_cleanup) {
293 : : struct registered_cb *r;
294 : :
295 [ - + ]: 253 : while (!LIST_EMPTY(&cb_list_head)) {
296 : : r = LIST_FIRST(&cb_list_head);
297 [ # # ]: 0 : LIST_REMOVE(r, list);
298 : 0 : free(r);
299 : : }
300 : 253 : }
|