Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit be8755e1 authored by Michael Wu's avatar Michael Wu Committed by David S. Miller
Browse files

[MAC80211]: improve locking of sta_info related structures



The sta_info code has some awkward locking which prevents some driver
callbacks from being allowed to sleep. This patch makes the locking more
focused so code that calls driver callbacks are allowed to sleep. It also
converts sta_lock to a rwlock.

Signed-off-by: default avatarMichael Wu <flamingice@sourmilk.net>
Signed-off-by: default avatarJiri Benc <jbenc@suse.cz>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent c2d1560a
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -626,8 +626,7 @@ struct ieee80211_ops {
	 * station hwaddr for individual keys. aid of the station is given
	 * to help low-level driver in selecting which key->hw_key_idx to use
	 * for this key. TX control data will use the hw_key_idx selected by
	 * the low-level driver.
	 * Must be atomic. */
	 * the low-level driver. */
	int (*set_key)(struct ieee80211_hw *hw, set_key_cmd cmd,
		       u8 *addr, struct ieee80211_key_conf *key, int aid);

+3 −3
Original line number Diff line number Diff line
@@ -628,8 +628,8 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
	/* Remove STA entry for the old peer */
	sta = sta_info_get(local, sdata->u.wds.remote_addr);
	if (sta) {
		sta_info_free(sta);
		sta_info_put(sta);
		sta_info_free(sta, 0);
	} else {
		printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
		       "peer " MAC_FMT "\n",
@@ -776,13 +776,13 @@ static void ieee80211_stat_refresh(unsigned long data)
		return;

	/* go through all stations */
	spin_lock_bh(&local->sta_lock);
	read_lock_bh(&local->sta_lock);
	list_for_each_entry(sta, &local->sta_list, list) {
		sta->channel_use = (sta->channel_use_raw / local->stat_time) /
			CHAN_UTIL_PER_10MS;
		sta->channel_use_raw = 0;
	}
	spin_unlock_bh(&local->sta_lock);
	read_unlock_bh(&local->sta_lock);

	/* go through all subinterfaces */
	read_lock(&local->sub_if_lock);
+5 −6
Original line number Diff line number Diff line
@@ -417,10 +417,9 @@ struct ieee80211_local {
	struct sk_buff_head skb_queue_unreliable;

	/* Station data structures */
	spinlock_t sta_lock; /* mutex for STA data structures */
	rwlock_t sta_lock; /* protects STA data structures */
	int num_sta; /* number of stations in sta_list */
	struct list_head sta_list;
	struct list_head deleted_sta_list;
	struct sta_info *sta_hash[STA_HASH_SIZE];
	struct timer_list sta_cleanup;

@@ -669,9 +668,9 @@ static inline void __bss_tim_set(struct ieee80211_if_ap *bss, int aid)
static inline void bss_tim_set(struct ieee80211_local *local,
			       struct ieee80211_if_ap *bss, int aid)
{
	spin_lock_bh(&local->sta_lock);
	read_lock_bh(&local->sta_lock);
	__bss_tim_set(bss, aid);
	spin_unlock_bh(&local->sta_lock);
	read_unlock_bh(&local->sta_lock);
}

static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid)
@@ -686,9 +685,9 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid)
static inline void bss_tim_clear(struct ieee80211_local *local,
				 struct ieee80211_if_ap *bss, int aid)
{
	spin_lock_bh(&local->sta_lock);
	read_lock_bh(&local->sta_lock);
	__bss_tim_clear(bss, aid);
	spin_unlock_bh(&local->sta_lock);
	read_unlock_bh(&local->sta_lock);
}

/**
+1 −1
Original line number Diff line number Diff line
@@ -272,8 +272,8 @@ void ieee80211_if_reinit(struct net_device *dev)
	case IEEE80211_IF_TYPE_WDS:
		sta = sta_info_get(local, sdata->u.wds.remote_addr);
		if (sta) {
			sta_info_free(sta);
			sta_info_put(sta);
			sta_info_free(sta, 0);
		} else {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
			printk(KERN_DEBUG "%s: Someone had deleted my STA "
+14 −6
Original line number Diff line number Diff line
@@ -773,7 +773,7 @@ static void ieee80211_associated(struct net_device *dev,
				       "range\n",
				       dev->name, MAC_ARG(ifsta->bssid));
				disassoc = 1;
				sta_info_free(sta, 0);
				sta_info_free(sta);
				ifsta->probereq_poll = 0;
			} else {
				ieee80211_send_probe_req(dev, ifsta->bssid,
@@ -1890,7 +1890,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
	int active = 0;
	struct sta_info *sta;

	spin_lock_bh(&local->sta_lock);
	read_lock_bh(&local->sta_lock);
	list_for_each_entry(sta, &local->sta_list, list) {
		if (sta->dev == dev &&
		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
@@ -1899,7 +1899,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
			break;
		}
	}
	spin_unlock_bh(&local->sta_lock);
	read_unlock_bh(&local->sta_lock);

	return active;
}
@@ -1909,16 +1909,24 @@ static void ieee80211_sta_expire(struct net_device *dev)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct sta_info *sta, *tmp;
	LIST_HEAD(tmp_list);

	spin_lock_bh(&local->sta_lock);
	write_lock_bh(&local->sta_lock);
	list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
		if (time_after(jiffies, sta->last_rx +
			       IEEE80211_IBSS_INACTIVITY_LIMIT)) {
			printk(KERN_DEBUG "%s: expiring inactive STA " MAC_FMT
			       "\n", dev->name, MAC_ARG(sta->addr));
			sta_info_free(sta, 1);
			__sta_info_get(sta);
			sta_info_remove(sta);
			list_add(&sta->list, &tmp_list);
		}
	write_unlock_bh(&local->sta_lock);

	list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
		sta_info_free(sta);
		sta_info_put(sta);
	}
	spin_unlock_bh(&local->sta_lock);
}


Loading