Root/package/madwifi/patches/416-wprobe.patch

1--- /dev/null
2+++ b/ath/ath_wprobe.c
3@@ -0,0 +1,433 @@
4+#include <net80211/ieee80211_node.h>
5+#include <linux/wprobe.h>
6+
7+atomic_t cleanup_tasks = ATOMIC_INIT(0);
8+
9+enum wp_node_val {
10+ WP_NODE_RSSI,
11+ WP_NODE_SIGNAL,
12+ WP_NODE_RX_RATE,
13+ WP_NODE_TX_RATE,
14+ WP_NODE_RETRANSMIT_200,
15+ WP_NODE_RETRANSMIT_400,
16+ WP_NODE_RETRANSMIT_800,
17+ WP_NODE_RETRANSMIT_1600,
18+};
19+
20+enum wp_global_val {
21+ WP_GLOBAL_NOISE,
22+ WP_GLOBAL_PHY_BUSY,
23+ WP_GLOBAL_PHY_RX,
24+ WP_GLOBAL_PHY_TX,
25+ WP_GLOBAL_FRAMES,
26+ WP_GLOBAL_PROBEREQ,
27+};
28+
29+static struct wprobe_item ath_wprobe_globals[] = {
30+ [WP_GLOBAL_NOISE] = {
31+ .name = "noise",
32+ .type = WPROBE_VAL_S16,
33+ .flags = WPROBE_F_KEEPSTAT
34+ },
35+ [WP_GLOBAL_PHY_BUSY] = {
36+ .name = "phy_busy",
37+ .type = WPROBE_VAL_U8,
38+ .flags = WPROBE_F_KEEPSTAT
39+ },
40+ [WP_GLOBAL_PHY_RX] = {
41+ .name = "phy_rx",
42+ .type = WPROBE_VAL_U8,
43+ .flags = WPROBE_F_KEEPSTAT
44+ },
45+ [WP_GLOBAL_PHY_TX] = {
46+ .name = "phy_tx",
47+ .type = WPROBE_VAL_U8,
48+ .flags = WPROBE_F_KEEPSTAT
49+ },
50+ [WP_GLOBAL_FRAMES] = {
51+ .name = "frames",
52+ .type = WPROBE_VAL_U32,
53+ },
54+ [WP_GLOBAL_PROBEREQ] = {
55+ .name = "probereq",
56+ .type = WPROBE_VAL_U32,
57+ },
58+};
59+
60+static struct wprobe_item ath_wprobe_link[] = {
61+ [WP_NODE_RSSI] = {
62+ .name = "rssi",
63+ .type = WPROBE_VAL_U8,
64+ .flags = WPROBE_F_KEEPSTAT
65+ },
66+ [WP_NODE_SIGNAL] = {
67+ .name = "signal",
68+ .type = WPROBE_VAL_S16,
69+ .flags = WPROBE_F_KEEPSTAT
70+ },
71+ [WP_NODE_RX_RATE] = {
72+ .name = "rx_rate",
73+ .type = WPROBE_VAL_U16,
74+ .flags = WPROBE_F_KEEPSTAT
75+ },
76+ [WP_NODE_TX_RATE] = {
77+ .name = "tx_rate",
78+ .type = WPROBE_VAL_U16,
79+ .flags = WPROBE_F_KEEPSTAT
80+ },
81+ [WP_NODE_RETRANSMIT_200] = {
82+ .name = "retransmit_200",
83+ .type = WPROBE_VAL_U8,
84+ .flags = WPROBE_F_KEEPSTAT
85+ },
86+ [WP_NODE_RETRANSMIT_400] = {
87+ .name = "retransmit_400",
88+ .type = WPROBE_VAL_U8,
89+ .flags = WPROBE_F_KEEPSTAT
90+ },
91+ [WP_NODE_RETRANSMIT_800] = {
92+ .name = "retransmit_800",
93+ .type = WPROBE_VAL_U8,
94+ .flags = WPROBE_F_KEEPSTAT
95+ },
96+ [WP_NODE_RETRANSMIT_1600] = {
97+ .name = "retransmit_1600",
98+ .type = WPROBE_VAL_U8,
99+ .flags = WPROBE_F_KEEPSTAT
100+ },
101+};
102+
103+#define AR5K_MIBC 0x0040
104+#define AR5K_MIBC_FREEZE (1 << 1)
105+#define AR5K_TXFC 0x80ec
106+#define AR5K_RXFC 0x80f0
107+#define AR5K_RXCLEAR 0x80f4
108+#define AR5K_CYCLES 0x80f8
109+
110+#define READ_CLR(_ah, _reg) \
111+ ({ u32 __val = OS_REG_READ(_ah, _reg); OS_REG_WRITE(_ah, _reg, 0); __val; })
112+
113+static bool
114+wprobe_disabled(void)
115+{
116+ return (!wprobe_add_iface || IS_ERR(wprobe_add_iface));
117+}
118+
119+static int
120+ath_wprobe_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure)
121+{
122+ struct ath_vap *avp = container_of(dev, struct ath_vap, av_wpif);
123+ struct ieee80211vap *vap = &avp->av_vap;
124+ struct ieee80211com *ic = vap->iv_ic;
125+ struct ath_softc *sc = ic->ic_dev->priv;
126+ struct ath_hal *ah = sc->sc_ah;
127+ u32 cc, busy, rx, tx;
128+ s16 noise;
129+
130+ if (l)
131+ goto out;
132+
133+ OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE);
134+ cc = READ_CLR(ah, AR5K_CYCLES);
135+ busy = READ_CLR(ah, AR5K_RXCLEAR);
136+ rx = READ_CLR(ah, AR5K_RXFC);
137+ tx = READ_CLR(ah, AR5K_TXFC);
138+ OS_REG_WRITE(ah, AR5K_MIBC, 0);
139+ noise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
140+ ic->ic_channoise = noise;
141+
142+ WPROBE_FILL_BEGIN(val, ath_wprobe_globals);
143+ if (cc & 0xf0000000) {
144+ /* scale down if the counters are near max */
145+ cc >>= 8;
146+ busy >>= 8;
147+ rx >>= 8;
148+ tx >>= 8;
149+ }
150+ if (ah->ah_macType < 5212)
151+ goto phy_skip;
152+ if (!cc)
153+ goto phy_skip;
154+ if (busy > cc)
155+ goto phy_skip;
156+ if (rx > cc)
157+ goto phy_skip;
158+ if (tx > cc)
159+ goto phy_skip;
160+ busy = (busy * 100) / cc;
161+ rx = (rx * 100) / cc;
162+ tx = (tx * 100) / cc;
163+ WPROBE_SET(WP_GLOBAL_PHY_BUSY, U8, busy);
164+ WPROBE_SET(WP_GLOBAL_PHY_RX, U8, rx);
165+ WPROBE_SET(WP_GLOBAL_PHY_TX, U8, tx);
166+ WPROBE_SET(WP_GLOBAL_FRAMES, U32, avp->av_rxframes);
167+ WPROBE_SET(WP_GLOBAL_PROBEREQ, U32, avp->av_rxprobereq);
168+
169+phy_skip:
170+ WPROBE_SET(WP_GLOBAL_NOISE, S16, noise);
171+ WPROBE_FILL_END();
172+
173+out:
174+ return 0;
175+}
176+
177+#undef AR5K_TXFC
178+#undef AR5K_RXFC
179+#undef AR5K_RXCLEAR
180+#undef AR5K_CYCLES
181+#undef AR5K_MIBC
182+#undef AR5K_MIBC_FREEZE
183+#undef READ_CLR
184+
185+static const struct wprobe_iface ath_wprobe_dev = {
186+ .link_items = ath_wprobe_link,
187+ .n_link_items = ARRAY_SIZE(ath_wprobe_link),
188+ .global_items = ath_wprobe_globals,
189+ .n_global_items = ARRAY_SIZE(ath_wprobe_globals),
190+ .sync_data = ath_wprobe_sync,
191+};
192+
193+static int
194+ath_lookup_rateval(struct ieee80211_node *ni, int rate)
195+{
196+ struct ieee80211vap *vap = ni->ni_vap;
197+ struct ieee80211com *ic = vap->iv_ic;
198+ struct ath_softc *sc = ic->ic_dev->priv;
199+ const HAL_RATE_TABLE *rt = sc->sc_currates;
200+
201+ if ((!rt) || (rate < 0) || (rate >= ARRAY_SIZE(sc->sc_hwmap)))
202+ return -1;
203+
204+ rate = sc->sc_hwmap[rate].ieeerate;
205+ rate = sc->sc_rixmap[rate & IEEE80211_RATE_VAL];
206+ if ((rate < 0) || (rate >= rt->rateCount))
207+ return -1;
208+
209+ return rt->info[rate].rateKbps;
210+}
211+
212+static void
213+ath_wprobe_report_rx(struct ieee80211vap *vap, struct ath_rx_status *rs, struct sk_buff *skb)
214+{
215+ const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
216+ struct wprobe_wlan_hdr hdr;
217+ struct ath_vap *avp;
218+ int hdrsize;
219+
220+ if (wprobe_disabled())
221+ return;
222+
223+ avp = ATH_VAP(vap);
224+ avp->av_rxframes++;
225+ if (wh->i_fc[0] == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ))
226+ avp->av_rxprobereq++;
227+
228+ memset(&hdr, 0, sizeof(hdr));
229+ hdr.len = skb->len;
230+ hdr.snr = rs->rs_rssi;
231+ hdr.type = WPROBE_PKT_RX;
232+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
233+ hdrsize = sizeof(struct ieee80211_ctlframe_addr2);
234+ else
235+ hdrsize = ieee80211_hdrsize(skb->data);
236+ wprobe_add_frame(&avp->av_wpif, &hdr, skb->data, hdrsize + 0x42);
237+}
238+
239+
240+static void
241+ath_node_sample_rx(struct ieee80211_node *ni, struct ath_rx_status *rs)
242+{
243+ struct ath_node *an = ATH_NODE(ni);
244+ struct ieee80211vap *vap = ni->ni_vap;
245+ struct ieee80211com *ic = vap->iv_ic;
246+ struct wprobe_link *l = &an->an_wplink;
247+ struct wprobe_value *v = l->val;
248+ unsigned long flags;
249+ int rate;
250+
251+ if (wprobe_disabled() || !an->an_wplink_active || !l->val)
252+ return;
253+
254+ rate = ath_lookup_rateval(ni, rs->rs_rate);
255+
256+ spin_lock_irqsave(&l->iface->lock, flags);
257+ WPROBE_FILL_BEGIN(v, ath_wprobe_link);
258+ WPROBE_SET(WP_NODE_RSSI, U8, rs->rs_rssi);
259+ WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + rs->rs_rssi);
260+ if ((rate > 0) && (rate <= 600000))
261+ WPROBE_SET(WP_NODE_RX_RATE, U16, rate);
262+ WPROBE_FILL_END();
263+ wprobe_update_stats(l->iface, l);
264+ spin_unlock_irqrestore(&l->iface->lock, flags);
265+}
266+
267+static void
268+ath_wprobe_report_tx(struct ieee80211vap *vap, struct ath_tx_status *ts, struct sk_buff *skb)
269+{
270+ const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
271+ struct wprobe_wlan_hdr hdr;
272+ struct ath_vap *avp;
273+ int hdrsize;
274+
275+ if (wprobe_disabled())
276+ return;
277+
278+ avp = ATH_VAP(vap);
279+
280+ memset(&hdr, 0, sizeof(hdr));
281+ hdr.len = skb->len;
282+ hdr.snr = ts->ts_rssi;
283+ hdr.type = WPROBE_PKT_TX;
284+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
285+ hdrsize = sizeof(struct ieee80211_ctlframe_addr2);
286+ else
287+ hdrsize = ieee80211_hdrsize(skb->data);
288+ wprobe_add_frame(&avp->av_wpif, &hdr, skb->data, hdrsize + 0x42);
289+}
290+
291+
292+
293+static void
294+ath_node_sample_tx(struct ieee80211_node *ni, struct ath_tx_status *ts, struct sk_buff *skb)
295+{
296+ struct ath_node *an = ATH_NODE(ni);
297+ struct ieee80211vap *vap = ni->ni_vap;
298+ struct ieee80211com *ic = vap->iv_ic;
299+ struct wprobe_link *l = &an->an_wplink;
300+ struct wprobe_value *v = l->val;
301+ unsigned long flags;
302+ int rate, rexmit_counter;
303+ int len = skb->len;
304+
305+ if (wprobe_disabled() || !an->an_wplink_active || !l->val)
306+ return;
307+
308+ ath_wprobe_report_tx(vap, ts, skb);
309+ rate = ath_lookup_rateval(ni, ts->ts_rate);
310+
311+ spin_lock_irqsave(&l->iface->lock, flags);
312+ WPROBE_FILL_BEGIN(v, ath_wprobe_link);
313+ WPROBE_SET(WP_NODE_RSSI, U8, ts->ts_rssi);
314+ WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + ts->ts_rssi);
315+
316+ if (len <= 200)
317+ rexmit_counter = WP_NODE_RETRANSMIT_200;
318+ else if (len <= 400)
319+ rexmit_counter = WP_NODE_RETRANSMIT_400;
320+ else if (len <= 800)
321+ rexmit_counter = WP_NODE_RETRANSMIT_800;
322+ else
323+ rexmit_counter = WP_NODE_RETRANSMIT_1600;
324+ WPROBE_SET(rexmit_counter, U8, ts->ts_longretry);
325+
326+ if ((rate > 0) && (rate <= 600000))
327+ WPROBE_SET(WP_NODE_TX_RATE, U16, rate);
328+ WPROBE_FILL_END();
329+ wprobe_update_stats(l->iface, l);
330+ spin_unlock_irqrestore(&l->iface->lock, flags);
331+}
332+
333+static void
334+ath_wprobe_node_join(struct ieee80211vap *vap, struct ieee80211_node *ni)
335+{
336+ struct wprobe_iface *dev;
337+ struct wprobe_link *l;
338+ struct ath_vap *avp;
339+ struct ath_node *an = ATH_NODE(ni);
340+
341+ if (wprobe_disabled() || an->an_wplink_active)
342+ return;
343+
344+ avp = ATH_VAP(vap);
345+ dev = &avp->av_wpif;
346+ l = &an->an_wplink;
347+
348+ ieee80211_ref_node(ni);
349+ wprobe_add_link(dev, l, ni->ni_macaddr);
350+ an->an_wplink_active = 1;
351+}
352+
353+static void
354+ath_wprobe_do_node_leave(struct work_struct *work)
355+{
356+ struct ath_node *an = container_of(work, struct ath_node, an_destroy);
357+ struct ieee80211_node *ni = &an->an_node;
358+ struct ieee80211vap *vap = ni->ni_vap;
359+ struct wprobe_iface *dev;
360+ struct wprobe_link *l;
361+ struct ath_vap *avp;
362+
363+ avp = ATH_VAP(vap);
364+ dev = &avp->av_wpif;
365+ l = &an->an_wplink;
366+
367+ wprobe_remove_link(dev, l);
368+ ieee80211_unref_node(&ni);
369+ atomic_dec(&cleanup_tasks);
370+}
371+
372+static void
373+ath_wprobe_node_leave(struct ieee80211vap *vap, struct ieee80211_node *ni)
374+{
375+ struct ath_node *an = ATH_NODE(ni);
376+
377+ if (wprobe_disabled() || !an->an_wplink_active)
378+ return;
379+
380+ atomic_inc(&cleanup_tasks);
381+ an->an_wplink_active = 0;
382+ IEEE80211_INIT_WORK(&an->an_destroy, ath_wprobe_do_node_leave);
383+ schedule_work(&an->an_destroy);
384+}
385+
386+static void
387+ath_init_wprobe_dev(struct ath_vap *avp)
388+{
389+ struct ieee80211vap *vap = &avp->av_vap;
390+ struct wprobe_iface *dev = &avp->av_wpif;
391+
392+ if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
393+ return;
394+
395+ memcpy(dev, &ath_wprobe_dev, sizeof(struct wprobe_iface));
396+ dev->addr = vap->iv_myaddr;
397+ dev->name = vap->iv_dev->name;
398+ wprobe_add_iface(dev);
399+}
400+
401+static void
402+ath_remove_wprobe_dev(struct ath_vap *avp)
403+{
404+ struct ieee80211vap *vap = &avp->av_vap;
405+ struct ieee80211com *ic = vap->iv_ic;
406+ struct ieee80211_node *ni;
407+ struct wprobe_iface *dev = &avp->av_wpif;
408+ struct wprobe_link *l;
409+ struct ath_node *an;
410+ unsigned long flags;
411+
412+ if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
413+ return;
414+
415+restart:
416+ rcu_read_lock();
417+ list_for_each_entry_rcu(l, &dev->links, list) {
418+ an = container_of(l, struct ath_node, an_wplink);
419+
420+ if (!an->an_wplink_active)
421+ continue;
422+
423+ ni = &an->an_node;
424+ ath_wprobe_node_leave(vap, ni);
425+ rcu_read_unlock();
426+ goto restart;
427+ }
428+ rcu_read_unlock();
429+
430+ /* wait for the cleanup tasks to finish */
431+ while (atomic_read(&cleanup_tasks) != 0) {
432+ schedule();
433+ }
434+
435+ wprobe_remove_iface(dev);
436+}
437--- a/ath/if_ath.c
438+++ b/ath/if_ath.c
439@@ -400,6 +400,7 @@ static int countrycode = -1;
440 static int maxvaps = -1;
441 static int outdoor = -1;
442 static int xchanmode = -1;
443+#include "ath_wprobe.c"
444 static int beacon_cal = 1;
445 
446 static const struct ath_hw_detect generic_hw_info = {
447@@ -1525,6 +1526,7 @@ ath_vap_create(struct ieee80211com *ic,
448         ath_hal_intrset(ah, sc->sc_imask);
449     }
450 
451+ ath_init_wprobe_dev(avp);
452     return vap;
453 }
454 
455@@ -1606,6 +1608,7 @@ ath_vap_delete(struct ieee80211vap *vap)
456         decrease = 0;
457 
458     ieee80211_vap_detach(vap);
459+ ath_remove_wprobe_dev(ATH_VAP(vap));
460     /* NB: memory is reclaimed through dev->destructor callback */
461     if (decrease)
462         sc->sc_nvaps--;
463@@ -5940,6 +5943,7 @@ ath_node_cleanup(struct ieee80211_node *
464     /* Clean up node-specific rate things - this currently appears to
465      * always be a no-op */
466     sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni));
467+ ath_wprobe_node_leave(ni->ni_vap, ni);
468 
469     ATH_NODE_UAPSD_LOCK_IRQ(an);
470 #ifdef IEEE80211_DEBUG_REFCNT
471@@ -7010,6 +7014,8 @@ drop_micfail:
472                 goto lookup_slowpath;
473             }
474             ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
475+ ath_node_sample_rx(ni, rs);
476+ ath_wprobe_report_rx(ni->ni_vap, rs, skb);
477             type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
478             ieee80211_unref_node(&ni);
479         } else {
480@@ -7024,15 +7030,21 @@ lookup_slowpath:
481             else
482                 vap = ieee80211_find_rxvap(ic, wh->i_addr1);
483 
484- if (vap)
485+ if (vap) {
486+ ath_wprobe_report_rx(vap, rs, skb);
487                 ni = ieee80211_find_rxnode(ic, vap, wh);
488- else
489+ } else {
490+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
491+ ath_wprobe_report_rx(vap, rs, skb);
492+ }
493                 ni = NULL;
494+ }
495 
496             if (ni != NULL) {
497                 ieee80211_keyix_t keyix;
498 
499                 ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
500+ ath_node_sample_rx(ni, rs);
501                 type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
502                 /*
503                  * If the station has a key cache slot assigned
504@@ -8612,6 +8624,7 @@ ath_tx_processq(struct ath_softc *sc, st
505                 sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
506                 ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi,
507                     ts->ts_rssi);
508+ ath_node_sample_tx(&an->an_node, ts, bf->bf_skb);
509                 if (bf->bf_skb->priority == WME_AC_VO ||
510                     bf->bf_skb->priority == WME_AC_VI)
511                     ni->ni_ic->ic_wme.wme_hipri_traffic++;
512@@ -10111,6 +10124,7 @@ ath_newassoc(struct ieee80211_node *ni,
513     struct ath_softc *sc = ic->ic_dev->priv;
514 
515     sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew);
516+ ath_wprobe_node_join(ni->ni_vap, ni);
517 
518     /* are we supporting compression? */
519     if (!(vap->iv_ath_cap & ni->ni_ath_flags & IEEE80211_NODE_COMP))
520--- a/ath/if_athvar.h
521+++ b/ath/if_athvar.h
522@@ -46,6 +46,7 @@
523 #include "ah_desc.h"
524 #include "ah_os.h"
525 #include "if_athioctl.h"
526+#include <linux/wprobe.h>
527 #include "net80211/ieee80211.h" /* XXX for WME_NUM_AC */
528 #include <asm/io.h>
529 #include <linux/list.h>
530@@ -352,6 +353,9 @@ typedef STAILQ_HEAD(, ath_buf) ath_bufhe
531 /* driver-specific node state */
532 struct ath_node {
533     struct ieee80211_node an_node; /* base class */
534+ struct wprobe_link an_wplink;
535+ uint8_t an_wplink_active;
536+ struct work_struct an_destroy;
537     u_int16_t an_decomp_index; /* decompression mask index */
538     u_int32_t an_avgrssi; /* average rssi over all rx frames */
539     u_int8_t an_prevdatarix; /* rate ix of last data frame */
540@@ -521,6 +525,9 @@ struct ath_vap {
541 #else
542     unsigned int av_beacon_alloc;
543 #endif
544+ struct wprobe_iface av_wpif;
545+ u32 av_rxframes;
546+ u32 av_rxprobereq;
547 };
548 #define ATH_VAP(_v) ((struct ath_vap *)(_v))
549 
550

Archive Download this file



interactive