From 8c3be08d37358330041745f231f57d6964ab7fb8 Mon Sep 17 00:00:00 2001 From: nbd Date: Wed, 4 Mar 2009 21:39:46 +0000 Subject: [PATCH] madwifi: improve the autochannel decision making on systems with multiple cards git-svn-id: svn://svn.openwrt.org/openwrt/trunk@14748 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../patches/460-autochannel_multi.patch | 347 ++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 package/madwifi/patches/460-autochannel_multi.patch diff --git a/package/madwifi/patches/460-autochannel_multi.patch b/package/madwifi/patches/460-autochannel_multi.patch new file mode 100644 index 000000000..ab48e70b2 --- /dev/null +++ b/package/madwifi/patches/460-autochannel_multi.patch @@ -0,0 +1,347 @@ +--- a/net80211/ieee80211_scan.c ++++ b/net80211/ieee80211_scan.c +@@ -97,6 +97,123 @@ struct scan_state { + static void scan_restart_pwrsav(unsigned long); + static void scan_next(unsigned long); + ++spinlock_t channel_lock = SPIN_LOCK_UNLOCKED; ++static LIST_HEAD(channels_inuse); ++ ++struct channel_inuse { ++ struct list_head list; ++ struct ieee80211com *ic; ++ u16 freq; ++ u8 bw; ++}; ++ ++static inline u32 ++get_signal(u8 bw, u8 distance) ++{ ++ u32 v; ++ ++ /* signal = 1 - (distance / bw)^2 [scale: 100] */ ++ v = 100 * distance / bw; ++ v = (100 - ((v * v) / 100)); ++ return v; ++} ++ ++static u32 ++get_overlap(u16 f1, u16 f2, u8 b1, u8 b2) ++{ ++ u32 v; ++ u16 d, c; ++ ++ /* add offsets for sidechannel interference */ ++ b1 += (b1 / 5); ++ b2 += (b2 / 5); ++ ++ /* use only one direction */ ++ b1 /= 2; ++ b2 /= 2; ++ ++ if (f1 + b1 < f2 - b2) ++ return 0; ++ ++ d = f2 - f1; ++ c = d * b1 / (b1 + b2); ++ v = get_signal(b1, c); ++ ++ return v * v / 100; ++} ++ ++static u8 ++get_channel_bw(struct ieee80211_channel *c) ++{ ++ switch(c->ic_flags & ( ++ IEEE80211_CHAN_HALF | ++ IEEE80211_CHAN_QUARTER | ++ IEEE80211_CHAN_TURBO | ++ IEEE80211_CHAN_STURBO)) { ++ case IEEE80211_CHAN_QUARTER: ++ return 5; ++ case IEEE80211_CHAN_HALF: ++ return 10; ++ case IEEE80211_CHAN_TURBO: ++ case IEEE80211_CHAN_STURBO: ++ return 40; ++ default: ++ return 20; ++ } ++} ++ ++/* must be called with channel_lock held */ ++u32 ++ieee80211_scan_get_bias(struct ieee80211_channel *c) ++{ ++ struct channel_inuse *ch; ++ u8 bw = get_channel_bw(c); ++ u32 bias = 0; ++ ++ list_for_each_entry(ch, &channels_inuse, list) { ++ if (ch->freq == c->ic_freq) { ++ bias += 50; ++ continue; ++ } ++ if (c->ic_freq < ch->freq) ++ bias += get_overlap(c->ic_freq, ch->freq, bw, ch->bw); ++ else ++ bias += get_overlap(ch->freq, c->ic_freq, ch->bw, bw); ++ } ++ return min(bias, (u32) 100); ++} ++EXPORT_SYMBOL(ieee80211_scan_get_bias); ++ ++/* must be called with channel_lock held */ ++void ++ieee80211_scan_set_bss_channel(struct ieee80211com *ic, struct ieee80211_channel *c) ++{ ++ unsigned long flags; ++ struct channel_inuse *ch; ++ ++ list_for_each_entry(ch, &channels_inuse, list) { ++ if (ch->ic == ic) ++ goto found; ++ } ++ ch = NULL; ++found: ++ if (c && (c != IEEE80211_CHAN_ANYC)) { ++ if (!ch) { ++ ch = kmalloc(sizeof(struct channel_inuse), GFP_ATOMIC); ++ ch->ic = ic; ++ INIT_LIST_HEAD(&ch->list); ++ list_add(&ch->list, &channels_inuse); ++ } ++ ch->freq = c->ic_freq; ++ ch->bw = get_channel_bw(c); ++ } else if (ch) { ++ list_del(&ch->list); ++ kfree(ch); ++ } ++} ++EXPORT_SYMBOL(ieee80211_scan_set_bss_channel); ++ ++ + void + ieee80211_scan_attach(struct ieee80211com *ic) + { +@@ -1155,7 +1272,7 @@ ieee80211_scan_dfs_action(struct ieee802 + IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT; + ic->ic_flags |= IEEE80211_F_CHANSWITCH; + } else { +- ++ unsigned long flags; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, + "%s: directly switching to channel " + "%3d (%4d MHz)\n", __func__, +@@ -1166,6 +1283,9 @@ ieee80211_scan_dfs_action(struct ieee802 + * change the channel here. */ + change_channel(ic, new_channel); + ic->ic_bsschan = new_channel; ++ spin_lock_irqsave(&channel_lock, flags); ++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); ++ spin_unlock_irqrestore(&channel_lock, flags); + if (vap->iv_bss) + vap->iv_bss->ni_chan = new_channel; + } +--- a/net80211/ieee80211_scan.h ++++ b/net80211/ieee80211_scan.h +@@ -35,6 +35,7 @@ + + #define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX + ++extern spinlock_t channel_lock; + struct ieee80211_scanner; + struct ieee80211_scan_entry; + +@@ -116,6 +117,8 @@ void ieee80211_scan_flush(struct ieee802 + struct ieee80211_scan_entry; + typedef int ieee80211_scan_iter_func(void *, const struct ieee80211_scan_entry *); + int ieee80211_scan_iterate(struct ieee80211com *, ieee80211_scan_iter_func *, void *); ++u32 ieee80211_scan_get_bias(struct ieee80211_channel *c); ++void ieee80211_scan_set_bss_channel(struct ieee80211com *ic, struct ieee80211_channel *c); + + /* + * Parameters supplied when adding/updating an entry in a +--- a/net80211/ieee80211.c ++++ b/net80211/ieee80211.c +@@ -373,8 +373,16 @@ void + ieee80211_ifdetach(struct ieee80211com *ic) + { + struct ieee80211vap *vap; ++ unsigned long flags; + int count; + ++ /* mark the channel as no longer in use */ ++ ic->ic_bsschan = IEEE80211_CHAN_ANYC; ++ spin_lock_irqsave(&channel_lock, flags); ++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); ++ spin_unlock_irqrestore(&channel_lock, flags); ++ ++ + /* bring down all vaps */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + ieee80211_stop(vap->iv_dev); +--- a/net80211/ieee80211_input.c ++++ b/net80211/ieee80211_input.c +@@ -2772,6 +2772,7 @@ static void + ieee80211_doth_switch_channel(struct ieee80211vap *vap) + { + struct ieee80211com *ic = vap->iv_ic; ++ unsigned long flags; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, + "%s: Channel switch to %3d (%4d MHz) NOW!\n", +@@ -2794,6 +2795,9 @@ ieee80211_doth_switch_channel(struct iee + + ic->ic_curchan = ic->ic_bsschan = vap->iv_csa_chan; + ic->ic_set_channel(ic); ++ spin_lock_irqsave(&channel_lock, flags); ++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); ++ spin_unlock_irqrestore(&channel_lock, flags); + } + + static void +--- a/net80211/ieee80211_node.c ++++ b/net80211/ieee80211_node.c +@@ -308,6 +308,7 @@ ieee80211_create_ibss(struct ieee80211va + { + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; ++ unsigned long flags; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: creating ibss on channel %u\n", __func__, +@@ -386,6 +387,9 @@ ieee80211_create_ibss(struct ieee80211va + ic->ic_bsschan = chan; + ieee80211_node_set_chan(ic, ni); + ic->ic_curmode = ieee80211_chan2mode(chan); ++ spin_lock_irqsave(&channel_lock, flags); ++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); ++ spin_unlock_irqrestore(&channel_lock, flags); + + /* Update country ie information */ + ieee80211_build_countryie(ic); +@@ -622,6 +626,7 @@ ieee80211_sta_join1(struct ieee80211_nod + struct ieee80211vap *vap = selbs->ni_vap; + struct ieee80211com *ic = selbs->ni_ic; + struct ieee80211_node *obss; ++ unsigned long flags; + int canreassoc; + + if (vap->iv_opmode == IEEE80211_M_IBSS) { +@@ -650,6 +655,9 @@ ieee80211_sta_join1(struct ieee80211_nod + ic->ic_curchan = ic->ic_bsschan; + ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); + ic->ic_set_channel(ic); ++ spin_lock_irqsave(&channel_lock, flags); ++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); ++ spin_unlock_irqrestore(&channel_lock, flags); + /* + * Set the erp state (mostly the slot time) to deal with + * the auto-select case; this should be redundant if the +--- a/net80211/ieee80211_proto.c ++++ b/net80211/ieee80211_proto.c +@@ -1225,6 +1225,7 @@ ieee80211_dturbo_switch(struct ieee80211 + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + #endif + struct ieee80211_channel *chan; ++ unsigned long flags; + + chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); + if (chan == NULL) { /* XXX should not happen */ +@@ -1243,6 +1244,9 @@ ieee80211_dturbo_switch(struct ieee80211 + ic->ic_bsschan = chan; + ic->ic_curchan = chan; + ic->ic_set_channel(ic); ++ spin_lock_irqsave(&channel_lock, flags); ++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); ++ spin_unlock_irqrestore(&channel_lock, flags); + /* NB: do not need to reset ERP state because in sta mode */ + } + EXPORT_SYMBOL(ieee80211_dturbo_switch); +--- a/net80211/ieee80211_wireless.c ++++ b/net80211/ieee80211_wireless.c +@@ -4076,8 +4076,13 @@ ieee80211_ioctl_setchanlist(struct net_d + if (nchan == 0) /* no valid channels, disallow */ + return -EINVAL; + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */ +- isclr(chanlist, ic->ic_bsschan->ic_ieee)) ++ isclr(chanlist, ic->ic_bsschan->ic_ieee)) { ++ unsigned long flags; + ic->ic_bsschan = IEEE80211_CHAN_ANYC; /* invalidate */ ++ spin_lock_irqsave(&channel_lock, flags); ++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); ++ spin_unlock_irqrestore(&channel_lock, flags); ++ } + + memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); + /* update Supported Channels information element */ +--- a/net80211/ieee80211_scan_ap.c ++++ b/net80211/ieee80211_scan_ap.c +@@ -213,9 +213,15 @@ ap_start(struct ieee80211_scan_state *ss + struct ieee80211com *ic = NULL; + int i; + unsigned int mode = 0; ++ unsigned long sflags; + + SCAN_AP_LOCK_IRQ(as); + ic = vap->iv_ic; ++ ++ spin_lock_irqsave(&channel_lock, sflags); ++ ieee80211_scan_set_bss_channel(ic, NULL); ++ spin_unlock_irqrestore(&channel_lock, sflags); ++ + /* Determine mode flags to match, or leave zero for auto mode */ + as->as_vap_desired_mode = vap->iv_des_mode; + as->as_required_mode = 0; +@@ -429,8 +435,10 @@ pc_cmp_idletime(struct ieee80211_channel + if (!a->ic_idletime || !b->ic_idletime) + return 0; + +- /* a is better than b (return < 0) when a has more idle time than b */ +- return b->ic_idletime - a->ic_idletime; ++ /* a is better than b (return < 0) when a has more idle and less bias time than b */ ++ return ++ ((100 - (u32) a->ic_idletime) + ieee80211_scan_get_bias(a)) - ++ ((100 - (u32) b->ic_idletime) + ieee80211_scan_get_bias(b)); + } + + +@@ -616,6 +624,7 @@ ap_end(struct ieee80211_scan_state *ss, + struct ap_state *as = ss->ss_priv; + struct ieee80211_channel *bestchan = NULL; + struct ieee80211com *ic = NULL; ++ unsigned long sflags; + int res = 1; + + SCAN_AP_LOCK_IRQ(as); +@@ -624,8 +633,11 @@ ap_end(struct ieee80211_scan_state *ss, + ("wrong opmode %u", vap->iv_opmode)); + + ic = vap->iv_ic; ++ spin_lock_irqsave(&channel_lock, sflags); ++ ieee80211_scan_set_bss_channel(ic, NULL); + bestchan = pick_channel(ss, vap, flags); + if (bestchan == NULL) { ++ spin_unlock_irqrestore(&channel_lock, sflags); + if (ss->ss_last > 0) { + /* no suitable channel, should not happen */ + printk(KERN_ERR "%s: %s: no suitable channel! " +@@ -644,6 +656,7 @@ ap_end(struct ieee80211_scan_state *ss, + bestchan->ic_freq, bestchan->ic_flags & + ~IEEE80211_CHAN_TURBO)) == NULL) { + /* should never happen ?? */ ++ spin_unlock_irqrestore(&channel_lock, sflags); + SCAN_AP_UNLOCK_IRQ_EARLY(as); + return 0; + } +@@ -656,6 +669,9 @@ ap_end(struct ieee80211_scan_state *ss, + as->as_action = action; + as->as_selbss = se; + ++ ieee80211_scan_set_bss_channel(ic, bestchan); ++ spin_unlock_irqrestore(&channel_lock, sflags); ++ + /* Must defer action to avoid possible recursive call through + * 80211 state machine, which would result in recursive + * locking. */ -- 2.35.1