Commit 206eea78 authored by Johannes Berg's avatar Johannes Berg Committed by Emmanuel Grumbach

iwlwifi: pcie: support frag SKBs

Allow frag SKBs in PCIe and advertise the maximum number of frags
to the opmode. As a fallback. linearize the SKB if it exceeds the
maximum number of fragments. This allows using the hardware better
(filling more TBs) and should improve performance when used by the
opmode.

Also adjust tracing to be able to deal with this.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 17564dde
...@@ -35,8 +35,8 @@ ...@@ -35,8 +35,8 @@
TRACE_EVENT(iwlwifi_dev_tx_data, TRACE_EVENT(iwlwifi_dev_tx_data,
TP_PROTO(const struct device *dev, TP_PROTO(const struct device *dev,
struct sk_buff *skb, struct sk_buff *skb,
void *data, size_t data_len), u8 hdr_len, size_t data_len),
TP_ARGS(dev, skb, data, data_len), TP_ARGS(dev, skb, hdr_len, data_len),
TP_STRUCT__entry( TP_STRUCT__entry(
DEV_ENTRY DEV_ENTRY
...@@ -45,7 +45,8 @@ TRACE_EVENT(iwlwifi_dev_tx_data, ...@@ -45,7 +45,8 @@ TRACE_EVENT(iwlwifi_dev_tx_data,
TP_fast_assign( TP_fast_assign(
DEV_ASSIGN; DEV_ASSIGN;
if (iwl_trace_data(skb)) if (iwl_trace_data(skb))
memcpy(__get_dynamic_array(data), data, data_len); skb_copy_bits(skb, hdr_len,
__get_dynamic_array(data), data_len);
), ),
TP_printk("[%s] TX frame data", __get_str(dev)) TP_printk("[%s] TX frame data", __get_str(dev))
); );
......
...@@ -248,6 +248,8 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) ...@@ -248,6 +248,8 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
* @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle. * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
* @CMD_WAKE_UP_TRANS: The command response should wake up the trans * @CMD_WAKE_UP_TRANS: The command response should wake up the trans
* (i.e. mark it as non-idle). * (i.e. mark it as non-idle).
* @CMD_TB_BITMAP_POS: Position of the first bit for the TB bitmap. We need to
* check that we leave enough room for the TBs bitmap which needs 20 bits.
*/ */
enum CMD_MODE { enum CMD_MODE {
CMD_ASYNC = BIT(0), CMD_ASYNC = BIT(0),
...@@ -257,6 +259,8 @@ enum CMD_MODE { ...@@ -257,6 +259,8 @@ enum CMD_MODE {
CMD_SEND_IN_IDLE = BIT(4), CMD_SEND_IN_IDLE = BIT(4),
CMD_MAKE_TRANS_IDLE = BIT(5), CMD_MAKE_TRANS_IDLE = BIT(5),
CMD_WAKE_UP_TRANS = BIT(6), CMD_WAKE_UP_TRANS = BIT(6),
CMD_TB_BITMAP_POS = 11,
}; };
#define DEF_CMD_PAYLOAD_SIZE 320 #define DEF_CMD_PAYLOAD_SIZE 320
...@@ -641,6 +645,8 @@ enum iwl_d0i3_mode { ...@@ -641,6 +645,8 @@ enum iwl_d0i3_mode {
* @cfg - pointer to the configuration * @cfg - pointer to the configuration
* @status: a bit-mask of transport status flags * @status: a bit-mask of transport status flags
* @dev - pointer to struct device * that represents the device * @dev - pointer to struct device * that represents the device
* @max_skb_frags: maximum number of fragments an SKB can have when transmitted.
* 0 indicates that frag SKBs (NETIF_F_SG) aren't supported.
* @hw_id: a u32 with the ID of the device / sub-device. * @hw_id: a u32 with the ID of the device / sub-device.
* Set during transport allocation. * Set during transport allocation.
* @hw_id_str: a string with info about HW ID. Set during transport allocation. * @hw_id_str: a string with info about HW ID. Set during transport allocation.
...@@ -669,6 +675,7 @@ struct iwl_trans { ...@@ -669,6 +675,7 @@ struct iwl_trans {
unsigned long status; unsigned long status;
struct device *dev; struct device *dev;
u32 max_skb_frags;
u32 hw_rev; u32 hw_rev;
u32 hw_id; u32 hw_id;
char hw_id_str[52]; char hw_id_str[52];
......
...@@ -44,6 +44,12 @@ ...@@ -44,6 +44,12 @@
#include "iwl-io.h" #include "iwl-io.h"
#include "iwl-op-mode.h" #include "iwl-op-mode.h"
/* We need 2 entries for the TX command and header, and another one might
* be needed for potential data in the SKB's head. The remaining ones can
* be used for frags.
*/
#define IWL_PCIE_MAX_FRAGS (IWL_NUM_OF_TBS - 3)
/* /*
* RX related structures and functions * RX related structures and functions
*/ */
......
...@@ -2627,6 +2627,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, ...@@ -2627,6 +2627,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
if (!trans) if (!trans)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
trans->max_skb_frags = IWL_PCIE_MAX_FRAGS;
trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
trans_pcie->trans = trans; trans_pcie->trans = trans;
......
...@@ -388,11 +388,18 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, ...@@ -388,11 +388,18 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans,
/* first TB is never freed - it's the scratchbuf data */ /* first TB is never freed - it's the scratchbuf data */
for (i = 1; i < num_tbs; i++) for (i = 1; i < num_tbs; i++) {
dma_unmap_single(trans->dev, iwl_pcie_tfd_tb_get_addr(tfd, i), if (meta->flags & BIT(i + CMD_TB_BITMAP_POS))
iwl_pcie_tfd_tb_get_len(tfd, i), dma_unmap_page(trans->dev,
DMA_TO_DEVICE); iwl_pcie_tfd_tb_get_addr(tfd, i),
iwl_pcie_tfd_tb_get_len(tfd, i),
DMA_TO_DEVICE);
else
dma_unmap_single(trans->dev,
iwl_pcie_tfd_tb_get_addr(tfd, i),
iwl_pcie_tfd_tb_get_len(tfd, i),
DMA_TO_DEVICE);
}
tfd->num_tbs = 0; tfd->num_tbs = 0;
} }
...@@ -468,7 +475,7 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, ...@@ -468,7 +475,7 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len); iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len);
return 0; return num_tbs;
} }
static int iwl_pcie_txq_alloc(struct iwl_trans *trans, static int iwl_pcie_txq_alloc(struct iwl_trans *trans,
...@@ -1546,6 +1553,8 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1546,6 +1553,8 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false); iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false);
} }
BUILD_BUG_ON(IWL_NUM_OF_TBS + CMD_TB_BITMAP_POS >
sizeof(out_meta->flags) * BITS_PER_BYTE);
out_meta->flags = cmd->flags; out_meta->flags = cmd->flags;
if (WARN_ON_ONCE(txq->entries[idx].free_buf)) if (WARN_ON_ONCE(txq->entries[idx].free_buf))
kzfree(txq->entries[idx].free_buf); kzfree(txq->entries[idx].free_buf);
...@@ -1789,7 +1798,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1789,7 +1798,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_cmd *dev_cmd, int txq_id) struct iwl_device_cmd *dev_cmd, int txq_id)
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr;
struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
struct iwl_cmd_meta *out_meta; struct iwl_cmd_meta *out_meta;
struct iwl_txq *txq; struct iwl_txq *txq;
...@@ -1798,9 +1807,10 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1798,9 +1807,10 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
void *tb1_addr; void *tb1_addr;
u16 len, tb1_len, tb2_len; u16 len, tb1_len, tb2_len;
bool wait_write_ptr; bool wait_write_ptr;
__le16 fc = hdr->frame_control; __le16 fc;
u8 hdr_len = ieee80211_hdrlen(fc); u8 hdr_len;
u16 wifi_seq; u16 wifi_seq;
int i;
txq = &trans_pcie->txq[txq_id]; txq = &trans_pcie->txq[txq_id];
q = &txq->q; q = &txq->q;
...@@ -1809,6 +1819,18 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1809,6 +1819,18 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
"TX on unused queue %d\n", txq_id)) "TX on unused queue %d\n", txq_id))
return -EINVAL; return -EINVAL;
if (skb_is_nonlinear(skb) &&
skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS &&
__skb_linearize(skb))
return -ENOMEM;
/* mac80211 always puts the full header into the SKB's head,
* so there's no need to check if it's readable there
*/
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
hdr_len = ieee80211_hdrlen(fc);
spin_lock(&txq->lock); spin_lock(&txq->lock);
/* In AGG mode, the index in the ring must correspond to the WiFi /* In AGG mode, the index in the ring must correspond to the WiFi
...@@ -1839,6 +1861,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1839,6 +1861,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
/* Set up first empty entry in queue's array of Tx/cmd buffers */ /* Set up first empty entry in queue's array of Tx/cmd buffers */
out_meta = &txq->entries[q->write_ptr].meta; out_meta = &txq->entries[q->write_ptr].meta;
out_meta->flags = 0;
/* /*
* The second TB (tb1) points to the remainder of the TX command * The second TB (tb1) points to the remainder of the TX command
...@@ -1872,9 +1895,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1872,9 +1895,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
/* /*
* Set up TFD's third entry to point directly to remainder * Set up TFD's third entry to point directly to remainder
* of skb, if any (802.11 null frames have no payload). * of skb's head, if any
*/ */
tb2_len = skb->len - hdr_len; tb2_len = skb_headlen(skb) - hdr_len;
if (tb2_len > 0) { if (tb2_len > 0) {
dma_addr_t tb2_phys = dma_map_single(trans->dev, dma_addr_t tb2_phys = dma_map_single(trans->dev,
skb->data + hdr_len, skb->data + hdr_len,
...@@ -1887,6 +1910,29 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1887,6 +1910,29 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
} }
/* set up the remaining entries to point to the data */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
dma_addr_t tb_phys;
int tb_idx;
if (!skb_frag_size(frag))
continue;
tb_phys = skb_frag_dma_map(trans->dev, frag, 0,
skb_frag_size(frag), DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
iwl_pcie_tfd_unmap(trans, out_meta,
&txq->tfds[q->write_ptr]);
goto out_err;
}
tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys,
skb_frag_size(frag), false);
out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS);
}
/* Set up entry for this TFD in Tx byte-count array */ /* Set up entry for this TFD in Tx byte-count array */
iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len));
...@@ -1896,7 +1942,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1896,7 +1942,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
&dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len,
skb->data + hdr_len, tb2_len); skb->data + hdr_len, tb2_len);
trace_iwlwifi_dev_tx_data(trans->dev, skb, trace_iwlwifi_dev_tx_data(trans->dev, skb,
skb->data + hdr_len, tb2_len); hdr_len, skb->len - hdr_len);
wait_write_ptr = ieee80211_has_morefrags(fc); wait_write_ptr = ieee80211_has_morefrags(fc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment