1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 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 <stdarg.h>
42219820Sjeff#include <time.h>
43219820Sjeff#include <string.h>
44219820Sjeff#include <signal.h>
45219820Sjeff#include <getopt.h>
46219820Sjeff
47219820Sjeff#include <infiniband/common.h>
48219820Sjeff#include <infiniband/umad.h>
49219820Sjeff#include <infiniband/mad.h>
50219820Sjeff
51219820Sjeff#include "ibdiag_common.h"
52219820Sjeff
53219820Sjeff#undef DEBUG
54219820Sjeff#define	DEBUG	if (verbose) IBWARN
55219820Sjeff
56219820Sjeffstatic int dest_type = IB_DEST_LID;
57219820Sjeffstatic int verbose;
58219820Sjeffstatic char host_and_domain[IB_VENDOR_RANGE2_DATA_SIZE];
59219820Sjeffstatic char last_host[IB_VENDOR_RANGE2_DATA_SIZE];
60219820Sjeff
61219820Sjeffchar *argv0 = "ibping";
62219820Sjeff
63219820Sjeffstatic void
64219820Sjeffget_host_and_domain(char *data, int sz)
65219820Sjeff{
66219820Sjeff	char *s = data;
67219820Sjeff	int n;
68219820Sjeff
69219820Sjeff	if (gethostname(s, sz) < 0)
70219820Sjeff		snprintf(s, sz, "?hostname?");
71219820Sjeff
72219820Sjeff	s[sz-1] = 0;
73219820Sjeff	if ((n = strlen(s)) >= sz)
74219820Sjeff		return;
75219820Sjeff	s[n] = '.';
76219820Sjeff	s += n + 1;
77219820Sjeff	sz -= n + 1;
78219820Sjeff
79219820Sjeff	if (getdomainname(s, sz) < 0)
80219820Sjeff		snprintf(s, sz, "?domainname?");
81219820Sjeff	if (strlen(s) == 0)
82219820Sjeff		s[-1] = 0;	/* no domain */
83219820Sjeff}
84219820Sjeff
85219820Sjeffstatic char *
86219820Sjeffibping_serv(void)
87219820Sjeff{
88219820Sjeff	void *umad;
89219820Sjeff	void *mad;
90219820Sjeff	char *data;
91219820Sjeff
92219820Sjeff	DEBUG("starting to serve...");
93219820Sjeff
94219820Sjeff	while ((umad = mad_receive(0, -1))) {
95219820Sjeff
96219820Sjeff		mad = umad_get_mad(umad);
97219820Sjeff		data = (char *)mad + IB_VENDOR_RANGE2_DATA_OFFS;
98219820Sjeff
99219820Sjeff		memcpy(data, host_and_domain, IB_VENDOR_RANGE2_DATA_SIZE);
100219820Sjeff
101219820Sjeff		DEBUG("Pong: %s", data);
102219820Sjeff
103219820Sjeff		if (mad_respond(umad, 0, 0) < 0)
104219820Sjeff			DEBUG("respond failed");
105219820Sjeff
106219820Sjeff		mad_free(umad);
107219820Sjeff	}
108219820Sjeff
109219820Sjeff	DEBUG("server out");
110219820Sjeff	return 0;
111219820Sjeff}
112219820Sjeff
113219820Sjeffstatic uint64_t
114219820Sjeffibping(ib_portid_t *portid, int quiet)
115219820Sjeff{
116219820Sjeff	char data[IB_VENDOR_RANGE2_DATA_SIZE] = {0};
117219820Sjeff	ib_vendor_call_t call;
118219820Sjeff	uint64_t start, rtt;
119219820Sjeff
120219820Sjeff	DEBUG("Ping..");
121219820Sjeff
122219820Sjeff	start = getcurrenttime();
123219820Sjeff
124219820Sjeff	call.method = IB_MAD_METHOD_GET;
125219820Sjeff	call.mgmt_class = IB_VENDOR_OPENIB_PING_CLASS;
126219820Sjeff	call.attrid = 0;
127219820Sjeff	call.mod = 0;
128219820Sjeff	call.oui = IB_OPENIB_OUI;
129219820Sjeff	call.timeout = 0;
130219820Sjeff	memset(&call.rmpp, 0, sizeof call.rmpp);
131219820Sjeff
132219820Sjeff	if (!ib_vendor_call(data, portid, &call))
133219820Sjeff		return ~0llu;
134219820Sjeff
135219820Sjeff	rtt = getcurrenttime() - start;
136219820Sjeff
137219820Sjeff	if (!last_host[0])
138219820Sjeff		memcpy(last_host, data, sizeof last_host);
139219820Sjeff
140219820Sjeff	if (!quiet)
141219820Sjeff		printf("Pong from %s (%s): time %" PRIu64 ".%03" PRIu64 " ms\n",
142219820Sjeff			data, portid2str(portid), rtt/1000, rtt%1000);
143219820Sjeff
144219820Sjeff	return rtt;
145219820Sjeff}
146219820Sjeff
147219820Sjeffstatic void
148219820Sjeffusage(void)
149219820Sjeff{
150219820Sjeff	char *basename;
151219820Sjeff
152219820Sjeff	if (!(basename = strrchr(argv0, '/')))
153219820Sjeff		basename = argv0;
154219820Sjeff	else
155219820Sjeff		basename++;
156219820Sjeff
157219820Sjeff	fprintf(stderr, "Usage: %s [-d(ebug) -e(rr_show) -v(erbose) -G(uid) -s smlid -V(ersion) -C ca_name -P ca_port "
158219820Sjeff			"-t(imeout) timeout_ms -c ping_count -f(lood) -o oui -S(erver)] <dest lid|guid>\n",
159219820Sjeff			basename);
160219820Sjeff	exit(-1);
161219820Sjeff}
162219820Sjeff
163219820Sjeffstatic uint64_t minrtt = ~0ull, maxrtt, total_rtt;
164219820Sjeffstatic uint64_t start, total_time, replied, lost, ntrans;
165219820Sjeffstatic ib_portid_t portid = {0};
166219820Sjeff
167219820Sjeffvoid
168219820Sjeffreport(int sig)
169219820Sjeff{
170219820Sjeff	total_time = getcurrenttime() - start;
171219820Sjeff
172219820Sjeff	DEBUG("out due signal %d", sig);
173219820Sjeff
174219820Sjeff	printf("\n--- %s (%s) ibping statistics ---\n", last_host, portid2str(&portid));
175219820Sjeff	printf("%" PRIu64 " packets transmitted, %" PRIu64 " received, %" PRIu64 "%% packet loss, time %" PRIu64 " ms\n",
176219820Sjeff		ntrans, replied,
177219820Sjeff		(lost != 0) ?  lost * 100 / ntrans : 0, total_time / 1000);
178219820Sjeff	printf("rtt min/avg/max = %" PRIu64 ".%03" PRIu64 "/%" PRIu64 ".%03" PRIu64 "/%" PRIu64 ".%03" PRIu64 " ms\n",
179219820Sjeff		minrtt == ~0ull ? 0 : minrtt/1000,
180219820Sjeff		minrtt == ~0ull ? 0 : minrtt%1000,
181219820Sjeff		replied ? total_rtt/replied/1000 : 0,
182219820Sjeff		replied ? (total_rtt/replied)%1000 : 0,
183219820Sjeff		maxrtt/1000, maxrtt%1000);
184219820Sjeff
185219820Sjeff	exit(0);
186219820Sjeff}
187219820Sjeff
188219820Sjeffint
189219820Sjeffmain(int argc, char **argv)
190219820Sjeff{
191219820Sjeff	int mgmt_classes[3] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS};
192219820Sjeff	int ping_class = IB_VENDOR_OPENIB_PING_CLASS;
193219820Sjeff	ib_portid_t *sm_id = 0, sm_portid = {0};
194219820Sjeff	int timeout = 0, udebug = 0, server = 0, flood = 0;
195219820Sjeff	int oui = IB_OPENIB_OUI;
196219820Sjeff	uint64_t rtt;
197219820Sjeff	unsigned count = ~0;
198219820Sjeff	extern int ibdebug;
199219820Sjeff	char *err;
200219820Sjeff	char *ca = 0;
201219820Sjeff	int ca_port = 0;
202219820Sjeff
203219820Sjeff	static char const str_opts[] = "C:P:t:s:c:o:devGfSVhu";
204219820Sjeff	static const struct option long_opts[] = {
205219820Sjeff		{ "C", 1, 0, 'C'},
206219820Sjeff		{ "P", 1, 0, 'P'},
207219820Sjeff		{ "debug", 0, 0, 'd'},
208219820Sjeff		{ "err_show", 0, 0, 'e'},
209219820Sjeff		{ "verbose", 0, 0, 'v'},
210219820Sjeff		{ "Guid", 0, 0, 'G'},
211219820Sjeff		{ "s", 1, 0, 's'},
212219820Sjeff		{ "timeout", 1, 0, 't'},
213219820Sjeff		{ "c", 1, 0, 'c'},
214219820Sjeff		{ "flood", 0, 0, 'f'},
215219820Sjeff		{ "o", 1, 0, 'o'},
216219820Sjeff		{ "Server", 0, 0, 'S'},
217219820Sjeff		{ "Version", 0, 0, 'V'},
218219820Sjeff		{ "help", 0, 0, 'h'},
219219820Sjeff		{ "usage", 0, 0, 'u'},
220219820Sjeff		{ }
221219820Sjeff	};
222219820Sjeff
223219820Sjeff	argv0 = argv[0];
224219820Sjeff
225219820Sjeff	while (1) {
226219820Sjeff		int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);
227219820Sjeff		if ( ch == -1 )
228219820Sjeff			break;
229219820Sjeff		switch(ch) {
230219820Sjeff		case 'C':
231219820Sjeff			ca = optarg;
232219820Sjeff			break;
233219820Sjeff		case 'P':
234219820Sjeff			ca_port = strtoul(optarg, 0, 0);
235219820Sjeff			break;
236219820Sjeff		case 'c':
237219820Sjeff			count = strtoul(optarg, 0, 0);
238219820Sjeff			break;
239219820Sjeff		case 'd':
240219820Sjeff			ibdebug++;
241219820Sjeff			madrpc_show_errors(1);
242219820Sjeff			umad_debug(udebug);
243219820Sjeff			udebug++;
244219820Sjeff			break;
245219820Sjeff		case 'e':
246219820Sjeff			madrpc_show_errors(1);
247219820Sjeff			break;
248219820Sjeff		case 'f':
249219820Sjeff			flood++;
250219820Sjeff			break;
251219820Sjeff		case 'G':
252219820Sjeff			dest_type = IB_DEST_GUID;
253219820Sjeff			break;
254219820Sjeff		case 'o':
255219820Sjeff			oui = strtoul(optarg, 0, 0);
256219820Sjeff			break;
257219820Sjeff		case 's':
258219820Sjeff			if (ib_resolve_portid_str(&sm_portid, optarg, IB_DEST_LID, 0) < 0)
259219820Sjeff				IBERROR("can't resolve SM destination port %s", optarg);
260219820Sjeff			sm_id = &sm_portid;
261219820Sjeff			break;
262219820Sjeff		case 'S':
263219820Sjeff			server++;
264219820Sjeff			break;
265219820Sjeff		case 't':
266219820Sjeff			timeout = strtoul(optarg, 0, 0);
267219820Sjeff			madrpc_set_timeout(timeout);
268219820Sjeff			break;
269219820Sjeff		case 'v':
270219820Sjeff			verbose++;
271219820Sjeff			break;
272219820Sjeff		case 'V':
273219820Sjeff			fprintf(stderr, "%s %s\n", argv0, get_build_version() );
274219820Sjeff			exit(-1);
275219820Sjeff		default:
276219820Sjeff			usage();
277219820Sjeff			break;
278219820Sjeff		}
279219820Sjeff	}
280219820Sjeff	argc -= optind;
281219820Sjeff	argv += optind;
282219820Sjeff
283219820Sjeff	if (!argc && !server)
284219820Sjeff		usage();
285219820Sjeff
286219820Sjeff	madrpc_init(ca, ca_port, mgmt_classes, 3);
287219820Sjeff
288219820Sjeff	if (server) {
289219820Sjeff		if (mad_register_server(ping_class, 0, 0, oui) < 0)
290219820Sjeff			IBERROR("can't serve class %d on this port", ping_class);
291219820Sjeff
292219820Sjeff		get_host_and_domain(host_and_domain, sizeof host_and_domain);
293219820Sjeff
294219820Sjeff		if ((err = ibping_serv()))
295219820Sjeff			IBERROR("ibping to %s: %s", portid2str(&portid), err);
296219820Sjeff		exit(0);
297219820Sjeff	}
298219820Sjeff
299219820Sjeff	if (mad_register_client(ping_class, 0) < 0)
300219820Sjeff		IBERROR("can't register ping class %d on this port", ping_class);
301219820Sjeff
302219820Sjeff	if (ib_resolve_portid_str(&portid, argv[0], dest_type, sm_id) < 0)
303219820Sjeff		IBERROR("can't resolve destination port %s", argv[0]);
304219820Sjeff
305219820Sjeff	signal(SIGINT, report);
306219820Sjeff	signal(SIGTERM, report);
307219820Sjeff
308219820Sjeff	start = getcurrenttime();
309219820Sjeff
310219820Sjeff	while (count-- > 0) {
311219820Sjeff		ntrans++;
312219820Sjeff		if ((rtt = ibping(&portid, flood)) == ~0ull) {
313219820Sjeff			DEBUG("ibping to %s failed", portid2str(&portid));
314219820Sjeff			lost++;
315219820Sjeff		} else {
316219820Sjeff			if (rtt < minrtt)
317219820Sjeff				minrtt = rtt;
318219820Sjeff			if (rtt > maxrtt)
319219820Sjeff				maxrtt = rtt;
320219820Sjeff			total_rtt += rtt;
321219820Sjeff			replied++;
322219820Sjeff		}
323219820Sjeff
324219820Sjeff		if (!flood)
325219820Sjeff			sleep(1);
326219820Sjeff	}
327219820Sjeff
328219820Sjeff	report(0);
329219820Sjeff
330219820Sjeff	exit(-1);
331219820Sjeff}
332