Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2018 Microsoft Corp.
3 : : * Copyright (c) 2010-2012 Citrix Inc.
4 : : * Copyright (c) 2012 NetApp Inc.
5 : : * All rights reserved.
6 : : */
7 : :
8 : : /*
9 : : * Network Virtualization Service.
10 : : */
11 : :
12 : :
13 : : #include <stdint.h>
14 : : #include <string.h>
15 : : #include <stdio.h>
16 : : #include <errno.h>
17 : : #include <unistd.h>
18 : :
19 : : #include <rte_ethdev.h>
20 : : #include <rte_string_fns.h>
21 : : #include <rte_memzone.h>
22 : : #include <rte_malloc.h>
23 : : #include <rte_atomic.h>
24 : : #include <rte_branch_prediction.h>
25 : : #include <rte_ether.h>
26 : : #include <rte_common.h>
27 : : #include <rte_errno.h>
28 : : #include <rte_cycles.h>
29 : : #include <rte_memory.h>
30 : : #include <rte_eal.h>
31 : : #include <dev_driver.h>
32 : : #include <bus_vmbus_driver.h>
33 : :
34 : : #include "hn_logs.h"
35 : : #include "hn_var.h"
36 : : #include "hn_nvs.h"
37 : :
38 : : static const uint32_t hn_nvs_version[] = {
39 : : NVS_VERSION_61,
40 : : NVS_VERSION_6,
41 : : NVS_VERSION_5,
42 : : NVS_VERSION_4,
43 : : NVS_VERSION_2,
44 : : NVS_VERSION_1
45 : : };
46 : :
47 : : static int hn_nvs_req_send(struct hn_data *hv,
48 : : void *req, uint32_t reqlen)
49 : : {
50 : 0 : return rte_vmbus_chan_send(hn_primary_chan(hv),
51 : : VMBUS_CHANPKT_TYPE_INBAND,
52 : : req, reqlen, 0,
53 : : VMBUS_CHANPKT_FLAG_NONE, NULL);
54 : : }
55 : :
56 : : static int
57 : 0 : __hn_nvs_execute(struct hn_data *hv,
58 : : void *req, uint32_t reqlen,
59 : : void *resp, uint32_t resplen,
60 : : uint32_t type)
61 : : {
62 : : struct vmbus_channel *chan = hn_primary_chan(hv);
63 : : char buffer[NVS_RESPSIZE_MAX];
64 : : const struct hn_nvs_hdr *hdr;
65 : : uint64_t xactid;
66 : : uint32_t len;
67 : : int ret;
68 : :
69 : : /* Send request to ring buffer */
70 : 0 : ret = rte_vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_INBAND,
71 : : req, reqlen, 0,
72 : : VMBUS_CHANPKT_FLAG_RC, NULL);
73 : :
74 [ # # ]: 0 : if (ret) {
75 : 0 : PMD_DRV_LOG(ERR, "send request failed: %d", ret);
76 : 0 : return ret;
77 : : }
78 : :
79 : 0 : retry:
80 : 0 : len = sizeof(buffer);
81 : 0 : ret = rte_vmbus_chan_recv(chan, buffer, &len, &xactid);
82 [ # # ]: 0 : if (ret == -EAGAIN) {
83 : 0 : rte_delay_us(HN_CHAN_INTERVAL_US);
84 : 0 : goto retry;
85 : : }
86 : :
87 [ # # ]: 0 : if (ret < 0) {
88 : 0 : PMD_DRV_LOG(ERR, "recv response failed: %d", ret);
89 : 0 : return ret;
90 : : }
91 : :
92 [ # # ]: 0 : if (len < sizeof(*hdr)) {
93 : 0 : PMD_DRV_LOG(ERR, "response missing NVS header");
94 : 0 : return -EINVAL;
95 : : }
96 : :
97 : : hdr = (struct hn_nvs_hdr *)buffer;
98 : :
99 : : /* Silently drop received packets while waiting for response */
100 [ # # # ]: 0 : switch (hdr->type) {
101 : 0 : case NVS_TYPE_RNDIS:
102 : 0 : hn_nvs_ack_rxbuf(chan, xactid);
103 : : /* fallthrough */
104 : :
105 : 0 : case NVS_TYPE_TXTBL_NOTE:
106 : 0 : PMD_DRV_LOG(DEBUG, "discard packet type 0x%x", hdr->type);
107 : 0 : goto retry;
108 : : }
109 : :
110 [ # # ]: 0 : if (hdr->type != type) {
111 : 0 : PMD_DRV_LOG(ERR, "unexpected NVS resp %#x, expect %#x",
112 : : hdr->type, type);
113 : 0 : return -EINVAL;
114 : : }
115 : :
116 [ # # ]: 0 : if (len < resplen) {
117 : 0 : PMD_DRV_LOG(ERR,
118 : : "invalid NVS resp len %u (expect %u)",
119 : : len, resplen);
120 : 0 : return -EINVAL;
121 : : }
122 : :
123 : 0 : memcpy(resp, buffer, resplen);
124 : :
125 : : /* All pass! */
126 : 0 : return 0;
127 : : }
128 : :
129 : :
130 : : /*
131 : : * Execute one control command and get the response.
132 : : * Only one command can be active on a channel at once
133 : : * Unlike BSD, DPDK does not have an interrupt context
134 : : * so the polling is required to wait for response.
135 : : */
136 : : static int
137 : 0 : hn_nvs_execute(struct hn_data *hv,
138 : : void *req, uint32_t reqlen,
139 : : void *resp, uint32_t resplen,
140 : : uint32_t type)
141 : : {
142 : 0 : struct hn_rx_queue *rxq = hv->primary;
143 : : int ret;
144 : :
145 : 0 : rte_spinlock_lock(&rxq->ring_lock);
146 : 0 : ret = __hn_nvs_execute(hv, req, reqlen, resp, resplen, type);
147 : : rte_spinlock_unlock(&rxq->ring_lock);
148 : :
149 : 0 : return ret;
150 : : }
151 : :
152 : : static int
153 : 0 : hn_nvs_doinit(struct hn_data *hv, uint32_t nvs_ver)
154 : : {
155 : : struct hn_nvs_init init;
156 : : struct hn_nvs_init_resp resp;
157 : : uint32_t status;
158 : : int error;
159 : :
160 : : memset(&init, 0, sizeof(init));
161 : 0 : init.type = NVS_TYPE_INIT;
162 : 0 : init.ver_min = nvs_ver;
163 : 0 : init.ver_max = nvs_ver;
164 : :
165 : 0 : error = hn_nvs_execute(hv, &init, sizeof(init),
166 : : &resp, sizeof(resp),
167 : : NVS_TYPE_INIT_RESP);
168 [ # # ]: 0 : if (error)
169 : : return error;
170 : :
171 : 0 : status = resp.status;
172 [ # # ]: 0 : if (status != NVS_STATUS_OK) {
173 : : /* Not fatal, try other versions */
174 : 0 : PMD_INIT_LOG(DEBUG, "nvs init failed for ver 0x%x",
175 : : nvs_ver);
176 : 0 : return -EINVAL;
177 : : }
178 : :
179 : : return 0;
180 : : }
181 : :
182 : : static int
183 : 0 : hn_nvs_conn_rxbuf(struct hn_data *hv)
184 : : {
185 : : struct hn_nvs_rxbuf_conn conn;
186 : : struct hn_nvs_rxbuf_connresp resp;
187 : : uint32_t status;
188 : : int error;
189 : :
190 : : /* Kernel has already setup RXBUF on primary channel. */
191 : :
192 : : /*
193 : : * Connect RXBUF to NVS.
194 : : */
195 : 0 : conn.type = NVS_TYPE_RXBUF_CONN;
196 : 0 : conn.gpadl = hv->rxbuf_res.phys_addr;
197 : 0 : conn.sig = NVS_RXBUF_SIG;
198 : 0 : PMD_DRV_LOG(DEBUG, "connect rxbuff va=%p gpad=%#" PRIx64,
199 : : hv->rxbuf_res.addr,
200 : : hv->rxbuf_res.phys_addr);
201 : :
202 : 0 : error = hn_nvs_execute(hv, &conn, sizeof(conn),
203 : : &resp, sizeof(resp),
204 : : NVS_TYPE_RXBUF_CONNRESP);
205 [ # # ]: 0 : if (error) {
206 : 0 : PMD_DRV_LOG(ERR,
207 : : "exec nvs rxbuf conn failed: %d",
208 : : error);
209 : 0 : return error;
210 : : }
211 : :
212 : 0 : status = resp.status;
213 [ # # ]: 0 : if (status != NVS_STATUS_OK) {
214 : 0 : PMD_DRV_LOG(ERR,
215 : : "nvs rxbuf conn failed: %x", status);
216 : 0 : return -EIO;
217 : : }
218 [ # # ]: 0 : if (resp.nsect != 1) {
219 : 0 : PMD_DRV_LOG(ERR,
220 : : "nvs rxbuf response num sections %u != 1",
221 : : resp.nsect);
222 : 0 : return -EIO;
223 : : }
224 : :
225 : 0 : PMD_DRV_LOG(INFO,
226 : : "receive buffer size %u count %u",
227 : : resp.nvs_sect[0].slotsz,
228 : : resp.nvs_sect[0].slotcnt);
229 : 0 : hv->rxbuf_section_cnt = resp.nvs_sect[0].slotcnt;
230 : :
231 : : /*
232 : : * Primary queue's rxbuf_info is not allocated at creation time.
233 : : * Now we can allocate it after we figure out the slotcnt.
234 : : */
235 : 0 : hv->primary->rxbuf_info = rte_calloc("HN_RXBUF_INFO",
236 : : hv->rxbuf_section_cnt,
237 : : sizeof(*hv->primary->rxbuf_info),
238 : : RTE_CACHE_LINE_SIZE);
239 [ # # ]: 0 : if (!hv->primary->rxbuf_info) {
240 : 0 : PMD_DRV_LOG(ERR,
241 : : "could not allocate rxbuf info");
242 : 0 : return -ENOMEM;
243 : : }
244 : :
245 : : return 0;
246 : : }
247 : :
248 : : static void
249 : 0 : hn_nvs_disconn_rxbuf(struct hn_data *hv)
250 : : {
251 : : struct hn_nvs_rxbuf_disconn disconn;
252 : : int error;
253 : :
254 : : /*
255 : : * Disconnect RXBUF from NVS.
256 : : */
257 : : memset(&disconn, 0, sizeof(disconn));
258 : 0 : disconn.type = NVS_TYPE_RXBUF_DISCONN;
259 : 0 : disconn.sig = NVS_RXBUF_SIG;
260 : :
261 : : /* NOTE: No response. */
262 : : error = hn_nvs_req_send(hv, &disconn, sizeof(disconn));
263 [ # # ]: 0 : if (error) {
264 : 0 : PMD_DRV_LOG(ERR,
265 : : "send nvs rxbuf disconn failed: %d",
266 : : error);
267 : : }
268 : :
269 : : /*
270 : : * Linger long enough for NVS to disconnect RXBUF.
271 : : */
272 : : rte_delay_ms(200);
273 : 0 : }
274 : :
275 : : static void
276 : 0 : hn_nvs_disconn_chim(struct hn_data *hv)
277 : : {
278 : : int error;
279 : :
280 [ # # ]: 0 : if (hv->chim_cnt != 0) {
281 : : struct hn_nvs_chim_disconn disconn;
282 : :
283 : : /* Disconnect chimney sending buffer from NVS. */
284 : : memset(&disconn, 0, sizeof(disconn));
285 : 0 : disconn.type = NVS_TYPE_CHIM_DISCONN;
286 : 0 : disconn.sig = NVS_CHIM_SIG;
287 : :
288 : : /* NOTE: No response. */
289 : : error = hn_nvs_req_send(hv, &disconn, sizeof(disconn));
290 : :
291 [ # # ]: 0 : if (error) {
292 : 0 : PMD_DRV_LOG(ERR,
293 : : "send nvs chim disconn failed: %d", error);
294 : : }
295 : :
296 : 0 : hv->chim_cnt = 0;
297 : : /*
298 : : * Linger long enough for NVS to disconnect chimney
299 : : * sending buffer.
300 : : */
301 : : rte_delay_ms(200);
302 : : }
303 : 0 : }
304 : :
305 : : static int
306 : 0 : hn_nvs_conn_chim(struct hn_data *hv)
307 : : {
308 : : struct hn_nvs_chim_conn chim;
309 : : struct hn_nvs_chim_connresp resp;
310 : : uint32_t sectsz;
311 : 0 : unsigned long len = hv->chim_res.len;
312 : : int error;
313 : :
314 : : /* Connect chimney sending buffer to NVS */
315 : : memset(&chim, 0, sizeof(chim));
316 : 0 : chim.type = NVS_TYPE_CHIM_CONN;
317 : 0 : chim.gpadl = hv->chim_res.phys_addr;
318 : 0 : chim.sig = NVS_CHIM_SIG;
319 : 0 : PMD_DRV_LOG(DEBUG, "connect send buf va=%p gpad=%#" PRIx64,
320 : : hv->chim_res.addr,
321 : : hv->chim_res.phys_addr);
322 : :
323 : 0 : error = hn_nvs_execute(hv, &chim, sizeof(chim),
324 : : &resp, sizeof(resp),
325 : : NVS_TYPE_CHIM_CONNRESP);
326 [ # # ]: 0 : if (error) {
327 : 0 : PMD_DRV_LOG(ERR, "exec nvs chim conn failed");
328 : 0 : return error;
329 : : }
330 : :
331 [ # # ]: 0 : if (resp.status != NVS_STATUS_OK) {
332 : 0 : PMD_DRV_LOG(ERR, "nvs chim conn failed: %x",
333 : : resp.status);
334 : 0 : return -EIO;
335 : : }
336 : :
337 : 0 : sectsz = resp.sectsz;
338 [ # # # # ]: 0 : if (sectsz == 0 || sectsz & (sizeof(uint32_t) - 1)) {
339 : : /* Can't use chimney sending buffer; done! */
340 : 0 : PMD_DRV_LOG(NOTICE,
341 : : "invalid chimney sending buffer section size: %u",
342 : : sectsz);
343 : : error = -EINVAL;
344 : 0 : goto cleanup;
345 : : }
346 : :
347 : 0 : hv->chim_szmax = sectsz;
348 : 0 : hv->chim_cnt = len / sectsz;
349 : :
350 : 0 : PMD_DRV_LOG(INFO, "send buffer %lu section size:%u, count:%u",
351 : : len, hv->chim_szmax, hv->chim_cnt);
352 : :
353 : : /* Done! */
354 : 0 : return 0;
355 : :
356 : : cleanup:
357 : 0 : hn_nvs_disconn_chim(hv);
358 : 0 : return error;
359 : : }
360 : :
361 : : /*
362 : : * Configure MTU and enable VLAN.
363 : : */
364 : : static int
365 [ # # ]: 0 : hn_nvs_conf_ndis(struct hn_data *hv, unsigned int mtu)
366 : : {
367 : : struct hn_nvs_ndis_conf conf;
368 : : int error;
369 : :
370 : : memset(&conf, 0, sizeof(conf));
371 : 0 : conf.type = NVS_TYPE_NDIS_CONF;
372 : 0 : conf.mtu = mtu + RTE_ETHER_HDR_LEN;
373 : 0 : conf.caps = NVS_NDIS_CONF_VLAN;
374 : :
375 : : /* enable SRIOV */
376 [ # # ]: 0 : if (hv->nvs_ver >= NVS_VERSION_5)
377 : 0 : conf.caps |= NVS_NDIS_CONF_SRIOV;
378 : :
379 : : /* NOTE: No response. */
380 : : error = hn_nvs_req_send(hv, &conf, sizeof(conf));
381 [ # # ]: 0 : if (error) {
382 : 0 : PMD_DRV_LOG(ERR,
383 : : "send nvs ndis conf failed: %d", error);
384 : 0 : return error;
385 : : }
386 : :
387 : : return 0;
388 : : }
389 : :
390 : : static int
391 : 0 : hn_nvs_init_ndis(struct hn_data *hv)
392 : : {
393 : : struct hn_nvs_ndis_init ndis;
394 : : int error;
395 : :
396 : : memset(&ndis, 0, sizeof(ndis));
397 : 0 : ndis.type = NVS_TYPE_NDIS_INIT;
398 : 0 : ndis.ndis_major = NDIS_VERSION_MAJOR(hv->ndis_ver);
399 : 0 : ndis.ndis_minor = NDIS_VERSION_MINOR(hv->ndis_ver);
400 : :
401 : : /* NOTE: No response. */
402 : : error = hn_nvs_req_send(hv, &ndis, sizeof(ndis));
403 [ # # ]: 0 : if (error)
404 : 0 : PMD_DRV_LOG(ERR,
405 : : "send nvs ndis init failed: %d", error);
406 : :
407 : 0 : return error;
408 : : }
409 : :
410 : : static int
411 : 0 : hn_nvs_init(struct hn_data *hv)
412 : : {
413 : : unsigned int i;
414 : : int error;
415 : :
416 : : /*
417 : : * Find the supported NVS version and set NDIS version accordingly.
418 : : */
419 [ # # ]: 0 : for (i = 0; i < RTE_DIM(hn_nvs_version); ++i) {
420 : 0 : error = hn_nvs_doinit(hv, hn_nvs_version[i]);
421 [ # # ]: 0 : if (error) {
422 : 0 : PMD_INIT_LOG(DEBUG, "version %#x error %d",
423 : : hn_nvs_version[i], error);
424 : : continue;
425 : : }
426 : :
427 : 0 : hv->nvs_ver = hn_nvs_version[i];
428 : :
429 : : /* Set NDIS version according to NVS version. */
430 : 0 : hv->ndis_ver = NDIS_VERSION_6_30;
431 [ # # ]: 0 : if (hv->nvs_ver <= NVS_VERSION_4)
432 : 0 : hv->ndis_ver = NDIS_VERSION_6_1;
433 : :
434 : 0 : PMD_INIT_LOG(DEBUG,
435 : : "NVS version %#x, NDIS version %u.%u",
436 : : hv->nvs_ver, NDIS_VERSION_MAJOR(hv->ndis_ver),
437 : : NDIS_VERSION_MINOR(hv->ndis_ver));
438 : 0 : return 0;
439 : : }
440 : :
441 : 0 : PMD_DRV_LOG(ERR,
442 : : "no NVS compatible version available");
443 : 0 : return -ENXIO;
444 : : }
445 : :
446 : : int
447 : 0 : hn_nvs_attach(struct hn_data *hv, unsigned int mtu)
448 : : {
449 : : int error;
450 : :
451 : : /*
452 : : * Initialize NVS.
453 : : */
454 : 0 : error = hn_nvs_init(hv);
455 [ # # ]: 0 : if (error)
456 : : return error;
457 : :
458 : : /** Configure NDIS before initializing it. */
459 [ # # ]: 0 : if (hv->nvs_ver >= NVS_VERSION_2) {
460 : 0 : error = hn_nvs_conf_ndis(hv, mtu);
461 [ # # ]: 0 : if (error)
462 : : return error;
463 : : }
464 : :
465 : : /*
466 : : * Initialize NDIS.
467 : : */
468 : 0 : error = hn_nvs_init_ndis(hv);
469 [ # # ]: 0 : if (error)
470 : : return error;
471 : :
472 : : /*
473 : : * Connect RXBUF.
474 : : */
475 : 0 : error = hn_nvs_conn_rxbuf(hv);
476 [ # # ]: 0 : if (error)
477 : : return error;
478 : :
479 : : /*
480 : : * Connect chimney sending buffer.
481 : : */
482 : 0 : error = hn_nvs_conn_chim(hv);
483 [ # # ]: 0 : if (error) {
484 : 0 : hn_nvs_disconn_rxbuf(hv);
485 : 0 : return error;
486 : : }
487 : :
488 : : return 0;
489 : : }
490 : :
491 : : void
492 : 0 : hn_nvs_detach(struct hn_data *hv __rte_unused)
493 : : {
494 : 0 : PMD_INIT_FUNC_TRACE();
495 : :
496 : : /* NOTE: there are no requests to stop the NVS. */
497 : 0 : hn_nvs_disconn_rxbuf(hv);
498 : 0 : hn_nvs_disconn_chim(hv);
499 : 0 : }
500 : :
501 : : /*
502 : : * Ack the consumed RXBUF associated w/ this channel packet,
503 : : * so that this RXBUF can be recycled by the hypervisor.
504 : : */
505 : : void
506 : 0 : hn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid)
507 : : {
508 : : unsigned int retries = 0;
509 : 0 : struct hn_nvs_rndis_ack ack = {
510 : : .type = NVS_TYPE_RNDIS_ACK,
511 : : .status = NVS_STATUS_OK,
512 : : };
513 : : int error;
514 : :
515 : : PMD_RX_LOG(DEBUG, "ack RX id %" PRIu64, tid);
516 : :
517 : 0 : again:
518 : 0 : error = rte_vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
519 : : &ack, sizeof(ack), tid,
520 : : VMBUS_CHANPKT_FLAG_NONE, NULL);
521 : :
522 [ # # ]: 0 : if (error == 0)
523 : 0 : return;
524 : :
525 [ # # ]: 0 : if (error == -EAGAIN) {
526 : : /*
527 : : * NOTE:
528 : : * This should _not_ happen in real world, since the
529 : : * consumption of the TX bufring from the TX path is
530 : : * controlled.
531 : : */
532 : : PMD_RX_LOG(NOTICE, "RXBUF ack retry");
533 [ # # ]: 0 : if (++retries < 10) {
534 : : rte_delay_ms(1);
535 : 0 : goto again;
536 : : }
537 : : }
538 : : /* RXBUF leaks! */
539 : 0 : PMD_DRV_LOG(ERR, "RXBUF ack failed");
540 : : }
541 : :
542 : : int
543 : 0 : hn_nvs_alloc_subchans(struct hn_data *hv, uint32_t *nsubch)
544 : : {
545 : : struct hn_nvs_subch_req req;
546 : : struct hn_nvs_subch_resp resp;
547 : : int error;
548 : :
549 : : memset(&req, 0, sizeof(req));
550 : 0 : req.type = NVS_TYPE_SUBCH_REQ;
551 : 0 : req.op = NVS_SUBCH_OP_ALLOC;
552 : 0 : req.nsubch = *nsubch;
553 : :
554 : 0 : error = hn_nvs_execute(hv, &req, sizeof(req),
555 : : &resp, sizeof(resp),
556 : : NVS_TYPE_SUBCH_RESP);
557 [ # # ]: 0 : if (error)
558 : : return error;
559 : :
560 [ # # ]: 0 : if (resp.status != NVS_STATUS_OK) {
561 : 0 : PMD_INIT_LOG(ERR,
562 : : "nvs subch alloc failed: %#x",
563 : : resp.status);
564 : 0 : return -EIO;
565 : : }
566 : :
567 [ # # ]: 0 : if (resp.nsubch > *nsubch) {
568 : 0 : PMD_INIT_LOG(NOTICE,
569 : : "%u subchans are allocated, requested %u",
570 : : resp.nsubch, *nsubch);
571 : : }
572 : 0 : *nsubch = resp.nsubch;
573 : :
574 : 0 : return 0;
575 : : }
576 : :
577 : : int
578 : 0 : hn_nvs_set_datapath(struct hn_data *hv, uint32_t path)
579 : : {
580 : : struct hn_nvs_datapath dp;
581 : : int error;
582 : :
583 [ # # ]: 0 : PMD_DRV_LOG(DEBUG, "set datapath %s",
584 : : path ? "VF" : "Synthetic");
585 : :
586 : : memset(&dp, 0, sizeof(dp));
587 : 0 : dp.type = NVS_TYPE_SET_DATAPATH;
588 : 0 : dp.active_path = path;
589 : :
590 : : error = hn_nvs_req_send(hv, &dp, sizeof(dp));
591 [ # # ]: 0 : if (error) {
592 : 0 : PMD_DRV_LOG(ERR,
593 : : "send set datapath failed: %d",
594 : : error);
595 : : }
596 : :
597 : 0 : return error;
598 : : }
|