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

Commit 34e89507 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

mac80211: allow station add/remove to sleep



Many drivers would like to sleep during station
addition and removal, and currently have a high
complexity there from not being able to.

This introduces two new callbacks sta_add() and
sta_remove() that drivers can implement instead
of using sta_notify() and that can sleep, and
the new sta_add() callback is also allowed to
fail.

The reason we didn't do this previously is that
the IBSS code wants to insert stations from the
RX path, which is a tasklet, so cannot sleep.
This patch will keep the station allocation in
that path, but moves adding the station to the
driver out of line. Since the addition can now
fail, we can have IBSS peer structs the driver
rejected -- in that case we still talk to the
station but never tell the driver about it in
the control.sta pointer. If there will ever be
a driver that has a low limit on the number of
stations and that cannot talk to any stations
that are not known to it, we need to do come up
with a new strategy of handling larger IBSSs,
maybe quicker expiry or rejecting peers.

Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 070bb547
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -814,7 +814,7 @@ enum set_key_cmd {
 * mac80211, any ieee80211_sta pointer you get access to must
 * either be protected by rcu_read_lock() explicitly or implicitly,
 * or you must take good care to not use such a pointer after a
 * call to your sta_notify callback that removed it.
 * call to your sta_remove callback that removed it.
 *
 * @addr: MAC address
 * @aid: AID we assigned to the station if we're an AP
@@ -840,8 +840,8 @@ struct ieee80211_sta {
 * indicates addition and removal of a station to station table,
 * or if a associated station made a power state transition.
 *
 * @STA_NOTIFY_ADD: a station was added to the station table
 * @STA_NOTIFY_REMOVE: a station being removed from the station table
 * @STA_NOTIFY_ADD: (DEPRECATED) a station was added to the station table
 * @STA_NOTIFY_REMOVE: (DEPRECATED) a station being removed from the station table
 * @STA_NOTIFY_SLEEP: a station is now sleeping
 * @STA_NOTIFY_AWAKE: a sleeping station woke up
 */
@@ -1534,9 +1534,14 @@ enum ieee80211_ampdu_mlme_action {
 * @set_rts_threshold: Configuration of RTS threshold (if device needs it)
 *	The callback can sleep.
 *
 * @sta_notify: Notifies low level driver about addition, removal or power
 *	state transition of an associated station, AP,  IBSS/WDS/mesh peer etc.
 *	Must be atomic.
 * @sta_add: Notifies low level driver about addition of an associated station,
 *	AP, IBSS/WDS/mesh peer etc. This callback can sleep.
 *
 * @sta_remove: Notifies low level driver about removal of an associated
 *	station, AP, IBSS/WDS/mesh peer etc. This callback can sleep.
 *
 * @sta_notify: Notifies low level driver about power state transition of an
 *	associated station, AP,  IBSS/WDS/mesh peer etc. Must be atomic.
 *
 * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
 *	bursting) for a hardware TX queue.
@@ -1635,6 +1640,10 @@ struct ieee80211_ops {
	void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
			     u32 *iv32, u16 *iv16);
	int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value);
	int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
		       struct ieee80211_sta *sta);
	int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			  struct ieee80211_sta *sta);
	void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			enum sta_notify_cmd, struct ieee80211_sta *sta);
	int (*conf_tx)(struct ieee80211_hw *hw, u16 queue,
+4 −19
Original line number Diff line number Diff line
@@ -747,9 +747,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
	layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
		sdata->vif.type == NL80211_IFTYPE_AP;

	rcu_read_lock();

	err = sta_info_insert(sta);
	err = sta_info_insert_rcu(sta);
	if (err) {
		rcu_read_unlock();
		return err;
@@ -768,26 +766,13 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
{
	struct ieee80211_local *local = wiphy_priv(wiphy);
	struct ieee80211_sub_if_data *sdata;
	struct sta_info *sta;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	if (mac) {
		rcu_read_lock();

		sta = sta_info_get_bss(sdata, mac);
		if (!sta) {
			rcu_read_unlock();
			return -ENOENT;
		}

		sta_info_unlink(&sta);
		rcu_read_unlock();
	if (mac)
		return sta_info_destroy_addr_bss(sdata, mac);

		sta_info_destroy(sta);
	} else
	sta_info_flush(local, sdata);

	return 0;
}

+34 −0
Original line number Diff line number Diff line
@@ -243,6 +243,40 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
	trace_drv_sta_notify(local, sdata, cmd, sta);
}

static inline int drv_sta_add(struct ieee80211_local *local,
			      struct ieee80211_sub_if_data *sdata,
			      struct ieee80211_sta *sta)
{
	int ret = 0;

	might_sleep();

	if (local->ops->sta_add)
		ret = local->ops->sta_add(&local->hw, &sdata->vif, sta);
	else if (local->ops->sta_notify)
		local->ops->sta_notify(&local->hw, &sdata->vif,
					STA_NOTIFY_ADD, sta);

	trace_drv_sta_add(local, sdata, sta, ret);

	return ret;
}

static inline void drv_sta_remove(struct ieee80211_local *local,
				  struct ieee80211_sub_if_data *sdata,
				  struct ieee80211_sta *sta)
{
	might_sleep();

	if (local->ops->sta_remove)
		local->ops->sta_remove(&local->hw, &sdata->vif, sta);
	else if (local->ops->sta_notify)
		local->ops->sta_notify(&local->hw, &sdata->vif,
					STA_NOTIFY_REMOVE, sta);

	trace_drv_sta_remove(local, sdata, sta);
}

static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
			      const struct ieee80211_tx_queue_params *params)
{
+52 −0
Original line number Diff line number Diff line
@@ -545,6 +545,58 @@ TRACE_EVENT(drv_sta_notify,
	)
);

