瀏覽代碼

madwifi: improve the autochannel decision making on systems with multiple cards

SVN-Revision: 14748
Felix Fietkau 17 年之前
父節點
當前提交
165f60bba9
共有 1 個文件被更改,包括 347 次插入0 次删除
  1. 347 0
      package/madwifi/patches/460-autochannel_multi.patch

+ 347 - 0
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. */