From c6ba35a9442047f7b54b42455a84c87bb1b4261f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 26 Mar 2009 20:56:58 +0000 Subject: [PATCH] implement support for wprobe in madwifi SVN-Revision: 15051 --- package/madwifi/Makefile | 13 + package/madwifi/extra/wprobe.spatch | 84 +++++ package/madwifi/patches/416-wprobe.patch | 461 +++++++++++++++++++++++ 3 files changed, 558 insertions(+) create mode 100644 package/madwifi/extra/wprobe.spatch create mode 100644 package/madwifi/patches/416-wprobe.patch diff --git a/package/madwifi/Makefile b/package/madwifi/Makefile index 943a13c215..dcff41c2f4 100644 --- a/package/madwifi/Makefile +++ b/package/madwifi/Makefile @@ -39,6 +39,8 @@ else PATCH_DIR=./patches endif +PKG_BUILD_DEPENDS:=wprobe + include $(INCLUDE_DIR)/package.mk ifneq ($(CONFIG_TARGET_atheros),) @@ -168,6 +170,16 @@ define KernelPackage/madwifi/config source "$(SOURCE)/Config.in" endef +MADWIFI_INC = \ + -I$(PKG_BUILD_DIR) \ + -I$(PKG_BUILD_DIR)/include \ + -I$(PKG_BUILD_DIR)/hal \ + -I$(PKG_BUILD_DIR)/ath \ + -I$(PKG_BUILD_DIR)/ath_hal \ + -I$(PKG_BUILD_DIR)/net80211 \ + -I$(STAGING_DIR)/usr/include \ + -include $(PKG_BUILD_DIR)/include/compat.h + MAKE_ARGS:= \ PATH="$(TARGET_PATH)" \ ARCH="$(LINUX_KARCH)" \ @@ -179,6 +191,7 @@ MAKE_ARGS:= \ LDOPTS="--no-warn-mismatch " \ ATH_RATE="ath_rate/$(RATE_CONTROL)" \ DO_MULTI=1 \ + INCS="$(MADWIFI_INC)" \ $(if $(CONFIG_MADWIFI_DEBUG),,DEBUG=) WARNINGS="-Wno-unused" MAKE_VARS:= \ diff --git a/package/madwifi/extra/wprobe.spatch b/package/madwifi/extra/wprobe.spatch new file mode 100644 index 0000000000..78718aa54c --- /dev/null +++ b/package/madwifi/extra/wprobe.spatch @@ -0,0 +1,84 @@ +@@ +@@ + #include "if_athioctl.h" ++#include "wprobe.h" + +@@ +@@ + struct ath_vap { + ... ++ struct wprobe_iface av_wpif; + }; + +@@ +@@ + struct ath_node { + ... ++ struct wprobe_link an_wplink; ++ uint8_t an_wplink_active; ++ struct work_struct an_destroy; + u_int16_t an_decomp_index; + ... + }; + +@@ +@@ + ath_vap_create(...) { + <... ++ ath_init_wprobe_dev(avp); + return vap; + ...> + } + +@ rule5 @ +expression vap; +@@ + ath_vap_delete(...) { + <... + ieee80211_vap_detach(vap); ++ ath_remove_wprobe_dev(ATH_VAP(vap)); + ...> + } + +@ rule6 @ +@@ + static int xchanmode = -1; ++ ++#include "ath_wprobe.c" + + +@ rule7 @ +expression sc, ni; +@@ + <... + sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew); ++ ath_wprobe_node_join(ni->ni_vap, ni); + ...> + +@ rule8 @ +expression ni; +expression sc; +@@ + <... + sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni)); ++ ath_wprobe_node_leave(ni->ni_vap, ni); + ...> + +@ rule9 @ +expression ni; +expression rs; +@@ + <... + ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi); ++ ath_node_sample_rx(ni, rs); + ...> + +@ rule10 @ +expression an; +expression ts; +@@ + <... + ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi, ts->ts_rssi); ++ ath_node_sample_tx(&an->an_node, ts, bf->bf_skb->len); + ...> + diff --git a/package/madwifi/patches/416-wprobe.patch b/package/madwifi/patches/416-wprobe.patch new file mode 100644 index 0000000000..5e181e5bca --- /dev/null +++ b/package/madwifi/patches/416-wprobe.patch @@ -0,0 +1,461 @@ +--- /dev/null 2009-03-26 21:01:06.000000000 +0100 ++++ ./ath/ath_wprobe.c 2009-03-26 20:58:07.000000000 +0100 +@@ -0,0 +1,364 @@ ++#include ++#include ++ ++atomic_t cleanup_tasks = ATOMIC_INIT(0); ++ ++enum wp_node_val { ++ WP_NODE_RSSI, ++ WP_NODE_SIGNAL, ++ WP_NODE_RX_RATE, ++ WP_NODE_TX_RATE, ++ WP_NODE_RETRANSMIT_200, ++ WP_NODE_RETRANSMIT_400, ++ WP_NODE_RETRANSMIT_800, ++ WP_NODE_RETRANSMIT_1600, ++}; ++ ++enum wp_global_val { ++ WP_GLOBAL_NOISE, ++ WP_GLOBAL_PHY_BUSY, ++ WP_GLOBAL_PHY_RX, ++ WP_GLOBAL_PHY_TX, ++}; ++ ++static struct wprobe_item ath_wprobe_globals[] = { ++ [WP_GLOBAL_NOISE] = { ++ .name = "noise", ++ .type = WPROBE_VAL_S16, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_GLOBAL_PHY_BUSY] = { ++ .name = "phy_busy", ++ .type = WPROBE_VAL_U8, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_GLOBAL_PHY_RX] = { ++ .name = "phy_rx", ++ .type = WPROBE_VAL_U8, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_GLOBAL_PHY_TX] = { ++ .name = "phy_tx", ++ .type = WPROBE_VAL_U8, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++}; ++ ++static struct wprobe_item ath_wprobe_link[] = { ++ [WP_NODE_RSSI] = { ++ .name = "rssi", ++ .type = WPROBE_VAL_U8, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_NODE_SIGNAL] = { ++ .name = "signal", ++ .type = WPROBE_VAL_S16, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_NODE_RX_RATE] = { ++ .name = "rx_rate", ++ .type = WPROBE_VAL_U16, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_NODE_TX_RATE] = { ++ .name = "tx_rate", ++ .type = WPROBE_VAL_U16, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_NODE_RETRANSMIT_200] = { ++ .name = "retransmit_200", ++ .type = WPROBE_VAL_U8, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_NODE_RETRANSMIT_400] = { ++ .name = "retransmit_400", ++ .type = WPROBE_VAL_U8, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_NODE_RETRANSMIT_800] = { ++ .name = "retransmit_800", ++ .type = WPROBE_VAL_U8, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++ [WP_NODE_RETRANSMIT_1600] = { ++ .name = "retransmit_1600", ++ .type = WPROBE_VAL_U8, ++ .flags = WPROBE_F_KEEPSTAT ++ }, ++}; ++ ++#define AR5K_MIBC 0x0040 ++#define AR5K_MIBC_FREEZE (1 << 1) ++#define AR5K_TXFC 0x80ec ++#define AR5K_RXFC 0x80f0 ++#define AR5K_RXCLEAR 0x80f4 ++#define AR5K_CYCLES 0x80f8 ++ ++#define READ_CLR(_ah, _reg) \ ++ ({ u32 __val = OS_REG_READ(_ah, _reg); OS_REG_WRITE(_ah, _reg, 0); __val; }) ++ ++static bool ++wprobe_disabled(void) ++{ ++ return (!wprobe_add_iface || IS_ERR(wprobe_add_iface)); ++} ++ ++static int ++ath_wprobe_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure) ++{ ++ struct ath_vap *avp = container_of(dev, struct ath_vap, av_wpif); ++ struct ieee80211vap *vap = &avp->av_vap; ++ struct ieee80211com *ic = vap->iv_ic; ++ struct ath_softc *sc = ic->ic_dev->priv; ++ struct ath_hal *ah = sc->sc_ah; ++ u32 cc, busy, rx, tx; ++ s16 noise; ++ ++ if (l) ++ goto out; ++ ++ OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE); ++ cc = READ_CLR(ah, AR5K_CYCLES); ++ busy = READ_CLR(ah, AR5K_RXCLEAR); ++ rx = READ_CLR(ah, AR5K_RXFC); ++ tx = READ_CLR(ah, AR5K_TXFC); ++ OS_REG_WRITE(ah, AR5K_MIBC, 0); ++ noise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan)); ++ ic->ic_channoise = noise; ++ ++ WPROBE_FILL_BEGIN(val, ath_wprobe_globals); ++ if (cc & 0xf0000000) { ++ /* scale down if the counters are near max */ ++ cc >>= 8; ++ busy >>= 8; ++ rx >>= 8; ++ tx >>= 8; ++ } ++ if (ah->ah_macType < 5212) ++ goto phy_skip; ++ if (!cc) ++ goto phy_skip; ++ if (busy > cc) ++ goto phy_skip; ++ if (rx > cc) ++ goto phy_skip; ++ if (tx > cc) ++ goto phy_skip; ++ busy = (busy * 100) / cc; ++ rx = (rx * 100) / cc; ++ tx = (tx * 100) / cc; ++ WPROBE_SET(WP_GLOBAL_PHY_BUSY, U8, busy); ++ WPROBE_SET(WP_GLOBAL_PHY_RX, U8, rx); ++ WPROBE_SET(WP_GLOBAL_PHY_TX, U8, tx); ++phy_skip: ++ WPROBE_SET(WP_GLOBAL_NOISE, S16, noise); ++ WPROBE_FILL_END(); ++ ++out: ++ return 0; ++} ++ ++#undef AR5K_TXFC ++#undef AR5K_RXFC ++#undef AR5K_RXCLEAR ++#undef AR5K_CYCLES ++#undef AR5K_MIBC ++#undef AR5K_MIBC_FREEZE ++#undef READ_CLR ++ ++static const struct wprobe_iface ath_wprobe_dev = { ++ .link_items = ath_wprobe_link, ++ .n_link_items = ARRAY_SIZE(ath_wprobe_link), ++ .global_items = ath_wprobe_globals, ++ .n_global_items = ARRAY_SIZE(ath_wprobe_globals), ++ .sync_data = ath_wprobe_sync, ++}; ++ ++static int ++ath_lookup_rateval(struct ieee80211_node *ni, int rate) ++{ ++ struct ieee80211vap *vap = ni->ni_vap; ++ struct ieee80211com *ic = vap->iv_ic; ++ struct ath_softc *sc = ic->ic_dev->priv; ++ const HAL_RATE_TABLE *rt = sc->sc_currates; ++ ++ if ((!rt) || (rate < 0) || (rate >= ARRAY_SIZE(sc->sc_hwmap))) ++ return -1; ++ ++ rate = sc->sc_hwmap[rate].ieeerate; ++ rate = sc->sc_rixmap[rate & IEEE80211_RATE_VAL]; ++ if ((rate < 0) || (rate >= rt->rateCount)) ++ return -1; ++ ++ return rt->info[rate].rateKbps / 10; ++} ++ ++static void ++ath_node_sample_rx(struct ieee80211_node *ni, struct ath_rx_status *rs) ++{ ++ struct ath_node *an = ATH_NODE(ni); ++ struct ieee80211vap *vap = ni->ni_vap; ++ struct ieee80211com *ic = vap->iv_ic; ++ struct wprobe_link *l = &an->an_wplink; ++ struct wprobe_value *v = l->val; ++ unsigned long flags; ++ int rate; ++ ++ if (wprobe_disabled() || !an->an_wplink_active || !l->val) ++ return; ++ ++ rate = ath_lookup_rateval(ni, rs->rs_rate); ++ ++ spin_lock_irqsave(&l->iface->lock, flags); ++ WPROBE_FILL_BEGIN(v, ath_wprobe_link); ++ WPROBE_SET(WP_NODE_RSSI, U8, rs->rs_rssi); ++ WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + rs->rs_rssi); ++ if ((rate > 0) && (rate <= 600000)) ++ WPROBE_SET(WP_NODE_RX_RATE, U16, rate); ++ WPROBE_FILL_END(); ++ wprobe_update_stats(l->iface, l); ++ spin_unlock_irqrestore(&l->iface->lock, flags); ++} ++ ++static void ++ath_node_sample_tx(struct ieee80211_node *ni, struct ath_tx_status *ts, int len) ++{ ++ struct ath_node *an = ATH_NODE(ni); ++ struct ieee80211vap *vap = ni->ni_vap; ++ struct ieee80211com *ic = vap->iv_ic; ++ struct wprobe_link *l = &an->an_wplink; ++ struct wprobe_value *v = l->val; ++ unsigned long flags; ++ int rate, rexmit_counter; ++ ++ if (wprobe_disabled() || !an->an_wplink_active || !l->val) ++ return; ++ ++ rate = ath_lookup_rateval(ni, ts->ts_rate); ++ ++ spin_lock_irqsave(&l->iface->lock, flags); ++ WPROBE_FILL_BEGIN(v, ath_wprobe_link); ++ WPROBE_SET(WP_NODE_RSSI, U8, ts->ts_rssi); ++ WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + ts->ts_rssi); ++ ++ if (len <= 200) ++ rexmit_counter = WP_NODE_RETRANSMIT_200; ++ else if (len <= 400) ++ rexmit_counter = WP_NODE_RETRANSMIT_400; ++ else if (len <= 800) ++ rexmit_counter = WP_NODE_RETRANSMIT_800; ++ else ++ rexmit_counter = WP_NODE_RETRANSMIT_1600; ++ WPROBE_SET(rexmit_counter, U8, ts->ts_longretry); ++ ++ if ((rate > 0) && (rate <= 600000)) ++ WPROBE_SET(WP_NODE_TX_RATE, U16, rate); ++ WPROBE_FILL_END(); ++ wprobe_update_stats(l->iface, l); ++ spin_unlock_irqrestore(&l->iface->lock, flags); ++} ++ ++static void ++ath_wprobe_node_join(struct ieee80211vap *vap, struct ieee80211_node *ni) ++{ ++ struct wprobe_iface *dev; ++ struct wprobe_link *l; ++ struct ath_vap *avp; ++ struct ath_node *an = ATH_NODE(ni); ++ ++ if (wprobe_disabled() || an->an_wplink_active) ++ return; ++ ++ avp = ATH_VAP(vap); ++ dev = &avp->av_wpif; ++ l = &an->an_wplink; ++ ++ ieee80211_ref_node(ni); ++ wprobe_add_link(dev, l, ni->ni_macaddr); ++ an->an_wplink_active = 1; ++} ++ ++static void ++ath_wprobe_do_node_leave(struct work_struct *work) ++{ ++ struct ath_node *an = container_of(work, struct ath_node, an_destroy); ++ struct ieee80211_node *ni = &an->an_node; ++ struct ieee80211vap *vap = ni->ni_vap; ++ struct wprobe_iface *dev; ++ struct wprobe_link *l; ++ struct ath_vap *avp; ++ ++ avp = ATH_VAP(vap); ++ dev = &avp->av_wpif; ++ l = &an->an_wplink; ++ ++ wprobe_remove_link(dev, l); ++ ieee80211_unref_node(&ni); ++ atomic_dec(&cleanup_tasks); ++} ++ ++static void ++ath_wprobe_node_leave(struct ieee80211vap *vap, struct ieee80211_node *ni) ++{ ++ struct ath_node *an = ATH_NODE(ni); ++ ++ if (wprobe_disabled() || !an->an_wplink_active) ++ return; ++ ++ atomic_inc(&cleanup_tasks); ++ an->an_wplink_active = 0; ++ IEEE80211_INIT_WORK(&an->an_destroy, ath_wprobe_do_node_leave); ++ schedule_work(&an->an_destroy); ++} ++ ++static void ++ath_init_wprobe_dev(struct ath_vap *avp) ++{ ++ struct ieee80211vap *vap = &avp->av_vap; ++ struct wprobe_iface *dev = &avp->av_wpif; ++ ++ if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS)) ++ return; ++ ++ memcpy(dev, &ath_wprobe_dev, sizeof(struct wprobe_iface)); ++ dev->addr = vap->iv_myaddr; ++ dev->name = vap->iv_dev->name; ++ wprobe_add_iface(dev); ++} ++ ++static void ++ath_remove_wprobe_dev(struct ath_vap *avp) ++{ ++ struct ieee80211vap *vap = &avp->av_vap; ++ struct ieee80211com *ic = vap->iv_ic; ++ struct ieee80211_node *ni; ++ struct wprobe_iface *dev = &avp->av_wpif; ++ struct wprobe_link *l; ++ struct ath_node *an; ++ unsigned long flags; ++ ++ if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS)) ++ return; ++ ++restart: ++ rcu_read_lock(); ++ list_for_each_entry_rcu(l, &dev->links, list) { ++ an = container_of(l, struct ath_node, an_wplink); ++ ++ if (!an->an_wplink_active) ++ continue; ++ ++ ni = &an->an_node; ++ ath_wprobe_node_leave(vap, ni); ++ rcu_read_unlock(); ++ goto restart; ++ } ++ rcu_read_unlock(); ++ ++ /* wait for the cleanup tasks to finish */ ++ while (atomic_read(&cleanup_tasks) != 0) { ++ schedule(); ++ } ++ ++ wprobe_remove_iface(dev); ++} +--- a/ath/if_ath.c 2009-03-26 19:54:36.000000000 +0100 ++++ /var/folders/DB/DBZUyxsHGRKP0B3nCU1mmU+++TI/-Tmp-/cocci-output-73937-18abc0-if_ath.c 2009-03-26 21:08:34.000000000 +0100 +@@ -400,6 +400,7 @@ static int countrycode = -1; + static int maxvaps = -1; + static int outdoor = -1; + static int xchanmode = -1; ++#include "ath_wprobe.c" + static int beacon_cal = 1; + + static const struct ath_hw_detect generic_hw_info = { +@@ -1525,6 +1526,7 @@ ath_vap_create(struct ieee80211com *ic, + ath_hal_intrset(ah, sc->sc_imask); + } + ++ ath_init_wprobe_dev(avp); + return vap; + } + +@@ -1605,6 +1607,7 @@ ath_vap_delete(struct ieee80211vap *vap) + decrease = 0; + + ieee80211_vap_detach(vap); ++ ath_remove_wprobe_dev(ATH_VAP(vap)); + /* NB: memory is reclaimed through dev->destructor callback */ + if (decrease) + sc->sc_nvaps--; +@@ -5931,6 +5934,7 @@ ath_node_cleanup(struct ieee80211_node * + /* Clean up node-specific rate things - this currently appears to + * always be a no-op */ + sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni)); ++ ath_wprobe_node_leave(ni->ni_vap, ni); + + ATH_NODE_UAPSD_LOCK_IRQ(an); + #ifdef IEEE80211_DEBUG_REFCNT +@@ -7001,6 +7005,7 @@ drop_micfail: + goto lookup_slowpath; + } + ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi); ++ ath_node_sample_rx(ni, rs); + type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf); + ieee80211_unref_node(&ni); + } else { +@@ -7020,6 +7025,7 @@ lookup_slowpath: + ieee80211_keyix_t keyix; + + ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi); ++ ath_node_sample_rx(ni, rs); + type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf); + /* + * If the station has a key cache slot assigned +@@ -8599,6 +8605,7 @@ ath_tx_processq(struct ath_softc *sc, st + sc->sc_stats.ast_tx_rssi = ts->ts_rssi; + ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi, + ts->ts_rssi); ++ ath_node_sample_tx(&an->an_node, ts, bf->bf_skb->len); + if (bf->bf_skb->priority == WME_AC_VO || + bf->bf_skb->priority == WME_AC_VI) + ni->ni_ic->ic_wme.wme_hipri_traffic++; +@@ -10090,6 +10097,7 @@ ath_newassoc(struct ieee80211_node *ni, + struct ath_softc *sc = ic->ic_dev->priv; + + sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew); ++ ath_wprobe_node_join(ni->ni_vap, ni); + + /* are we supporting compression? */ + if (!(vap->iv_ath_cap & ni->ni_ath_flags & IEEE80211_NODE_COMP)) +--- a/ath/if_athvar.h 2009-03-26 19:54:35.000000000 +0100 ++++ /var/folders/DB/DBZUyxsHGRKP0B3nCU1mmU+++TI/-Tmp-/cocci-output-73937-80429d-if_athvar.h 2009-03-26 21:08:42.000000000 +0100 +@@ -46,6 +46,7 @@ + #include "ah_desc.h" + #include "ah_os.h" + #include "if_athioctl.h" ++#include + #include "net80211/ieee80211.h" /* XXX for WME_NUM_AC */ + #include + #include +@@ -352,6 +353,9 @@ typedef STAILQ_HEAD(, ath_buf) ath_bufhe + /* driver-specific node state */ + struct ath_node { + struct ieee80211_node an_node; /* base class */ ++ struct wprobe_link an_wplink; ++ uint8_t an_wplink_active; ++ struct work_struct an_destroy; + u_int16_t an_decomp_index; /* decompression mask index */ + u_int32_t an_avgrssi; /* average rssi over all rx frames */ + u_int8_t an_prevdatarix; /* rate ix of last data frame */ +@@ -521,6 +525,7 @@ struct ath_vap { + #else + unsigned int av_beacon_alloc; + #endif ++ struct wprobe_iface av_wpif; + }; + #define ATH_VAP(_v) ((struct ath_vap *)(_v)) +