TRACE_EVENT(drv_sta_add,
	TP_PROTO(struct ieee80211_local *local,
		 struct ieee80211_sub_if_data *sdata,
		 struct ieee80211_sta *sta, int ret),

	TP_ARGS(local, sdata, sta, ret),

	TP_STRUCT__entry(
		LOCAL_ENTRY
		VIF_ENTRY
		STA_ENTRY
		__field(int, ret)
	),

	TP_fast_assign(
		LOCAL_ASSIGN;
		VIF_ASSIGN;
		STA_ASSIGN;
		__entry->ret = ret;
	),

	TP_printk(
		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " ret:%d",
		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ret
	)
);

TRACE_EVENT(drv_sta_remove,
	TP_PROTO(struct ieee80211_local *local,
		 struct ieee80211_sub_if_data *sdata,
		 struct ieee80211_sta *sta),

	TP_ARGS(local, sdata, sta),

	TP_STRUCT__entry(
		LOCAL_ENTRY
		VIF_ENTRY
		STA_ENTRY
	),

	TP_fast_assign(
		LOCAL_ASSIGN;
		VIF_ASSIGN;
		STA_ASSIGN;
	),

	TP_printk(
		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT,
		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
	)
);

TRACE_EVENT(drv_conf_tx,
	TP_PROTO(struct ieee80211_local *local, u16 queue,
		 const struct ieee80211_tx_queue_params *params,
+13 −9
Original line number Diff line number Diff line
@@ -275,10 +275,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
				    (unsigned long long) supp_rates,
				    (unsigned long long) sta->sta.supp_rates[band]);
#endif
		} else
			ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);

			rcu_read_unlock();
		} else {
			rcu_read_unlock();
			ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
					       supp_rates, GFP_KERNEL);
		}
	}

	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
@@ -368,7 +370,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
		       sdata->name, mgmt->bssid);
#endif
		ieee80211_sta_join_ibss(sdata, bss);
		ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
		ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
				       supp_rates, GFP_KERNEL);
	}

 put_bss:
@@ -381,7 +384,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 * must be callable in atomic context.
 */
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
					u8 *bssid,u8 *addr, u32 supp_rates)
					u8 *bssid,u8 *addr, u32 supp_rates,
					gfp_t gfp)
{
	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
	struct ieee80211_local *local = sdata->local;
@@ -410,7 +414,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
	       wiphy_name(local->hw.wiphy), addr, sdata->name);
#endif

	sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
	sta = sta_info_alloc(sdata, addr, gfp);
	if (!sta)
		return NULL;

@@ -422,9 +426,9 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,

	rate_control_rate_init(sta);

	/* If it fails, maybe we raced another insertion? */
	if (sta_info_insert(sta))
		return NULL;

		return sta_info_get(sdata, addr);
	return sta;
}

Loading