1/*-
2 * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 *
29 * $FreeBSD$
30 */
31
32#include "opt_ah.h"
33
34/*
35 * ath statistics class.
36 */
37#include <sys/types.h>
38#include <sys/file.h>
39#include <sys/sockio.h>
40#include <sys/socket.h>
41#include <net/if.h>
42#include <net/if_media.h>
43#include <net/if_var.h>
44
45#include <stdio.h>
46#include <stdlib.h>
47#include <signal.h>
48#include <string.h>
49#include <unistd.h>
50#include <err.h>
51
52#include "ah.h"
53#include "ah_desc.h"
54#include "net80211/ieee80211_ioctl.h"
55#include "net80211/ieee80211_radiotap.h"
56#include "if_athioctl.h"
57
58#include "athaggrstats.h"
59
60#define	NOTPRESENT	{ 0, "", "" }
61
62#define	AFTER(prev)	((prev)+1)
63
64static const struct fmt athaggrstats[] = {
65
66#define	S_SINGLE_PKT	0
67	{ 4,	"singlepkt",	"spkt",	"single frames scheduled" },
68#define	S_NONBAW_PKT			AFTER(S_SINGLE_PKT)
69	{ 5,	"nonbawpkt",	"nbpkt",	"frames outside of the BAW" },
70#define	S_AGGR_PKT			AFTER(S_NONBAW_PKT)
71	{ 6,	"aggrpkt",	"aggpkt",	"aggregate frames scheduled" },
72#define	S_BAW_CLOSED_SINGLE_PKT		AFTER(S_AGGR_PKT)
73	{ 8,	"bawclosedpkt",	"bawclpkt",	"single frames due to closed BAW" },
74#define	S_LOW_HWQ_SINGLE_PKT		AFTER(S_BAW_CLOSED_SINGLE_PKT)
75	{ 6,	"lhsinglepkt",	"lhspkt",	"single frames scheduled due to low HWQ depth" },
76#define	S_SCHED_NOPKT			AFTER(S_LOW_HWQ_SINGLE_PKT)
77	{ 6,	"schednopkt",	"snopkt",	"sched called with no frames" },
78#define	S_RTS_AGGR_LIMITED		AFTER(S_SCHED_NOPKT)
79	{ 8,	"rtsaggrlimit",	"rtslimit",	"RTS limited aggregates" },
80#define	S_PKT0				AFTER(S_RTS_AGGR_LIMITED)
81	{ 2,	"p0",	"p0",	"" },
82#define	S_PKT1				AFTER(S_PKT0)
83	{ 2,	"p1",	"p1",	"" },
84#define	S_PKT2				AFTER(S_PKT1)
85	{ 2,	"p2",	"p2",	"" },
86#define	S_PKT3				AFTER(S_PKT2)
87	{ 2,	"p3",	"p3",	"" },
88#define	S_PKT4				AFTER(S_PKT3)
89	{ 2,	"p4",	"p4",	"" },
90#define	S_PKT5				AFTER(S_PKT4)
91	{ 2,	"p5",	"p5",	"" },
92#define	S_PKT6				AFTER(S_PKT5)
93	{ 2,	"p6",	"p6",	"" },
94#define	S_PKT7				AFTER(S_PKT6)
95	{ 2,	"p7",	"p7",	"" },
96#define	S_PKT8				AFTER(S_PKT7)
97	{ 2,	"p8",	"p8",	"" },
98#define	S_PKT9				AFTER(S_PKT8)
99	{ 2,	"p9",	"p9",	"" },
100#define	S_PKT10				AFTER(S_PKT9)
101	{ 3,	"p10",	"p10",	"" },
102#define	S_PKT11				AFTER(S_PKT10)
103	{ 3,	"p11",	"p11",	"" },
104#define	S_PKT12				AFTER(S_PKT11)
105	{ 3,	"p12",	"p12",	"" },
106#define	S_PKT13				AFTER(S_PKT12)
107	{ 3,	"p13",	"p13",	"" },
108#define	S_PKT14				AFTER(S_PKT13)
109	{ 3,	"p14",	"p14",	"" },
110#define	S_PKT15				AFTER(S_PKT14)
111	{ 3,	"p15",	"p15",	"" },
112#define	S_PKT16				AFTER(S_PKT15)
113	{ 3,	"p16",	"p16",	"" },
114#define	S_PKT17				AFTER(S_PKT16)
115	{ 3,	"p17",	"p17",	"" },
116#define	S_PKT18				AFTER(S_PKT17)
117	{ 3,	"p18",	"p18",	"" },
118#define	S_PKT19				AFTER(S_PKT18)
119	{ 3,	"p19",	"p19",	"" },
120#define	S_PKT20				AFTER(S_PKT19)
121	{ 3,	"p20",	"p20",	"" },
122#define	S_PKT21				AFTER(S_PKT20)
123	{ 3,	"p21",	"p21",	"" },
124#define	S_PKT22				AFTER(S_PKT21)
125	{ 3,	"p22",	"p22",	"" },
126#define	S_PKT23				AFTER(S_PKT22)
127	{ 3,	"p23",	"p23",	"" },
128#define	S_PKT24				AFTER(S_PKT23)
129	{ 3,	"p24",	"p24",	"" },
130#define	S_PKT25				AFTER(S_PKT24)
131	{ 3,	"p25",	"p25",	"" },
132#define	S_PKT26				AFTER(S_PKT25)
133	{ 3,	"p26",	"p26",	"" },
134#define	S_PKT27				AFTER(S_PKT26)
135	{ 3,	"p27",	"p27",	"" },
136#define	S_PKT28				AFTER(S_PKT27)
137	{ 3,	"p28",	"p28",	"" },
138#define	S_PKT29				AFTER(S_PKT28)
139	{ 3,	"p29",	"p29",	"" },
140#define	S_PKT30				AFTER(S_PKT29)
141	{ 3,	"p30",	"p30",	"" },
142#define	S_PKT31				AFTER(S_PKT30)
143	{ 3,	"p31",	"p31",	"" },
144};
145
146#define	S_LAST		S_RTS_AGGR_LIMITED
147#define	S_MAX		(S_PKT31 + 1)
148
149struct athaggrstatfoo_p {
150	struct athaggrstatfoo base;
151	int s;
152	int optstats;
153	struct ifreq ifr;
154	struct ath_diag atd;
155	struct ath_tx_aggr_stats cur;
156	struct ath_tx_aggr_stats total;
157};
158
159static void
160ath_setifname(struct athaggrstatfoo *wf0, const char *ifname)
161{
162	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) wf0;
163
164	strncpy(wf->ifr.ifr_name, ifname, sizeof (wf->ifr.ifr_name));
165}
166
167static void
168ath_zerostats(struct athaggrstatfoo *wf0)
169{
170#if 0
171	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) wf0;
172
173	if (ioctl(wf->s, SIOCZATHSTATS, &wf->ifr) < 0)
174		err(-1, wf->ifr.ifr_name);
175#endif
176}
177
178static void
179ath_collect(struct athaggrstatfoo_p *wf, struct ath_tx_aggr_stats *stats)
180{
181	wf->ifr.ifr_data = (caddr_t) stats;
182	if (ioctl(wf->s, SIOCGATHAGSTATS, &wf->ifr) < 0)
183		err(1, wf->ifr.ifr_name);
184}
185
186static void
187ath_collect_cur(struct statfoo *sf)
188{
189	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
190
191	ath_collect(wf, &wf->cur);
192}
193
194static void
195ath_collect_tot(struct statfoo *sf)
196{
197	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
198
199	ath_collect(wf, &wf->total);
200}
201
202static void
203ath_update_tot(struct statfoo *sf)
204{
205	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
206
207	wf->total = wf->cur;
208}
209
210static void
211snprintrate(char b[], size_t bs, int rate)
212{
213	if (rate & IEEE80211_RATE_MCS)
214		snprintf(b, bs, "MCS%u", rate &~ IEEE80211_RATE_MCS);
215	else if (rate & 1)
216		snprintf(b, bs, "%u.5M", rate / 2);
217	else
218		snprintf(b, bs, "%uM", rate / 2);
219}
220
221static int
222ath_get_curstat(struct statfoo *sf, int s, char b[], size_t bs)
223{
224	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
225#define	STAT(x) \
226	snprintf(b, bs, "%u", wf->cur.aggr_##x - wf->total.aggr_##x); return 1
227#define	PKT(x) \
228	snprintf(b, bs, "%u", wf->cur.aggr_pkts[x] - wf->total.aggr_pkts[x]); return 1
229
230	switch (s) {
231	case S_SINGLE_PKT:		STAT(single_pkt);
232	case S_NONBAW_PKT:		STAT(nonbaw_pkt);
233	case S_AGGR_PKT:		STAT(aggr_pkt);
234	case S_BAW_CLOSED_SINGLE_PKT:	STAT(baw_closed_single_pkt);
235	case S_LOW_HWQ_SINGLE_PKT:	STAT(low_hwq_single_pkt);
236	case S_SCHED_NOPKT:		STAT(sched_nopkt);
237	case S_RTS_AGGR_LIMITED:	STAT(rts_aggr_limited);
238	case S_PKT0:			PKT(0);
239	case S_PKT1:			PKT(1);
240	case S_PKT2:			PKT(2);
241	case S_PKT3:			PKT(3);
242	case S_PKT4:			PKT(4);
243	case S_PKT5:			PKT(5);
244	case S_PKT6:			PKT(6);
245	case S_PKT7:			PKT(7);
246	case S_PKT8:			PKT(8);
247	case S_PKT9:			PKT(9);
248	case S_PKT10:			PKT(10);
249	case S_PKT11:			PKT(11);
250	case S_PKT12:			PKT(12);
251	case S_PKT13:			PKT(13);
252	case S_PKT14:			PKT(14);
253	case S_PKT15:			PKT(15);
254	case S_PKT16:			PKT(16);
255	case S_PKT17:			PKT(17);
256	case S_PKT18:			PKT(18);
257	case S_PKT19:			PKT(19);
258	case S_PKT20:			PKT(20);
259	case S_PKT21:			PKT(21);
260	case S_PKT22:			PKT(22);
261	case S_PKT23:			PKT(23);
262	case S_PKT24:			PKT(24);
263	case S_PKT25:			PKT(25);
264	case S_PKT26:			PKT(26);
265	case S_PKT27:			PKT(27);
266	case S_PKT28:			PKT(28);
267	case S_PKT29:			PKT(29);
268	case S_PKT30:			PKT(30);
269	case S_PKT31:			PKT(31);
270	}
271	b[0] = '\0';
272	return 0;
273#undef PKT
274#undef STAT
275}
276
277static int
278ath_get_totstat(struct statfoo *sf, int s, char b[], size_t bs)
279{
280	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
281#define	STAT(x) \
282	snprintf(b, bs, "%u", wf->total.aggr_##x); return 1
283#define	PKT(x) \
284	snprintf(b, bs, "%u", wf->total.aggr_pkts[x]); return 1
285
286	switch (s) {
287	case S_SINGLE_PKT:		STAT(single_pkt);
288	case S_NONBAW_PKT:		STAT(nonbaw_pkt);
289	case S_AGGR_PKT:		STAT(aggr_pkt);
290	case S_BAW_CLOSED_SINGLE_PKT:	STAT(baw_closed_single_pkt);
291	case S_LOW_HWQ_SINGLE_PKT:	STAT(low_hwq_single_pkt);
292	case S_SCHED_NOPKT:		STAT(sched_nopkt);
293	case S_RTS_AGGR_LIMITED:	STAT(rts_aggr_limited);
294	case S_PKT0:			PKT(0);
295	case S_PKT1:			PKT(1);
296	case S_PKT2:			PKT(2);
297	case S_PKT3:			PKT(3);
298	case S_PKT4:			PKT(4);
299	case S_PKT5:			PKT(5);
300	case S_PKT6:			PKT(6);
301	case S_PKT7:			PKT(7);
302	case S_PKT8:			PKT(8);
303	case S_PKT9:			PKT(9);
304	case S_PKT10:			PKT(10);
305	case S_PKT11:			PKT(11);
306	case S_PKT12:			PKT(12);
307	case S_PKT13:			PKT(13);
308	case S_PKT14:			PKT(14);
309	case S_PKT15:			PKT(15);
310	case S_PKT16:			PKT(16);
311	case S_PKT17:			PKT(17);
312	case S_PKT18:			PKT(18);
313	case S_PKT19:			PKT(19);
314	case S_PKT20:			PKT(20);
315	case S_PKT21:			PKT(21);
316	case S_PKT22:			PKT(22);
317	case S_PKT23:			PKT(23);
318	case S_PKT24:			PKT(24);
319	case S_PKT25:			PKT(25);
320	case S_PKT26:			PKT(26);
321	case S_PKT27:			PKT(27);
322	case S_PKT28:			PKT(28);
323	case S_PKT29:			PKT(29);
324	case S_PKT30:			PKT(30);
325	case S_PKT31:			PKT(31);
326	}
327	b[0] = '\0';
328	return 0;
329#undef PKT
330#undef STAT
331}
332
333static void
334ath_print_verbose(struct statfoo *sf, FILE *fd)
335{
336	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
337	const struct fmt *f;
338	char s[32];
339	const char *indent;
340	int i, width;
341
342	width = 0;
343	for (i = 0; i < S_LAST; i++) {
344		f = &sf->stats[i];
345		if (f->width > width)
346			width = f->width;
347	}
348	for (i = 0; i < S_LAST; i++) {
349		if (ath_get_totstat(sf, i, s, sizeof(s)) && strcmp(s, "0")) {
350			indent = "";
351			fprintf(fd, "%s%-*s %s\n", indent, width, s,
352			    athaggrstats[i].desc);
353		}
354	}
355
356	fprintf(fd, "\nAggregate size profile:\n\n");
357	for (i = 0; i < 64; i++) {
358		fprintf(fd, "%2d: %12u%s",
359		    i,
360		    wf->total.aggr_pkts[i],
361		    (i % 4 == 3) ? "\n" : " ");
362	}
363	fprintf(fd, "\n");
364}
365
366STATFOO_DEFINE_BOUNCE(athaggrstatfoo)
367
368struct athaggrstatfoo *
369athaggrstats_new(const char *ifname, const char *fmtstring)
370{
371#define	N(a)	(sizeof(a) / sizeof(a[0]))
372	struct athaggrstatfoo_p *wf;
373
374	wf = calloc(1, sizeof(struct athaggrstatfoo_p));
375	if (wf != NULL) {
376		statfoo_init(&wf->base.base, "athaggrstats",
377		    athaggrstats, N(athaggrstats));
378		/* override base methods */
379		wf->base.base.collect_cur = ath_collect_cur;
380		wf->base.base.collect_tot = ath_collect_tot;
381		wf->base.base.get_curstat = ath_get_curstat;
382		wf->base.base.get_totstat = ath_get_totstat;
383		wf->base.base.update_tot = ath_update_tot;
384		wf->base.base.print_verbose = ath_print_verbose;
385
386		/* setup bounce functions for public methods */
387		STATFOO_BOUNCE(wf, athaggrstatfoo);
388
389		/* setup our public methods */
390		wf->base.setifname = ath_setifname;
391#if 0
392		wf->base.setstamac = wlan_setstamac;
393#endif
394		wf->base.zerostats = ath_zerostats;
395		wf->s = socket(AF_INET, SOCK_DGRAM, 0);
396		if (wf->s < 0)
397			err(1, "socket");
398
399		ath_setifname(&wf->base, ifname);
400		wf->base.setfmt(&wf->base, fmtstring);
401	}
402	return &wf->base;
403#undef N
404}
405