Skip to content

Commit

Permalink
mac80211: RCU-ify STA info structure access
Browse files Browse the repository at this point in the history
This makes access to the STA hash table/list use RCU to protect
against freeing of items. However, it's not a true RCU, the
copy step is missing: whenever somebody changes a STA item it
is simply updated. This is an existing race condition that is
now somewhat understandable.

This patch also fixes the race key freeing vs. STA destruction
by making sure that sta_info_destroy() is always called under
RTNL and frees the key.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
jmberg authored and linvjw committed Mar 6, 2008
1 parent 5cf121c commit d0709a6
Show file tree
Hide file tree
Showing 25 changed files with 701 additions and 486 deletions.
31 changes: 18 additions & 13 deletions drivers/net/wireless/iwlwifi/iwl-3945-rs.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,10 +471,11 @@ static void rs_tx_status(void *priv_rate,
return;
}

rcu_read_lock();

sta = sta_info_get(local, hdr->addr1);
if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
rcu_read_unlock();
IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
return;
}
Expand Down Expand Up @@ -547,7 +548,7 @@ static void rs_tx_status(void *priv_rate,

spin_unlock_irqrestore(&rs_sta->lock, flags);

sta_info_put(sta);
rcu_read_unlock();

IWL_DEBUG_RATE("leave\n");

Expand Down Expand Up @@ -658,6 +659,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,

IWL_DEBUG_RATE("enter\n");

rcu_read_lock();

sta = sta_info_get(local, hdr->addr1);

/* Send management frames and broadcast/multicast data using lowest
Expand All @@ -668,8 +671,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
!sta || !sta->rate_ctrl_priv) {
IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
sel->rate = rate_lowest(local, band, sta);
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return;
}

Expand Down Expand Up @@ -811,7 +813,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
else
sta->txrate_idx = sta->last_txrate_idx;

sta_info_put(sta);
rcu_read_unlock();

IWL_DEBUG_RATE("leave: %d\n", index);

Expand Down Expand Up @@ -843,13 +845,15 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
unsigned long now = jiffies;
u32 max_time = 0;

rcu_read_lock();

sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta) {
sta_info_put(sta);
if (sta)
IWL_DEBUG_RATE("leave - no private rate data!\n");
} else
else
IWL_DEBUG_RATE("leave - no station!\n");
rcu_read_unlock();
return sprintf(buf, "station %d not found\n", sta_id);
}

Expand Down Expand Up @@ -890,7 +894,7 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
i = j;
}
spin_unlock_irqrestore(&rs_sta->lock, flags);
sta_info_put(sta);
rcu_read_unlock();

/* Display the average rate of all samples taken.
*
Expand Down Expand Up @@ -927,11 +931,12 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
return;
}

rcu_read_lock();

sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
IWL_DEBUG_RATE("leave - no private rate data!\n");
rcu_read_unlock();
return;
}

Expand All @@ -958,7 +963,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
break;
}

sta_info_put(sta);
rcu_read_unlock();
spin_unlock_irqrestore(&rs_sta->lock, flags);

rssi = priv->last_rx_rssi;
Expand Down
27 changes: 15 additions & 12 deletions drivers/net/wireless/iwlwifi/iwl-4965-rs.c
Original file line number Diff line number Diff line change
Expand Up @@ -847,12 +847,12 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
if (retries > 15)
retries = 15;

rcu_read_lock();

sta = sta_info_get(local, hdr->addr1);

if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return;
}

Expand Down Expand Up @@ -891,7 +891,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
if ((rs_index < 0) || (rs_index >= IWL_RATE_COUNT)) {
IWL_DEBUG_RATE("bad rate index at: %d rate 0x%X\n",
rs_index, tx_mcs.rate_n_flags);
sta_info_put(sta);
rcu_read_unlock();
return;
}

Expand All @@ -909,7 +909,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
IWL_DEBUG_RATE("initial rate does not match 0x%x 0x%x\n",
tx_mcs.rate_n_flags,
le32_to_cpu(table->rs_table[0].rate_n_flags));
sta_info_put(sta);
rcu_read_unlock();
return;
}

Expand Down Expand Up @@ -1025,7 +1025,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,

/* See if there's a better rate or modulation mode to try. */
rs_rate_scale_perform(priv, dev, hdr, sta);
sta_info_put(sta);
rcu_read_unlock();
return;
}

Expand Down Expand Up @@ -2219,6 +2219,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,

IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n");

rcu_read_lock();

sta = sta_info_get(local, hdr->addr1);

/* Send management frames and broadcast/multicast data using lowest
Expand All @@ -2227,8 +2229,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
!sta || !sta->rate_ctrl_priv) {
sel->rate = rate_lowest(local, sband, sta);
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return;
}

Expand Down Expand Up @@ -2261,7 +2262,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
sel->rate = rate_lowest(local, sband, sta);
return;
}
sta_info_put(sta);
rcu_read_unlock();

sel->rate = &priv->ieee_rates[i];
}
Expand Down Expand Up @@ -2735,13 +2736,15 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
u32 max_time = 0;
u8 lq_type, antenna;

rcu_read_lock();

sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta) {
sta_info_put(sta);
if (sta)
IWL_DEBUG_RATE("leave - no private rate data!\n");
} else
else
IWL_DEBUG_RATE("leave - no station!\n");
rcu_read_unlock();
return sprintf(buf, "station %d not found\n", sta_id);
}

Expand Down Expand Up @@ -2808,7 +2811,7 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
"active_search %d rate index %d\n", lq_type, antenna,
lq_sta->search_better_tbl, sta->last_txrate_idx);

sta_info_put(sta);
rcu_read_unlock();
return cnt;
}

Expand Down
Loading

0 comments on commit d0709a6

Please sign in to comment.