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 <signal.h>
43#include <getopt.h>
44
45#include <infiniband/umad.h>
46#include <infiniband/mad.h>
47#include <complib/cl_timer.h>
48
49#include "ibdiag_common.h"
50
51struct ibmad_port *srcport;
52
53static char host_and_domain[IB_VENDOR_RANGE2_DATA_SIZE];
54static char last_host[IB_VENDOR_RANGE2_DATA_SIZE];
55static ibmad_gid_t dgid;
56static int with_grh;
57
58static void get_host_and_domain(char *data, int sz)
59{
60	char *s = data;
61	int n;
62
63	if (gethostname(s, sz) < 0)
64		snprintf(s, sz, "?hostname?");
65
66	s[sz - 1] = 0;
67	if ((n = strlen(s)) >= sz)
68		return;
69	s[n] = '.';
70	s += n + 1;
71	sz -= n + 1;
72
73	if (getdomainname(s, sz) < 0)
74		snprintf(s, sz, "?domainname?");
75	if (strlen(s) == 0)
76		s[-1] = 0;	/* no domain */
77}
78
79static char *ibping_serv(void)
80{
81	void *umad;
82	void *mad;
83	char *data;
84
85	DEBUG("starting to serve...");
86
87	while ((umad = mad_receive_via(0, -1, srcport))) {
88
89		if (umad_status(umad) == 0) {
90			mad = umad_get_mad(umad);
91			data = (char *)mad + IB_VENDOR_RANGE2_DATA_OFFS;
92
93			memcpy(data, host_and_domain, IB_VENDOR_RANGE2_DATA_SIZE);
94
95			DEBUG("Pong: %s", data);
96
97			if (mad_respond_via(umad, 0, 0, srcport) < 0)
98				DEBUG("respond failed");
99
100		}
101		mad_free(umad);
102	}
103
104	DEBUG("server out");
105	return 0;
106}
107
108static int oui = IB_OPENIB_OUI;
109
110static uint64_t ibping(ib_portid_t * portid, int quiet)
111{
112	char data[IB_VENDOR_RANGE2_DATA_SIZE] = { 0 };
113	ib_vendor_call_t call;
114	uint64_t start, rtt;
115
116	DEBUG("Ping..");
117
118	start = cl_get_time_stamp();
119
120	call.method = IB_MAD_METHOD_GET;
121	call.mgmt_class = IB_VENDOR_OPENIB_PING_CLASS;
122	call.attrid = 0;
123	call.mod = 0;
124	call.oui = oui;
125	call.timeout = 0;
126	memset(&call.rmpp, 0, sizeof call.rmpp);
127
128	if (!ib_vendor_call_via(data, portid, &call, srcport))
129		return ~0ull;
130
131	rtt = cl_get_time_stamp() - start;
132
133	if (!last_host[0])
134		memcpy(last_host, data, sizeof last_host);
135
136	if (!quiet)
137		printf("Pong from %s (%s): time %" PRIu64 ".%03" PRIu64 " ms\n",
138		       data, portid2str(portid), rtt / 1000, rtt % 1000);
139
140	return rtt;
141}
142
143static uint64_t minrtt = ~0ull, maxrtt, total_rtt;
144static uint64_t start, total_time, replied, lost, ntrans;
145static ib_portid_t portid = { 0 };
146
147void report(int sig)
148{
149	total_time = cl_get_time_stamp() - start;
150
151	DEBUG("out due signal %d", sig);
152
153	printf("\n--- %s (%s) ibping statistics ---\n", last_host,
154	       portid2str(&portid));
155	printf("%" PRIu64 " packets transmitted, %" PRIu64 " received, %" PRIu64
156	       "%% packet loss, time %" PRIu64 " ms\n", ntrans, replied,
157	       (lost != 0) ? lost * 100 / ntrans : 0, total_time / 1000);
158	printf("rtt min/avg/max = %" PRIu64 ".%03" PRIu64 "/%" PRIu64 ".%03"
159	       PRIu64 "/%" PRIu64 ".%03" PRIu64 " ms\n",
160	       minrtt == ~0ull ? 0 : minrtt / 1000,
161	       minrtt == ~0ull ? 0 : minrtt % 1000,
162	       replied ? total_rtt / replied / 1000 : 0,
163	       replied ? (total_rtt / replied) % 1000 : 0, maxrtt / 1000,
164	       maxrtt % 1000);
165
166	exit(0);
167}
168
169static int server = 0, flood = 0;
170static unsigned count = ~0;
171
172static int process_opt(void *context, int ch, char *optarg)
173{
174	switch (ch) {
175	case 'c':
176		count = strtoul(optarg, 0, 0);
177		break;
178	case 'f':
179		flood++;
180		break;
181	case 'o':
182		oui = strtoul(optarg, 0, 0);
183		break;
184	case 'S':
185		server++;
186		break;
187	case 25:
188		if (!inet_pton(AF_INET6, optarg, &dgid)) {
189			printf("dgid format is wrong!\n");
190			ibdiag_show_usage();
191			return 1;
192		}
193		with_grh = 1;
194		break;
195	default:
196		return -1;
197	}
198	return 0;
199}
200
201int main(int argc, char **argv)
202{
203	int mgmt_classes[1] = { IB_SA_CLASS };
204	int ping_class = IB_VENDOR_OPENIB_PING_CLASS;
205	uint64_t rtt;
206	char *err;
207
208	const struct ibdiag_opt opts[] = {
209		{"count", 'c', 1, "<num>", "stop after count packets"},
210		{"flood", 'f', 0, NULL, "flood destination"},
211		{"oui", 'o', 1, NULL, "use specified OUI number"},
212		{"Server", 'S', 0, NULL, "start in server mode"},
213		{"dgid", 25, 1, NULL, "remote gid (IPv6 format)"},
214		{0}
215	};
216	char usage_args[] = "<dest lid|guid>";
217
218	ibdiag_process_opts(argc, argv, NULL, "DKy", opts, process_opt,
219			    usage_args, NULL);
220
221	argc -= optind;
222	argv += optind;
223
224	if (!argc && !server)
225		ibdiag_show_usage();
226
227	srcport = mad_rpc_open_port(ibd_ca, ibd_ca_port, mgmt_classes, 1);
228	if (!srcport)
229		IBEXIT("Failed to open '%s' port '%d'", ibd_ca, ibd_ca_port);
230
231	if (server) {
232		if (mad_register_server_via(ping_class, 0, 0, oui, srcport) < 0)
233			IBEXIT("can't serve class %d on this port",
234				ping_class);
235
236		get_host_and_domain(host_and_domain, sizeof host_and_domain);
237
238		if ((err = ibping_serv()))
239			IBEXIT("ibping to %s: %s", portid2str(&portid), err);
240		exit(0);
241	}
242
243	if (mad_register_client_via(ping_class, 0, srcport) < 0)
244		IBEXIT("can't register ping class %d on this port",
245			ping_class);
246
247	if (with_grh && ibd_dest_type != IB_DEST_LID)
248		IBEXIT("When using GRH, LID should be provided");
249	if (resolve_portid_str(ibd_ca, ibd_ca_port, &portid, argv[0],
250			       ibd_dest_type, ibd_sm_id, srcport) < 0)
251		IBEXIT("can't resolve destination port %s", argv[0]);
252
253	if (with_grh) {
254		portid.grh_present = 1;
255		memcpy(&portid.gid, &dgid, sizeof(portid.gid));
256	}
257
258	signal(SIGINT, report);
259	signal(SIGTERM, report);
260
261	start = cl_get_time_stamp();
262
263	while (count-- > 0) {
264		ntrans++;
265		if ((rtt = ibping(&portid, flood)) == ~0ull) {
266			DEBUG("ibping to %s failed", portid2str(&portid));
267			lost++;
268		} else {
269			if (rtt < minrtt)
270				minrtt = rtt;
271			if (rtt > maxrtt)
272				maxrtt = rtt;
273			total_rtt += rtt;
274			replied++;
275		}
276
277		if (!flood)
278			sleep(1);
279	}
280
281	report(0);
282
283	mad_rpc_close_port(srcport);
284
285	exit(-1);
286}
287