wl1271: Support firmware TX packet aggregation
Instead of sending one packet at a time to the firmware, try to send all available packets at once. This optimization decreases the number of transactions, which saves CPU cycles and increases network throughput. Signed-off-by: Ido Yariv <ido@wizery.com> Tested-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
This commit is contained in:
parent
1f37cbc936
commit
a19606b433
@ -43,13 +43,17 @@ static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb)
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
|
static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
|
||||||
|
u32 buf_offset)
|
||||||
{
|
{
|
||||||
struct wl1271_tx_hw_descr *desc;
|
struct wl1271_tx_hw_descr *desc;
|
||||||
u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
|
u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
|
||||||
u32 total_blocks;
|
u32 total_blocks;
|
||||||
int id, ret = -EBUSY;
|
int id, ret = -EBUSY;
|
||||||
|
|
||||||
|
if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
/* allocate free identifier for the packet */
|
/* allocate free identifier for the packet */
|
||||||
id = wl1271_tx_id(wl, skb);
|
id = wl1271_tx_id(wl, skb);
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
@ -82,7 +86,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
|
static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
|
||||||
u32 extra, struct ieee80211_tx_info *control)
|
u32 extra, struct ieee80211_tx_info *control)
|
||||||
{
|
{
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
@ -133,59 +137,17 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
|
|||||||
desc->tx_attr = cpu_to_le16(tx_attr);
|
desc->tx_attr = cpu_to_le16(tx_attr);
|
||||||
|
|
||||||
wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad);
|
wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb,
|
|
||||||
struct ieee80211_tx_info *control)
|
|
||||||
{
|
|
||||||
|
|
||||||
struct wl1271_tx_hw_descr *desc;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
/* FIXME: This is a workaround for getting non-aligned packets.
|
|
||||||
This happens at least with EAPOL packets from the user space.
|
|
||||||
Our DMA requires packets to be aligned on a 4-byte boundary.
|
|
||||||
*/
|
|
||||||
if (unlikely((long)skb->data & 0x03)) {
|
|
||||||
int offset = (4 - (long)skb->data) & 0x03;
|
|
||||||
wl1271_debug(DEBUG_TX, "skb offset %d", offset);
|
|
||||||
|
|
||||||
/* check whether the current skb can be used */
|
|
||||||
if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) {
|
|
||||||
unsigned char *src = skb->data;
|
|
||||||
|
|
||||||
/* align the buffer on a 4-byte boundary */
|
|
||||||
skb_reserve(skb, offset);
|
|
||||||
memmove(skb->data, src, skb->len);
|
|
||||||
} else {
|
|
||||||
wl1271_info("No handler, fixme!");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
len = WL1271_TX_ALIGN(skb->len);
|
|
||||||
|
|
||||||
/* perform a fixed address block write with the packet */
|
|
||||||
wl1271_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true);
|
|
||||||
|
|
||||||
/* write packet new counter into the write access register */
|
|
||||||
wl->tx_packets_count++;
|
|
||||||
|
|
||||||
desc = (struct wl1271_tx_hw_descr *) skb->data;
|
|
||||||
wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)",
|
|
||||||
desc->id, skb, len, desc->length);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* caller must hold wl->mutex */
|
/* caller must hold wl->mutex */
|
||||||
static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
|
static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
|
||||||
|
u32 buf_offset)
|
||||||
{
|
{
|
||||||
struct ieee80211_tx_info *info;
|
struct ieee80211_tx_info *info;
|
||||||
u32 extra = 0;
|
u32 extra = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
u8 idx;
|
u8 idx;
|
||||||
|
u32 total_len;
|
||||||
|
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -208,19 +170,22 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = wl1271_tx_allocate(wl, skb, extra);
|
ret = wl1271_tx_allocate(wl, skb, extra, buf_offset);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = wl1271_tx_fill_hdr(wl, skb, extra, info);
|
wl1271_tx_fill_hdr(wl, skb, extra, info);
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = wl1271_tx_send_packet(wl, skb, info);
|
/*
|
||||||
if (ret < 0)
|
* The length of each packet is stored in terms of words. Thus, we must
|
||||||
return ret;
|
* pad the skb data to make sure its length is aligned.
|
||||||
|
* The number of padding bytes is computed and set in wl1271_tx_fill_hdr
|
||||||
|
*/
|
||||||
|
total_len = WL1271_TX_ALIGN(skb->len);
|
||||||
|
memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len);
|
||||||
|
memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len);
|
||||||
|
|
||||||
return ret;
|
return total_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
|
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
|
||||||
@ -245,7 +210,7 @@ void wl1271_tx_work(struct work_struct *work)
|
|||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
bool woken_up = false;
|
bool woken_up = false;
|
||||||
u32 sta_rates = 0;
|
u32 sta_rates = 0;
|
||||||
u32 prev_tx_packets_count;
|
u32 buf_offset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* check if the rates supported by the AP have changed */
|
/* check if the rates supported by the AP have changed */
|
||||||
@ -262,14 +227,15 @@ void wl1271_tx_work(struct work_struct *work)
|
|||||||
if (unlikely(wl->state == WL1271_STATE_OFF))
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
prev_tx_packets_count = wl->tx_packets_count;
|
|
||||||
|
|
||||||
/* if rates have changed, re-configure the rate policy */
|
/* if rates have changed, re-configure the rate policy */
|
||||||
if (unlikely(sta_rates)) {
|
if (unlikely(sta_rates)) {
|
||||||
wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
|
wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
|
||||||
wl1271_acx_rate_policies(wl);
|
wl1271_acx_rate_policies(wl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prepare the transfer buffer, by aggregating all
|
||||||
|
* available packets */
|
||||||
|
buf_offset = 0;
|
||||||
while ((skb = skb_dequeue(&wl->tx_queue))) {
|
while ((skb = skb_dequeue(&wl->tx_queue))) {
|
||||||
if (!woken_up) {
|
if (!woken_up) {
|
||||||
ret = wl1271_ps_elp_wakeup(wl, false);
|
ret = wl1271_ps_elp_wakeup(wl, false);
|
||||||
@ -278,21 +244,30 @@ void wl1271_tx_work(struct work_struct *work)
|
|||||||
woken_up = true;
|
woken_up = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = wl1271_tx_frame(wl, skb);
|
ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
|
||||||
if (ret == -EBUSY) {
|
if (ret == -EBUSY) {
|
||||||
/* firmware buffer is full, lets stop transmitting. */
|
/*
|
||||||
|
* Either the firmware buffer is full, or the
|
||||||
|
* aggregation buffer is.
|
||||||
|
* Queue back last skb, and stop aggregating.
|
||||||
|
*/
|
||||||
skb_queue_head(&wl->tx_queue, skb);
|
skb_queue_head(&wl->tx_queue, skb);
|
||||||
goto out_ack;
|
goto out_ack;
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
goto out_ack;
|
goto out_ack;
|
||||||
}
|
}
|
||||||
|
buf_offset += ret;
|
||||||
|
wl->tx_packets_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_ack:
|
out_ack:
|
||||||
/* interrupt the firmware with the new packets */
|
if (buf_offset) {
|
||||||
if (prev_tx_packets_count != wl->tx_packets_count)
|
wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
|
||||||
|
buf_offset, true);
|
||||||
|
/* interrupt the firmware with the new packets */
|
||||||
wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
|
wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (woken_up)
|
if (woken_up)
|
||||||
|
Loading…
Reference in New Issue
Block a user