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#define _GNU_SOURCE
34#include <config.h>
35
36#include <infiniband/endian.h>
37#include <stdio.h>
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <unistd.h>
42#include <stdlib.h>
43#include <alloca.h>
44#include <errno.h>
45
46#include "ibverbs.h"
47
48/* Hack to avoid GCC's -Wmissing-prototypes and the similar error from sparse
49   with these prototypes. Symbol versionining requires the goofy names, the
50   prototype must match the version in verbs.h.
51 */
52struct ibv_device **__ibv_get_device_list(int *num_devices);
53void __ibv_free_device_list(struct ibv_device **list);
54const char *__ibv_get_device_name(struct ibv_device *device);
55__be64 __ibv_get_device_guid(struct ibv_device *device);
56struct ibv_context *__ibv_open_device(struct ibv_device *device);
57int __ibv_close_device(struct ibv_context *context);
58int __ibv_get_async_event(struct ibv_context *context,
59			  struct ibv_async_event *event);
60void __ibv_ack_async_event(struct ibv_async_event *event);
61
62static pthread_once_t device_list_once = PTHREAD_ONCE_INIT;
63static int num_devices;
64static struct ibv_device **device_list;
65
66static void count_devices(void)
67{
68	num_devices = ibverbs_init(&device_list);
69}
70
71struct ibv_device **__ibv_get_device_list(int *num)
72{
73	struct ibv_device **l;
74	int i;
75
76	if (num)
77		*num = 0;
78
79	pthread_once(&device_list_once, count_devices);
80
81	if (num_devices < 0) {
82		errno = -num_devices;
83		return NULL;
84	}
85
86	l = calloc(num_devices + 1, sizeof (struct ibv_device *));
87	if (!l) {
88		errno = ENOMEM;
89		return NULL;
90	}
91
92	for (i = 0; i < num_devices; ++i)
93		l[i] = device_list[i];
94	if (num)
95		*num = num_devices;
96
97	return l;
98}
99default_symver(__ibv_get_device_list, ibv_get_device_list);
100
101void __ibv_free_device_list(struct ibv_device **list)
102{
103	free(list);
104}
105default_symver(__ibv_free_device_list, ibv_free_device_list);
106
107const char *__ibv_get_device_name(struct ibv_device *device)
108{
109	return device->name;
110}
111default_symver(__ibv_get_device_name, ibv_get_device_name);
112
113__be64 __ibv_get_device_guid(struct ibv_device *device)
114{
115	char attr[24];
116	uint64_t guid = 0;
117	uint16_t parts[4];
118	int i;
119
120	if (ibv_read_sysfs_file(device->ibdev_path, "node_guid",
121				attr, sizeof attr) < 0)
122		return 0;
123
124	if (sscanf(attr, "%hx:%hx:%hx:%hx",
125		   parts, parts + 1, parts + 2, parts + 3) != 4)
126		return 0;
127
128	for (i = 0; i < 4; ++i)
129		guid = (guid << 16) | parts[i];
130
131	return htobe64(guid);
132}
133default_symver(__ibv_get_device_guid, ibv_get_device_guid);
134
135void verbs_init_cq(struct ibv_cq *cq, struct ibv_context *context,
136		       struct ibv_comp_channel *channel,
137		       void *cq_context)
138{
139	cq->context		   = context;
140	cq->channel		   = channel;
141
142	if (cq->channel) {
143		pthread_mutex_lock(&context->mutex);
144		++cq->channel->refcnt;
145		pthread_mutex_unlock(&context->mutex);
146	}
147
148	cq->cq_context		   = cq_context;
149	cq->comp_events_completed  = 0;
150	cq->async_events_completed = 0;
151	pthread_mutex_init(&cq->mutex, NULL);
152	pthread_cond_init(&cq->cond, NULL);
153}
154
155static struct ibv_cq_ex *
156__lib_ibv_create_cq_ex(struct ibv_context *context,
157		       struct ibv_cq_init_attr_ex *cq_attr)
158{
159	struct verbs_context *vctx = verbs_get_ctx(context);
160	struct ibv_cq_ex *cq;
161
162	if (cq_attr->wc_flags & ~IBV_CREATE_CQ_SUP_WC_FLAGS) {
163		errno = EOPNOTSUPP;
164		return NULL;
165	}
166
167	cq = vctx->priv->create_cq_ex(context, cq_attr);
168
169	if (cq)
170		verbs_init_cq(ibv_cq_ex_to_cq(cq), context,
171			        cq_attr->channel, cq_attr->cq_context);
172
173	return cq;
174}
175
176struct ibv_context *__ibv_open_device(struct ibv_device *device)
177{
178	struct verbs_device *verbs_device = verbs_get_device(device);
179	char *devpath;
180	int cmd_fd, ret;
181	struct ibv_context *context;
182	struct verbs_context *context_ex;
183
184	if (asprintf(&devpath, "/dev/%s", device->dev_name) < 0)
185		return NULL;
186
187	/*
188	 * We'll only be doing writes, but we need O_RDWR in case the
189	 * provider needs to mmap() the file.
190	 */
191	cmd_fd = open(devpath, O_RDWR | O_CLOEXEC);
192	free(devpath);
193
194	if (cmd_fd < 0)
195		return NULL;
196
197	if (!verbs_device->ops->init_context) {
198		context = verbs_device->ops->alloc_context(device, cmd_fd);
199		if (!context)
200			goto err;
201	} else {
202		struct verbs_ex_private *priv;
203
204		/* Library now allocates the context */
205		context_ex = calloc(1, sizeof(*context_ex) +
206				       verbs_device->size_of_context);
207		if (!context_ex) {
208			errno = ENOMEM;
209			goto err;
210		}
211
212		priv = calloc(1, sizeof(*priv));
213		if (!priv) {
214			errno = ENOMEM;
215			free(context_ex);
216			goto err;
217		}
218
219		context_ex->priv = priv;
220		context_ex->context.abi_compat  = __VERBS_ABI_IS_EXTENDED;
221		context_ex->sz = sizeof(*context_ex);
222
223		context = &context_ex->context;
224		ret = verbs_device->ops->init_context(verbs_device, context, cmd_fd);
225		if (ret)
226			goto verbs_err;
227		/*
228		 * In order to maintain backward/forward binary compatibility
229		 * with apps compiled against libibverbs-1.1.8 that use the
230		 * flow steering addition, we need to set the two
231		 * ABI_placeholder entries to match the driver set flow
232		 * entries.  This is because apps compiled against
233		 * libibverbs-1.1.8 use an inline ibv_create_flow and
234		 * ibv_destroy_flow function that looks in the placeholder
235		 * spots for the proper entry points.  For apps compiled
236		 * against libibverbs-1.1.9 and later, the inline functions
237		 * will be looking in the right place.
238		 */
239		context_ex->ABI_placeholder1 = (void (*)(void)) context_ex->ibv_create_flow;
240		context_ex->ABI_placeholder2 = (void (*)(void)) context_ex->ibv_destroy_flow;
241
242		if (context_ex->create_cq_ex) {
243			priv->create_cq_ex = context_ex->create_cq_ex;
244			context_ex->create_cq_ex = __lib_ibv_create_cq_ex;
245		}
246	}
247
248	context->device = device;
249	context->cmd_fd = cmd_fd;
250	pthread_mutex_init(&context->mutex, NULL);
251
252	return context;
253
254verbs_err:
255	free(context_ex->priv);
256	free(context_ex);
257err:
258	close(cmd_fd);
259	return NULL;
260}
261default_symver(__ibv_open_device, ibv_open_device);
262
263int __ibv_close_device(struct ibv_context *context)
264{
265	int async_fd = context->async_fd;
266	int cmd_fd   = context->cmd_fd;
267	int cq_fd    = -1;
268	struct verbs_context *context_ex;
269	struct verbs_device *verbs_device = verbs_get_device(context->device);
270
271	context_ex = verbs_get_ctx(context);
272	if (context_ex) {
273		verbs_device->ops->uninit_context(verbs_device, context);
274		free(context_ex->priv);
275		free(context_ex);
276	} else {
277		verbs_device->ops->free_context(context);
278	}
279
280	close(async_fd);
281	close(cmd_fd);
282	if (abi_ver <= 2)
283		close(cq_fd);
284
285	return 0;
286}
287default_symver(__ibv_close_device, ibv_close_device);
288
289int __ibv_get_async_event(struct ibv_context *context,
290			  struct ibv_async_event *event)
291{
292	struct ibv_kern_async_event ev;
293
294	if (read(context->async_fd, &ev, sizeof ev) != sizeof ev)
295		return -1;
296
297	event->event_type = ev.event_type;
298
299	switch (event->event_type) {
300	case IBV_EVENT_CQ_ERR:
301		event->element.cq = (void *) (uintptr_t) ev.element;
302		break;
303
304	case IBV_EVENT_QP_FATAL:
305	case IBV_EVENT_QP_REQ_ERR:
306	case IBV_EVENT_QP_ACCESS_ERR:
307	case IBV_EVENT_COMM_EST:
308	case IBV_EVENT_SQ_DRAINED:
309	case IBV_EVENT_PATH_MIG:
310	case IBV_EVENT_PATH_MIG_ERR:
311	case IBV_EVENT_QP_LAST_WQE_REACHED:
312		event->element.qp = (void *) (uintptr_t) ev.element;
313		break;
314
315	case IBV_EVENT_SRQ_ERR:
316	case IBV_EVENT_SRQ_LIMIT_REACHED:
317		event->element.srq = (void *) (uintptr_t) ev.element;
318		break;
319
320	case IBV_EVENT_WQ_FATAL:
321		event->element.wq = (void *) (uintptr_t) ev.element;
322		break;
323	default:
324		event->element.port_num = ev.element;
325		break;
326	}
327
328	if (context->ops.async_event)
329		context->ops.async_event(event);
330
331	return 0;
332}
333default_symver(__ibv_get_async_event, ibv_get_async_event);
334
335void __ibv_ack_async_event(struct ibv_async_event *event)
336{
337	switch (event->event_type) {
338	case IBV_EVENT_CQ_ERR:
339	{
340		struct ibv_cq *cq = event->element.cq;
341
342		pthread_mutex_lock(&cq->mutex);
343		++cq->async_events_completed;
344		pthread_cond_signal(&cq->cond);
345		pthread_mutex_unlock(&cq->mutex);
346
347		return;
348	}
349
350	case IBV_EVENT_QP_FATAL:
351	case IBV_EVENT_QP_REQ_ERR:
352	case IBV_EVENT_QP_ACCESS_ERR:
353	case IBV_EVENT_COMM_EST:
354	case IBV_EVENT_SQ_DRAINED:
355	case IBV_EVENT_PATH_MIG:
356	case IBV_EVENT_PATH_MIG_ERR:
357	case IBV_EVENT_QP_LAST_WQE_REACHED:
358	{
359		struct ibv_qp *qp = event->element.qp;
360
361		pthread_mutex_lock(&qp->mutex);
362		++qp->events_completed;
363		pthread_cond_signal(&qp->cond);
364		pthread_mutex_unlock(&qp->mutex);
365
366		return;
367	}
368
369	case IBV_EVENT_SRQ_ERR:
370	case IBV_EVENT_SRQ_LIMIT_REACHED:
371	{
372		struct ibv_srq *srq = event->element.srq;
373
374		pthread_mutex_lock(&srq->mutex);
375		++srq->events_completed;
376		pthread_cond_signal(&srq->cond);
377		pthread_mutex_unlock(&srq->mutex);
378
379		return;
380	}
381
382	case IBV_EVENT_WQ_FATAL:
383	{
384		struct ibv_wq *wq = event->element.wq;
385
386		pthread_mutex_lock(&wq->mutex);
387		++wq->events_completed;
388		pthread_cond_signal(&wq->cond);
389		pthread_mutex_unlock(&wq->mutex);
390
391		return;
392	}
393
394	default:
395		return;
396	}
397}
398default_symver(__ibv_ack_async_event, ibv_ack_async_event);
399