1321936Shselasky/*
2321936Shselasky * Copyright (c) 2004-2009 Voltaire Inc.  All rights reserved.
3321936Shselasky *
4321936Shselasky * This software is available to you under a choice of one of two
5321936Shselasky * licenses.  You may choose to be licensed under the terms of the GNU
6321936Shselasky * General Public License (GPL) Version 2, available from the file
7321936Shselasky * COPYING in the main directory of this source tree, or the
8321936Shselasky * OpenIB.org BSD license below:
9321936Shselasky *
10321936Shselasky *     Redistribution and use in source and binary forms, with or
11321936Shselasky *     without modification, are permitted provided that the following
12321936Shselasky *     conditions are met:
13321936Shselasky *
14321936Shselasky *      - Redistributions of source code must retain the above
15321936Shselasky *        copyright notice, this list of conditions and the following
16321936Shselasky *        disclaimer.
17321936Shselasky *
18321936Shselasky *      - Redistributions in binary form must reproduce the above
19321936Shselasky *        copyright notice, this list of conditions and the following
20321936Shselasky *        disclaimer in the documentation and/or other materials
21321936Shselasky *        provided with the distribution.
22321936Shselasky *
23321936Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24321936Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25321936Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26321936Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27321936Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28321936Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29321936Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30321936Shselasky * SOFTWARE.
31321936Shselasky *
32321936Shselasky */
33321936Shselasky
34321936Shselasky#if HAVE_CONFIG_H
35321936Shselasky#  include <config.h>
36321936Shselasky#endif				/* HAVE_CONFIG_H */
37321936Shselasky
38321936Shselasky#include <stdio.h>
39321936Shselasky#include <stdlib.h>
40321936Shselasky#include <unistd.h>
41321936Shselasky#include <string.h>
42321936Shselasky#include <signal.h>
43321936Shselasky#include <getopt.h>
44321936Shselasky
45321936Shselasky#include <infiniband/umad.h>
46321936Shselasky#include <infiniband/mad.h>
47321936Shselasky#include <complib/cl_timer.h>
48321936Shselasky
49321936Shselasky#include "ibdiag_common.h"
50321936Shselasky
51321936Shselaskystruct ibmad_port *srcport;
52321936Shselasky
53321936Shselaskystatic char host_and_domain[IB_VENDOR_RANGE2_DATA_SIZE];
54321936Shselaskystatic char last_host[IB_VENDOR_RANGE2_DATA_SIZE];
55321936Shselaskystatic ibmad_gid_t dgid;
56321936Shselaskystatic int with_grh;
57321936Shselasky
58321936Shselaskystatic void get_host_and_domain(char *data, int sz)
59321936Shselasky{
60321936Shselasky	char *s = data;
61321936Shselasky	int n;
62321936Shselasky
63321936Shselasky	if (gethostname(s, sz) < 0)
64321936Shselasky		snprintf(s, sz, "?hostname?");
65321936Shselasky
66321936Shselasky	s[sz - 1] = 0;
67321936Shselasky	if ((n = strlen(s)) >= sz)
68321936Shselasky		return;
69321936Shselasky	s[n] = '.';
70321936Shselasky	s += n + 1;
71321936Shselasky	sz -= n + 1;
72321936Shselasky
73321936Shselasky	if (getdomainname(s, sz) < 0)
74321936Shselasky		snprintf(s, sz, "?domainname?");
75321936Shselasky	if (strlen(s) == 0)
76321936Shselasky		s[-1] = 0;	/* no domain */
77321936Shselasky}
78321936Shselasky
79321936Shselaskystatic char *ibping_serv(void)
80321936Shselasky{
81321936Shselasky	void *umad;
82321936Shselasky	void *mad;
83321936Shselasky	char *data;
84321936Shselasky
85321936Shselasky	DEBUG("starting to serve...");
86321936Shselasky
87321936Shselasky	while ((umad = mad_receive_via(0, -1, srcport))) {
88321936Shselasky
89321936Shselasky		if (umad_status(umad) == 0) {
90321936Shselasky			mad = umad_get_mad(umad);
91321936Shselasky			data = (char *)mad + IB_VENDOR_RANGE2_DATA_OFFS;
92321936Shselasky
93321936Shselasky			memcpy(data, host_and_domain, IB_VENDOR_RANGE2_DATA_SIZE);
94321936Shselasky
95321936Shselasky			DEBUG("Pong: %s", data);
96321936Shselasky
97321936Shselasky			if (mad_respond_via(umad, 0, 0, srcport) < 0)
98321936Shselasky				DEBUG("respond failed");
99321936Shselasky
100321936Shselasky		}
101321936Shselasky		mad_free(umad);
102321936Shselasky	}
103321936Shselasky
104321936Shselasky	DEBUG("server out");
105321936Shselasky	return 0;
106321936Shselasky}
107321936Shselasky
108321936Shselaskystatic int oui = IB_OPENIB_OUI;
109321936Shselasky
110321936Shselaskystatic uint64_t ibping(ib_portid_t * portid, int quiet)
111321936Shselasky{
112321936Shselasky	char data[IB_VENDOR_RANGE2_DATA_SIZE] = { 0 };
113321936Shselasky	ib_vendor_call_t call;
114321936Shselasky	uint64_t start, rtt;
115321936Shselasky
116321936Shselasky	DEBUG("Ping..");
117321936Shselasky
118321936Shselasky	start = cl_get_time_stamp();
119321936Shselasky
120321936Shselasky	call.method = IB_MAD_METHOD_GET;
121321936Shselasky	call.mgmt_class = IB_VENDOR_OPENIB_PING_CLASS;
122321936Shselasky	call.attrid = 0;
123321936Shselasky	call.mod = 0;
124321936Shselasky	call.oui = oui;
125321936Shselasky	call.timeout = 0;
126321936Shselasky	memset(&call.rmpp, 0, sizeof call.rmpp);
127321936Shselasky
128321936Shselasky	if (!ib_vendor_call_via(data, portid, &call, srcport))
129321936Shselasky		return ~0ull;
130321936Shselasky
131321936Shselasky	rtt = cl_get_time_stamp() - start;
132321936Shselasky
133321936Shselasky	if (!last_host[0])
134321936Shselasky		memcpy(last_host, data, sizeof last_host);
135321936Shselasky
136321936Shselasky	if (!quiet)
137321936Shselasky		printf("Pong from %s (%s): time %" PRIu64 ".%03" PRIu64 " ms\n",
138321936Shselasky		       data, portid2str(portid), rtt / 1000, rtt % 1000);
139321936Shselasky
140321936Shselasky	return rtt;
141321936Shselasky}
142321936Shselasky
143321936Shselaskystatic uint64_t minrtt = ~0ull, maxrtt, total_rtt;
144321936Shselaskystatic uint64_t start, total_time, replied, lost, ntrans;
145321936Shselaskystatic ib_portid_t portid = { 0 };
146321936Shselasky
147321936Shselaskyvoid report(int sig)
148321936Shselasky{
149321936Shselasky	total_time = cl_get_time_stamp() - start;
150321936Shselasky
151321936Shselasky	DEBUG("out due signal %d", sig);
152321936Shselasky
153321936Shselasky	printf("\n--- %s (%s) ibping statistics ---\n", last_host,
154321936Shselasky	       portid2str(&portid));
155321936Shselasky	printf("%" PRIu64 " packets transmitted, %" PRIu64 " received, %" PRIu64
156321936Shselasky	       "%% packet loss, time %" PRIu64 " ms\n", ntrans, replied,
157321936Shselasky	       (lost != 0) ? lost * 100 / ntrans : 0, total_time / 1000);
158321936Shselasky	printf("rtt min/avg/max = %" PRIu64 ".%03" PRIu64 "/%" PRIu64 ".%03"
159321936Shselasky	       PRIu64 "/%" PRIu64 ".%03" PRIu64 " ms\n",
160321936Shselasky	       minrtt == ~0ull ? 0 : minrtt / 1000,
161321936Shselasky	       minrtt == ~0ull ? 0 : minrtt % 1000,
162321936Shselasky	       replied ? total_rtt / replied / 1000 : 0,
163321936Shselasky	       replied ? (total_rtt / replied) % 1000 : 0, maxrtt / 1000,
164321936Shselasky	       maxrtt % 1000);
165321936Shselasky
166321936Shselasky	exit(0);
167321936Shselasky}
168321936Shselasky
169321936Shselaskystatic int server = 0, flood = 0;
170321936Shselaskystatic unsigned count = ~0;
171321936Shselasky
172321936Shselaskystatic int process_opt(void *context, int ch, char *optarg)
173321936Shselasky{
174321936Shselasky	switch (ch) {
175321936Shselasky	case 'c':
176321936Shselasky		count = strtoul(optarg, 0, 0);
177321936Shselasky		break;
178321936Shselasky	case 'f':
179321936Shselasky		flood++;
180321936Shselasky		break;
181321936Shselasky	case 'o':
182321936Shselasky		oui = strtoul(optarg, 0, 0);
183321936Shselasky		break;
184321936Shselasky	case 'S':
185321936Shselasky		server++;
186321936Shselasky		break;
187321936Shselasky	case 25:
188321936Shselasky		if (!inet_pton(AF_INET6, optarg, &dgid)) {
189321936Shselasky			printf("dgid format is wrong!\n");
190321936Shselasky			ibdiag_show_usage();
191321936Shselasky			return 1;
192321936Shselasky		}
193321936Shselasky		with_grh = 1;
194321936Shselasky		break;
195321936Shselasky	default:
196321936Shselasky		return -1;
197321936Shselasky	}
198321936Shselasky	return 0;
199321936Shselasky}
200321936Shselasky
201321936Shselaskyint main(int argc, char **argv)
202321936Shselasky{
203321936Shselasky	int mgmt_classes[1] = { IB_SA_CLASS };
204321936Shselasky	int ping_class = IB_VENDOR_OPENIB_PING_CLASS;
205321936Shselasky	uint64_t rtt;
206321936Shselasky	char *err;
207321936Shselasky
208321936Shselasky	const struct ibdiag_opt opts[] = {
209321936Shselasky		{"count", 'c', 1, "<num>", "stop after count packets"},
210321936Shselasky		{"flood", 'f', 0, NULL, "flood destination"},
211321936Shselasky		{"oui", 'o', 1, NULL, "use specified OUI number"},
212321936Shselasky		{"Server", 'S', 0, NULL, "start in server mode"},
213321936Shselasky		{"dgid", 25, 1, NULL, "remote gid (IPv6 format)"},
214321936Shselasky		{0}
215321936Shselasky	};
216321936Shselasky	char usage_args[] = "<dest lid|guid>";
217321936Shselasky
218321936Shselasky	ibdiag_process_opts(argc, argv, NULL, "DKy", opts, process_opt,
219321936Shselasky			    usage_args, NULL);
220321936Shselasky
221321936Shselasky	argc -= optind;
222321936Shselasky	argv += optind;
223321936Shselasky
224321936Shselasky	if (!argc && !server)
225321936Shselasky		ibdiag_show_usage();
226321936Shselasky
227321936Shselasky	srcport = mad_rpc_open_port(ibd_ca, ibd_ca_port, mgmt_classes, 1);
228321936Shselasky	if (!srcport)
229321936Shselasky		IBEXIT("Failed to open '%s' port '%d'", ibd_ca, ibd_ca_port);
230321936Shselasky
231321936Shselasky	if (server) {
232321936Shselasky		if (mad_register_server_via(ping_class, 0, 0, oui, srcport) < 0)
233321936Shselasky			IBEXIT("can't serve class %d on this port",
234321936Shselasky				ping_class);
235321936Shselasky
236321936Shselasky		get_host_and_domain(host_and_domain, sizeof host_and_domain);
237321936Shselasky
238321936Shselasky		if ((err = ibping_serv()))
239321936Shselasky			IBEXIT("ibping to %s: %s", portid2str(&portid), err);
240321936Shselasky		exit(0);
241321936Shselasky	}
242321936Shselasky
243321936Shselasky	if (mad_register_client_via(ping_class, 0, srcport) < 0)
244321936Shselasky		IBEXIT("can't register ping class %d on this port",
245321936Shselasky			ping_class);
246321936Shselasky
247321936Shselasky	if (with_grh && ibd_dest_type != IB_DEST_LID)
248321936Shselasky		IBEXIT("When using GRH, LID should be provided");
249321936Shselasky	if (resolve_portid_str(ibd_ca, ibd_ca_port, &portid, argv[0],
250321936Shselasky			       ibd_dest_type, ibd_sm_id, srcport) < 0)
251321936Shselasky		IBEXIT("can't resolve destination port %s", argv[0]);
252321936Shselasky
253321936Shselasky	if (with_grh) {
254321936Shselasky		portid.grh_present = 1;
255321936Shselasky		memcpy(&portid.gid, &dgid, sizeof(portid.gid));
256321936Shselasky	}
257321936Shselasky
258321936Shselasky	signal(SIGINT, report);
259321936Shselasky	signal(SIGTERM, report);
260321936Shselasky
261321936Shselasky	start = cl_get_time_stamp();
262321936Shselasky
263321936Shselasky	while (count-- > 0) {
264321936Shselasky		ntrans++;
265321936Shselasky		if ((rtt = ibping(&portid, flood)) == ~0ull) {
266321936Shselasky			DEBUG("ibping to %s failed", portid2str(&portid));
267321936Shselasky			lost++;
268321936Shselasky		} else {
269321936Shselasky			if (rtt < minrtt)
270321936Shselasky				minrtt = rtt;
271321936Shselasky			if (rtt > maxrtt)
272321936Shselasky				maxrtt = rtt;
273321936Shselasky			total_rtt += rtt;
274321936Shselasky			replied++;
275321936Shselasky		}
276321936Shselasky
277321936Shselasky		if (!flood)
278321936Shselasky			sleep(1);
279321936Shselasky	}
280321936Shselasky
281321936Shselasky	report(0);
282321936Shselasky
283321936Shselasky	mad_rpc_close_port(srcport);
284321936Shselasky
285321936Shselasky	exit(-1);
286321936Shselasky}
287