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
105typedef struct ibnd_port_cache_key {
106	uint64_t guid;
107	uint8_t portnum;
108} ibnd_port_cache_key_t;
109
110typedef 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
119typedef 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
129typedef 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
149static 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
177static size_t _unmarshall8(uint8_t * inbuf, uint8_t * num)
178{
179	(*num) = inbuf[0];
180
181	return (sizeof(*num));
182}
183
184static 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
191static 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
201static 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
215static size_t _unmarshall_buf(const void *inbuf, void *outbuf, unsigned int len)
216{
217	memcpy(outbuf, inbuf, len);
218
219	return len;
220}
221
222static 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
258static 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
266static 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
299static 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
311static 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
395cleanup:
396	_destroy_ibnd_node_cache(node_cache);
397	return -1;
398}
399
400static 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
412static 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
460cleanup:
461	free(port);
462	free(port_cache);
463	return -1;
464}
465
466static 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
482static 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
497static 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
528static 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
581static 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
624ibnd_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
694cleanup:
695	ibnd_destroy_fabric((ibnd_fabric_t *)f_int);
696	_destroy_ibnd_fabric_cache(fabric_cache);
697	close(fd);
698	return NULL;
699}
700
701static 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
721static size_t _marshall8(uint8_t * outbuf, uint8_t num)
722{
723	outbuf[0] = num;
724
725	return (sizeof(num));
726}
727
728static 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
736static 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
746static 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
760static size_t _marshall_buf(void *outbuf, const void *inbuf, unsigned int len)
761{
762	memcpy(outbuf, inbuf, len);
763
764	return len;
765}
766
767static 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
790static 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
810static 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
854static 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
882int 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
963cleanup:
964	unlink(file);
965	close(fd);
966	return -1;
967}
968