iwlwifi: mvm: allocate reorder buffer according to need
Now that we may have up to 256 entries per reorder buffer, and possibly up to 16 queues, we can use a LOT of memory for this (64k for each station). Allocate it according to what we need, which is of course much less for HT stations (only 16k at a max of 16 queues). However, this comes at the expense of complicating the code a bit to calculate the right entry structure to use for each frame. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
This commit is contained in:
parent
66cc044249
commit
dfdddd92a5
|
@ -590,8 +590,6 @@ enum iwl_mvm_tdls_cs_state {
|
|||
* @last_amsdu: track last ASMDU SN for duplication detection
|
||||
* @last_sub_index: track ASMDU sub frame index for duplication detection
|
||||
* @tid: the tid
|
||||
* @entries: list of skbs stored
|
||||
* @reorder_time: time the packet was stored in the reorder buffer
|
||||
* @reorder_timer: timer for frames are in the reorder buffer. For AMSDU
|
||||
* it is the time of last received sub-frame
|
||||
* @removed: prevent timer re-arming
|
||||
|
@ -608,8 +606,6 @@ struct iwl_mvm_reorder_buffer {
|
|||
u16 last_amsdu;
|
||||
u8 last_sub_index;
|
||||
u8 tid;
|
||||
struct sk_buff_head entries[IEEE80211_MAX_AMPDU_BUF];
|
||||
unsigned long reorder_time[IEEE80211_MAX_AMPDU_BUF];
|
||||
struct timer_list reorder_timer;
|
||||
bool removed;
|
||||
bool valid;
|
||||
|
@ -617,16 +613,39 @@ struct iwl_mvm_reorder_buffer {
|
|||
struct iwl_mvm *mvm;
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
/**
|
||||
* struct _iwl_mvm_reorder_buf_entry - reorder buffer entry per-queue/per-seqno
|
||||
* @frames: list of skbs stored
|
||||
* @reorder_time: time the packet was stored in the reorder buffer
|
||||
*/
|
||||
struct _iwl_mvm_reorder_buf_entry {
|
||||
struct sk_buff_head frames;
|
||||
unsigned long reorder_time;
|
||||
};
|
||||
|
||||
/* make this indirection to get the aligned thing */
|
||||
struct iwl_mvm_reorder_buf_entry {
|
||||
struct _iwl_mvm_reorder_buf_entry e;
|
||||
}
|
||||
#ifndef __CHECKER__
|
||||
/* sparse doesn't like this construct: "bad integer constant expression" */
|
||||
__aligned(roundup_pow_of_two(sizeof(struct _iwl_mvm_reorder_buf_entry)))
|
||||
#endif
|
||||
;
|
||||
|
||||
/**
|
||||
* struct iwl_mvm_baid_data - BA session data
|
||||
* @sta_id: station id
|
||||
* @tid: tid of the session
|
||||
* @baid baid of the session
|
||||
* @timeout: the timeout set in the addba request
|
||||
* @entries_per_queue: # of buffers per queue, this actually gets
|
||||
* aligned up to avoid cache line sharing between queues
|
||||
* @last_rx: last rx jiffies, updated only if timeout passed from last update
|
||||
* @session_timer: timer to check if BA session expired, runs at 2 * timeout
|
||||
* @mvm: mvm pointer, needed for timer context
|
||||
* @reorder_buf: reorder buffer, allocated per queue
|
||||
* @reorder_buf_data: data
|
||||
*/
|
||||
struct iwl_mvm_baid_data {
|
||||
struct rcu_head rcu_head;
|
||||
|
@ -634,12 +653,22 @@ struct iwl_mvm_baid_data {
|
|||
u8 tid;
|
||||
u8 baid;
|
||||
u16 timeout;
|
||||
u16 entries_per_queue;
|
||||
unsigned long last_rx;
|
||||
struct timer_list session_timer;
|
||||
struct iwl_mvm *mvm;
|
||||
struct iwl_mvm_reorder_buffer reorder_buf[];
|
||||
struct iwl_mvm_reorder_buffer reorder_buf[IWL_MAX_RX_HW_QUEUES];
|
||||
struct iwl_mvm_reorder_buf_entry entries[];
|
||||
};
|
||||
|
||||
static inline struct iwl_mvm_baid_data *
|
||||
iwl_mvm_baid_data_from_reorder_buf(struct iwl_mvm_reorder_buffer *buf)
|
||||
{
|
||||
return (void *)((u8 *)buf -
|
||||
offsetof(struct iwl_mvm_baid_data, reorder_buf) -
|
||||
sizeof(*buf) * buf->queue);
|
||||
}
|
||||
|
||||
/*
|
||||
* enum iwl_mvm_queue_status - queue status
|
||||
* @IWL_MVM_QUEUE_FREE: the queue is not allocated nor reserved
|
||||
|
|
|
@ -412,6 +412,11 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
|
|||
struct iwl_mvm_reorder_buffer *reorder_buf,
|
||||
u16 nssn)
|
||||
{
|
||||
struct iwl_mvm_baid_data *baid_data =
|
||||
iwl_mvm_baid_data_from_reorder_buf(reorder_buf);
|
||||
struct iwl_mvm_reorder_buf_entry *entries =
|
||||
&baid_data->entries[reorder_buf->queue *
|
||||
baid_data->entries_per_queue];
|
||||
u16 ssn = reorder_buf->head_sn;
|
||||
|
||||
lockdep_assert_held(&reorder_buf->lock);
|
||||
|
@ -422,7 +427,7 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
|
|||
|
||||
while (iwl_mvm_is_sn_less(ssn, nssn, reorder_buf->buf_size)) {
|
||||
int index = ssn % reorder_buf->buf_size;
|
||||
struct sk_buff_head *skb_list = &reorder_buf->entries[index];
|
||||
struct sk_buff_head *skb_list = &entries[index].e.frames;
|
||||
struct sk_buff *skb;
|
||||
|
||||
ssn = ieee80211_sn_inc(ssn);
|
||||
|
@ -445,11 +450,11 @@ set_timer:
|
|||
if (reorder_buf->num_stored && !reorder_buf->removed) {
|
||||
u16 index = reorder_buf->head_sn % reorder_buf->buf_size;
|
||||
|
||||
while (skb_queue_empty(&reorder_buf->entries[index]))
|
||||
while (skb_queue_empty(&entries[index].e.frames))
|
||||
index = (index + 1) % reorder_buf->buf_size;
|
||||
/* modify timer to match next frame's expiration time */
|
||||
mod_timer(&reorder_buf->reorder_timer,
|
||||
reorder_buf->reorder_time[index] + 1 +
|
||||
entries[index].e.reorder_time + 1 +
|
||||
RX_REORDER_BUF_TIMEOUT_MQ);
|
||||
} else {
|
||||
del_timer(&reorder_buf->reorder_timer);
|
||||
|
@ -459,6 +464,10 @@ set_timer:
|
|||
void iwl_mvm_reorder_timer_expired(unsigned long data)
|
||||
{
|
||||
struct iwl_mvm_reorder_buffer *buf = (void *)data;
|
||||
struct iwl_mvm_baid_data *baid_data =
|
||||
iwl_mvm_baid_data_from_reorder_buf(buf);
|
||||
struct iwl_mvm_reorder_buf_entry *entries =
|
||||
&baid_data->entries[buf->queue * baid_data->entries_per_queue];
|
||||
int i;
|
||||
u16 sn = 0, index = 0;
|
||||
bool expired = false;
|
||||
|
@ -474,7 +483,7 @@ void iwl_mvm_reorder_timer_expired(unsigned long data)
|
|||
for (i = 0; i < buf->buf_size ; i++) {
|
||||
index = (buf->head_sn + i) % buf->buf_size;
|
||||
|
||||
if (skb_queue_empty(&buf->entries[index])) {
|
||||
if (skb_queue_empty(&entries[index].e.frames)) {
|
||||
/*
|
||||
* If there is a hole and the next frame didn't expire
|
||||
* we want to break and not advance SN
|
||||
|
@ -482,7 +491,8 @@ void iwl_mvm_reorder_timer_expired(unsigned long data)
|
|||
cont = false;
|
||||
continue;
|
||||
}
|
||||
if (!cont && !time_after(jiffies, buf->reorder_time[index] +
|
||||
if (!cont &&
|
||||
!time_after(jiffies, entries[index].e.reorder_time +
|
||||
RX_REORDER_BUF_TIMEOUT_MQ))
|
||||
break;
|
||||
|
||||
|
@ -515,7 +525,7 @@ void iwl_mvm_reorder_timer_expired(unsigned long data)
|
|||
* accordingly to this frame.
|
||||
*/
|
||||
mod_timer(&buf->reorder_timer,
|
||||
buf->reorder_time[index] +
|
||||
entries[index].e.reorder_time +
|
||||
1 + RX_REORDER_BUF_TIMEOUT_MQ);
|
||||
}
|
||||
spin_unlock(&buf->lock);
|
||||
|
@ -610,6 +620,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
|
|||
u8 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
|
||||
u8 sub_frame_idx = desc->amsdu_info &
|
||||
IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
|
||||
struct iwl_mvm_reorder_buf_entry *entries;
|
||||
int index;
|
||||
u16 nssn, sn;
|
||||
u8 baid;
|
||||
|
@ -660,6 +671,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
|
|||
IWL_RX_MPDU_REORDER_SN_SHIFT;
|
||||
|
||||
buffer = &baid_data->reorder_buf[queue];
|
||||
entries = &baid_data->entries[queue * baid_data->entries_per_queue];
|
||||
|
||||
spin_lock_bh(&buffer->lock);
|
||||
|
||||
|
@ -716,7 +728,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
|
|||
* If it is the same SN then if the subframe index is incrementing it
|
||||
* is the same AMSDU - otherwise it is a retransmission.
|
||||
*/
|
||||
tail = skb_peek_tail(&buffer->entries[index]);
|
||||
tail = skb_peek_tail(&entries[index].e.frames);
|
||||
if (tail && !amsdu)
|
||||
goto drop;
|
||||
else if (tail && (sn != buffer->last_amsdu ||
|
||||
|
@ -724,9 +736,9 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
|
|||
goto drop;
|
||||
|
||||
/* put in reorder buffer */
|
||||
__skb_queue_tail(&buffer->entries[index], skb);
|
||||
__skb_queue_tail(&entries[index].e.frames, skb);
|
||||
buffer->num_stored++;
|
||||
buffer->reorder_time[index] = jiffies;
|
||||
entries[index].e.reorder_time = jiffies;
|
||||
|
||||
if (amsdu) {
|
||||
buffer->last_amsdu = sn;
|
||||
|
|
|
@ -2104,6 +2104,8 @@ static void iwl_mvm_free_reorder(struct iwl_mvm *mvm,
|
|||
int j;
|
||||
struct iwl_mvm_reorder_buffer *reorder_buf =
|
||||
&data->reorder_buf[i];
|
||||
struct iwl_mvm_reorder_buf_entry *entries =
|
||||
&data->entries[i * data->entries_per_queue];
|
||||
|
||||
spin_lock_bh(&reorder_buf->lock);
|
||||
if (likely(!reorder_buf->num_stored)) {
|
||||
|
@ -2119,7 +2121,7 @@ static void iwl_mvm_free_reorder(struct iwl_mvm *mvm,
|
|||
WARN_ON(1);
|
||||
|
||||
for (j = 0; j < reorder_buf->buf_size; j++)
|
||||
__skb_queue_purge(&reorder_buf->entries[j]);
|
||||
__skb_queue_purge(&entries[j].e.frames);
|
||||
/*
|
||||
* Prevent timer re-arm. This prevents a very far fetched case
|
||||
* where we timed out on the notification. There may be prior
|
||||
|
@ -2144,6 +2146,8 @@ static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm,
|
|||
for (i = 0; i < mvm->trans->num_rx_queues; i++) {
|
||||
struct iwl_mvm_reorder_buffer *reorder_buf =
|
||||
&data->reorder_buf[i];
|
||||
struct iwl_mvm_reorder_buf_entry *entries =
|
||||
&data->entries[i * data->entries_per_queue];
|
||||
int j;
|
||||
|
||||
reorder_buf->num_stored = 0;
|
||||
|
@ -2161,7 +2165,7 @@ static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm,
|
|||
reorder_buf->tid = data->tid;
|
||||
reorder_buf->valid = false;
|
||||
for (j = 0; j < reorder_buf->buf_size; j++)
|
||||
__skb_queue_head_init(&reorder_buf->entries[j]);
|
||||
__skb_queue_head_init(&entries[j].e.frames);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2182,16 +2186,44 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
|
|||
}
|
||||
|
||||
if (iwl_mvm_has_new_rx_api(mvm) && start) {
|
||||
u16 reorder_buf_size = buf_size * sizeof(baid_data->entries[0]);
|
||||
|
||||
/* sparse doesn't like the __align() so don't check */
|
||||
#ifndef __CHECKER__
|
||||
/*
|
||||
* The division below will be OK if either the cache line size
|
||||
* can be divided by the entry size (ALIGN will round up) or if
|
||||
* if the entry size can be divided by the cache line size, in
|
||||
* which case the ALIGN() will do nothing.
|
||||
*/
|
||||
BUILD_BUG_ON(SMP_CACHE_BYTES % sizeof(baid_data->entries[0]) &&
|
||||
sizeof(baid_data->entries[0]) % SMP_CACHE_BYTES);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Upward align the reorder buffer size to fill an entire cache
|
||||
* line for each queue, to avoid sharing cache lines between
|
||||
* different queues.
|
||||
*/
|
||||
reorder_buf_size = ALIGN(reorder_buf_size, SMP_CACHE_BYTES);
|
||||
|
||||
/*
|
||||
* Allocate here so if allocation fails we can bail out early
|
||||
* before starting the BA session in the firmware
|
||||
*/
|
||||
baid_data = kzalloc(sizeof(*baid_data) +
|
||||
mvm->trans->num_rx_queues *
|
||||
sizeof(baid_data->reorder_buf[0]),
|
||||
reorder_buf_size,
|
||||
GFP_KERNEL);
|
||||
if (!baid_data)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* This division is why we need the above BUILD_BUG_ON(),
|
||||
* if that doesn't hold then this will not be right.
|
||||
*/
|
||||
baid_data->entries_per_queue =
|
||||
reorder_buf_size / sizeof(baid_data->entries[0]);
|
||||
}
|
||||
|
||||
cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
|
||||
|
|
Loading…
Reference in New Issue