mac80211: make client powersave independent of interface type

This patch prepares mac80211 for a later implementation of mesh or
ad-hoc powersave clients.
The structures related to powersave (buffer, TIM map, counters) are
moved from the AP-specific interface structure to a generic structure
that can be embedded into any interface type.
The functions related to powersave are prepared to allow easy
extension with different interface types. For example with:

+ } else if (sta->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
+         ps = &sdata->u.mesh.ps;

Some references to the AP's beacon structure are removed where they
were obviously not used.

The patch compiles without warning and has been briefly tested as AP
interface with one client in PS mode.

Signed-off-by: Marco Porsch <marco.porsch@etit.tu-chemnitz.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Marco Porsch 2012-10-10 12:39:50 -07:00 committed by Johannes Berg
parent 5c95b940bd
commit d012a60510
6 changed files with 122 additions and 74 deletions

View File

@ -395,14 +395,14 @@ __IEEE80211_IF_FILE_W(uapsd_max_sp_len);
/* AP attributes */
IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC);
IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
IEEE80211_IF_FILE(num_sta_ps, u.ap.ps.num_sta_ps, ATOMIC);
IEEE80211_IF_FILE(dtim_count, u.ap.ps.dtim_count, DEC);
static ssize_t ieee80211_if_fmt_num_buffered_multicast(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
return scnprintf(buf, buflen, "%u\n",
skb_queue_len(&sdata->u.ap.ps_bc_buf));
skb_queue_len(&sdata->u.ap.ps.bc_buf));
}
__IEEE80211_IF_FILE(num_buffered_multicast, NULL);

View File

@ -280,21 +280,25 @@ struct probe_resp {
u8 data[0];
};
struct ps_data {
/* yes, this looks ugly, but guarantees that we can later use
* bitmap_empty :)
* NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */
u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
struct sk_buff_head bc_buf;
atomic_t num_sta_ps; /* number of stations in PS mode */
int dtim_count;
bool dtim_bc_mc;
};
struct ieee80211_if_ap {
struct beacon_data __rcu *beacon;
struct probe_resp __rcu *probe_resp;
struct list_head vlans;
/* yes, this looks ugly, but guarantees that we can later use
* bitmap_empty :)
* NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */
u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
struct sk_buff_head ps_bc_buf;
atomic_t num_sta_ps; /* number of stations in PS mode */
struct ps_data ps;
atomic_t num_mcast_sta; /* number of stations receiving multicast */
int dtim_count;
bool dtim_bc_mc;
};
struct ieee80211_if_wds {

View File

@ -767,8 +767,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
WARN_ON(!list_empty(&sdata->u.ap.vlans));
/* free all potentially still buffered bcast frames */
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps_bc_buf);
skb_queue_purge(&sdata->u.ap.ps_bc_buf);
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ieee80211_mgd_stop(sdata);
}
@ -1171,7 +1171,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->vif.p2p = true;
/* fall through */
case NL80211_IFTYPE_AP:
skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
INIT_LIST_HEAD(&sdata->u.ap.vlans);
break;
case NL80211_IFTYPE_P2P_CLIENT:

View File

@ -1141,12 +1141,19 @@ ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
static void ap_sta_ps_start(struct sta_info *sta)
static void sta_ps_start(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ps_data *ps;
atomic_inc(&sdata->bss->num_sta_ps);
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
ps = &sdata->bss->ps;
else
return;
atomic_inc(&ps->num_sta_ps);
set_sta_flag(sta, WLAN_STA_PS_STA);
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
@ -1154,7 +1161,7 @@ static void ap_sta_ps_start(struct sta_info *sta)
sta->sta.addr, sta->sta.aid);
}
static void ap_sta_ps_end(struct sta_info *sta)
static void sta_ps_end(struct sta_info *sta)
{
ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n",
sta->sta.addr, sta->sta.aid);
@ -1181,9 +1188,9 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start)
return -EINVAL;
if (start)
ap_sta_ps_start(sta_inf);
sta_ps_start(sta_inf);
else
ap_sta_ps_end(sta_inf);
sta_ps_end(sta_inf);
return 0;
}
@ -1335,10 +1342,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
*/
if (ieee80211_is_data(hdr->frame_control) &&
!ieee80211_has_pm(hdr->frame_control))
ap_sta_ps_end(sta);
sta_ps_end(sta);
} else {
if (ieee80211_has_pm(hdr->frame_control))
ap_sta_ps_start(sta);
sta_ps_start(sta);
}
}

View File

