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(&params, 0, sizeof(params));
613     params.cmd_fd = -1;
614     params.context = NULL;
615     return usd_open_with_params(dev_name, &params, 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