mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-02-17 22:26:42 +07:00
mac80211: add sta_state callback
(based on Eliad's patch) Add a callback to notify the low-level driver whenever the state of a station changes. The driver is only notified when the station is actually in the mac80211 hash table, not for pre-insert state transitions. To allow the driver to replace sta_add/remove calls with this, call extra transitions with the NOTEXIST state. This callback can fail, so we need to be careful in handling it when a station is inserted, particularly in the IBSS case where we still keep the station entry around for mac80211 purposes. Signed-off-by: Eliad Peller <eliad@wizery.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
71ec375c75
commit
f09603a259
@ -981,6 +981,25 @@ enum set_key_cmd {
|
|||||||
SET_KEY, DISABLE_KEY,
|
SET_KEY, DISABLE_KEY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ieee80211_sta_state - station state
|
||||||
|
*
|
||||||
|
* @IEEE80211_STA_NOTEXIST: station doesn't exist at all,
|
||||||
|
* this is a special state for add/remove transitions
|
||||||
|
* @IEEE80211_STA_NONE: station exists without special state
|
||||||
|
* @IEEE80211_STA_AUTH: station is authenticated
|
||||||
|
* @IEEE80211_STA_ASSOC: station is associated
|
||||||
|
* @IEEE80211_STA_AUTHORIZED: station is authorized (802.1X)
|
||||||
|
*/
|
||||||
|
enum ieee80211_sta_state {
|
||||||
|
/* NOTE: These need to be ordered correctly! */
|
||||||
|
IEEE80211_STA_NOTEXIST,
|
||||||
|
IEEE80211_STA_NONE,
|
||||||
|
IEEE80211_STA_AUTH,
|
||||||
|
IEEE80211_STA_ASSOC,
|
||||||
|
IEEE80211_STA_AUTHORIZED,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ieee80211_sta - station table entry
|
* struct ieee80211_sta - station table entry
|
||||||
*
|
*
|
||||||
@ -1974,6 +1993,13 @@ enum ieee80211_frame_release_type {
|
|||||||
* in AP mode, this callback will not be called when the flag
|
* in AP mode, this callback will not be called when the flag
|
||||||
* %IEEE80211_HW_AP_LINK_PS is set. Must be atomic.
|
* %IEEE80211_HW_AP_LINK_PS is set. Must be atomic.
|
||||||
*
|
*
|
||||||
|
* @sta_state: Notifies low level driver about state transition of a
|
||||||
|
* station (which can be the AP, a client, IBSS/WDS/mesh peer etc.)
|
||||||
|
* This callback is mutually exclusive with @sta_add/@sta_remove.
|
||||||
|
* It must not fail for down transitions but may fail for transitions
|
||||||
|
* up the list of states.
|
||||||
|
* The callback can sleep.
|
||||||
|
*
|
||||||
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
|
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
|
||||||
* bursting) for a hardware TX queue.
|
* bursting) for a hardware TX queue.
|
||||||
* Returns a negative error code on failure.
|
* Returns a negative error code on failure.
|
||||||
@ -2193,6 +2219,10 @@ struct ieee80211_ops {
|
|||||||
struct ieee80211_sta *sta);
|
struct ieee80211_sta *sta);
|
||||||
void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
enum sta_notify_cmd, struct ieee80211_sta *sta);
|
enum sta_notify_cmd, struct ieee80211_sta *sta);
|
||||||
|
int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
|
struct ieee80211_sta *sta,
|
||||||
|
enum ieee80211_sta_state old_state,
|
||||||
|
enum ieee80211_sta_state new_state);
|
||||||
int (*conf_tx)(struct ieee80211_hw *hw,
|
int (*conf_tx)(struct ieee80211_hw *hw,
|
||||||
struct ieee80211_vif *vif, u16 queue,
|
struct ieee80211_vif *vif, u16 queue,
|
||||||
const struct ieee80211_tx_queue_params *params);
|
const struct ieee80211_tx_queue_params *params);
|
||||||
|
@ -478,6 +478,28 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
|
|||||||
trace_drv_return_void(local);
|
trace_drv_return_void(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline __must_check
|
||||||
|
int drv_sta_state(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct sta_info *sta,
|
||||||
|
enum ieee80211_sta_state old_state,
|
||||||
|
enum ieee80211_sta_state new_state)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
might_sleep();
|
||||||
|
|
||||||
|
sdata = get_bss_sdata(sdata);
|
||||||
|
check_sdata_in_driver(sdata);
|
||||||
|
|
||||||
|
trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
|
||||||
|
if (local->ops->sta_state)
|
||||||
|
ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
|
||||||
|
old_state, new_state);
|
||||||
|
trace_drv_return_int(local, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int drv_conf_tx(struct ieee80211_local *local,
|
static inline int drv_conf_tx(struct ieee80211_local *local,
|
||||||
struct ieee80211_sub_if_data *sdata, u16 queue,
|
struct ieee80211_sub_if_data *sdata, u16 queue,
|
||||||
const struct ieee80211_tx_queue_params *params)
|
const struct ieee80211_tx_queue_params *params)
|
||||||
|
@ -635,6 +635,38 @@ TRACE_EVENT(drv_sta_notify,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(drv_sta_state,
|
||||||
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_sta *sta,
|
||||||
|
enum ieee80211_sta_state old_state,
|
||||||
|
enum ieee80211_sta_state new_state),
|
||||||
|
|
||||||
|
TP_ARGS(local, sdata, sta, old_state, new_state),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
LOCAL_ENTRY
|
||||||
|
VIF_ENTRY
|
||||||
|
STA_ENTRY
|
||||||
|
__field(u32, old_state)
|
||||||
|
__field(u32, new_state)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
LOCAL_ASSIGN;
|
||||||
|
VIF_ASSIGN;
|
||||||
|
STA_ASSIGN;
|
||||||
|
__entry->old_state = old_state;
|
||||||
|
__entry->new_state = new_state;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(
|
||||||
|
LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " state: %d->%d",
|
||||||
|
LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG,
|
||||||
|
__entry->old_state, __entry->new_state
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
TRACE_EVENT(drv_sta_add,
|
TRACE_EVENT(drv_sta_add,
|
||||||
TP_PROTO(struct ieee80211_local *local,
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
struct ieee80211_sub_if_data *sdata,
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
@ -535,6 +535,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
|||||||
int priv_size, i;
|
int priv_size, i;
|
||||||
struct wiphy *wiphy;
|
struct wiphy *wiphy;
|
||||||
|
|
||||||
|
if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/* Ensure 32-byte alignment of our private data and hw private data.
|
/* Ensure 32-byte alignment of our private data and hw private data.
|
||||||
* We use the wiphy priv data for both our ieee80211_local and for
|
* We use the wiphy priv data for both our ieee80211_local and for
|
||||||
* the driver's private data
|
* the driver's private data
|
||||||
|
@ -97,9 +97,17 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
|||||||
/* tear down aggregation sessions and remove STAs */
|
/* tear down aggregation sessions and remove STAs */
|
||||||
mutex_lock(&local->sta_mtx);
|
mutex_lock(&local->sta_mtx);
|
||||||
list_for_each_entry(sta, &local->sta_list, list) {
|
list_for_each_entry(sta, &local->sta_list, list) {
|
||||||
if (sta->uploaded)
|
if (sta->uploaded) {
|
||||||
|
enum ieee80211_sta_state state;
|
||||||
|
|
||||||
drv_sta_remove(local, sta->sdata, &sta->sta);
|
drv_sta_remove(local, sta->sdata, &sta->sta);
|
||||||
|
|
||||||
|
state = sta->sta_state;
|
||||||
|
for (; state > IEEE80211_STA_NOTEXIST; state--)
|
||||||
|
WARN_ON(drv_sta_state(local, sdata, sta,
|
||||||
|
state, state - 1));
|
||||||
|
}
|
||||||
|
|
||||||
mesh_plink_quiesce(sta);
|
mesh_plink_quiesce(sta);
|
||||||
}
|
}
|
||||||
mutex_unlock(&local->sta_mtx);
|
mutex_unlock(&local->sta_mtx);
|
||||||
|
@ -351,6 +351,38 @@ static int sta_info_insert_check(struct sta_info *sta)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sta_info_insert_drv_state(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
enum ieee80211_sta_state state;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) {
|
||||||
|
err = drv_sta_state(local, sdata, sta, state, state + 1);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
sta->uploaded = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
|
||||||
|
printk(KERN_DEBUG
|
||||||
|
"%s: failed to move IBSS STA %pM to state %d (%d) - keeping it anyway.\n",
|
||||||
|
sdata->name, sta->sta.addr, state + 1, err);
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unwind on error */
|
||||||
|
for (; state > IEEE80211_STA_NOTEXIST; state--)
|
||||||
|
WARN_ON(drv_sta_state(local, sdata, sta, state, state - 1));
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* should be called with sta_mtx locked
|
* should be called with sta_mtx locked
|
||||||
* this function replaces the mutex lock
|
* this function replaces the mutex lock
|
||||||
@ -392,8 +424,11 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
|
|||||||
printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to "
|
printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to "
|
||||||
"driver (%d) - keeping it anyway.\n",
|
"driver (%d) - keeping it anyway.\n",
|
||||||
sdata->name, sta->sta.addr, err);
|
sdata->name, sta->sta.addr, err);
|
||||||
} else
|
} else {
|
||||||
sta->uploaded = true;
|
err = sta_info_insert_drv_state(local, sdata, sta);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dummy_reinsert) {
|
if (!dummy_reinsert) {
|
||||||
@ -759,15 +794,19 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
|
|||||||
RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
|
RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
|
||||||
|
|
||||||
while (sta->sta_state > IEEE80211_STA_NONE) {
|
while (sta->sta_state > IEEE80211_STA_NONE) {
|
||||||
int err = sta_info_move_state(sta, sta->sta_state - 1);
|
ret = sta_info_move_state(sta, sta->sta_state - 1);
|
||||||
if (err) {
|
if (ret) {
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sta->uploaded)
|
if (sta->uploaded) {
|
||||||
drv_sta_remove(local, sdata, &sta->sta);
|
drv_sta_remove(local, sdata, &sta->sta);
|
||||||
|
ret = drv_sta_state(local, sdata, sta, IEEE80211_STA_NONE,
|
||||||
|
IEEE80211_STA_NOTEXIST);
|
||||||
|
WARN_ON_ONCE(ret != 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At this point, after we wait for an RCU grace period,
|
* At this point, after we wait for an RCU grace period,
|
||||||
@ -1404,37 +1443,25 @@ int sta_info_move_state(struct sta_info *sta,
|
|||||||
if (sta->sta_state == new_state)
|
if (sta->sta_state == new_state)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* check allowed transitions first */
|
||||||
|
|
||||||
switch (new_state) {
|
switch (new_state) {
|
||||||
case IEEE80211_STA_NONE:
|
case IEEE80211_STA_NONE:
|
||||||
if (sta->sta_state == IEEE80211_STA_AUTH)
|
if (sta->sta_state != IEEE80211_STA_AUTH)
|
||||||
clear_bit(WLAN_STA_AUTH, &sta->_flags);
|
|
||||||
else
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
break;
|
break;
|
||||||
case IEEE80211_STA_AUTH:
|
case IEEE80211_STA_AUTH:
|
||||||
if (sta->sta_state == IEEE80211_STA_NONE)
|
if (sta->sta_state != IEEE80211_STA_NONE &&
|
||||||
set_bit(WLAN_STA_AUTH, &sta->_flags);
|
sta->sta_state != IEEE80211_STA_ASSOC)
|
||||||
else if (sta->sta_state == IEEE80211_STA_ASSOC)
|
|
||||||
clear_bit(WLAN_STA_ASSOC, &sta->_flags);
|
|
||||||
else
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
break;
|
break;
|
||||||
case IEEE80211_STA_ASSOC:
|
case IEEE80211_STA_ASSOC:
|
||||||
if (sta->sta_state == IEEE80211_STA_AUTH) {
|
if (sta->sta_state != IEEE80211_STA_AUTH &&
|
||||||
set_bit(WLAN_STA_ASSOC, &sta->_flags);
|
sta->sta_state != IEEE80211_STA_AUTHORIZED)
|
||||||
} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
|
|
||||||
if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
|
|
||||||
atomic_dec(&sta->sdata->u.ap.num_sta_authorized);
|
|
||||||
clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
|
|
||||||
} else
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
break;
|
break;
|
||||||
case IEEE80211_STA_AUTHORIZED:
|
case IEEE80211_STA_AUTHORIZED:
|
||||||
if (sta->sta_state == IEEE80211_STA_ASSOC) {
|
if (sta->sta_state != IEEE80211_STA_ASSOC)
|
||||||
if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
|
|
||||||
atomic_inc(&sta->sdata->u.ap.num_sta_authorized);
|
|
||||||
set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
|
|
||||||
} else
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1444,6 +1471,51 @@ int sta_info_move_state(struct sta_info *sta,
|
|||||||
|
|
||||||
printk(KERN_DEBUG "%s: moving STA %pM to state %d\n",
|
printk(KERN_DEBUG "%s: moving STA %pM to state %d\n",
|
||||||
sta->sdata->name, sta->sta.addr, new_state);
|
sta->sdata->name, sta->sta.addr, new_state);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* notify the driver before the actual changes so it can
|
||||||
|
* fail the transition
|
||||||
|
*/
|
||||||
|
if (test_sta_flag(sta, WLAN_STA_INSERTED)) {
|
||||||
|
int err = drv_sta_state(sta->local, sta->sdata, sta,
|
||||||
|
sta->sta_state, new_state);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reflect the change in all state variables */
|
||||||
|
|
||||||
|
switch (new_state) {
|
||||||
|
case IEEE80211_STA_NONE:
|
||||||
|
if (sta->sta_state == IEEE80211_STA_AUTH)
|
||||||
|
clear_bit(WLAN_STA_AUTH, &sta->_flags);
|
||||||
|
break;
|
||||||
|
case IEEE80211_STA_AUTH:
|
||||||
|
if (sta->sta_state == IEEE80211_STA_NONE)
|
||||||
|
set_bit(WLAN_STA_AUTH, &sta->_flags);
|
||||||
|
else if (sta->sta_state == IEEE80211_STA_ASSOC)
|
||||||
|
clear_bit(WLAN_STA_ASSOC, &sta->_flags);
|
||||||
|
break;
|
||||||
|
case IEEE80211_STA_ASSOC:
|
||||||
|
if (sta->sta_state == IEEE80211_STA_AUTH) {
|
||||||
|
set_bit(WLAN_STA_ASSOC, &sta->_flags);
|
||||||
|
} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
|
||||||
|
if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
|
||||||
|
atomic_dec(&sta->sdata->u.ap.num_sta_authorized);
|
||||||
|
clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IEEE80211_STA_AUTHORIZED:
|
||||||
|
if (sta->sta_state == IEEE80211_STA_ASSOC) {
|
||||||
|
if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
|
||||||
|
atomic_inc(&sta->sdata->u.ap.num_sta_authorized);
|
||||||
|
set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
sta->sta_state = new_state;
|
sta->sta_state = new_state;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -75,15 +75,6 @@ enum ieee80211_sta_info_flags {
|
|||||||
WLAN_STA_INSERTED,
|
WLAN_STA_INSERTED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ieee80211_sta_state {
|
|
||||||
/* NOTE: These need to be ordered correctly! */
|
|
||||||
IEEE80211_STA_NOTEXIST,
|
|
||||||
IEEE80211_STA_NONE,
|
|
||||||
IEEE80211_STA_AUTH,
|
|
||||||
IEEE80211_STA_ASSOC,
|
|
||||||
IEEE80211_STA_AUTHORIZED,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define STA_TID_NUM 16
|
#define STA_TID_NUM 16
|
||||||
#define ADDBA_RESP_INTERVAL HZ
|
#define ADDBA_RESP_INTERVAL HZ
|
||||||
#define HT_AGG_MAX_RETRIES 15
|
#define HT_AGG_MAX_RETRIES 15
|
||||||
|
@ -1184,8 +1184,16 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||||||
/* add STAs back */
|
/* add STAs back */
|
||||||
mutex_lock(&local->sta_mtx);
|
mutex_lock(&local->sta_mtx);
|
||||||
list_for_each_entry(sta, &local->sta_list, list) {
|
list_for_each_entry(sta, &local->sta_list, list) {
|
||||||
if (sta->uploaded)
|
if (sta->uploaded) {
|
||||||
|
enum ieee80211_sta_state state;
|
||||||
|
|
||||||
WARN_ON(drv_sta_add(local, sta->sdata, &sta->sta));
|
WARN_ON(drv_sta_add(local, sta->sdata, &sta->sta));
|
||||||
|
|
||||||
|
for (state = IEEE80211_STA_NOTEXIST;
|
||||||
|
state < sta->sta_state - 1; state++)
|
||||||
|
WARN_ON(drv_sta_state(local, sta->sdata, sta,
|
||||||
|
state, state + 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&local->sta_mtx);
|
mutex_unlock(&local->sta_mtx);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user