@ -98,6 +98,7 @@ static void free_sta_work(struct work_struct *wk)
struct tid_ampdu_tx *tid_tx;
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ps_data *ps;
/*
* At this point, when being called as call_rcu callback,
@ -107,11 +108,15 @@ static void free_sta_work(struct work_struct *wk)
*/
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
BUG_ON(!sdata->bss);
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
ps = &sdata->bss->ps;
else
return;
clear_sta_flag(sta, WLAN_STA_PS_STA);
atomic_dec(&sdata->bss->num_sta_ps);
atomic_dec(&ps->num_sta_ps);
sta_info_recalc_tim(sta);
}
@ -502,22 +507,22 @@ int sta_info_insert(struct sta_info *sta)
return err;
}
static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
static inline void __bss_tim_set(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __set_bit() format.
*/
bss->tim[aid / 8] |= (1 << (aid % 8));
tim[id / 8] |= (1 << (id % 8));
}
static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
static inline void __bss_tim_clear(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __clear_bit() format.
*/
bss->tim[aid / 8] &= ~(1 << (aid % 8));
tim[id / 8] &= ~(1 << (id % 8));
}
static unsigned long ieee80211_tids_for_ac(int ac)
@ -541,14 +546,23 @@ static unsigned long ieee80211_tids_for_ac(int ac)
void sta_info_recalc_tim(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_if_ap *bss = sta->sdata->bss;
struct ps_data *ps;
unsigned long flags;
bool indicate_tim = false;
u8 ignore_for_tim = sta->sta.uapsd_queues;
int ac;
u16 id;
if (WARN_ON_ONCE(!sta->sdata->bss))
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
if (WARN_ON_ONCE(!sta->sdata->bss))
return;
ps = &sta->sdata->bss->ps;
id = sta->sta.aid;
} else {
return;
}
/* No need to do anything if the driver does all */
if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
@ -587,9 +601,9 @@ void sta_info_recalc_tim(struct sta_info *sta)
spin_lock_irqsave(&local->tim_lock, flags);
if (indicate_tim)
__bss_tim_set(bss, sta->sta.aid);
__bss_tim_set(ps->tim, id);
else
__bss_tim_clear(bss, sta->sta.aid);
__bss_tim_clear(ps->tim, id);
if (local->ops->set_tim) {
local->tim_in_locked_section = true;
@ -948,10 +962,17 @@ static void clear_sta_ps_flags(void *_sta)
{
struct sta_info *sta = _sta;
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ps_data *ps;
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
ps = &sdata->bss->ps;
else
return;
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
atomic_dec(&sdata->bss->num_sta_ps);
atomic_dec(&ps->num_sta_ps);
}
/* powersave support code */

View File

@ -325,16 +325,19 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
struct sta_info *sta;
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
struct ieee80211_if_ap *ap;
if (sdata->vif.type != NL80211_IFTYPE_AP)
struct ps_data *ps;
if (sdata->vif.type == NL80211_IFTYPE_AP)
ps = &sdata->u.ap.ps;
else
continue;
ap = &sdata->u.ap;
skb = skb_dequeue(&ap->ps_bc_buf);
skb = skb_dequeue(&ps->bc_buf);
if (skb) {
purged++;
dev_kfree_skb(skb);
}
total += skb_queue_len(&ap->ps_bc_buf);
total += skb_queue_len(&ps->bc_buf);
}
/*
@ -364,6 +367,7 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
struct ps_data *ps;
/*
* broadcast/multicast frame
@ -373,16 +377,24 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
* This is done either by the hardware or us.
*/
/* powersaving STAs only in AP/VLAN mode */
if (!tx->sdata->bss)
/* powersaving STAs currently only in AP/VLAN mode */
if (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
if (!tx->sdata->bss)
return TX_CONTINUE;
ps = &tx->sdata->bss->ps;
} else {
return TX_CONTINUE;
}
/* no buffering for ordered frames */
if (ieee80211_has_order(hdr->frame_control))
return TX_CONTINUE;
/* no stations in PS mode */
if (!atomic_read(&tx->sdata->bss->num_sta_ps))
if (!atomic_read(&ps->num_sta_ps))
return TX_CONTINUE;
info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
@ -397,14 +409,14 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
purge_old_ps_buffers(tx->local);
if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) {
if (skb_queue_len(&ps->bc_buf) >= AP_MAX_BC_BUFFER) {
ps_dbg(tx->sdata,
"BC TX buffer full - dropping the oldest frame\n");
dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
dev_kfree_skb(skb_dequeue(&ps->bc_buf));
} else
tx->local->total_ps_buffered++;
skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
skb_queue_tail(&ps->bc_buf, tx->skb);
return TX_QUEUED;
}
@ -2246,9 +2258,8 @@ void ieee80211_tx_pending(unsigned long data)
/* functions for drivers to get certain frames */
static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_ap *bss,
struct sk_buff *skb,
struct beacon_data *beacon)
struct ps_data *ps,
struct sk_buff *skb)
{
u8 *pos, *tim;
int aid0 = 0;
@ -2256,27 +2267,27 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
/* Generate bitmap for TIM only if there are any STAs in power save
* mode. */
if (atomic_read(&bss->num_sta_ps) > 0)
if (atomic_read(&ps->num_sta_ps) > 0)
/* in the hope that this is faster than
* checking byte-for-byte */
have_bits = !bitmap_empty((unsigned long*)bss->tim,
have_bits = !bitmap_empty((unsigned long*)ps->tim,
IEEE80211_MAX_AID+1);
if (bss->dtim_count == 0)
bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
if (ps->dtim_count == 0)
ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
else
bss->dtim_count--;
ps->dtim_count--;
tim = pos = (u8 *) skb_put(skb, 6);
*pos++ = WLAN_EID_TIM;
*pos++ = 4;
*pos++ = bss->dtim_count;
*pos++ = ps->dtim_count;
*pos++ = sdata->vif.bss_conf.dtim_period;
if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
if (ps->dtim_count == 0 && !skb_queue_empty(&ps->bc_buf))
aid0 = 1;
bss->dtim_bc_mc = aid0 == 1;
ps->dtim_bc_mc = aid0 == 1;
if (have_bits) {
/* Find largest even number N1 so that bits numbered 1 through
@ -2284,14 +2295,14 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
* (N2 + 1) x 8 through 2007 are 0. */
n1 = 0;
for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
if (bss->tim[i]) {
if (ps->tim[i]) {
n1 = i & 0xfe;
break;
}
}
n2 = n1;
for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
if (bss->tim[i]) {
if (ps->tim[i]) {
n2 = i;
break;
}
@ -2301,7 +2312,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
*pos++ = n1 | aid0;
/* Part Virt Bitmap */
skb_put(skb, n2 - n1);
memcpy(pos, bss->tim + n1, n2 - n1 + 1);
memcpy(pos, ps->tim + n1, n2 - n1 + 1);
tim[1] = n2 - n1 + 4;
} else {
@ -2318,8 +2329,6 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct ieee80211_tx_info *info;
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_if_ap *ap = NULL;
struct beacon_data *beacon;
enum ieee80211_band band;
struct ieee80211_tx_rate_control txrc;
struct ieee80211_chanctx_conf *chanctx_conf;
@ -2338,8 +2347,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*tim_length = 0;
if (sdata->vif.type == NL80211_IFTYPE_AP) {
ap = &sdata->u.ap;
beacon = rcu_dereference(ap->beacon);
struct ieee80211_if_ap *ap = &sdata->u.ap;
struct beacon_data *beacon = rcu_dereference(ap->beacon);
if (beacon) {
/*
* headroom, head length,
@ -2363,14 +2373,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
* of the tim bitmap in mac80211 and the driver.
*/
if (local->tim_in_locked_section) {
ieee80211_beacon_add_tim(sdata, ap, skb,
beacon);
ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
} else {
unsigned long flags;
spin_lock_irqsave(&local->tim_lock, flags);
ieee80211_beacon_add_tim(sdata, ap, skb,
beacon);
ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
spin_unlock_irqrestore(&local->tim_lock, flags);
}
@ -2694,32 +2702,40 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct ieee80211_tx_data tx;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_ap *bss = NULL;
struct beacon_data *beacon;
struct ps_data *ps;
struct ieee80211_tx_info *info;
struct ieee80211_chanctx_conf *chanctx_conf;
sdata = vif_to_sdata(vif);
bss = &sdata->u.ap;
rcu_read_lock();
beacon = rcu_dereference(bss->beacon);
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head ||
!chanctx_conf)
if (!chanctx_conf)
goto out;
if (bss->dtim_count != 0 || !bss->dtim_bc_mc)
if (sdata->vif.type == NL80211_IFTYPE_AP) {
struct beacon_data *beacon =
rcu_dereference(sdata->u.ap.beacon);
if (!beacon || !beacon->head)
goto out;
ps = &sdata->u.ap.ps;
} else {
goto out;
}
if (ps->dtim_count != 0 || !ps->dtim_bc_mc)
goto out; /* send buffered bc/mc only after DTIM beacon */
while (1) {
skb = skb_dequeue(&bss->ps_bc_buf);
skb = skb_dequeue(&ps->bc_buf);
if (!skb)
goto out;
local->total_ps_buffered--;
if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
if (!skb_queue_empty(&ps->bc_buf) && skb->len >= 2) {
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) skb->data;
/* more buffered multicast/broadcast frames ==> set