1/*
2 * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
3 * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 */
33
34#if HAVE_CONFIG_H
35#  include <config.h>
36#endif /* HAVE_CONFIG_H */
37
38#include <stdio.h>
39#include <netinet/in.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43#include <unistd.h>
44#include <stdlib.h>
45#include <alloca.h>
46#include <errno.h>
47
48#include <infiniband/arch.h>
49
50#include "ibverbs.h"
51
52static pthread_mutex_t device_list_lock = PTHREAD_MUTEX_INITIALIZER;
53static int num_devices;
54static struct ibv_device **device_list;
55
56struct ibv_device **__ibv_get_device_list(int *num)
57{
58	struct ibv_device **l = 0;
59	int i;
60
61	if (num)
62		*num = 0;
63
64	pthread_mutex_lock(&device_list_lock);
65
66	if (!num_devices)
67		num_devices = ibverbs_init(&device_list);
68
69	if (num_devices < 0) {
70		errno = -num_devices;
71		goto out;
72	}
73
74	l = calloc(num_devices + 1, sizeof (struct ibv_device *));
75	if (!l) {
76		errno = ENOMEM;
77		goto out;
78	}
79
80	for (i = 0; i < num_devices; ++i)
81		l[i] = device_list[i];
82	if (num)
83		*num = num_devices;
84
85out:
86	pthread_mutex_unlock(&device_list_lock);
87	return l;
88}
89default_symver(__ibv_get_device_list, ibv_get_device_list);
90
91void __ibv_free_device_list(struct ibv_device **list)
92{
93	free(list);
94}
95default_symver(__ibv_free_device_list, ibv_free_device_list);
96
97const char *__ibv_get_device_name(struct ibv_device *device)
98{
99	return device->name;
100}
101default_symver(__ibv_get_device_name, ibv_get_device_name);
102
103uint64_t __ibv_get_device_guid(struct ibv_device *device)
104{
105	char attr[24];
106	uint64_t guid = 0;
107	uint16_t parts[4];
108	int i;
109
110	if (ibv_read_sysfs_file(device->ibdev_path, "node_guid",
111				attr, sizeof attr) < 0)
112		return 0;
113
114	if (sscanf(attr, "%hx:%hx:%hx:%hx",
115		   parts, parts + 1, parts + 2, parts + 3) != 4)
116		return 0;
117
118	for (i = 0; i < 4; ++i)
119		guid = (guid << 16) | parts[i];
120
121	return htonll(guid);
122}
123default_symver(__ibv_get_device_guid, ibv_get_device_guid);
124
125struct ibv_context *__ibv_open_device(struct ibv_device *device)
126{
127	char *devpath;
128	int cmd_fd;
129	struct ibv_context *context;
130
131	if (asprintf(&devpath, "/dev/%s", device->dev_name) < 0)
132		return NULL;
133
134	/*
135	 * We'll only be doing writes, but we need O_RDWR in case the
136	 * provider needs to mmap() the file.
137	 */
138	cmd_fd = open(devpath, O_RDWR);
139	free(devpath);
140
141	if (cmd_fd < 0)
142		return NULL;
143
144	context = device->ops.alloc_context(device, cmd_fd);
145	if (!context)
146		goto err;
147
148	context->device = device;
149	context->cmd_fd = cmd_fd;
150	pthread_mutex_init(&context->mutex, NULL);
151
152	return context;
153
154err:
155	close(cmd_fd);
156
157	return NULL;
158}
159default_symver(__ibv_open_device, ibv_open_device);
160
161int __ibv_close_device(struct ibv_context *context)
162{
163	int async_fd = context->async_fd;
164	int cmd_fd   = context->cmd_fd;
165	int cq_fd    = -1;
166
167	if (abi_ver <= 2) {
168		struct ibv_abi_compat_v2 *t = context->abi_compat;
169		cq_fd = t->channel.fd;
170		free(context->abi_compat);
171	}
172
173	context->device->ops.free_context(context);
174
175	close(async_fd);
176	close(cmd_fd);
177	if (abi_ver <= 2)
178		close(cq_fd);
179
180	return 0;
181}
182default_symver(__ibv_close_device, ibv_close_device);
183
184int __ibv_get_async_event(struct ibv_context *context,
185			  struct ibv_async_event *event)
186{
187	struct ibv_kern_async_event ev;
188
189	if (read(context->async_fd, &ev, sizeof ev) != sizeof ev)
190		return -1;
191
192	event->event_type = ev.event_type;
193
194	if (event->event_type & IBV_XRC_QP_EVENT_FLAG) {
195		event->element.xrc_qp_num = ev.element;
196	} else
197		switch (event->event_type) {
198		case IBV_EVENT_CQ_ERR:
199			event->element.cq = (void *) (uintptr_t) ev.element;
200			break;
201
202		case IBV_EVENT_QP_FATAL:
203		case IBV_EVENT_QP_REQ_ERR:
204		case IBV_EVENT_QP_ACCESS_ERR:
205		case IBV_EVENT_COMM_EST:
206		case IBV_EVENT_SQ_DRAINED:
207		case IBV_EVENT_PATH_MIG:
208		case IBV_EVENT_PATH_MIG_ERR:
209		case IBV_EVENT_QP_LAST_WQE_REACHED:
210			event->element.qp = (void *) (uintptr_t) ev.element;
211			break;
212
213		case IBV_EVENT_SRQ_ERR:
214		case IBV_EVENT_SRQ_LIMIT_REACHED:
215			event->element.srq = (void *) (uintptr_t) ev.element;
216			break;
217		default:
218			event->element.port_num = ev.element;
219			break;
220		}
221
222	if (context->ops.async_event)
223		context->ops.async_event(event);
224
225	return 0;
226}
227default_symver(__ibv_get_async_event, ibv_get_async_event);
228
229void __ibv_ack_async_event(struct ibv_async_event *event)
230{
231	switch (event->event_type) {
232	case IBV_EVENT_CQ_ERR:
233	{
234		struct ibv_cq *cq = event->element.cq;
235
236		pthread_mutex_lock(&cq->mutex);
237		++cq->async_events_completed;
238		pthread_cond_signal(&cq->cond);
239		pthread_mutex_unlock(&cq->mutex);
240
241		return;
242	}
243
244	case IBV_EVENT_QP_FATAL:
245	case IBV_EVENT_QP_REQ_ERR:
246	case IBV_EVENT_QP_ACCESS_ERR:
247	case IBV_EVENT_COMM_EST:
248	case IBV_EVENT_SQ_DRAINED:
249	case IBV_EVENT_PATH_MIG:
250	case IBV_EVENT_PATH_MIG_ERR:
251	case IBV_EVENT_QP_LAST_WQE_REACHED:
252	{
253		struct ibv_qp *qp = event->element.qp;
254
255		pthread_mutex_lock(&qp->mutex);
256		++qp->events_completed;
257		pthread_cond_signal(&qp->cond);
258		pthread_mutex_unlock(&qp->mutex);
259
260		return;
261	}
262
263	case IBV_EVENT_SRQ_ERR:
264	case IBV_EVENT_SRQ_LIMIT_REACHED:
265	{
266		struct ibv_srq *srq = event->element.srq;
267
268		pthread_mutex_lock(&srq->mutex);
269		++srq->events_completed;
270		pthread_cond_signal(&srq->cond);
271		pthread_mutex_unlock(&srq->mutex);
272
273		return;
274	}
275
276	default:
277		return;
278	}
279}
280default_symver(__ibv_ack_async_event, ibv_ack_async_event);
281