1/*
2 * Copyright (c) 2004-2009 Voltaire Inc.  All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 *
32 */
33
34#if HAVE_CONFIG_H
35#  include <config.h>
36#endif				/* HAVE_CONFIG_H */
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <unistd.h>
41#include <string.h>
42#include <getopt.h>
43
44#include <infiniband/umad.h>
45#include <infiniband/mad.h>
46
47#include <sys/sysctl.h>
48
49#include "ibdiag_common.h"
50
51#define MAX_CPUS 8
52
53struct ibmad_port *srcport;
54
55static ibmad_gid_t dgid;
56static int with_grh;
57
58enum ib_sysstat_attr_t {
59	IB_PING_ATTR = 0x10,
60	IB_HOSTINFO_ATTR = 0x11,
61	IB_CPUINFO_ATTR = 0x12,
62};
63
64typedef struct cpu_info {
65	char *model;
66	char *mhz;
67} cpu_info;
68
69static cpu_info cpus[MAX_CPUS];
70static int host_ncpu;
71static int server = 0, oui = IB_OPENIB_OUI;
72
73static int server_respond(void *umad, int size)
74{
75	ib_rpc_t rpc = { 0 };
76	ib_rmpp_hdr_t rmpp = { 0 };
77	ib_portid_t rport;
78	uint8_t *mad = umad_get_mad(umad);
79	ib_mad_addr_t *mad_addr;
80
81	if (!(mad_addr = umad_get_mad_addr(umad)))
82		return -1;
83
84	memset(&rport, 0, sizeof(rport));
85
86	rport.lid = ntohs(mad_addr->lid);
87	rport.qp = ntohl(mad_addr->qpn);
88	rport.qkey = ntohl(mad_addr->qkey);
89	rport.sl = mad_addr->sl;
90	if (!rport.qkey && rport.qp == 1)
91		rport.qkey = IB_DEFAULT_QP1_QKEY;
92	rport.grh_present = mad_addr->grh_present;
93	if (rport.grh_present)
94		memcpy(rport.gid, mad_addr->gid, 16);
95
96	rpc.mgtclass = mad_get_field(mad, 0, IB_MAD_MGMTCLASS_F);
97	rpc.method = IB_MAD_METHOD_GET | IB_MAD_RESPONSE;
98	rpc.attr.id = mad_get_field(mad, 0, IB_MAD_ATTRID_F);
99	rpc.attr.mod = mad_get_field(mad, 0, IB_MAD_ATTRMOD_F);
100	rpc.oui = mad_get_field(mad, 0, IB_VEND2_OUI_F);
101	rpc.trid = mad_get_field64(mad, 0, IB_MAD_TRID_F);
102
103	if (size > IB_MAD_SIZE)
104		rmpp.flags = IB_RMPP_FLAG_ACTIVE;
105
106	DEBUG("responding %d bytes to %s, attr 0x%x mod 0x%x qkey %x",
107	      size, portid2str(&rport), rpc.attr.id, rpc.attr.mod, rport.qkey);
108
109	if (mad_build_pkt(umad, &rpc, &rport, &rmpp, 0) < 0)
110		return -1;
111
112	if (ibdebug > 1)
113		xdump(stderr, "mad respond pkt\n", mad, IB_MAD_SIZE);
114
115	if (umad_send(mad_rpc_portid(srcport),
116		      mad_rpc_class_agent(srcport, rpc.mgtclass), umad, size,
117		      rpc.timeout, 0) < 0) {
118		DEBUG("send failed; %m");
119		return -1;
120	}
121
122	return 0;
123}
124
125static int mk_reply(int attr, void *data, int sz)
126{
127	char *s = data;
128	int n, i, ret = 0;
129
130	switch (attr) {
131	case IB_PING_ATTR:
132		break;		/* nothing to do here, just reply */
133	case IB_HOSTINFO_ATTR:
134		if (gethostname(s, sz) < 0)
135			snprintf(s, sz, "?hostname?");
136		s[sz - 1] = 0;
137		if ((n = strlen(s)) >= sz - 1) {
138			ret = sz;
139			break;
140		}
141		s[n] = '.';
142		s += n + 1;
143		sz -= n + 1;
144		ret += n + 1;
145		if (getdomainname(s, sz) < 0)
146			snprintf(s, sz, "?domainname?");
147		if ((n = strlen(s)) == 0)
148			s[-1] = 0;	/* no domain */
149		else
150			ret += n;
151		break;
152	case IB_CPUINFO_ATTR:
153		s[0] = '\0';
154		for (i = 0; i < host_ncpu && sz > 0; i++) {
155			n = snprintf(s, sz, "cpu %d: model %s MHZ %s\n",
156				     i, cpus[i].model, cpus[i].mhz);
157			if (n >= sz) {
158				IBWARN("cpuinfo truncated");
159				ret = sz;
160				break;
161			}
162			sz -= n;
163			s += n;
164			ret += n;
165		}
166		ret++;
167		break;
168	default:
169		DEBUG("unknown attr %d", attr);
170	}
171	return ret;
172}
173
174static uint8_t buf[2048];
175
176static char *ibsystat_serv(void)
177{
178	void *umad;
179	void *mad;
180	int attr, mod, size;
181
182	DEBUG("starting to serve...");
183
184	while ((umad = mad_receive_via(buf, -1, srcport))) {
185		if (umad_status(buf)) {
186			DEBUG("drop mad with status %x: %s", umad_status(buf),
187			      strerror(umad_status(buf)));
188			continue;
189		}
190
191		mad = umad_get_mad(umad);
192
193		attr = mad_get_field(mad, 0, IB_MAD_ATTRID_F);
194		mod = mad_get_field(mad, 0, IB_MAD_ATTRMOD_F);
195
196		DEBUG("got packet: attr 0x%x mod 0x%x", attr, mod);
197
198		size =
199		    mk_reply(attr, (uint8_t *) mad + IB_VENDOR_RANGE2_DATA_OFFS,
200			     sizeof(buf) - umad_size() -
201			     IB_VENDOR_RANGE2_DATA_OFFS);
202
203		if (server_respond(umad, IB_VENDOR_RANGE2_DATA_OFFS + size) < 0)
204			DEBUG("respond failed");
205	}
206
207	DEBUG("server out");
208	return 0;
209}
210
211static int match_attr(char *str)
212{
213	if (!strcmp(str, "ping"))
214		return IB_PING_ATTR;
215	if (!strcmp(str, "host"))
216		return IB_HOSTINFO_ATTR;
217	if (!strcmp(str, "cpu"))
218		return IB_CPUINFO_ATTR;
219	return -1;
220}
221
222static char *ibsystat(ib_portid_t * portid, int attr)
223{
224	ib_rpc_t rpc = { 0 };
225	int fd, agent, timeout, len;
226	void *data = (uint8_t *) umad_get_mad(buf) + IB_VENDOR_RANGE2_DATA_OFFS;
227
228	DEBUG("Sysstat ping..");
229
230	rpc.mgtclass = IB_VENDOR_OPENIB_SYSSTAT_CLASS;
231	rpc.method = IB_MAD_METHOD_GET;
232	rpc.attr.id = attr;
233	rpc.attr.mod = 0;
234	rpc.oui = oui;
235	rpc.timeout = 0;
236	rpc.datasz = IB_VENDOR_RANGE2_DATA_SIZE;
237	rpc.dataoffs = IB_VENDOR_RANGE2_DATA_OFFS;
238
239	portid->qp = 1;
240	if (!portid->qkey)
241		portid->qkey = IB_DEFAULT_QP1_QKEY;
242
243	if ((len = mad_build_pkt(buf, &rpc, portid, NULL, NULL)) < 0)
244		IBPANIC("cannot build packet.");
245
246	fd = mad_rpc_portid(srcport);
247	agent = mad_rpc_class_agent(srcport, rpc.mgtclass);
248	timeout = ibd_timeout ? ibd_timeout : MAD_DEF_TIMEOUT_MS;
249
250	if (umad_send(fd, agent, buf, len, timeout, 0) < 0)
251		IBPANIC("umad_send failed.");
252
253	len = sizeof(buf) - umad_size();
254	if (umad_recv(fd, buf, &len, timeout) < 0)
255		IBPANIC("umad_recv failed.");
256
257	if (umad_status(buf))
258		return strerror(umad_status(buf));
259
260	DEBUG("Got sysstat pong..");
261	if (attr != IB_PING_ATTR)
262		puts(data);
263	else
264		printf("sysstat ping succeeded\n");
265	return 0;
266}
267
268int build_cpuinfo(void)
269{
270	int ret;
271	size_t size = sizeof(ret);
272
273	if (sysctlbyname("hw.ncpu", &ret, &size, NULL, 0) != 0 || ret < 1)
274		ret = 1;
275	return ret;
276
277	DEBUG("ncpu %d", ret);
278}
279
280static int process_opt(void *context, int ch, char *optarg)
281{
282	switch (ch) {
283	case 'o':
284		oui = strtoul(optarg, 0, 0);
285		break;
286	case 'S':
287		server++;
288		break;
289	case 25:
290		if (!inet_pton(AF_INET6, optarg, &dgid)) {
291			fprintf(stderr, "dgid format is wrong!\n");
292			ibdiag_show_usage();
293			return 1;
294		}
295		with_grh = 1;
296		break;
297	default:
298		return -1;
299	}
300	return 0;
301}
302
303int main(int argc, char **argv)
304{
305	int mgmt_classes[3] =
306	    { IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS };
307	int sysstat_class = IB_VENDOR_OPENIB_SYSSTAT_CLASS;
308	ib_portid_t portid = { 0 };
309	int attr = IB_PING_ATTR;
310	char *err;
311
312	const struct ibdiag_opt opts[] = {
313		{"oui", 'o', 1, NULL, "use specified OUI number"},
314		{"Server", 'S', 0, NULL, "start in server mode"},
315		{"dgid", 25, 1, NULL, "remote gid (IPv6 format)"},
316		{0}
317	};
318	char usage_args[] = "<dest lid|guid> [<op>]";
319
320	ibdiag_process_opts(argc, argv, NULL, "DKy", opts, process_opt,
321			    usage_args, NULL);
322
323	argc -= optind;
324	argv += optind;
325
326	if (!argc && !server)
327		ibdiag_show_usage();
328
329	if (argc > 1 && (attr = match_attr(argv[1])) < 0)
330		ibdiag_show_usage();
331
332	srcport = mad_rpc_open_port(ibd_ca, ibd_ca_port, mgmt_classes, 3);
333	if (!srcport)
334		IBEXIT("Failed to open '%s' port '%d'", ibd_ca, ibd_ca_port);
335
336	if (server) {
337		if (mad_register_server_via(sysstat_class, 1, 0, oui, srcport) <
338		    0)
339			IBEXIT("can't serve class %d", sysstat_class);
340
341		host_ncpu = build_cpuinfo();
342
343		if ((err = ibsystat_serv()))
344			IBEXIT("ibssystat to %s: %s", portid2str(&portid),
345				err);
346		exit(0);
347	}
348
349	if (mad_register_client_via(sysstat_class, 1, srcport) < 0)
350		IBEXIT("can't register to sysstat class %d", sysstat_class);
351
352	if (with_grh && ibd_dest_type != IB_DEST_LID)
353		IBEXIT("When using GRH, LID should be provided");
354	if (resolve_portid_str(ibd_ca, ibd_ca_port, &portid, argv[0],
355			       ibd_dest_type, ibd_sm_id, srcport) < 0)
356		IBEXIT("can't resolve destination port %s", argv[0]);
357	if (with_grh) {
358		portid.grh_present = 1;
359		memcpy(&portid.gid, &dgid, sizeof(portid.gid));
360	}
361
362	if ((err = ibsystat(&portid, attr)))
363		IBEXIT("ibsystat to %s: %s", portid2str(&portid), err);
364
365	mad_rpc_close_port(srcport);
366	exit(0);
367}
368