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 
30 #include <sys/param.h>
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 
64 static 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 
149 struct 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 
159 static void
160 ath_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 
167 static void
168 ath_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 
178 static void
179 ath_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, "%s: ioctl: %s", __func__, wf->ifr.ifr_name);
184 }
185 
186 static void
187 ath_collect_cur(struct bsdstat *sf)
188 {
189 	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
190 
191 	ath_collect(wf, &wf->cur);
192 }
193 
194 static void
195 ath_collect_tot(struct bsdstat *sf)
196 {
197 	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
198 
199 	ath_collect(wf, &wf->total);
200 }
201 
202 static void
203 ath_update_tot(struct bsdstat *sf)
204 {
205 	struct athaggrstatfoo_p *wf = (struct athaggrstatfoo_p *) sf;
206 
207 	wf->total = wf->cur;
208 }
209 
210 static void
211 snprintrate(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 
221 static int
222 ath_get_curstat(struct bsdstat *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 
277 static int
278 ath_get_totstat(struct bsdstat *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 
333 static void
334 ath_print_verbose(struct bsdstat *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 
366 BSDSTAT_DEFINE_BOUNCE(athaggrstatfoo)
367 
368 struct athaggrstatfoo *
369 athaggrstats_new(const char *ifname, const char *fmtstring)
370 {
371 	struct athaggrstatfoo_p *wf;
372 
373 	wf = calloc(1, sizeof(struct athaggrstatfoo_p));
374 	if (wf != NULL) {
375 		bsdstat_init(&wf->base.base, "athaggrstats",
376 		    athaggrstats, nitems(athaggrstats));
377 		/* override base methods */
378 		wf->base.base.collect_cur = ath_collect_cur;
379 		wf->base.base.collect_tot = ath_collect_tot;
380 		wf->base.base.get_curstat = ath_get_curstat;
381 		wf->base.base.get_totstat = ath_get_totstat;
382 		wf->base.base.update_tot = ath_update_tot;
383 		wf->base.base.print_verbose = ath_print_verbose;
384 
385 		/* setup bounce functions for public methods */
386 		BSDSTAT_BOUNCE(wf, athaggrstatfoo);
387 
388 		/* setup our public methods */
389 		wf->base.setifname = ath_setifname;
390 #if 0
391 		wf->base.setstamac = wlan_setstamac;
392 #endif
393 		wf->base.zerostats = ath_zerostats;
394 		wf->s = socket(AF_INET, SOCK_DGRAM, 0);
395 		if (wf->s < 0)
396 			err(1, "socket");
397 
398 		ath_setifname(&wf->base, ifname);
399 		wf->base.setfmt(&wf->base, fmtstring);
400 	}
401 	return &wf->base;
402 }
403