1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2006 Voltaire Inc.  All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff *
32219820Sjeff */
33219820Sjeff
34219820Sjeff#if HAVE_CONFIG_H
35219820Sjeff#  include <config.h>
36219820Sjeff#endif /* HAVE_CONFIG_H */
37219820Sjeff
38219820Sjeff#include <stdio.h>
39219820Sjeff#include <stdlib.h>
40219820Sjeff#include <unistd.h>
41219820Sjeff#include <pthread.h>
42219820Sjeff#include <sys/time.h>
43219820Sjeff#include <string.h>
44219820Sjeff#include <errno.h>
45219820Sjeff
46219820Sjeff#include <infiniband/umad.h>
47219820Sjeff#include "mad.h"
48219820Sjeff
49219820Sjeff#define MAX_CLASS 256
50219820Sjeff
51219820Sjeffstruct ibmad_port {
52219820Sjeff	int port_id;  /* file descriptor returned by umad_open() */
53219820Sjeff	int class_agents[MAX_CLASS]; /* class2agent mapper */
54219820Sjeff};
55219820Sjeff
56219820Sjeffint ibdebug;
57219820Sjeff
58219820Sjeffstatic int mad_portid = -1;
59219820Sjeffstatic int iberrs;
60219820Sjeff
61219820Sjeffstatic int madrpc_retries = MAD_DEF_RETRIES;
62219820Sjeffstatic int def_madrpc_timeout = MAD_DEF_TIMEOUT_MS;
63219820Sjeffstatic void *save_mad;
64219820Sjeffstatic int save_mad_len = 256;
65219820Sjeff
66219820Sjeff#undef DEBUG
67219820Sjeff#define DEBUG	if (ibdebug)	IBWARN
68219820Sjeff#define ERRS	if (iberrs || ibdebug)	IBWARN
69219820Sjeff
70219820Sjeff#define MAD_TID(mad)	(*((uint64_t *)((char *)(mad) + 8)))
71219820Sjeff
72219820Sjeffvoid
73219820Sjeffmadrpc_show_errors(int set)
74219820Sjeff{
75219820Sjeff	iberrs = set;
76219820Sjeff}
77219820Sjeff
78219820Sjeffvoid
79219820Sjeffmadrpc_save_mad(void *madbuf, int len)
80219820Sjeff{
81219820Sjeff	save_mad = madbuf;
82219820Sjeff	save_mad_len = len;
83219820Sjeff}
84219820Sjeff
85219820Sjeffint
86219820Sjeffmadrpc_set_retries(int retries)
87219820Sjeff{
88219820Sjeff	if (retries > 0)
89219820Sjeff		madrpc_retries = retries;
90219820Sjeff	return madrpc_retries;
91219820Sjeff}
92219820Sjeff
93219820Sjeffint
94219820Sjeffmadrpc_set_timeout(int timeout)
95219820Sjeff{
96219820Sjeff	def_madrpc_timeout = timeout;
97219820Sjeff	return 0;
98219820Sjeff}
99219820Sjeff
100219820Sjeffint
101219820Sjeffmadrpc_def_timeout(void)
102219820Sjeff{
103219820Sjeff	return def_madrpc_timeout;
104219820Sjeff}
105219820Sjeff
106219820Sjeffint
107219820Sjeffmadrpc_portid(void)
108219820Sjeff{
109219820Sjeff	return mad_portid;
110219820Sjeff}
111219820Sjeff
112219820Sjeffstatic int
113219820Sjeff_do_madrpc(int port_id, void *sndbuf, void *rcvbuf, int agentid, int len,
114219820Sjeff	   int timeout)
115219820Sjeff{
116219820Sjeff	uint32_t trid; /* only low 32 bits */
117219820Sjeff	int retries;
118219820Sjeff	int length, status;
119219820Sjeff
120219820Sjeff	if (!timeout)
121219820Sjeff		timeout = def_madrpc_timeout;
122219820Sjeff
123219820Sjeff	if (ibdebug > 1) {
124219820Sjeff		IBWARN(">>> sending: len %d pktsz %zu", len, umad_size() + len);
125219820Sjeff		xdump(stderr, "send buf\n", sndbuf, umad_size() + len);
126219820Sjeff	}
127219820Sjeff
128219820Sjeff	if (save_mad) {
129219820Sjeff		memcpy(save_mad, umad_get_mad(sndbuf),
130219820Sjeff		       save_mad_len < len ? save_mad_len : len);
131219820Sjeff		save_mad = 0;
132219820Sjeff	}
133219820Sjeff
134219820Sjeff	trid = mad_get_field64(umad_get_mad(sndbuf), 0, IB_MAD_TRID_F);
135219820Sjeff
136219820Sjeff	for (retries = 0; retries < madrpc_retries; retries++) {
137219820Sjeff		if (retries) {
138219820Sjeff			ERRS("retry %d (timeout %d ms)", retries, timeout);
139219820Sjeff		}
140219820Sjeff
141219820Sjeff		length = len;
142219820Sjeff		if (umad_send(port_id, agentid, sndbuf, length, timeout, 0) < 0) {
143219820Sjeff			IBWARN("send failed; %s", strerror(errno));
144219820Sjeff			return -1;
145219820Sjeff		}
146219820Sjeff
147219820Sjeff		/* Use same timeout on receive side just in case */
148219820Sjeff		/* send packet is lost somewhere. */
149219820Sjeff		do {
150219820Sjeff			if (umad_recv(port_id, rcvbuf, &length, timeout) < 0) {
151219820Sjeff				IBWARN("recv failed: %s", strerror(errno));
152219820Sjeff				return -1;
153219820Sjeff			}
154219820Sjeff
155219820Sjeff			if (ibdebug > 1) {
156219820Sjeff				IBWARN("rcv buf:");
157219820Sjeff				xdump(stderr, "rcv buf\n", umad_get_mad(rcvbuf), IB_MAD_SIZE);
158219820Sjeff			}
159219820Sjeff		} while ((uint32_t)mad_get_field64(umad_get_mad(rcvbuf), 0, IB_MAD_TRID_F) != trid);
160219820Sjeff
161219820Sjeff		status = umad_status(rcvbuf);
162219820Sjeff		if (!status)
163219820Sjeff			return length;		/* done */
164219820Sjeff		if (status == ENOMEM)
165219820Sjeff			return length;
166219820Sjeff	}
167219820Sjeff
168219820Sjeff	ERRS("timeout after %d retries, %d ms", retries, timeout * retries);
169219820Sjeff	return -1;
170219820Sjeff}
171219820Sjeff
172219820Sjeffvoid *
173219820Sjeffmad_rpc(const void *port_id, ib_rpc_t *rpc, ib_portid_t *dport, void *payload,
174219820Sjeff	void *rcvdata)
175219820Sjeff{
176219820Sjeff	const struct ibmad_port *p = port_id;
177219820Sjeff	int status, len;
178219820Sjeff	uint8_t sndbuf[1024], rcvbuf[1024], *mad;
179219820Sjeff
180219820Sjeff	len = 0;
181219820Sjeff	memset(sndbuf, 0, umad_size() + IB_MAD_SIZE);
182219820Sjeff
183219820Sjeff	if ((len = mad_build_pkt(sndbuf, rpc, dport, 0, payload)) < 0)
184219820Sjeff		return 0;
185219820Sjeff
186219820Sjeff	if ((len = _do_madrpc(p->port_id, sndbuf, rcvbuf,
187219820Sjeff			      p->class_agents[rpc->mgtclass],
188219820Sjeff			      len, rpc->timeout)) < 0) {
189219820Sjeff		IBWARN("_do_madrpc failed; dport (%s)", portid2str(dport));
190219820Sjeff		return 0;
191219820Sjeff	}
192219820Sjeff
193219820Sjeff	mad = umad_get_mad(rcvbuf);
194219820Sjeff
195219820Sjeff	if ((status = mad_get_field(mad, 0, IB_DRSMP_STATUS_F)) != 0) {
196219820Sjeff		ERRS("MAD completed with error status 0x%x; dport (%s)",
197219820Sjeff			status, portid2str(dport));
198219820Sjeff		return 0;
199219820Sjeff	}
200219820Sjeff
201219820Sjeff	if (ibdebug) {
202219820Sjeff		IBWARN("data offs %d sz %d", rpc->dataoffs, rpc->datasz);
203219820Sjeff		xdump(stderr, "mad data\n", mad + rpc->dataoffs, rpc->datasz);
204219820Sjeff	}
205219820Sjeff
206219820Sjeff	if (rcvdata)
207219820Sjeff		memcpy(rcvdata, mad + rpc->dataoffs, rpc->datasz);
208219820Sjeff
209219820Sjeff	return rcvdata;
210219820Sjeff}
211219820Sjeff
212219820Sjeffvoid *
213219820Sjeffmad_rpc_rmpp(const void *port_id, ib_rpc_t *rpc, ib_portid_t *dport,
214219820Sjeff	     ib_rmpp_hdr_t *rmpp, void *data)
215219820Sjeff{
216219820Sjeff	const struct ibmad_port *p = port_id;
217219820Sjeff	int status, len;
218219820Sjeff	uint8_t sndbuf[1024], rcvbuf[1024], *mad;
219219820Sjeff
220219820Sjeff	memset(sndbuf, 0, umad_size() + IB_MAD_SIZE);
221219820Sjeff
222219820Sjeff	DEBUG("rmpp %p data %p", rmpp, data);
223219820Sjeff
224219820Sjeff	if ((len = mad_build_pkt(sndbuf, rpc, dport, rmpp, data)) < 0)
225219820Sjeff		return 0;
226219820Sjeff
227219820Sjeff	if ((len = _do_madrpc(p->port_id, sndbuf, rcvbuf,
228219820Sjeff			      p->class_agents[rpc->mgtclass],
229219820Sjeff			      len, rpc->timeout)) < 0) {
230219820Sjeff		IBWARN("_do_madrpc failed; dport (%s)", portid2str(dport));
231219820Sjeff		return 0;
232219820Sjeff	}
233219820Sjeff
234219820Sjeff	mad = umad_get_mad(rcvbuf);
235219820Sjeff
236219820Sjeff	if ((status = mad_get_field(mad, 0, IB_MAD_STATUS_F)) != 0) {
237219820Sjeff		ERRS("MAD completed with error status 0x%x; dport (%s)",
238219820Sjeff			status, portid2str(dport));
239219820Sjeff		return 0;
240219820Sjeff	}
241219820Sjeff
242219820Sjeff	if (ibdebug) {
243219820Sjeff		IBWARN("data offs %d sz %d", rpc->dataoffs, rpc->datasz);
244219820Sjeff		xdump(stderr, "rmpp mad data\n", mad + rpc->dataoffs,
245219820Sjeff		      rpc->datasz);
246219820Sjeff	}
247219820Sjeff
248219820Sjeff	if (rmpp) {
249219820Sjeff		rmpp->flags = mad_get_field(mad, 0, IB_SA_RMPP_FLAGS_F);
250219820Sjeff		if ((rmpp->flags & 0x3) &&
251219820Sjeff		    mad_get_field(mad, 0, IB_SA_RMPP_VERS_F) != 1) {
252219820Sjeff			IBWARN("bad rmpp version");
253219820Sjeff			return 0;
254219820Sjeff		}
255219820Sjeff		rmpp->type = mad_get_field(mad, 0, IB_SA_RMPP_TYPE_F);
256219820Sjeff		rmpp->status = mad_get_field(mad, 0, IB_SA_RMPP_STATUS_F);
257219820Sjeff		DEBUG("rmpp type %d status %d", rmpp->type, rmpp->status);
258219820Sjeff		rmpp->d1.u = mad_get_field(mad, 0, IB_SA_RMPP_D1_F);
259219820Sjeff		rmpp->d2.u = mad_get_field(mad, 0, IB_SA_RMPP_D2_F);
260219820Sjeff	}
261219820Sjeff
262219820Sjeff	if (data)
263219820Sjeff		memcpy(data, mad + rpc->dataoffs, rpc->datasz);
264219820Sjeff
265219820Sjeff	rpc->recsz = mad_get_field(mad, 0, IB_SA_ATTROFFS_F);
266219820Sjeff
267219820Sjeff	return data;
268219820Sjeff}
269219820Sjeff
270219820Sjeffvoid *
271219820Sjeffmadrpc(ib_rpc_t *rpc, ib_portid_t *dport, void *payload, void *rcvdata)
272219820Sjeff{
273219820Sjeff	struct ibmad_port port;
274219820Sjeff
275219820Sjeff	port.port_id = mad_portid;
276219820Sjeff	port.class_agents[rpc->mgtclass] = mad_class_agent(rpc->mgtclass);
277219820Sjeff	return mad_rpc(&port, rpc, dport, payload, rcvdata);
278219820Sjeff}
279219820Sjeff
280219820Sjeffvoid *
281219820Sjeffmadrpc_rmpp(ib_rpc_t *rpc, ib_portid_t *dport, ib_rmpp_hdr_t *rmpp, void *data)
282219820Sjeff{
283219820Sjeff	struct ibmad_port port;
284219820Sjeff
285219820Sjeff	port.port_id = mad_portid;
286219820Sjeff	port.class_agents[rpc->mgtclass] = mad_class_agent(rpc->mgtclass);
287219820Sjeff	return mad_rpc_rmpp(&port, rpc, dport, rmpp, data);
288219820Sjeff}
289219820Sjeff
290219820Sjeffstatic pthread_mutex_t rpclock = PTHREAD_MUTEX_INITIALIZER;
291219820Sjeff
292219820Sjeffvoid
293219820Sjeffmadrpc_lock(void)
294219820Sjeff{
295219820Sjeff	pthread_mutex_lock(&rpclock);
296219820Sjeff}
297219820Sjeff
298219820Sjeffvoid
299219820Sjeffmadrpc_unlock(void)
300219820Sjeff{
301219820Sjeff	pthread_mutex_unlock(&rpclock);
302219820Sjeff}
303219820Sjeff
304219820Sjeffvoid
305219820Sjeffmadrpc_init(char *dev_name, int dev_port, int *mgmt_classes, int num_classes)
306219820Sjeff{
307219820Sjeff	if (umad_init() < 0)
308219820Sjeff		IBPANIC("can't init UMAD library");
309219820Sjeff
310219820Sjeff	if ((mad_portid = umad_open_port(dev_name, dev_port)) < 0)
311219820Sjeff		IBPANIC("can't open UMAD port (%s:%d)", dev_name, dev_port);
312219820Sjeff
313219820Sjeff	if (num_classes >= MAX_CLASS)
314219820Sjeff		IBPANIC("too many classes %d requested", num_classes);
315219820Sjeff
316219820Sjeff	while (num_classes--) {
317219820Sjeff		int rmpp_version = 0;
318219820Sjeff		int mgmt = *mgmt_classes++;
319219820Sjeff
320219820Sjeff		if (mgmt == IB_SA_CLASS)
321219820Sjeff			rmpp_version = 1;
322219820Sjeff		if (mad_register_client(mgmt, rmpp_version) < 0)
323219820Sjeff			IBPANIC("client_register for mgmt class %d failed", mgmt);
324219820Sjeff	}
325219820Sjeff}
326219820Sjeff
327219820Sjeffvoid *
328219820Sjeffmad_rpc_open_port(char *dev_name, int dev_port,
329219820Sjeff		  int *mgmt_classes, int num_classes)
330219820Sjeff{
331219820Sjeff	struct ibmad_port *p;
332219820Sjeff	int port_id;
333219820Sjeff
334219820Sjeff	if (num_classes >= MAX_CLASS) {
335219820Sjeff		IBWARN("too many classes %d requested", num_classes);
336219820Sjeff		errno = EINVAL;
337219820Sjeff		return NULL;
338219820Sjeff	}
339219820Sjeff
340219820Sjeff	if (umad_init() < 0) {
341219820Sjeff		IBWARN("can't init UMAD library");
342219820Sjeff		errno = ENODEV;
343219820Sjeff		return NULL;
344219820Sjeff	}
345219820Sjeff
346219820Sjeff	p = malloc(sizeof(*p));
347219820Sjeff	if (!p) {
348219820Sjeff		errno = ENOMEM;
349219820Sjeff		return NULL;
350219820Sjeff	}
351219820Sjeff	memset(p, 0, sizeof(*p));
352219820Sjeff
353219820Sjeff	if ((port_id = umad_open_port(dev_name, dev_port)) < 0) {
354219820Sjeff		IBWARN("can't open UMAD port (%s:%d)", dev_name, dev_port);
355219820Sjeff		if (!errno)
356219820Sjeff			errno = EIO;
357219820Sjeff		free(p);
358219820Sjeff		return NULL;
359219820Sjeff	}
360219820Sjeff
361219820Sjeff	while (num_classes--) {
362219820Sjeff		int rmpp_version = 0;
363219820Sjeff		int mgmt = *mgmt_classes++;
364219820Sjeff		int agent;
365219820Sjeff
366219820Sjeff		if (mgmt == IB_SA_CLASS)
367219820Sjeff			rmpp_version = 1;
368219820Sjeff		if (mgmt < 0 || mgmt >= MAX_CLASS ||
369219820Sjeff		    (agent = mad_register_port_client(port_id, mgmt,
370219820Sjeff						      rmpp_version)) < 0) {
371219820Sjeff			IBWARN("client_register for mgmt %d failed", mgmt);
372219820Sjeff			if(!errno)
373219820Sjeff				errno = EINVAL;
374219820Sjeff			umad_close_port(port_id);
375219820Sjeff  			free(p);
376219820Sjeff  			return NULL;
377219820Sjeff		}
378219820Sjeff		p->class_agents[mgmt] = agent;
379219820Sjeff	}
380219820Sjeff
381219820Sjeff	p->port_id = port_id;
382219820Sjeff	return p;
383219820Sjeff}
384219820Sjeff
385219820Sjeffvoid
386219820Sjeffmad_rpc_close_port(void *port_id)
387219820Sjeff{
388219820Sjeff	struct ibmad_port *p = port_id;
389219820Sjeff
390219820Sjeff	umad_close_port(p->port_id);
391219820Sjeff	free(p);
392219820Sjeff}
393219820Sjeff
394219820Sjeffuint8_t *
395219820Sjeffsa_call(void *rcvbuf, ib_portid_t *portid, ib_sa_call_t *sa, unsigned timeout)
396219820Sjeff{
397219820Sjeff	struct ibmad_port port;
398219820Sjeff
399219820Sjeff	port.port_id = mad_portid;
400219820Sjeff	port.class_agents[IB_SA_CLASS] = mad_class_agent(IB_SA_CLASS);
401219820Sjeff	return sa_rpc_call(&port, rcvbuf, portid, sa, timeout);
402219820Sjeff}
403