1 /*
2 * Copyright (c) 2014-2016, Cisco Systems, Inc. All rights reserved.
3 *
4 * LICENSE_BEGIN
5 *
6 * This software is available to you under a choice of one of two
7 * licenses. You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * BSD license below:
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * - Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer.
19 *
20 * - Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 *
38 * LICENSE_END
39 *
40 *
41 */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <string.h>
47 #include <fcntl.h>
48 #include <dirent.h>
49 #include <pthread.h>
50 #include <errno.h>
51 #include <sys/stat.h>
52 #include <sys/mman.h>
53
54 #include "usnic_direct.h"
55 #include "usd.h"
56 #include "usd_ib_sysfs.h"
57 #include "usd_ib_cmd.h"
58 #include "usd_socket.h"
59 #include "usd_device.h"
60
61 static pthread_once_t usd_init_once = PTHREAD_ONCE_INIT;
62
63 static struct usd_ib_dev *usd_ib_dev_list;
64 static int usd_init_error;
65
66 TAILQ_HEAD(,usd_device) usd_device_list =
67 TAILQ_HEAD_INITIALIZER(usd_device_list);
68
69 /*
70 * Perform one-time initialization
71 */
72 static void
do_usd_init(void)73 do_usd_init(void)
74 {
75 usd_init_error = usd_ib_get_devlist(&usd_ib_dev_list);
76 }
77
78 /*
79 * Unmap group vector when releasing usd_dev
80 */
81 static void
usd_unmap_grp_vect(struct usd_device * dev)82 usd_unmap_grp_vect(struct usd_device *dev)
83 {
84 if (dev->grp_vect_map.va != NULL) {
85 munmap(dev->grp_vect_map.va, dev->grp_vect_map.len);
86 dev->grp_vect_map.va = NULL;
87 }
88 }
89
90 /*
91 * Init routine
92 */
93 static int
usd_init(void)94 usd_init(void)
95 {
96 /* Do initialization one time */
97 pthread_once(&usd_init_once, do_usd_init);
98 return usd_init_error;
99 }
100
101 /*
102 * Return list of currently available devices
103 */
104 int
usd_get_device_list(struct usd_device_entry * entries,int * num_entries)105 usd_get_device_list(
106 struct usd_device_entry *entries,
107 int *num_entries)
108 {
109 int n;
110 struct usd_ib_dev *idp;
111 int ret;
112
113 n = 0;
114
115 ret = usd_init();
116 if (ret != 0) {
117 goto out;
118 }
119
120 idp = usd_ib_dev_list;
121 while (idp != NULL && n < *num_entries) {
122 strncpy(entries[n].ude_devname, idp->id_usnic_name,
123 sizeof(entries[n].ude_devname) - 1);
124 ++n;
125 idp = idp->id_next;
126 }
127
128 out:
129 *num_entries = n;
130 return ret;
131 }
132
133 /*
134 * Allocate a context from the driver
135 */
136 static int
usd_open_ibctx(struct usd_context * uctx)137 usd_open_ibctx(struct usd_context *uctx)
138 {
139 int ret;
140
141 ret = usd_ib_cmd_get_context(uctx);
142 return ret;
143 }
144
145 const char *
usd_devid_to_pid(uint32_t vendor_id,uint32_t device_id)146 usd_devid_to_pid(uint32_t vendor_id, uint32_t device_id)
147 {
148 const char *pid;
149
150 if (vendor_id != 0x1137)
151 return "Unknown";
152
153 switch (device_id) {
154 case 0x4f:
155 // Vasona
156 pid = "UCSC-VIC-M82-8P";
157 break;
158 case 0x84:
159 // Cotati
160 pid = "UCSB-MLOM-40G-01";
161 break;
162 case 0x85:
163 // Lexington
164 pid = "UCSC-PCIE-CSC-02";
165 break;
166 case 0xcd:
167 // Icehouse
168 pid = "UCSC-PCIE-C40Q-02";
169 break;
170 case 0xce:
171 // Kirkwood Lake
172 pid = "UCSC-PCIE-C10T-02";
173 break;
174 case 0x12e:
175 // Susanville MLOM
176 pid = "UCSC-MLOM-CSC-02";
177 break;
178 case 0x139:
179 // Torrance MLOM
180 pid = "UCSC-MLOM-C10T-02";
181 break;
182
183 case 0x12c:
184 // Calistoga MLOM
185 pid = "UCSB-MLOM-40G-03";
186 break;
187 case 0x137:
188 // Mountain View (Cruz mezz)
189 pid = "UCSB-VIC-M83-8P";
190 break;
191 case 0x138:
192 // Walnut Creek
193 pid = "UCSB-B3116S-LOM";
194 break;
195 case 0x14b:
196 // Mount Tian
197 pid = "UCSC-C3260-SIOC";
198 break;
199 case 0x14d:
200 // Clearlake
201 pid = "UCSC-PCIE-C40Q-03";
202 break;
203 case 0x157:
204 // Mount Tian2
205 pid = "UCSC-C3260-SIOC";
206 break;
207 case 0x15d:
208 // Claremont MLOM
209 pid = "UCSC-MLOM-C40Q-03";
210 break;
211
212 case 0x0218:
213 // Bradbury
214 pid = "UCSC-MLOM-C25Q-04";
215 break;
216 case 0x0217:
217 // Brentwood
218 pid = "UCSC-PCIE-C25Q-04";
219 break;
220 case 0x021a:
221 // Burlingame
222 pid = "UCSC-MLOM-C40Q-04";
223 break;
224 case 0x0219:
225 // Bayside
226 pid = "UCSC-PCIE-C40Q-04";
227 break;
228 case 0x0215:
229 // Bakersfield
230 pid = "UCSB-MLOM-40G-04";
231 break;
232 case 0x0216:
233 // Boonville
234 pid = "UCSB-VIC-M84-4P";
235 break;
236 case 0x024a:
237 // Benicia
238 pid = "UCSC-PCIE-C100-04";
239 break;
240 case 0x024b:
241 // Beaumont
242 pid = "UCSC-MLOM-C100-04";
243 break;
244
245 default:
246 pid = "Unknown Cisco Device";
247 break;
248 }
249
250 return pid;
251 }
252
253 const char *
usd_devid_to_nicname(uint32_t vendor_id,uint32_t device_id)254 usd_devid_to_nicname(uint32_t vendor_id, uint32_t device_id)
255 {
256 const char *nicname;
257
258 if (vendor_id != 0x1137)
259 return "Unknown";
260
261 switch (device_id) {
262 case 0x4f:
263 // Vasona
264 nicname = "VIC 1280";
265 break;
266 case 0x84:
267 // Cotati
268 nicname = "VIC 1240";
269 break;
270 case 0x85:
271 // Lexington
272 nicname = "VIC 1225";
273 break;
274 case 0xcd:
275 // Icehouse
276 nicname = "VIC 1285";
277 break;
278 case 0xce:
279 // Kirkwood Lake
280 nicname = "VIC 1225T";
281 break;
282 case 0x12e:
283 // Susanville MLOM
284 nicname = "VIC 1227";
285 break;
286 case 0x139:
287 // Torrance MLOM
288 nicname = "VIC 1227T";
289 break;
290
291 case 0x12c:
292 // Calistoga MLOM
293 nicname = "VIC 1340";
294 break;
295 case 0x137:
296 // Mountain View (Cruz mezz)
297 nicname = "VIC 1380";
298 break;
299 case 0x138:
300 // Walnut Creek
301 nicname = "UCSB-B3116S";
302 break;
303 case 0x14b:
304 // Mount Tian
305 nicname = "";
306 break;
307 case 0x14d:
308 // Clearlake
309 nicname = "VIC 1385";
310 break;
311 case 0x157:
312 // Mount Tian2
313 nicname = "";
314 break;
315 case 0x15d:
316 // Claremont MLOM
317 nicname = "VIC 1387";
318 break;
319
320 case 0x0218:
321 // Bradbury
322 nicname = "VIC 1457";
323 break;
324 case 0x0217:
325 // Brentwood
326 nicname = "VIC 1455";
327 break;
328 case 0x021a:
329 // Burlingame
330 nicname = "VIC 1487";
331 break;
332 case 0x0219:
333 // Bayside
334 nicname = "VIC 1485";
335 break;
336 case 0x0215:
337 // Bakersfield
338 nicname = "VIC 1440";
339 break;
340 case 0x0216:
341 // Boonville
342 nicname = "VIC 1480";
343 break;
344 case 0x024a:
345 // Benicia
346 nicname = "VIC 1495";
347 break;
348 case 0x024b:
349 // Beaumont
350 nicname = "VIC 1497";
351 break;
352
353 default:
354 nicname = "Unknown Cisco Device";
355 break;
356 }
357
358 return nicname;
359 }
360
361 /*
362 * Rummage around and collect all the info about this device we can find
363 */
364 static int
usd_discover_device_attrs(struct usd_device * dev,const char * dev_name)365 usd_discover_device_attrs(
366 struct usd_device *dev,
367 const char *dev_name)
368 {
369 struct usd_device_attrs *dap;
370 int ret;
371
372 /* find interface name */
373 ret = usd_get_iface(dev);
374 if (ret != 0)
375 return ret;
376
377 ret = usd_get_mac(dev, dev->ud_attrs.uda_mac_addr);
378 if (ret != 0)
379 return ret;
380
381 ret = usd_get_usnic_config(dev);
382 if (ret != 0)
383 return ret;
384
385 ret = usd_get_firmware(dev);
386 if (ret != 0)
387 return ret;
388
389 /* ipaddr, netmask, mtu */
390 ret = usd_get_dev_if_info(dev);
391 if (ret != 0)
392 return ret;
393
394 /* get what attributes we can from querying IB */
395 ret = usd_ib_query_dev(dev);
396 if (ret != 0)
397 return ret;
398
399 /* constants that should come from driver */
400 dap = &dev->ud_attrs;
401 dap->uda_max_cqe = (1 << 16) - 1;;
402 dap->uda_max_send_credits = (1 << 12) - 1;
403 dap->uda_max_recv_credits = (1 << 12) - 1;
404 strncpy(dap->uda_devname, dev_name, sizeof(dap->uda_devname) - 1);
405
406 return 0;
407 }
408
409 static void
usd_dev_free(struct usd_device * dev)410 usd_dev_free(struct usd_device *dev)
411 {
412 if (dev->ud_arp_sockfd != -1)
413 close(dev->ud_arp_sockfd);
414
415 if (dev->ud_ctx != NULL &&
416 (dev->ud_flags & USD_DEVF_CLOSE_CTX)) {
417 usd_close_context(dev->ud_ctx);
418 }
419 free(dev);
420 }
421
422 /*
423 * Allocate a usd_device without allocating a PD
424 */
425 static int
usd_dev_alloc_init(const char * dev_name,struct usd_open_params * uop_param,struct usd_device ** dev_o)426 usd_dev_alloc_init(const char *dev_name, struct usd_open_params *uop_param,
427 struct usd_device **dev_o)
428 {
429 struct usd_device *dev = NULL;
430 int ret;
431
432 dev = calloc(sizeof(*dev), 1);
433 if (dev == NULL) {
434 ret = -errno;
435 goto out;
436 }
437
438 dev->ud_flags = 0;
439 if (uop_param->context == NULL) {
440 ret = usd_open_context(dev_name, uop_param->cmd_fd,
441 &dev->ud_ctx);
442 if (ret != 0) {
443 goto out;
444 }
445 dev->ud_flags |= USD_DEVF_CLOSE_CTX;
446 } else {
447 dev->ud_ctx = uop_param->context;
448 }
449
450 dev->ud_arp_sockfd = -1;
451
452 TAILQ_INIT(&dev->ud_pending_reqs);
453 TAILQ_INIT(&dev->ud_completed_reqs);
454
455 if (uop_param->context == NULL)
456 ret = usd_discover_device_attrs(dev, dev_name);
457 else
458 ret = usd_discover_device_attrs(dev,
459 uop_param->context->ucx_ib_dev->id_usnic_name);
460 if (ret != 0)
461 goto out;
462
463 dev->ud_attrs.uda_event_fd = dev->ud_ctx->event_fd;
464 dev->ud_attrs.uda_num_comp_vectors = dev->ud_ctx->num_comp_vectors;
465
466 if (!(uop_param->flags & UOPF_SKIP_LINK_CHECK)) {
467 ret = usd_device_ready(dev);
468 if (ret != 0) {
469 goto out;
470 }
471 }
472
473 *dev_o = dev;
474 return 0;
475
476 out:
477 if (dev != NULL)
478 usd_dev_free(dev);
479 return ret;
480 }
481
482 int
usd_close_context(struct usd_context * ctx)483 usd_close_context(struct usd_context *ctx)
484 {
485 pthread_mutex_destroy(&ctx->ucx_mutex);
486
487 /* XXX - verify all other resources closed out */
488 if (ctx->ucx_flags & USD_CTXF_CLOSE_CMD_FD)
489 close(ctx->ucx_ib_dev_fd);
490 if (ctx->ucmd_ib_dev_fd != -1)
491 close(ctx->ucmd_ib_dev_fd);
492
493 free(ctx);
494
495 return 0;
496 }
497
498 int
usd_open_context(const char * dev_name,int cmd_fd,struct usd_context ** ctx_o)499 usd_open_context(const char *dev_name, int cmd_fd,
500 struct usd_context **ctx_o)
501 {
502 struct usd_context *ctx = NULL;
503 struct usd_ib_dev *idp;
504 int ret;
505
506 if (dev_name == NULL)
507 return -EINVAL;
508
509 ret = usd_init();
510 if (ret != 0) {
511 return ret;
512 }
513
514 /* Look for matching device */
515 idp = usd_ib_dev_list;
516 while (idp != NULL) {
517 if (dev_name == NULL || strcmp(idp->id_usnic_name, dev_name) == 0) {
518 break;
519 }
520 idp = idp->id_next;
521 }
522
523 /* not found, leave now */
524 if (idp == NULL) {
525 ret = -ENXIO;
526 goto out;
527 }
528
529 /*
530 * Found matching device, open an instance
531 */
532 ctx = calloc(sizeof(*ctx), 1);
533 if (ctx == NULL) {
534 ret = -errno;
535 goto out;
536 }
537 ctx->ucx_ib_dev_fd = -1;
538 ctx->ucmd_ib_dev_fd = -1;
539 ctx->ucx_flags = 0;
540
541 /* Save pointer to IB device */
542 ctx->ucx_ib_dev = idp;
543
544 /* Open the fd we will be using for IB commands */
545 if (cmd_fd == -1) {
546 ctx->ucx_ib_dev_fd = open(idp->id_dev_path, O_RDWR);
547 if (ctx->ucx_ib_dev_fd == -1) {
548 ret = -ENODEV;
549 goto out;
550 }
551 ctx->ucx_flags |= USD_CTXF_CLOSE_CMD_FD;
552 } else {
553 ctx->ucx_ib_dev_fd = cmd_fd;
554 }
555
556 /*
557 * Open another fd to send encapsulated user commands through
558 * CMD_GET_CONTEXT call. The reason to open an additional fd is
559 * that ib core does not allow multiple get_context call on one
560 * file descriptor.
561 */
562 ctx->ucmd_ib_dev_fd = open(idp->id_dev_path, O_RDWR | O_CLOEXEC);
563 if (ctx->ucmd_ib_dev_fd == -1) {
564 ret = -ENODEV;
565 goto out;
566 }
567
568 /* allocate a context from driver */
569 ret = usd_open_ibctx(ctx);
570 if (ret != 0) {
571 goto out;
572 }
573
574 LIST_INIT(&ctx->ucx_intr_list);
575 if (pthread_mutex_init(&ctx->ucx_mutex, NULL) != 0)
576 goto out;
577
578 *ctx_o = ctx;
579 return 0;
580
581 out:
582 if (ctx != NULL)
583 usd_close_context(ctx);
584 return ret;
585 }
586
587 /*
588 * Close a raw USNIC device
589 */
590 int
usd_close(struct usd_device * dev)591 usd_close(
592 struct usd_device *dev)
593 {
594 usd_unmap_grp_vect(dev);
595
596 TAILQ_REMOVE(&usd_device_list, dev, ud_link);
597 usd_dev_free(dev);
598
599 return 0;
600 }
601
602 /*
603 * Open a raw USNIC device
604 */
605 int
usd_open(const char * dev_name,struct usd_device ** dev_o)606 usd_open(
607 const char *dev_name,
608 struct usd_device **dev_o)
609 {
610 struct usd_open_params params;
611
612 memset(¶ms, 0, sizeof(params));
613 params.cmd_fd = -1;
614 params.context = NULL;
615 return usd_open_with_params(dev_name, ¶ms, dev_o);
616 }
617
618 /*
619 * Most generic usd device open function
620 */
usd_open_with_params(const char * dev_name,struct usd_open_params * uop_param,struct usd_device ** dev_o)621 int usd_open_with_params(const char *dev_name,
622 struct usd_open_params* uop_param,
623 struct usd_device **dev_o)
624 {
625 struct usd_device *dev = NULL;
626 int ret;
627
628 ret = usd_dev_alloc_init(dev_name, uop_param, &dev);
629 if (ret != 0) {
630 goto out;
631 }
632
633 if (!(uop_param->flags & UOPF_SKIP_PD_ALLOC)) {
634 ret = usd_ib_cmd_alloc_pd(dev, &dev->ud_pd_handle);
635 if (ret != 0) {
636 goto out;
637 }
638 }
639
640 TAILQ_INSERT_TAIL(&usd_device_list, dev, ud_link);
641 *dev_o = dev;
642 return 0;
643
644 out:
645 if (dev != NULL)
646 usd_dev_free(dev);
647 return ret;
648 }
649
650 /*
651 * Return attributes of a device
652 */
653 int
usd_get_device_attrs(struct usd_device * dev,struct usd_device_attrs * dattrs)654 usd_get_device_attrs(
655 struct usd_device *dev,
656 struct usd_device_attrs *dattrs)
657 {
658 int ret;
659
660 /* ipaddr, netmask, mtu */
661 ret = usd_get_dev_if_info(dev);
662 if (ret != 0)
663 return ret;
664
665 /* get what attributes we can from querying IB */
666 ret = usd_ib_query_dev(dev);
667 if (ret != 0)
668 return ret;
669
670 *dattrs = dev->ud_attrs;
671 return 0;
672 }
673
674 /*
675 * Check that device is ready to have queues created
676 */
677 int
usd_device_ready(struct usd_device * dev)678 usd_device_ready(
679 struct usd_device *dev)
680 {
681 if (dev->ud_attrs.uda_ipaddr_be == 0) {
682 return -EADDRNOTAVAIL;
683 }
684 if (dev->ud_attrs.uda_link_state != USD_LINK_UP) {
685 return -ENETDOWN;
686 }
687
688 return 0;
689 }
690