mac80211: avoid scheduling while atomic in mesh_rx_plink_frame

While mesh_rx_plink_frame holds sta->lock...

mesh_rx_plink_frame ->
	mesh_plink_inc_estab_count ->
		ieee80211_bss_info_change_notify

...but ieee80211_bss_info_change_notify is allowed to sleep.  A driver
taking advantage of that allowance can cause a scheduling while
atomic bug.  Similar paths exist for mesh_plink_dec_estab_count,
so work around those as well.

http://bugzilla.kernel.org/show_bug.cgi?id=16099

Also, correct a minor kerneldoc comment error (mismatched function names).

Signed-off-by: John W. Linville <linville@tuxdriver.com>
Cc: stable@kernel.org
This commit is contained in:
John W. Linville 2010-06-21 17:14:07 -04:00
parent de66bfd85c
commit c937019761

View file

@ -65,7 +65,6 @@ void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
{ {
atomic_inc(&sdata->u.mesh.mshstats.estab_plinks); atomic_inc(&sdata->u.mesh.mshstats.estab_plinks);
mesh_accept_plinks_update(sdata); mesh_accept_plinks_update(sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
} }
static inline static inline
@ -73,7 +72,6 @@ void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
{ {
atomic_dec(&sdata->u.mesh.mshstats.estab_plinks); atomic_dec(&sdata->u.mesh.mshstats.estab_plinks);
mesh_accept_plinks_update(sdata); mesh_accept_plinks_update(sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
} }
/** /**
@ -115,7 +113,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
} }
/** /**
* mesh_plink_deactivate - deactivate mesh peer link * __mesh_plink_deactivate - deactivate mesh peer link
* *
* @sta: mesh peer link to deactivate * @sta: mesh peer link to deactivate
* *
@ -123,18 +121,23 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
* *
* Locking: the caller must hold sta->lock * Locking: the caller must hold sta->lock
*/ */
static void __mesh_plink_deactivate(struct sta_info *sta) static bool __mesh_plink_deactivate(struct sta_info *sta)
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
bool deactivated = false;
if (sta->plink_state == PLINK_ESTAB) if (sta->plink_state == PLINK_ESTAB) {
mesh_plink_dec_estab_count(sdata); mesh_plink_dec_estab_count(sdata);
deactivated = true;
}
sta->plink_state = PLINK_BLOCKED; sta->plink_state = PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta); mesh_path_flush_by_nexthop(sta);
return deactivated;
} }
/** /**
* __mesh_plink_deactivate - deactivate mesh peer link * mesh_plink_deactivate - deactivate mesh peer link
* *
* @sta: mesh peer link to deactivate * @sta: mesh peer link to deactivate
* *
@ -142,9 +145,15 @@ static void __mesh_plink_deactivate(struct sta_info *sta)
*/ */
void mesh_plink_deactivate(struct sta_info *sta) void mesh_plink_deactivate(struct sta_info *sta)
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata;
bool deactivated;
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->lock);
__mesh_plink_deactivate(sta); deactivated = __mesh_plink_deactivate(sta);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
if (deactivated)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
} }
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
@ -381,10 +390,16 @@ int mesh_plink_open(struct sta_info *sta)
void mesh_plink_block(struct sta_info *sta) void mesh_plink_block(struct sta_info *sta)
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata;
bool deactivated;
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->lock);
__mesh_plink_deactivate(sta); deactivated = __mesh_plink_deactivate(sta);
sta->plink_state = PLINK_BLOCKED; sta->plink_state = PLINK_BLOCKED;
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
if (deactivated)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
} }
@ -397,6 +412,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
enum plink_event event; enum plink_event event;
enum plink_frame_type ftype; enum plink_frame_type ftype;
size_t baselen; size_t baselen;
bool deactivated;
u8 ie_len; u8 ie_len;
u8 *baseaddr; u8 *baseaddr;
__le16 plid, llid, reason; __le16 plid, llid, reason;
@ -651,8 +667,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
case CNF_ACPT: case CNF_ACPT:
del_timer(&sta->plink_timer); del_timer(&sta->plink_timer);
sta->plink_state = PLINK_ESTAB; sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
mesh_plink_inc_estab_count(sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
mpl_dbg("Mesh plink with %pM ESTABLISHED\n", mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
sta->sta.addr); sta->sta.addr);
break; break;
@ -684,8 +701,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
case OPN_ACPT: case OPN_ACPT:
del_timer(&sta->plink_timer); del_timer(&sta->plink_timer);
sta->plink_state = PLINK_ESTAB; sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
mesh_plink_inc_estab_count(sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
mpl_dbg("Mesh plink with %pM ESTABLISHED\n", mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
sta->sta.addr); sta->sta.addr);
mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
@ -702,11 +720,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
case CLS_ACPT: case CLS_ACPT:
reason = cpu_to_le16(MESH_CLOSE_RCVD); reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason; sta->reason = reason;
__mesh_plink_deactivate(sta); deactivated = __mesh_plink_deactivate(sta);
sta->plink_state = PLINK_HOLDING; sta->plink_state = PLINK_HOLDING;
llid = sta->llid; llid = sta->llid;
mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
if (deactivated)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
plid, reason); plid, reason);
break; break;