1/*
2 * Copyright (c) 2004-2008 Voltaire Inc.  All rights reserved.
3 * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 *
33 */
34
35#if HAVE_CONFIG_H
36#  include <config.h>
37#endif /* HAVE_CONFIG_H */
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <unistd.h>
42#include <stdarg.h>
43#include <getopt.h>
44#include <netinet/in.h>
45
46#include <infiniband/common.h>
47#include <infiniband/umad.h>
48#include <infiniband/mad.h>
49
50#include "ibdiag_common.h"
51
52struct perf_count {
53	uint32_t portselect;
54	uint32_t counterselect;
55	uint32_t symbolerrors;
56	uint32_t linkrecovers;
57	uint32_t linkdowned;
58	uint32_t rcverrors;
59	uint32_t rcvremotephyerrors;
60	uint32_t rcvswrelayerrors;
61	uint32_t xmtdiscards;
62	uint32_t xmtconstrainterrors;
63	uint32_t rcvconstrainterrors;
64	uint32_t linkintegrityerrors;
65	uint32_t excbufoverrunerrors;
66	uint32_t vl15dropped;
67	uint32_t xmtdata;
68	uint32_t rcvdata;
69	uint32_t xmtpkts;
70	uint32_t rcvpkts;
71};
72
73struct perf_count_ext {
74	uint32_t portselect;
75	uint32_t counterselect;
76	uint64_t portxmitdata;
77	uint64_t portrcvdata;
78	uint64_t portxmitpkts;
79	uint64_t portrcvpkts;
80	uint64_t portunicastxmitpkts;
81	uint64_t portunicastrcvpkts;
82	uint64_t portmulticastxmitpkits;
83	uint64_t portmulticastrcvpkts;
84};
85
86static uint8_t pc[1024];
87
88struct perf_count perf_count = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
89struct perf_count_ext perf_count_ext = {0,0,0,0,0,0,0,0,0,0};
90
91char *argv0 = "perfquery";
92
93#define ALL_PORTS 0xFF
94
95static void
96usage(void)
97{
98	char *basename;
99
100	if (!(basename = strrchr(argv0, '/')))
101		basename = argv0;
102	else
103		basename++;
104
105	fprintf(stderr, "Usage: %s [-d(ebug) -G(uid) -a(ll_ports) -l(oop_ports) -r(eset_after_read) -C ca_name -P ca_port "
106			"-R(eset_only) -t(imeout) timeout_ms -V(ersion) -h(elp)] [<lid|guid> [[port] [reset_mask]]]\n",
107			basename);
108	fprintf(stderr, "\tExamples:\n");
109	fprintf(stderr, "\t\t%s\t\t# read local port's performance counters\n", basename);
110	fprintf(stderr, "\t\t%s 32 1\t\t# read performance counters from lid 32, port 1\n", basename);
111	fprintf(stderr, "\t\t%s -e 32 1\t# read extended performance counters from lid 32, port 1\n", basename);
112	fprintf(stderr, "\t\t%s -a 32\t\t# read performance counters from lid 32, all ports\n", basename);
113	fprintf(stderr, "\t\t%s -r 32 1\t# read performance counters and reset\n", basename);
114	fprintf(stderr, "\t\t%s -e -r 32 1\t# read extended performance counters and reset\n", basename);
115	fprintf(stderr, "\t\t%s -R 0x20 1\t# reset performance counters of port 1 only\n", basename);
116	fprintf(stderr, "\t\t%s -e -R 0x20 1\t# reset extended performance counters of port 1 only\n", basename);
117	fprintf(stderr, "\t\t%s -R -a 32\t# reset performance counters of all ports\n", basename);
118	fprintf(stderr, "\t\t%s -R 32 2 0x0fff\t# reset only error counters of port 2\n", basename);
119	fprintf(stderr, "\t\t%s -R 32 2 0xf000\t# reset only non-error counters of port 2\n", basename);
120	exit(-1);
121}
122
123/* Notes: IB semantics is to cap counters if count has exceeded limits.
124 * Therefore we must check for overflows and cap the counters if necessary.
125 *
126 * mad_decode_field and mad_encode_field assume 32 bit integers passed in
127 * for fields < 32 bits in length.
128 */
129
130static void aggregate_4bit(uint32_t *dest, uint32_t val)
131{
132	if ((((*dest) + val) < (*dest))
133	    || ((*dest) + val) > 0xf)
134		(*dest) = 0xf;
135	else
136		(*dest) = (*dest) + val;
137}
138
139static void aggregate_8bit(uint32_t *dest, uint32_t val)
140{
141	if ((((*dest) + val) < (*dest))
142	    || ((*dest) + val) > 0xff)
143		(*dest) = 0xff;
144	else
145		(*dest) = (*dest) + val;
146}
147
148static void aggregate_16bit(uint32_t *dest, uint32_t val)
149{
150	if ((((*dest) + val) < (*dest))
151	    || ((*dest) + val) > 0xffff)
152		(*dest) = 0xffff;
153	else
154		(*dest) = (*dest) + val;
155}
156
157static void aggregate_32bit(uint32_t *dest, uint32_t val)
158{
159	if (((*dest) + val) < (*dest))
160		(*dest) = 0xffffffff;
161	else
162		(*dest) = (*dest) + val;
163}
164
165static void aggregate_64bit(uint64_t *dest, uint64_t val)
166{
167	if (((*dest) + val) < (*dest))
168		(*dest) = 0xffffffffffffffffULL;
169	else
170		(*dest) = (*dest) + val;
171}
172
173static void aggregate_perfcounters(void)
174{
175	uint32_t val;
176
177	mad_decode_field(pc, IB_PC_PORT_SELECT_F, &val);
178	perf_count.portselect = val;
179        mad_decode_field(pc, IB_PC_COUNTER_SELECT_F, &val);
180	perf_count.counterselect = val;
181        mad_decode_field(pc, IB_PC_ERR_SYM_F, &val);
182	aggregate_16bit(&perf_count.symbolerrors, val);
183        mad_decode_field(pc, IB_PC_LINK_RECOVERS_F, &val);
184	aggregate_8bit(&perf_count.linkrecovers, val);
185        mad_decode_field(pc, IB_PC_LINK_DOWNED_F, &val);
186	aggregate_8bit(&perf_count.linkdowned, val);
187        mad_decode_field(pc, IB_PC_ERR_RCV_F, &val);
188	aggregate_16bit(&perf_count.rcverrors, val);
189        mad_decode_field(pc, IB_PC_ERR_PHYSRCV_F, &val);
190	aggregate_16bit(&perf_count.rcvremotephyerrors, val);
191        mad_decode_field(pc, IB_PC_ERR_SWITCH_REL_F, &val);
192	aggregate_16bit(&perf_count.rcvswrelayerrors, val);
193        mad_decode_field(pc, IB_PC_XMT_DISCARDS_F, &val);
194	aggregate_16bit(&perf_count.xmtdiscards, val);
195        mad_decode_field(pc, IB_PC_ERR_XMTCONSTR_F, &val);
196	aggregate_8bit(&perf_count.xmtconstrainterrors, val);
197        mad_decode_field(pc, IB_PC_ERR_RCVCONSTR_F, &val);
198	aggregate_8bit(&perf_count.rcvconstrainterrors, val);
199        mad_decode_field(pc, IB_PC_ERR_LOCALINTEG_F, &val);
200	aggregate_4bit(&perf_count.linkintegrityerrors, val);
201        mad_decode_field(pc, IB_PC_ERR_EXCESS_OVR_F, &val);
202	aggregate_4bit(&perf_count.excbufoverrunerrors, val);
203        mad_decode_field(pc, IB_PC_VL15_DROPPED_F, &val);
204	aggregate_16bit(&perf_count.vl15dropped, val);
205        mad_decode_field(pc, IB_PC_XMT_BYTES_F, &val);
206	aggregate_32bit(&perf_count.xmtdata, val);
207        mad_decode_field(pc, IB_PC_RCV_BYTES_F, &val);
208	aggregate_32bit(&perf_count.rcvdata, val);
209        mad_decode_field(pc, IB_PC_XMT_PKTS_F, &val);
210	aggregate_32bit(&perf_count.xmtpkts, val);
211        mad_decode_field(pc, IB_PC_RCV_PKTS_F, &val);
212	aggregate_32bit(&perf_count.rcvpkts, val);
213}
214
215static void output_aggregate_perfcounters(ib_portid_t *portid)
216{
217	char buf[1024];
218	uint32_t val = ALL_PORTS;
219
220	/* set port_select to 255 to emulate AllPortSelect */
221	mad_encode_field(pc, IB_PC_PORT_SELECT_F, &val);
222	mad_encode_field(pc, IB_PC_COUNTER_SELECT_F, &perf_count.counterselect);
223	mad_encode_field(pc, IB_PC_ERR_SYM_F, &perf_count.symbolerrors);
224	mad_encode_field(pc, IB_PC_LINK_RECOVERS_F, &perf_count.linkrecovers);
225	mad_encode_field(pc, IB_PC_LINK_DOWNED_F, &perf_count.linkdowned);
226	mad_encode_field(pc, IB_PC_ERR_RCV_F, &perf_count.rcverrors);
227	mad_encode_field(pc, IB_PC_ERR_PHYSRCV_F, &perf_count.rcvremotephyerrors);
228	mad_encode_field(pc, IB_PC_ERR_SWITCH_REL_F, &perf_count.rcvswrelayerrors);
229	mad_encode_field(pc, IB_PC_XMT_DISCARDS_F, &perf_count.xmtdiscards);
230	mad_encode_field(pc, IB_PC_ERR_XMTCONSTR_F, &perf_count.xmtconstrainterrors);
231	mad_encode_field(pc, IB_PC_ERR_RCVCONSTR_F, &perf_count.rcvconstrainterrors);
232	mad_encode_field(pc, IB_PC_ERR_LOCALINTEG_F, &perf_count.linkintegrityerrors);
233	mad_encode_field(pc, IB_PC_ERR_EXCESS_OVR_F, &perf_count.excbufoverrunerrors);
234	mad_encode_field(pc, IB_PC_VL15_DROPPED_F, &perf_count.vl15dropped);
235	mad_encode_field(pc, IB_PC_XMT_BYTES_F, &perf_count.xmtdata);
236	mad_encode_field(pc, IB_PC_RCV_BYTES_F, &perf_count.rcvdata);
237	mad_encode_field(pc, IB_PC_XMT_PKTS_F, &perf_count.xmtpkts);
238	mad_encode_field(pc, IB_PC_RCV_PKTS_F, &perf_count.rcvpkts);
239
240	mad_dump_perfcounters(buf, sizeof buf, pc, sizeof pc);
241
242	printf("# Port counters: %s port %d\n%s", portid2str(portid), ALL_PORTS, buf);
243}
244
245static void aggregate_perfcounters_ext(void)
246{
247	uint32_t val;
248	uint64_t val64;
249
250	mad_decode_field(pc, IB_PC_EXT_PORT_SELECT_F, &val);
251	perf_count_ext.portselect = val;
252	mad_decode_field(pc, IB_PC_EXT_COUNTER_SELECT_F, &val);
253	perf_count_ext.counterselect = val;
254	mad_decode_field(pc, IB_PC_EXT_XMT_BYTES_F, &val64);
255	aggregate_64bit(&perf_count_ext.portxmitdata, val64);
256	mad_decode_field(pc, IB_PC_EXT_RCV_BYTES_F, &val64);
257	aggregate_64bit(&perf_count_ext.portrcvdata, val64);
258	mad_decode_field(pc, IB_PC_EXT_XMT_PKTS_F, &val64);
259	aggregate_64bit(&perf_count_ext.portxmitpkts, val64);
260	mad_decode_field(pc, IB_PC_EXT_RCV_PKTS_F, &val64);
261	aggregate_64bit(&perf_count_ext.portrcvpkts, val64);
262	mad_decode_field(pc, IB_PC_EXT_XMT_UPKTS_F, &val64);
263	aggregate_64bit(&perf_count_ext.portunicastxmitpkts, val64);
264	mad_decode_field(pc, IB_PC_EXT_RCV_UPKTS_F, &val64);
265	aggregate_64bit(&perf_count_ext.portunicastrcvpkts, val64);
266	mad_decode_field(pc, IB_PC_EXT_XMT_MPKTS_F, &val64);
267	aggregate_64bit(&perf_count_ext.portmulticastxmitpkits, val64);
268	mad_decode_field(pc, IB_PC_EXT_RCV_MPKTS_F, &val64);
269	aggregate_64bit(&perf_count_ext.portmulticastrcvpkts, val64);
270}
271
272static void output_aggregate_perfcounters_ext(ib_portid_t *portid)
273{
274	char buf[1024];
275	uint32_t val = ALL_PORTS;
276
277        /* set port_select to 255 to emulate AllPortSelect */
278        mad_encode_field(pc, IB_PC_EXT_PORT_SELECT_F, &val);
279	mad_encode_field(pc, IB_PC_EXT_COUNTER_SELECT_F, &perf_count_ext.counterselect);
280	mad_encode_field(pc, IB_PC_EXT_XMT_BYTES_F, &perf_count_ext.portxmitdata);
281	mad_encode_field(pc, IB_PC_EXT_RCV_BYTES_F, &perf_count_ext.portrcvdata);
282	mad_encode_field(pc, IB_PC_EXT_XMT_PKTS_F, &perf_count_ext.portxmitpkts);
283	mad_encode_field(pc, IB_PC_EXT_RCV_PKTS_F, &perf_count_ext.portrcvpkts);
284	mad_encode_field(pc, IB_PC_EXT_XMT_UPKTS_F, &perf_count_ext.portunicastxmitpkts);
285	mad_encode_field(pc, IB_PC_EXT_RCV_UPKTS_F, &perf_count_ext.portunicastrcvpkts);
286	mad_encode_field(pc, IB_PC_EXT_XMT_MPKTS_F, &perf_count_ext.portmulticastxmitpkits);
287	mad_encode_field(pc, IB_PC_EXT_RCV_MPKTS_F, &perf_count_ext.portmulticastrcvpkts);
288
289	mad_dump_perfcounters_ext(buf, sizeof buf, pc, sizeof pc);
290
291	printf("# Port counters: %s port %d\n%s", portid2str(portid), ALL_PORTS, buf);
292}
293
294static void dump_perfcounters(int extended, int timeout, uint16_t cap_mask, ib_portid_t *portid,
295			      int port, int aggregate)
296{
297	char buf[1024];
298
299	if (extended != 1) {
300		if (!port_performance_query(pc, portid, port, timeout))
301			IBERROR("perfquery");
302		if (aggregate)
303			aggregate_perfcounters();
304		else
305			mad_dump_perfcounters(buf, sizeof buf, pc, sizeof pc);
306	} else {
307		if (!(cap_mask & 0x200)) /* 1.2 errata: bit 9 is extended counter support */
308			IBWARN("PerfMgt ClassPortInfo 0x%x extended counters not indicated\n", cap_mask);
309
310		if (!port_performance_ext_query(pc, portid, port, timeout))
311			IBERROR("perfextquery");
312		if (aggregate)
313			aggregate_perfcounters_ext();
314		else
315			mad_dump_perfcounters_ext(buf, sizeof buf, pc, sizeof pc);
316	}
317
318	if (!aggregate)
319		printf("# Port counters: %s port %d\n%s", portid2str(portid), port, buf);
320}
321
322static void reset_counters(int extended, int timeout, int mask, ib_portid_t *portid, int port)
323{
324	if (extended != 1) {
325		if (!port_performance_reset(pc, portid, port, mask, timeout))
326			IBERROR("perf reset");
327	} else {
328		if (!port_performance_ext_reset(pc, portid, port, mask, timeout))
329			IBERROR("perf ext reset");
330	}
331}
332
333int
334main(int argc, char **argv)
335{
336	int mgmt_classes[4] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS, IB_PERFORMANCE_CLASS};
337	ib_portid_t *sm_id = 0, sm_portid = {0};
338	ib_portid_t portid = {0};
339	extern int ibdebug;
340	int dest_type = IB_DEST_LID;
341	int timeout = 0;	/* use default */
342	int mask = 0xffff, all_ports = 0;
343	int reset = 0, reset_only = 0;
344	int port = 0;
345	int udebug = 0;
346	char *ca = 0;
347	int ca_port = 0;
348	int extended = 0;
349	uint16_t cap_mask;
350	int all_ports_loop = 0;
351	int loop_ports = 0;
352	int node_type, num_ports = 0;
353	uint8_t data[IB_SMP_DATA_SIZE];
354	int start_port = 1;
355	int enhancedport0;
356	int i;
357
358	static char const str_opts[] = "C:P:s:t:dGealrRVhu";
359	static const struct option long_opts[] = {
360		{ "C", 1, 0, 'C'},
361		{ "P", 1, 0, 'P'},
362		{ "debug", 0, 0, 'd'},
363		{ "Guid", 0, 0, 'G'},
364		{ "extended", 0, 0, 'e'},
365		{ "all_ports", 0, 0, 'a'},
366		{ "loop_ports", 0, 0, 'l'},
367		{ "reset_after_read", 0, 0, 'r'},
368		{ "Reset_only", 0, 0, 'R'},
369		{ "sm_portid", 1, 0, 's'},
370		{ "timeout", 1, 0, 't'},
371		{ "Version", 0, 0, 'V'},
372		{ "help", 0, 0, 'h'},
373		{ "usage", 0, 0, 'u'},
374		{ }
375	};
376
377	argv0 = argv[0];
378
379	while (1) {
380		int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);
381		if ( ch == -1 )
382			break;
383		switch(ch) {
384		case 'C':
385			ca = optarg;
386			break;
387		case 'P':
388			ca_port = strtoul(optarg, 0, 0);
389			break;
390		case 'e':
391			extended = 1;
392			break;
393		case 'a':
394			all_ports++;
395			port = ALL_PORTS;
396			break;
397		case 'l':
398			loop_ports++;
399			break;
400		case 'd':
401			ibdebug++;
402			madrpc_show_errors(1);
403			umad_debug(udebug);
404			udebug++;
405			break;
406		case 'G':
407			dest_type = IB_DEST_GUID;
408			break;
409		case 's':
410			if (ib_resolve_portid_str(&sm_portid, optarg, IB_DEST_LID, 0) < 0)
411				IBERROR("can't resolve SM destination port %s", optarg);
412			sm_id = &sm_portid;
413			break;
414		case 'r':
415			reset++;
416			break;
417		case 'R':
418			reset_only++;
419			break;
420		case 't':
421			timeout = strtoul(optarg, 0, 0);
422			madrpc_set_timeout(timeout);
423			break;
424		case 'V':
425			fprintf(stderr, "%s %s\n", argv0, get_build_version() );
426			exit(-1);
427		default:
428			usage();
429			break;
430		}
431	}
432	argc -= optind;
433	argv += optind;
434
435	if (argc > 1)
436		port = strtoul(argv[1], 0, 0);
437	if (argc > 2)
438		mask = strtoul(argv[2], 0, 0);
439
440	madrpc_init(ca, ca_port, mgmt_classes, 4);
441
442	if (argc) {
443		if (ib_resolve_portid_str(&portid, argv[0], dest_type, sm_id) < 0)
444			IBERROR("can't resolve destination port %s", argv[0]);
445	} else {
446		if (ib_resolve_self(&portid, &port, 0) < 0)
447			IBERROR("can't resolve self port %s", argv[0]);
448	}
449
450	/* PerfMgt ClassPortInfo is a required attribute */
451	if (!perf_classportinfo_query(pc, &portid, port, timeout))
452		IBERROR("classportinfo query");
453	/* ClassPortInfo should be supported as part of libibmad */
454	memcpy(&cap_mask, pc+2, sizeof(cap_mask));	/* CapabilityMask */
455	cap_mask = ntohs(cap_mask);
456	if (!(cap_mask & 0x100)) { /* bit 8 is AllPortSelect */
457		if (!all_ports && port == ALL_PORTS)
458			IBERROR("AllPortSelect not supported");
459		if (all_ports)
460			all_ports_loop = 1;
461	}
462
463	if (all_ports_loop || (loop_ports && (all_ports || port == ALL_PORTS))) {
464		if (smp_query(data, &portid, IB_ATTR_NODE_INFO, 0, 0) < 0)
465			IBERROR("smp query nodeinfo failed");
466		node_type = mad_get_field(data, 0, IB_NODE_TYPE_F);
467		mad_decode_field(data, IB_NODE_NPORTS_F, &num_ports);
468		if (!num_ports)
469			IBERROR("smp query nodeinfo: num ports invalid");
470
471		if (node_type == IB_NODE_SWITCH) {
472			if (smp_query(data, &portid, IB_ATTR_SWITCH_INFO, 0, 0) < 0)
473				IBERROR("smp query nodeinfo failed");
474			enhancedport0 = mad_get_field(data, 0, IB_SW_ENHANCED_PORT0_F);
475			if (enhancedport0)
476				start_port = 0;
477		}
478		if (all_ports_loop && !loop_ports)
479			IBWARN("Emulating AllPortSelect by iterating through all ports");
480	}
481
482	if (reset_only)
483		goto do_reset;
484
485	if (all_ports_loop || (loop_ports && (all_ports || port == ALL_PORTS))) {
486		for (i = start_port; i <= num_ports; i++)
487			dump_perfcounters(extended, timeout, cap_mask, &portid, i,
488					  (all_ports_loop && !loop_ports));
489		if (all_ports_loop && !loop_ports) {
490			if (extended != 1)
491				output_aggregate_perfcounters(&portid);
492			else
493				output_aggregate_perfcounters_ext(&portid);
494		}
495	}
496	else
497		dump_perfcounters(extended, timeout, cap_mask, &portid, port, 0);
498
499	if (!reset)
500		exit(0);
501
502do_reset:
503
504	if (all_ports_loop || (loop_ports && (all_ports || port == ALL_PORTS))) {
505		for (i = start_port; i <= num_ports; i++)
506			reset_counters(extended, timeout, mask, &portid, i);
507	}
508	else
509		reset_counters(extended, timeout, mask, &portid, port);
510
511	exit(0);
512}
513