]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ofed/libibnetdisc/ibnetdisc_cache.c
amd64: use register macros for gdb_cpu_getreg()
[FreeBSD/FreeBSD.git] / contrib / ofed / libibnetdisc / ibnetdisc_cache.c
1 /*
2  * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
3  * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
4  * Copyright (c) 2008 Lawrence Livermore National Laboratory
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  * OpenIB.org 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  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35
36 #if HAVE_CONFIG_H
37 #include <config.h>
38 #endif                          /* HAVE_CONFIG_H */
39
40 #define _GNU_SOURCE
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <string.h>
48 #include <errno.h>
49 #include <inttypes.h>
50
51 #include <infiniband/ibnetdisc.h>
52
53 #include "internal.h"
54 #include "chassis.h"
55
56 /* For this caching lib, we always cache little endian */
57
58 /* Cache format
59  *
60  * Bytes 1-4 - magic number
61  * Bytes 5-8 - version number
62  * Bytes 9-12 - node count
63  * Bytes 13-16 - port count
64  * Bytes 17-24 - "from node" guid
65  * Bytes 25-28 - maxhops discovered
66  * Bytes X-Y - nodes (variable length)
67  * Bytes X-Y - ports (variable length)
68  *
69  * Nodes are cached as
70  *
71  * 2 bytes - smalid
72  * 1 byte - smalmc
73  * 1 byte - smaenhsp0 flag
74  * IB_SMP_DATA_SIZE bytes - switchinfo
75  * 8 bytes - guid
76  * 1 byte - type
77  * 1 byte - numports
78  * IB_SMP_DATA_SIZE bytes - info
79  * IB_SMP_DATA_SIZE bytes - nodedesc
80  * 1 byte - number of ports stored
81  * 8 bytes - portguid A
82  * 1 byte - port num A
83  * 8 bytes - portguid B
84  * 1 byte - port num B
85  * ... etc., depending on number of ports stored
86  *
87  * Ports are cached as
88  *
89  * 8 bytes - guid
90  * 1 byte - portnum
91  * 1 byte - external portnum
92  * 2 bytes - base lid
93  * 1 byte - lmc
94  * IB_SMP_DATA_SIZE bytes - info
95  * 8 bytes - node guid port "owned" by
96  * 1 byte - flag indicating if remote port exists
97  * 8 bytes - port guid remotely connected to
98  * 1 byte - port num remotely connected to
99  */
100
101 /* Structs that hold cache info temporarily before
102  * the real structs can be reconstructed.
103  */
104
105 typedef struct ibnd_port_cache_key {
106         uint64_t guid;
107         uint8_t portnum;
108 } ibnd_port_cache_key_t;
109
110 typedef struct ibnd_node_cache {
111         ibnd_node_t *node;
112         uint8_t ports_stored_count;
113         ibnd_port_cache_key_t *port_cache_keys;
114         struct ibnd_node_cache *next;
115         struct ibnd_node_cache *htnext;
116         int node_stored_to_fabric;
117 } ibnd_node_cache_t;
118
119 typedef struct ibnd_port_cache {
120         ibnd_port_t *port;
121         uint64_t node_guid;
122         uint8_t remoteport_flag;
123         ibnd_port_cache_key_t remoteport_cache_key;
124         struct ibnd_port_cache *next;
125         struct ibnd_port_cache *htnext;
126         int port_stored_to_fabric;
127 } ibnd_port_cache_t;
128
129 typedef struct ibnd_fabric_cache {
130         f_internal_t *f_int;
131         uint64_t from_node_guid;
132         ibnd_node_cache_t *nodes_cache;
133         ibnd_port_cache_t *ports_cache;
134         ibnd_node_cache_t *nodescachetbl[HTSZ];
135         ibnd_port_cache_t *portscachetbl[HTSZ];
136 } ibnd_fabric_cache_t;
137
138 #define IBND_FABRIC_CACHE_BUFLEN  4096
139 #define IBND_FABRIC_CACHE_MAGIC   0x8FE7832B
140 #define IBND_FABRIC_CACHE_VERSION 0x00000001
141
142 #define IBND_FABRIC_CACHE_COUNT_OFFSET 8
143
144 #define IBND_FABRIC_CACHE_HEADER_LEN   (28)
145 #define IBND_NODE_CACHE_HEADER_LEN     (15 + IB_SMP_DATA_SIZE*3)
146 #define IBND_PORT_CACHE_KEY_LEN        (8 + 1)
147 #define IBND_PORT_CACHE_LEN            (31 + IB_SMP_DATA_SIZE)
148
149 static ssize_t ibnd_read(int fd, void *buf, size_t count)
150 {
151         size_t count_done = 0;
152         ssize_t ret;
153
154         while ((count - count_done) > 0) {
155                 ret = read(fd, ((char *) buf) + count_done, count - count_done);
156                 if (ret < 0) {
157                         if (errno == EINTR)
158                                 continue;
159                         else {
160                                 IBND_DEBUG("read: %s\n", strerror(errno));
161                                 return -1;
162                         }
163                 }
164                 if (!ret)
165                         break;
166                 count_done += ret;
167         }
168
169         if (count_done != count) {
170                 IBND_DEBUG("read: read short\n");
171                 return -1;
172         }
173
174         return count_done;
175 }
176
177 static size_t _unmarshall8(uint8_t * inbuf, uint8_t * num)
178 {
179         (*num) = inbuf[0];
180
181         return (sizeof(*num));
182 }
183
184 static size_t _unmarshall16(uint8_t * inbuf, uint16_t * num)
185 {
186         (*num) = ((uint16_t) inbuf[1] << 8) | inbuf[0];
187
188         return (sizeof(*num));
189 }
190
191 static size_t _unmarshall32(uint8_t * inbuf, uint32_t * num)
192 {
193         (*num) = (uint32_t) inbuf[0];
194         (*num) |= ((uint32_t) inbuf[1] << 8);
195         (*num) |= ((uint32_t) inbuf[2] << 16);
196         (*num) |= ((uint32_t) inbuf[3] << 24);
197
198         return (sizeof(*num));
199 }
200
201 static size_t _unmarshall64(uint8_t * inbuf, uint64_t * num)
202 {
203         (*num) = (uint64_t) inbuf[0];
204         (*num) |= ((uint64_t) inbuf[1] << 8);
205         (*num) |= ((uint64_t) inbuf[2] << 16);
206         (*num) |= ((uint64_t) inbuf[3] << 24);
207         (*num) |= ((uint64_t) inbuf[4] << 32);
208         (*num) |= ((uint64_t) inbuf[5] << 40);
209         (*num) |= ((uint64_t) inbuf[6] << 48);
210         (*num) |= ((uint64_t) inbuf[7] << 56);
211
212         return (sizeof(*num));
213 }
214
215 static size_t _unmarshall_buf(const void *inbuf, void *outbuf, unsigned int len)
216 {
217         memcpy(outbuf, inbuf, len);
218
219         return len;
220 }
221
222 static int _load_header_info(int fd, ibnd_fabric_cache_t * fabric_cache,
223                              unsigned int *node_count, unsigned int *port_count)
224 {
225         uint8_t buf[IBND_FABRIC_CACHE_BUFLEN];
226         uint32_t magic = 0;
227         uint32_t version = 0;
228         size_t offset = 0;
229         uint32_t tmp32;
230
231         if (ibnd_read(fd, buf, IBND_FABRIC_CACHE_HEADER_LEN) < 0)
232                 return -1;
233
234         offset += _unmarshall32(buf + offset, &magic);
235
236         if (magic != IBND_FABRIC_CACHE_MAGIC) {
237                 IBND_DEBUG("invalid fabric cache file\n");
238                 return -1;
239         }
240
241         offset += _unmarshall32(buf + offset, &version);
242
243         if (version != IBND_FABRIC_CACHE_VERSION) {
244                 IBND_DEBUG("invalid fabric cache version\n");
245                 return -1;
246         }
247
248         offset += _unmarshall32(buf + offset, node_count);
249         offset += _unmarshall32(buf + offset, port_count);
250
251         offset += _unmarshall64(buf + offset, &fabric_cache->from_node_guid);
252         offset += _unmarshall32(buf + offset, &tmp32);
253         fabric_cache->f_int->fabric.maxhops_discovered = tmp32;
254
255         return 0;
256 }
257
258 static void _destroy_ibnd_node_cache(ibnd_node_cache_t * node_cache)
259 {
260         free(node_cache->port_cache_keys);
261         if (!node_cache->node_stored_to_fabric && node_cache->node)
262                 destroy_node(node_cache->node);
263         free(node_cache);
264 }
265
266 static void _destroy_ibnd_fabric_cache(ibnd_fabric_cache_t * fabric_cache)
267 {
268         ibnd_node_cache_t *node_cache;
269         ibnd_node_cache_t *node_cache_next;
270         ibnd_port_cache_t *port_cache;
271         ibnd_port_cache_t *port_cache_next;
272
273         if (!fabric_cache)
274                 return;
275
276         node_cache = fabric_cache->nodes_cache;
277         while (node_cache) {
278                 node_cache_next = node_cache->next;
279
280                 _destroy_ibnd_node_cache(node_cache);
281
282                 node_cache = node_cache_next;
283         }
284
285         port_cache = fabric_cache->ports_cache;
286         while (port_cache) {
287                 port_cache_next = port_cache->next;
288
289                 if (!port_cache->port_stored_to_fabric && port_cache->port)
290                         free(port_cache->port);
291                 free(port_cache);
292
293                 port_cache = port_cache_next;
294         }
295
296         free(fabric_cache);
297 }
298
299 static void store_node_cache(ibnd_node_cache_t * node_cache,
300                              ibnd_fabric_cache_t * fabric_cache)
301 {
302         int hash_indx = HASHGUID(node_cache->node->guid) % HTSZ;
303
304         node_cache->next = fabric_cache->nodes_cache;
305         fabric_cache->nodes_cache = node_cache;
306
307         node_cache->htnext = fabric_cache->nodescachetbl[hash_indx];
308         fabric_cache->nodescachetbl[hash_indx] = node_cache;
309 }
310
311 static int _load_node(int fd, ibnd_fabric_cache_t * fabric_cache)
312 {
313         uint8_t buf[IBND_FABRIC_CACHE_BUFLEN];
314         ibnd_node_cache_t *node_cache = NULL;
315         ibnd_node_t *node = NULL;
316         size_t offset = 0;
317         uint8_t tmp8;
318
319         node_cache = (ibnd_node_cache_t *) malloc(sizeof(ibnd_node_cache_t));
320         if (!node_cache) {
321                 IBND_DEBUG("OOM: node_cache\n");
322                 return -1;
323         }
324         memset(node_cache, '\0', sizeof(ibnd_node_cache_t));
325
326         node = (ibnd_node_t *) malloc(sizeof(ibnd_node_t));
327         if (!node) {
328                 IBND_DEBUG("OOM: node\n");
329                 free(node_cache);
330                 return -1;
331         }
332         memset(node, '\0', sizeof(ibnd_node_t));
333
334         node_cache->node = node;
335
336         if (ibnd_read(fd, buf, IBND_NODE_CACHE_HEADER_LEN) < 0)
337                 goto cleanup;
338
339         offset += _unmarshall16(buf + offset, &node->smalid);
340         offset += _unmarshall8(buf + offset, &node->smalmc);
341         offset += _unmarshall8(buf + offset, &tmp8);
342         node->smaenhsp0 = tmp8;
343         offset += _unmarshall_buf(buf + offset, node->switchinfo,
344                                   IB_SMP_DATA_SIZE);
345         offset += _unmarshall64(buf + offset, &node->guid);
346         offset += _unmarshall8(buf + offset, &tmp8);
347         node->type = tmp8;
348         offset += _unmarshall8(buf + offset, &tmp8);
349         node->numports = tmp8;
350         offset += _unmarshall_buf(buf + offset, node->info, IB_SMP_DATA_SIZE);
351         offset += _unmarshall_buf(buf + offset, node->nodedesc,
352                                   IB_SMP_DATA_SIZE);
353
354         offset += _unmarshall8(buf + offset, &node_cache->ports_stored_count);
355
356         if (node_cache->ports_stored_count) {
357                 unsigned int tomalloc = 0;
358                 unsigned int toread = 0;
359                 unsigned int i;
360
361                 tomalloc =
362                     sizeof(ibnd_port_cache_key_t) *
363                     node_cache->ports_stored_count;
364
365                 toread =
366                     IBND_PORT_CACHE_KEY_LEN * node_cache->ports_stored_count;
367
368                 node_cache->port_cache_keys =
369                     (ibnd_port_cache_key_t *) malloc(tomalloc);
370                 if (!node_cache->port_cache_keys) {
371                         IBND_DEBUG("OOM: node_cache port_cache_keys\n");
372                         goto cleanup;
373                 }
374
375                 if (ibnd_read(fd, buf, toread) < 0)
376                         goto cleanup;
377
378                 offset = 0;
379
380                 for (i = 0; i < node_cache->ports_stored_count; i++) {
381                         offset +=
382                             _unmarshall64(buf + offset,
383                                           &node_cache->port_cache_keys[i].guid);
384                         offset +=
385                             _unmarshall8(buf + offset,
386                                          &node_cache->
387                                          port_cache_keys[i].portnum);
388                 }
389         }
390
391         store_node_cache(node_cache, fabric_cache);
392
393         return 0;
394
395 cleanup:
396         _destroy_ibnd_node_cache(node_cache);
397         return -1;
398 }
399
400 static void store_port_cache(ibnd_port_cache_t * port_cache,
401                              ibnd_fabric_cache_t * fabric_cache)
402 {
403         int hash_indx = HASHGUID(port_cache->port->guid) % HTSZ;
404
405         port_cache->next = fabric_cache->ports_cache;
406         fabric_cache->ports_cache = port_cache;
407
408         port_cache->htnext = fabric_cache->portscachetbl[hash_indx];
409         fabric_cache->portscachetbl[hash_indx] = port_cache;
410 }
411
412 static int _load_port(int fd, ibnd_fabric_cache_t * fabric_cache)
413 {
414         uint8_t buf[IBND_FABRIC_CACHE_BUFLEN];
415         ibnd_port_cache_t *port_cache = NULL;
416         ibnd_port_t *port = NULL;
417         size_t offset = 0;
418         uint8_t tmp8;
419
420         port_cache = (ibnd_port_cache_t *) malloc(sizeof(ibnd_port_cache_t));
421         if (!port_cache) {
422                 IBND_DEBUG("OOM: port_cache\n");
423                 return -1;
424         }
425         memset(port_cache, '\0', sizeof(ibnd_port_cache_t));
426
427         port = (ibnd_port_t *) malloc(sizeof(ibnd_port_t));
428         if (!port) {
429                 IBND_DEBUG("OOM: port\n");
430                 free(port_cache);
431                 return -1;
432         }
433         memset(port, '\0', sizeof(ibnd_port_t));
434
435         port_cache->port = port;
436
437         if (ibnd_read(fd, buf, IBND_PORT_CACHE_LEN) < 0)
438                 goto cleanup;
439
440         offset += _unmarshall64(buf + offset, &port->guid);
441         offset += _unmarshall8(buf + offset, &tmp8);
442         port->portnum = tmp8;
443         offset += _unmarshall8(buf + offset, &tmp8);
444         port->ext_portnum = tmp8;
445         offset += _unmarshall16(buf + offset, &port->base_lid);
446         offset += _unmarshall8(buf + offset, &port->lmc);
447         offset += _unmarshall_buf(buf + offset, port->info, IB_SMP_DATA_SIZE);
448         offset += _unmarshall64(buf + offset, &port_cache->node_guid);
449         offset += _unmarshall8(buf + offset, &port_cache->remoteport_flag);
450         offset +=
451             _unmarshall64(buf + offset, &port_cache->remoteport_cache_key.guid);
452         offset +=
453             _unmarshall8(buf + offset,
454                          &port_cache->remoteport_cache_key.portnum);
455
456         store_port_cache(port_cache, fabric_cache);
457
458         return 0;
459
460 cleanup:
461         free(port);
462         free(port_cache);
463         return -1;
464 }
465
466 static ibnd_port_cache_t *_find_port(ibnd_fabric_cache_t * fabric_cache,
467                                      ibnd_port_cache_key_t * port_cache_key)
468 {
469         int hash_indx = HASHGUID(port_cache_key->guid) % HTSZ;
470         ibnd_port_cache_t *port_cache;
471
472         for (port_cache = fabric_cache->portscachetbl[hash_indx];
473              port_cache; port_cache = port_cache->htnext) {
474                 if (port_cache->port->guid == port_cache_key->guid
475                     && port_cache->port->portnum == port_cache_key->portnum)
476                         return port_cache;
477         }
478
479         return NULL;
480 }
481
482 static ibnd_node_cache_t *_find_node(ibnd_fabric_cache_t * fabric_cache,
483                                      uint64_t guid)
484 {
485         int hash_indx = HASHGUID(guid) % HTSZ;
486         ibnd_node_cache_t *node_cache;
487
488         for (node_cache = fabric_cache->nodescachetbl[hash_indx];
489              node_cache; node_cache = node_cache->htnext) {
490                 if (node_cache->node->guid == guid)
491                         return node_cache;
492         }
493
494         return NULL;
495 }
496
497 static int _fill_port(ibnd_fabric_cache_t * fabric_cache, ibnd_node_t * node,
498                       ibnd_port_cache_key_t * port_cache_key)
499 {
500         ibnd_port_cache_t *port_cache;
501
502         if (!(port_cache = _find_port(fabric_cache, port_cache_key))) {
503                 IBND_DEBUG("Cache invalid: cannot find port\n");
504                 return -1;
505         }
506
507         if (port_cache->port_stored_to_fabric) {
508                 IBND_DEBUG("Cache invalid: duplicate port discovered\n");
509                 return -1;
510         }
511
512         node->ports[port_cache->port->portnum] = port_cache->port;
513         port_cache->port_stored_to_fabric++;
514
515         /* achu: needed if user wishes to re-cache a loaded fabric.
516          * Otherwise, mostly unnecessary to do this.
517          */
518         int rc = add_to_portguid_hash(port_cache->port,
519                                       fabric_cache->f_int->fabric.portstbl);
520         if (rc) {
521                 IBND_DEBUG("Error Occurred when trying"
522                            " to insert new port guid 0x%016" PRIx64 " to DB\n",
523                            port_cache->port->guid);
524         }
525         return 0;
526 }
527
528 static int _rebuild_nodes(ibnd_fabric_cache_t * fabric_cache)
529 {
530         ibnd_node_cache_t *node_cache;
531         ibnd_node_cache_t *node_cache_next;
532
533         node_cache = fabric_cache->nodes_cache;
534         while (node_cache) {
535                 ibnd_node_t *node;
536                 int i;
537
538                 node_cache_next = node_cache->next;
539
540                 node = node_cache->node;
541
542                 /* Insert node into appropriate data structures */
543
544                 node->next = fabric_cache->f_int->fabric.nodes;
545                 fabric_cache->f_int->fabric.nodes = node;
546
547                 int rc = add_to_nodeguid_hash(node_cache->node,
548                                               fabric_cache->
549                                               f_int->
550                                               fabric.nodestbl);
551                 if (rc) {
552                         IBND_DEBUG("Error Occurred when trying"
553                                    " to insert new node guid 0x%016" PRIx64 " to DB\n",
554                                    node_cache->node->guid);
555                 }
556
557                 add_to_type_list(node_cache->node, fabric_cache->f_int);
558
559                 node_cache->node_stored_to_fabric++;
560
561                 /* Rebuild node ports array */
562
563                 if (!(node->ports =
564                       calloc(sizeof(*node->ports), node->numports + 1))) {
565                         IBND_DEBUG("OOM: node->ports\n");
566                         return -1;
567                 }
568
569                 for (i = 0; i < node_cache->ports_stored_count; i++) {
570                         if (_fill_port(fabric_cache, node,
571                                        &node_cache->port_cache_keys[i]) < 0)
572                                 return -1;
573                 }
574
575                 node_cache = node_cache_next;
576         }
577
578         return 0;
579 }
580
581 static int _rebuild_ports(ibnd_fabric_cache_t * fabric_cache)
582 {
583         ibnd_port_cache_t *port_cache;
584         ibnd_port_cache_t *port_cache_next;
585
586         port_cache = fabric_cache->ports_cache;
587         while (port_cache) {
588                 ibnd_node_cache_t *node_cache;
589                 ibnd_port_cache_t *remoteport_cache;
590                 ibnd_port_t *port;
591
592                 port_cache_next = port_cache->next;
593
594                 port = port_cache->port;
595
596                 if (!(node_cache =
597                       _find_node(fabric_cache, port_cache->node_guid))) {
598                         IBND_DEBUG("Cache invalid: cannot find node\n");
599                         return -1;
600                 }
601
602                 port->node = node_cache->node;
603
604                 if (port_cache->remoteport_flag) {
605                         if (!(remoteport_cache = _find_port(fabric_cache,
606                                                             &port_cache->remoteport_cache_key)))
607                         {
608                                 IBND_DEBUG
609                                     ("Cache invalid: cannot find remote port\n");
610                                 return -1;
611                         }
612
613                         port->remoteport = remoteport_cache->port;
614                 } else
615                         port->remoteport = NULL;
616
617                 add_to_portlid_hash(port, fabric_cache->f_int->lid2guid);
618                 port_cache = port_cache_next;
619         }
620
621         return 0;
622 }
623
624 ibnd_fabric_t *ibnd_load_fabric(const char *file, unsigned int flags)
625 {
626         unsigned int node_count = 0;
627         unsigned int port_count = 0;
628         ibnd_fabric_cache_t *fabric_cache = NULL;
629         f_internal_t *f_int = NULL;
630         ibnd_node_cache_t *node_cache = NULL;
631         int fd = -1;
632         unsigned int i;
633
634         if (!file) {
635                 IBND_DEBUG("file parameter NULL\n");
636                 return NULL;
637         }
638
639         if ((fd = open(file, O_RDONLY)) < 0) {
640                 IBND_DEBUG("open: %s\n", strerror(errno));
641                 return NULL;
642         }
643
644         fabric_cache =
645             (ibnd_fabric_cache_t *) malloc(sizeof(ibnd_fabric_cache_t));
646         if (!fabric_cache) {
647                 IBND_DEBUG("OOM: fabric_cache\n");
648                 goto cleanup;
649         }
650         memset(fabric_cache, '\0', sizeof(ibnd_fabric_cache_t));
651
652         f_int = allocate_fabric_internal();
653         if (!f_int) {
654                 IBND_DEBUG("OOM: fabric\n");
655                 goto cleanup;
656         }
657
658         fabric_cache->f_int = f_int;
659
660         if (_load_header_info(fd, fabric_cache, &node_count, &port_count) < 0)
661                 goto cleanup;
662
663         for (i = 0; i < node_count; i++) {
664                 if (_load_node(fd, fabric_cache) < 0)
665                         goto cleanup;
666         }
667
668         for (i = 0; i < port_count; i++) {
669                 if (_load_port(fd, fabric_cache) < 0)
670                         goto cleanup;
671         }
672
673         /* Special case - find from node */
674         if (!(node_cache =
675               _find_node(fabric_cache, fabric_cache->from_node_guid))) {
676                 IBND_DEBUG("Cache invalid: cannot find from node\n");
677                 goto cleanup;
678         }
679         f_int->fabric.from_node = node_cache->node;
680
681         if (_rebuild_nodes(fabric_cache) < 0)
682                 goto cleanup;
683
684         if (_rebuild_ports(fabric_cache) < 0)
685                 goto cleanup;
686
687         if (group_nodes(&f_int->fabric))
688                 goto cleanup;
689
690         _destroy_ibnd_fabric_cache(fabric_cache);
691         close(fd);
692         return (ibnd_fabric_t *)&f_int->fabric;
693
694 cleanup:
695         ibnd_destroy_fabric((ibnd_fabric_t *)f_int);
696         _destroy_ibnd_fabric_cache(fabric_cache);
697         close(fd);
698         return NULL;
699 }
700
701 static ssize_t ibnd_write(int fd, const void *buf, size_t count)
702 {
703         size_t count_done = 0;
704         ssize_t ret;
705
706         while ((count - count_done) > 0) {
707                 ret = write(fd, ((char *) buf) + count_done, count - count_done);
708                 if (ret < 0) {
709                         if (errno == EINTR)
710                                 continue;
711                         else {
712                                 IBND_DEBUG("write: %s\n", strerror(errno));
713                                 return -1;
714                         }
715                 }
716                 count_done += ret;
717         }
718         return count_done;
719 }
720
721 static size_t _marshall8(uint8_t * outbuf, uint8_t num)
722 {
723         outbuf[0] = num;
724
725         return (sizeof(num));
726 }
727
728 static size_t _marshall16(uint8_t * outbuf, uint16_t num)
729 {
730         outbuf[0] = num & 0x00FF;
731         outbuf[1] = (num & 0xFF00) >> 8;
732
733         return (sizeof(num));
734 }
735
736 static size_t _marshall32(uint8_t * outbuf, uint32_t num)
737 {
738         outbuf[0] = num & 0x000000FF;
739         outbuf[1] = (num & 0x0000FF00) >> 8;
740         outbuf[2] = (num & 0x00FF0000) >> 16;
741         outbuf[3] = (num & 0xFF000000) >> 24;
742
743         return (sizeof(num));
744 }
745
746 static size_t _marshall64(uint8_t * outbuf, uint64_t num)
747 {
748         outbuf[0] = (uint8_t) num;
749         outbuf[1] = (uint8_t) (num >> 8);
750         outbuf[2] = (uint8_t) (num >> 16);
751         outbuf[3] = (uint8_t) (num >> 24);
752         outbuf[4] = (uint8_t) (num >> 32);
753         outbuf[5] = (uint8_t) (num >> 40);
754         outbuf[6] = (uint8_t) (num >> 48);
755         outbuf[7] = (uint8_t) (num >> 56);
756
757         return (sizeof(num));
758 }
759
760 static size_t _marshall_buf(void *outbuf, const void *inbuf, unsigned int len)
761 {
762         memcpy(outbuf, inbuf, len);
763
764         return len;
765 }
766
767 static int _cache_header_info(int fd, ibnd_fabric_t * fabric)
768 {
769         uint8_t buf[IBND_FABRIC_CACHE_BUFLEN];
770         size_t offset = 0;
771
772         /* Store magic number, version, and other important info */
773         /* For this caching lib, we always assume cached as little endian */
774
775         offset += _marshall32(buf + offset, IBND_FABRIC_CACHE_MAGIC);
776         offset += _marshall32(buf + offset, IBND_FABRIC_CACHE_VERSION);
777         /* save space for node count */
778         offset += _marshall32(buf + offset, 0);
779         /* save space for port count */
780         offset += _marshall32(buf + offset, 0);
781         offset += _marshall64(buf + offset, fabric->from_node->guid);
782         offset += _marshall32(buf + offset, fabric->maxhops_discovered);
783
784         if (ibnd_write(fd, buf, offset) < 0)
785                 return -1;
786
787         return 0;
788 }
789
790 static int _cache_header_counts(int fd, unsigned int node_count,
791                                 unsigned int port_count)
792 {
793         uint8_t buf[IBND_FABRIC_CACHE_BUFLEN];
794         size_t offset = 0;
795
796         offset += _marshall32(buf + offset, node_count);
797         offset += _marshall32(buf + offset, port_count);
798
799         if (lseek(fd, IBND_FABRIC_CACHE_COUNT_OFFSET, SEEK_SET) < 0) {
800                 IBND_DEBUG("lseek: %s\n", strerror(errno));
801                 return -1;
802         }
803
804         if (ibnd_write(fd, buf, offset) < 0)
805                 return -1;
806
807         return 0;
808 }
809
810 static int _cache_node(int fd, ibnd_node_t * node)
811 {
812         uint8_t buf[IBND_FABRIC_CACHE_BUFLEN];
813         size_t offset = 0;
814         size_t ports_stored_offset = 0;
815         uint8_t ports_stored_count = 0;
816         int i;
817
818         offset += _marshall16(buf + offset, node->smalid);
819         offset += _marshall8(buf + offset, node->smalmc);
820         offset += _marshall8(buf + offset, (uint8_t) node->smaenhsp0);
821         offset += _marshall_buf(buf + offset, node->switchinfo,
822                                 IB_SMP_DATA_SIZE);
823         offset += _marshall64(buf + offset, node->guid);
824         offset += _marshall8(buf + offset, (uint8_t) node->type);
825         offset += _marshall8(buf + offset, (uint8_t) node->numports);
826         offset += _marshall_buf(buf + offset, node->info, IB_SMP_DATA_SIZE);
827         offset += _marshall_buf(buf + offset, node->nodedesc, IB_SMP_DATA_SIZE);
828         /* need to come back later and store number of stored ports
829          * because port entries can be NULL or (in the case of switches)
830          * there is an additional port 0 not accounted for in numports.
831          */
832         ports_stored_offset = offset;
833         offset += sizeof(uint8_t);
834
835         for (i = 0; i <= node->numports; i++) {
836                 if (node->ports[i]) {
837                         offset += _marshall64(buf + offset,
838                                               node->ports[i]->guid);
839                         offset += _marshall8(buf + offset,
840                                              (uint8_t) node->ports[i]->portnum);
841                         ports_stored_count++;
842                 }
843         }
844
845         /* go back and store number of port keys stored */
846         _marshall8(buf + ports_stored_offset, ports_stored_count);
847
848         if (ibnd_write(fd, buf, offset) < 0)
849                 return -1;
850
851         return 0;
852 }
853
854 static int _cache_port(int fd, ibnd_port_t * port)
855 {
856         uint8_t buf[IBND_FABRIC_CACHE_BUFLEN];
857         size_t offset = 0;
858
859         offset += _marshall64(buf + offset, port->guid);
860         offset += _marshall8(buf + offset, (uint8_t) port->portnum);
861         offset += _marshall8(buf + offset, (uint8_t) port->ext_portnum);
862         offset += _marshall16(buf + offset, port->base_lid);
863         offset += _marshall8(buf + offset, port->lmc);
864         offset += _marshall_buf(buf + offset, port->info, IB_SMP_DATA_SIZE);
865         offset += _marshall64(buf + offset, port->node->guid);
866         if (port->remoteport) {
867                 offset += _marshall8(buf + offset, 1);
868                 offset += _marshall64(buf + offset, port->remoteport->guid);
869                 offset += _marshall8(buf + offset, (uint8_t) port->remoteport->portnum);
870         } else {
871                 offset += _marshall8(buf + offset, 0);
872                 offset += _marshall64(buf + offset, 0);
873                 offset += _marshall8(buf + offset, 0);
874         }
875
876         if (ibnd_write(fd, buf, offset) < 0)
877                 return -1;
878
879         return 0;
880 }
881
882 int ibnd_cache_fabric(ibnd_fabric_t * fabric, const char *file,
883                       unsigned int flags)
884 {
885         struct stat statbuf;
886         ibnd_node_t *node = NULL;
887         ibnd_node_t *node_next = NULL;
888         unsigned int node_count = 0;
889         ibnd_port_t *port = NULL;
890         ibnd_port_t *port_next = NULL;
891         unsigned int port_count = 0;
892         int fd;
893         int i;
894
895         if (!fabric) {
896                 IBND_DEBUG("fabric parameter NULL\n");
897                 return -1;
898         }
899
900         if (!file) {
901                 IBND_DEBUG("file parameter NULL\n");
902                 return -1;
903         }
904
905         if (!(flags & IBND_CACHE_FABRIC_FLAG_NO_OVERWRITE)) {
906                 if (!stat(file, &statbuf)) {
907                         if (unlink(file) < 0) {
908                                 IBND_DEBUG("error removing '%s': %s\n",
909                                            file, strerror(errno));
910                                 return -1;
911                         }
912                 }
913         }
914         else {
915                 if (!stat(file, &statbuf)) {
916                         IBND_DEBUG("file '%s' already exists\n", file);
917                         return -1;
918                 }
919         }
920
921         if ((fd = open(file, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
922                 IBND_DEBUG("open: %s\n", strerror(errno));
923                 return -1;
924         }
925
926         if (_cache_header_info(fd, fabric) < 0)
927                 goto cleanup;
928
929         node = fabric->nodes;
930         while (node) {
931                 node_next = node->next;
932
933                 if (_cache_node(fd, node) < 0)
934                         goto cleanup;
935
936                 node_count++;
937                 node = node_next;
938         }
939
940         for (i = 0; i < HTSZ; i++) {
941                 port = fabric->portstbl[i];
942                 while (port) {
943                         port_next = port->htnext;
944
945                         if (_cache_port(fd, port) < 0)
946                                 goto cleanup;
947
948                         port_count++;
949                         port = port_next;
950                 }
951         }
952
953         if (_cache_header_counts(fd, node_count, port_count) < 0)
954                 goto cleanup;
955
956         if (close(fd) < 0) {
957                 IBND_DEBUG("close: %s\n", strerror(errno));
958                 goto cleanup;
959         }
960
961         return 0;
962
963 cleanup:
964         unlink(file);
965         close(fd);
966         return -1;
967 }