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