Commit 0cd1ad2d authored by Mordechay Goodstein's avatar Mordechay Goodstein Committed by Luca Coelho

iwlwifi: move all bus-independent TX functions to common code

After moving out all Tx fields not related to pcie-bus
it's time to move the code to a common place.

We also rename all pcie functions name to txq.
Signed-off-by: default avatarMordechay Goodstein <mordechay.goodstein@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20200930161256.3947a5276003.I3fe1bec2b25a965a49532df288f47b8b59eb1500@changeidSigned-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 0739a7d7
...@@ -13,6 +13,7 @@ iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o ...@@ -13,6 +13,7 @@ iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o
iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o
iwlwifi-objs += iwl-dbg-tlv.o iwlwifi-objs += iwl-dbg-tlv.o
iwlwifi-objs += iwl-trans.o iwlwifi-objs += iwl-trans.o
iwlwifi-objs += queue/tx.o
iwlwifi-objs += fw/img.o fw/notif-wait.o iwlwifi-objs += fw/img.o fw/notif-wait.o
iwlwifi-objs += fw/dbg.o iwlwifi-objs += fw/dbg.o
......
...@@ -66,6 +66,7 @@ ...@@ -66,6 +66,7 @@
#include "iwl-trans.h" #include "iwl-trans.h"
#include "iwl-drv.h" #include "iwl-drv.h"
#include "iwl-fh.h" #include "iwl-fh.h"
#include "queue/tx.h"
#include <linux/dmapool.h> #include <linux/dmapool.h>
struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
...@@ -150,11 +151,29 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, ...@@ -150,11 +151,29 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty); WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty);
trans->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
if (!trans->txqs.tso_hdr_page) {
kmem_cache_destroy(trans->dev_cmd_pool);
return NULL;
}
return trans; return trans;
} }
void iwl_trans_free(struct iwl_trans *trans) void iwl_trans_free(struct iwl_trans *trans)
{ {
int i;
for_each_possible_cpu(i) {
struct iwl_tso_hdr_page *p =
per_cpu_ptr(trans->txqs.tso_hdr_page, i);
if (p->page)
__free_page(p->page);
}
free_percpu(trans->txqs.tso_hdr_page);
kmem_cache_destroy(trans->dev_cmd_pool); kmem_cache_destroy(trans->dev_cmd_pool);
} }
......
...@@ -928,6 +928,7 @@ struct iwl_trans_txqs { ...@@ -928,6 +928,7 @@ struct iwl_trans_txqs {
bool bc_table_dword; bool bc_table_dword;
u8 page_offs; u8 page_offs;
u8 dev_cmd_offs; u8 dev_cmd_offs;
struct __percpu iwl_tso_hdr_page * tso_hdr_page;
struct { struct {
u8 fifo; u8 fifo;
......
...@@ -73,7 +73,7 @@ static void *_iwl_pcie_ctxt_info_dma_alloc_coherent(struct iwl_trans *trans, ...@@ -73,7 +73,7 @@ static void *_iwl_pcie_ctxt_info_dma_alloc_coherent(struct iwl_trans *trans,
if (!result) if (!result)
return NULL; return NULL;
if (unlikely(iwl_pcie_crosses_4g_boundary(*phys, size))) { if (unlikely(iwl_txq_crosses_4g_boundary(*phys, size))) {
void *old = result; void *old = result;
dma_addr_t oldphys = *phys; dma_addr_t oldphys = *phys;
......
...@@ -79,6 +79,7 @@ ...@@ -79,6 +79,7 @@
#include "iwl-io.h" #include "iwl-io.h"
#include "iwl-op-mode.h" #include "iwl-op-mode.h"
#include "iwl-drv.h" #include "iwl-drv.h"
#include "queue/tx.h"
/* /*
* RX related structures and functions * RX related structures and functions
...@@ -240,16 +241,6 @@ struct iwl_rb_allocator { ...@@ -240,16 +241,6 @@ struct iwl_rb_allocator {
struct work_struct rx_alloc; struct work_struct rx_alloc;
}; };
/**
* iwl_queue_inc_wrap - increment queue index, wrap back to beginning
* @index -- current index
*/
static inline int iwl_queue_inc_wrap(struct iwl_trans *trans, int index)
{
return ++index &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1);
}
/** /**
* iwl_get_closed_rb_stts - get closed rb stts from different structs * iwl_get_closed_rb_stts - get closed rb stts from different structs
* @rxq - the rxq to get the rb stts from * @rxq - the rxq to get the rb stts from
...@@ -268,28 +259,6 @@ static inline __le16 iwl_get_closed_rb_stts(struct iwl_trans *trans, ...@@ -268,28 +259,6 @@ static inline __le16 iwl_get_closed_rb_stts(struct iwl_trans *trans,
} }
} }
/**
* iwl_queue_dec_wrap - decrement queue index, wrap back to end
* @index -- current index
*/
static inline int iwl_queue_dec_wrap(struct iwl_trans *trans, int index)
{
return --index &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1);
}
static inline dma_addr_t
iwl_pcie_get_first_tb_dma(struct iwl_txq *txq, int idx)
{
return txq->first_tb_dma +
sizeof(struct iwl_pcie_first_tb_buf) * idx;
}
struct iwl_tso_hdr_page {
struct page *page;
u8 *pos;
};
#ifdef CONFIG_IWLWIFI_DEBUGFS #ifdef CONFIG_IWLWIFI_DEBUGFS
/** /**
* enum iwl_fw_mon_dbgfs_state - the different states of the monitor_data * enum iwl_fw_mon_dbgfs_state - the different states of the monitor_data
...@@ -427,8 +396,6 @@ struct iwl_trans_pcie { ...@@ -427,8 +396,6 @@ struct iwl_trans_pcie {
struct net_device napi_dev; struct net_device napi_dev;
struct __percpu iwl_tso_hdr_page *tso_hdr_page;
/* INT ICT Table */ /* INT ICT Table */
__le32 *ict_tbl; __le32 *ict_tbl;
dma_addr_t ict_tbl_dma; dma_addr_t ict_tbl_dma;
...@@ -566,19 +533,7 @@ void iwl_pcie_disable_ict(struct iwl_trans *trans); ...@@ -566,19 +533,7 @@ void iwl_pcie_disable_ict(struct iwl_trans *trans);
/***************************************************** /*****************************************************
* TX / HCMD * TX / HCMD
******************************************************/ ******************************************************/
/*
* We need this inline in case dma_addr_t is only 32-bits - since the
* hardware is always 64-bit, the issue can still occur in that case,
* so use u64 for 'phys' here to force the addition in 64-bit.
*/
static inline bool iwl_pcie_crosses_4g_boundary(u64 phys, u16 len)
{
return upper_32_bits(phys) != upper_32_bits(phys + len);
}
int iwl_pcie_tx_init(struct iwl_trans *trans); int iwl_pcie_tx_init(struct iwl_trans *trans);
int iwl_pcie_gen2_tx_init(struct iwl_trans *trans, int txq_id,
int queue_size);
void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr);
int iwl_pcie_tx_stop(struct iwl_trans *trans); int iwl_pcie_tx_stop(struct iwl_trans *trans);
void iwl_pcie_tx_free(struct iwl_trans *trans); void iwl_pcie_tx_free(struct iwl_trans *trans);
...@@ -589,14 +544,10 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue, ...@@ -589,14 +544,10 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue,
bool configure_scd); bool configure_scd);
void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id, void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id,
bool shared_mode); bool shared_mode);
void iwl_trans_pcie_log_scd_error(struct iwl_trans *trans,
struct iwl_txq *txq);
int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_tx_cmd *dev_cmd, int txq_id); struct iwl_device_tx_cmd *dev_cmd, int txq_id);
void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans);
int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans,
struct iwl_txq *txq);
void iwl_pcie_hcmd_complete(struct iwl_trans *trans, void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
struct iwl_rx_cmd_buffer *rxb); struct iwl_rx_cmd_buffer *rxb);
void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
...@@ -787,20 +738,6 @@ static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans) ...@@ -787,20 +738,6 @@ static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans)
} }
} }
static inline u16 iwl_pcie_get_cmd_index(const struct iwl_txq *q, u32 index)
{
return index & (q->n_window - 1);
}
static inline void *iwl_pcie_get_tfd(struct iwl_trans *trans,
struct iwl_txq *txq, int idx)
{
if (trans->trans_cfg->use_tfh)
idx = iwl_pcie_get_cmd_index(txq, idx);
return txq->tfds + trans->txqs.tfd.size * idx;
}
static inline const char *queue_name(struct device *dev, static inline const char *queue_name(struct device *dev,
struct iwl_trans_pcie *trans_p, int i) struct iwl_trans_pcie *trans_p, int i)
{ {
...@@ -852,37 +789,6 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) ...@@ -852,37 +789,6 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans); void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans);
static inline void iwl_wake_queue(struct iwl_trans *trans,
struct iwl_txq *txq)
{
if (test_and_clear_bit(txq->id, trans->txqs.queue_stopped)) {
IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->id);
iwl_op_mode_queue_not_full(trans->op_mode, txq->id);
}
}
static inline void iwl_stop_queue(struct iwl_trans *trans,
struct iwl_txq *txq)
{
if (!test_and_set_bit(txq->id, trans->txqs.queue_stopped)) {
iwl_op_mode_queue_full(trans->op_mode, txq->id);
IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->id);
} else
IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n",
txq->id);
}
static inline bool iwl_queue_used(const struct iwl_txq *q, int i)
{
int index = iwl_pcie_get_cmd_index(q, i);
int r = iwl_pcie_get_cmd_index(q, q->read_ptr);
int w = iwl_pcie_get_cmd_index(q, q->write_ptr);
return w >= r ?
(index >= r && index < w) :
!(index < r && index >= w);
}
static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
...@@ -949,23 +855,12 @@ bool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans); ...@@ -949,23 +855,12 @@ bool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans);
void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans, void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans,
bool was_in_rfkill); bool was_in_rfkill);
void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq); void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq);
int iwl_queue_space(struct iwl_trans *trans, const struct iwl_txq *q);
void iwl_pcie_apm_stop_master(struct iwl_trans *trans); void iwl_pcie_apm_stop_master(struct iwl_trans *trans);
void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie); void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie);
int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
int slots_num, bool cmd_queue);
int iwl_pcie_txq_alloc(struct iwl_trans *trans,
struct iwl_txq *txq, int slots_num, bool cmd_queue);
int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans,
struct iwl_dma_ptr *ptr, size_t size); struct iwl_dma_ptr *ptr, size_t size);
void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr); void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr);
void iwl_pcie_apply_destination(struct iwl_trans *trans); void iwl_pcie_apply_destination(struct iwl_trans *trans);
void iwl_pcie_free_tso_page(struct iwl_trans *trans,
struct sk_buff *skb);
#ifdef CONFIG_INET
struct iwl_tso_hdr_page *get_page_hdr(struct iwl_trans *trans, size_t len,
struct sk_buff *skb);
#endif
/* common functions that are used by gen3 transport */ /* common functions that are used by gen3 transport */
void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power); void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power);
...@@ -974,28 +869,10 @@ void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power); ...@@ -974,28 +869,10 @@ void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power);
int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
const struct fw_img *fw, bool run_in_rfkill); const struct fw_img *fw, bool run_in_rfkill);
void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr); void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr);
void iwl_pcie_gen2_txq_free_memory(struct iwl_trans *trans,
struct iwl_txq *txq);
int iwl_trans_pcie_dyn_txq_alloc_dma(struct iwl_trans *trans,
struct iwl_txq **intxq, int size,
unsigned int timeout);
int iwl_trans_pcie_txq_alloc_response(struct iwl_trans *trans,
struct iwl_txq *txq,
struct iwl_host_cmd *hcmd);
int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
__le16 flags, u8 sta_id, u8 tid,
int cmd_id, int size,
unsigned int timeout);
void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue);
int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_tx_cmd *dev_cmd, int txq_id);
int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd); struct iwl_host_cmd *cmd);
void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans); void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans);
void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans); void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans);
void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id);
void iwl_pcie_gen2_tx_free(struct iwl_trans *trans);
void iwl_pcie_gen2_tx_stop(struct iwl_trans *trans);
void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans, void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans,
bool test, bool reset); bool test, bool reset);
#endif /* __iwl_trans_int_pcie_h__ */ #endif /* __iwl_trans_int_pcie_h__ */
...@@ -1359,7 +1359,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, ...@@ -1359,7 +1359,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
sequence = le16_to_cpu(pkt->hdr.sequence); sequence = le16_to_cpu(pkt->hdr.sequence);
index = SEQ_TO_INDEX(sequence); index = SEQ_TO_INDEX(sequence);
cmd_index = iwl_pcie_get_cmd_index(txq, index); cmd_index = iwl_txq_get_cmd_index(txq, index);
if (rxq->id == trans_pcie->def_rx_queue) if (rxq->id == trans_pcie->def_rx_queue)
iwl_op_mode_rx(trans->op_mode, &rxq->napi, iwl_op_mode_rx(trans->op_mode, &rxq->napi,
......
...@@ -162,7 +162,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) ...@@ -162,7 +162,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
IWL_DEBUG_INFO(trans, IWL_DEBUG_INFO(trans,
"DEVICE_ENABLED bit was set and is now cleared\n"); "DEVICE_ENABLED bit was set and is now cleared\n");
iwl_pcie_gen2_tx_stop(trans); iwl_txq_gen2_tx_stop(trans);
iwl_pcie_rx_stop(trans); iwl_pcie_rx_stop(trans);
} }
...@@ -245,7 +245,7 @@ static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans) ...@@ -245,7 +245,7 @@ static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans)
return -ENOMEM; return -ENOMEM;
/* Allocate or reset and init all Tx and Command queues */ /* Allocate or reset and init all Tx and Command queues */
if (iwl_pcie_gen2_tx_init(trans, trans->txqs.cmd.q_id, queue_size)) if (iwl_txq_gen2_init(trans, trans->txqs.cmd.q_id, queue_size))
return -ENOMEM; return -ENOMEM;
/* enable shadow regs in HW */ /* enable shadow regs in HW */
......
...@@ -1955,7 +1955,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) ...@@ -1955,7 +1955,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
iwl_pcie_synchronize_irqs(trans); iwl_pcie_synchronize_irqs(trans);
if (trans->trans_cfg->gen2) if (trans->trans_cfg->gen2)
iwl_pcie_gen2_tx_free(trans); iwl_txq_gen2_tx_free(trans);
else else
iwl_pcie_tx_free(trans); iwl_pcie_tx_free(trans);
iwl_pcie_rx_free(trans); iwl_pcie_rx_free(trans);
...@@ -1979,15 +1979,6 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) ...@@ -1979,15 +1979,6 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
iwl_pcie_free_fw_monitor(trans); iwl_pcie_free_fw_monitor(trans);
for_each_possible_cpu(i) {
struct iwl_tso_hdr_page *p =
per_cpu_ptr(trans_pcie->tso_hdr_page, i);
if (p->page)
__free_page(p->page);
}
free_percpu(trans_pcie->tso_hdr_page);
mutex_destroy(&trans_pcie->mutex); mutex_destroy(&trans_pcie->mutex);
iwl_trans_free(trans); iwl_trans_free(trans);
} }
...@@ -2280,36 +2271,6 @@ static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block) ...@@ -2280,36 +2271,6 @@ static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block)
#define IWL_FLUSH_WAIT_MS 2000 #define IWL_FLUSH_WAIT_MS 2000
void iwl_trans_pcie_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq)
{
u32 txq_id = txq->id;
u32 status;
bool active;
u8 fifo;
if (trans->trans_cfg->use_tfh) {
IWL_ERR(trans, "Queue %d is stuck %d %d\n", txq_id,
txq->read_ptr, txq->write_ptr);
/* TODO: access new SCD registers and dump them */
return;
}
status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id));
fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
IWL_ERR(trans,
"Queue %d is %sactive on fifo %d and stuck for %u ms. SW [%d, %d] HW [%d, %d] FH TRB=0x0%x\n",
txq_id, active ? "" : "in", fifo,
jiffies_to_msecs(txq->wd_timeout),
txq->read_ptr, txq->write_ptr,
iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id)) &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1),
iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq_id)) &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1),
iwl_read_direct32(trans, FH_TX_TRB_REG(fifo)));
}
static int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue, static int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue,
struct iwl_trans_rxq_dma_data *data) struct iwl_trans_rxq_dma_data *data)
{ {
...@@ -2378,7 +2339,7 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) ...@@ -2378,7 +2339,7 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx)
if (txq->read_ptr != txq->write_ptr) { if (txq->read_ptr != txq->write_ptr) {
IWL_ERR(trans, IWL_ERR(trans,
"fail to flush all tx fifo queues Q %d\n", txq_idx); "fail to flush all tx fifo queues Q %d\n", txq_idx);
iwl_trans_pcie_log_scd_error(trans, txq); iwl_txq_log_scd_error(trans, txq);
return -ETIMEDOUT; return -ETIMEDOUT;
} }
...@@ -3339,7 +3300,7 @@ static struct iwl_trans_dump_data ...@@ -3339,7 +3300,7 @@ static struct iwl_trans_dump_data
spin_lock_bh(&cmdq->lock); spin_lock_bh(&cmdq->lock);
ptr = cmdq->write_ptr; ptr = cmdq->write_ptr;
for (i = 0; i < cmdq->n_window; i++) { for (i = 0; i < cmdq->n_window; i++) {
u8 idx = iwl_pcie_get_cmd_index(cmdq, ptr); u8 idx = iwl_txq_get_cmd_index(cmdq, ptr);
u8 tfdidx; u8 tfdidx;
u32 caplen, cmdlen; u32 caplen, cmdlen;
...@@ -3362,7 +3323,7 @@ static struct iwl_trans_dump_data ...@@ -3362,7 +3323,7 @@ static struct iwl_trans_dump_data
txcmd = (void *)((u8 *)txcmd->data + caplen); txcmd = (void *)((u8 *)txcmd->data + caplen);
} }
ptr = iwl_queue_dec_wrap(trans, ptr); ptr = iwl_txq_dec_wrap(trans, ptr);
} }
spin_unlock_bh(&cmdq->lock); spin_unlock_bh(&cmdq->lock);
...@@ -3481,13 +3442,13 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { ...@@ -3481,13 +3442,13 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
.send_cmd = iwl_trans_pcie_gen2_send_hcmd, .send_cmd = iwl_trans_pcie_gen2_send_hcmd,
.tx = iwl_trans_pcie_gen2_tx, .tx = iwl_txq_gen2_tx,
.reclaim = iwl_trans_pcie_reclaim, .reclaim = iwl_trans_pcie_reclaim,
.set_q_ptrs = iwl_trans_pcie_set_q_ptrs, .set_q_ptrs = iwl_trans_pcie_set_q_ptrs,
.txq_alloc = iwl_trans_pcie_dyn_txq_alloc, .txq_alloc = iwl_txq_dyn_alloc,
.txq_free = iwl_trans_pcie_dyn_txq_free, .txq_free = iwl_txq_dyn_free,
.wait_txq_empty = iwl_trans_pcie_wait_txq_empty, .wait_txq_empty = iwl_trans_pcie_wait_txq_empty,
.rxq_dma_data = iwl_trans_pcie_rxq_dma_data, .rxq_dma_data = iwl_trans_pcie_rxq_dma_data,
#ifdef CONFIG_IWLWIFI_DEBUGFS #ifdef CONFIG_IWLWIFI_DEBUGFS
...@@ -3534,11 +3495,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, ...@@ -3534,11 +3495,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
} }
INIT_WORK(&trans_pcie->rba.rx_alloc, iwl_pcie_rx_allocator_work); INIT_WORK(&trans_pcie->rba.rx_alloc, iwl_pcie_rx_allocator_work);
trans_pcie->tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
if (!trans_pcie->tso_hdr_page) {
ret = -ENOMEM;
goto out_no_pci;
}
trans_pcie->debug_rfkill = -1; trans_pcie->debug_rfkill = -1;
if (!cfg_trans->base_params->pcie_l1_allowed) { if (!cfg_trans->base_params->pcie_l1_allowed) {
...@@ -3671,7 +3627,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, ...@@ -3671,7 +3627,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
out_free_ict: out_free_ict:
iwl_pcie_free_ict(trans); iwl_pcie_free_ict(trans);
out_no_pci: out_no_pci:
free_percpu(trans_pcie->tso_hdr_page);
destroy_workqueue(trans_pcie->rba.alloc_wq); destroy_workqueue(trans_pcie->rba.alloc_wq);
out_free_trans: out_free_trans:
iwl_trans_free(trans); iwl_trans_free(trans);
......
...@@ -58,746 +58,7 @@ ...@@ -58,746 +58,7 @@
#include "iwl-io.h" #include "iwl-io.h"
#include "internal.h" #include "internal.h"
#include "fw/api/tx.h" #include "fw/api/tx.h"
#include "queue/tx.h"
/*
* iwl_pcie_gen2_tx_stop - Stop all Tx DMA channels
*/
void iwl_pcie_gen2_tx_stop(struct iwl_trans *trans)
{
int txq_id;
/*
* This function can be called before the op_mode disabled the
* queues. This happens when we have an rfkill interrupt.
* Since we stop Tx altogether - mark the queues as stopped.
*/
memset(trans->txqs.queue_stopped, 0,
sizeof(trans->txqs.queue_stopped));
memset(trans->txqs.queue_used, 0, sizeof(trans->txqs.queue_used));
/* Unmap DMA from host system and free skb's */
for (txq_id = 0; txq_id < ARRAY_SIZE(trans->txqs.txq); txq_id++) {
if (!trans->txqs.txq[txq_id])
continue;
iwl_pcie_gen2_txq_unmap(trans, txq_id);
}
}
/*
* iwl_pcie_txq_update_byte_tbl - Set up entry in Tx byte-count array
*/
static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans,
struct iwl_txq *txq, u16 byte_cnt,
int num_tbs)
{
int idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr);
u8 filled_tfd_size, num_fetch_chunks;
u16 len = byte_cnt;
__le16 bc_ent;
if (WARN(idx >= txq->n_window, "%d >= %d\n", idx, txq->n_window))
return;
filled_tfd_size = offsetof(struct iwl_tfh_tfd, tbs) +
num_tbs * sizeof(struct iwl_tfh_tb);
/*
* filled_tfd_size contains the number of filled bytes in the TFD.
* Dividing it by 64 will give the number of chunks to fetch
* to SRAM- 0 for one chunk, 1 for 2 and so on.
* If, for example, TFD contains only 3 TBs then 32 bytes
* of the TFD are used, and only one chunk of 64 bytes should
* be fetched
*/
num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1;
if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
struct iwl_gen3_bc_tbl *scd_bc_tbl_gen3 = txq->bc_tbl.addr;
/* Starting from AX210, the HW expects bytes */
WARN_ON(trans->txqs.bc_table_dword);
WARN_ON(len > 0x3FFF);
bc_ent = cpu_to_le16(len | (num_fetch_chunks << 14));
scd_bc_tbl_gen3->tfd_offset[idx] = bc_ent;
} else {
struct iwlagn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.addr;
/* Before AX210, the HW expects DW */
WARN_ON(!trans->txqs.bc_table_dword);
len = DIV_ROUND_UP(len, 4);
WARN_ON(len > 0xFFF);
bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12));
scd_bc_tbl->tfd_offset[idx] = bc_ent;
}
}
/*
* iwl_pcie_gen2_txq_inc_wr_ptr - Send new write index to hardware
*/
void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans,
struct iwl_txq *txq)
{
lockdep_assert_held(&txq->lock);
IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq->id, txq->write_ptr);
/*
* if not in power-save mode, uCode will never sleep when we're
* trying to tx (during RFKILL, we're not trying to tx).
*/
iwl_write32(trans, HBUS_TARG_WRPTR, txq->write_ptr | (txq->id << 16));
}
static u8 iwl_pcie_gen2_get_num_tbs(struct iwl_trans *trans,
struct iwl_tfh_tfd *tfd)
{
return le16_to_cpu(tfd->num_tbs) & 0x1f;
}
static void iwl_pcie_gen2_tfd_unmap(struct iwl_trans *trans,
struct iwl_cmd_meta *meta,
struct iwl_tfh_tfd *tfd)
{
int i, num_tbs;
/* Sanity check on number of chunks */
num_tbs = iwl_pcie_gen2_get_num_tbs(trans, tfd);
if (num_tbs > trans->txqs.tfd.max_tbs) {
IWL_ERR(trans, "Too many chunks: %i\n", num_tbs);
return;
}
/* first TB is never freed - it's the bidirectional DMA data */
for (i = 1; i < num_tbs; i++) {
if (meta->tbs & BIT(i))
dma_unmap_page(trans->dev,
le64_to_cpu(tfd->tbs[i].addr),
le16_to_cpu(tfd->tbs[i].tb_len),
DMA_TO_DEVICE);
else
dma_unmap_single(trans->dev,
le64_to_cpu(tfd->tbs[i].addr),
le16_to_cpu(tfd->tbs[i].tb_len),
DMA_TO_DEVICE);
}
tfd->num_tbs = 0;
}
static void iwl_pcie_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
{
/* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and
* idx is bounded by n_window
*/
int idx = iwl_pcie_get_cmd_index(txq, txq->read_ptr);
lockdep_assert_held(&txq->lock);
iwl_pcie_gen2_tfd_unmap(trans, &txq->entries[idx].meta,
iwl_pcie_get_tfd(trans, txq, idx));
/* free SKB */
if (txq->entries) {
struct sk_buff *skb;
skb = txq->entries[idx].skb;
/* Can be called from irqs-disabled context
* If skb is not NULL, it means that the whole queue is being
* freed and that the queue is not empty - free the skb
*/
if (skb) {
iwl_op_mode_free_skb(trans->op_mode, skb);
txq->entries[idx].skb = NULL;
}
}
}
static int iwl_pcie_gen2_set_tb(struct iwl_trans *trans,
struct iwl_tfh_tfd *tfd, dma_addr_t addr,
u16 len)
{
int idx = iwl_pcie_gen2_get_num_tbs(trans, tfd);
struct iwl_tfh_tb *tb;
/*
* Only WARN here so we know about the issue, but we mess up our
* unmap path because not every place currently checks for errors
* returned from this function - it can only return an error if
* there's no more space, and so when we know there is enough we
* don't always check ...
*/
WARN(iwl_pcie_crosses_4g_boundary(addr, len),
"possible DMA problem with iova:0x%llx, len:%d\n",
(unsigned long long)addr, len);
if (WARN_ON(idx >= IWL_TFH_NUM_TBS))
return -EINVAL;
tb = &tfd->tbs[idx];
/* Each TFD can point to a maximum max_tbs Tx buffers */
if (le16_to_cpu(tfd->num_tbs) >= trans->txqs.tfd.max_tbs) {
IWL_ERR(trans, "Error can not send more than %d chunks\n",
trans->txqs.tfd.max_tbs);
return -EINVAL;
}
put_unaligned_le64(addr, &tb->addr);
tb->tb_len = cpu_to_le16(len);
tfd->num_tbs = cpu_to_le16(idx + 1);
return idx;
}
static struct page *get_workaround_page(struct iwl_trans *trans,
struct sk_buff *skb)
{
struct page **page_ptr;
struct page *ret;
page_ptr = (void *)((u8 *)skb->cb + trans->txqs.page_offs);
ret = alloc_page(GFP_ATOMIC);
if (!ret)
return NULL;
/* set the chaining pointer to the previous page if there */
*(void **)(page_address(ret) + PAGE_SIZE - sizeof(void *)) = *page_ptr;
*page_ptr = ret;
return ret;
}
/*
* Add a TB and if needed apply the FH HW bug workaround;
* meta != NULL indicates that it's a page mapping and we
* need to dma_unmap_page() and set the meta->tbs bit in
* this case.
*/
static int iwl_pcie_gen2_set_tb_with_wa(struct iwl_trans *trans,
struct sk_buff *skb,
struct iwl_tfh_tfd *tfd,
dma_addr_t phys, void *virt,
u16 len, struct iwl_cmd_meta *meta)
{
dma_addr_t oldphys = phys;
struct page *page;
int ret;
if (unlikely(dma_mapping_error(trans->dev, phys)))
return -ENOMEM;
if (likely(!iwl_pcie_crosses_4g_boundary(phys, len))) {
ret = iwl_pcie_gen2_set_tb(trans, tfd, phys, len);
if (ret < 0)
goto unmap;
if (meta)
meta->tbs |= BIT(ret);
ret = 0;
goto trace;
}
/*
* Work around a hardware bug. If (as expressed in the
* condition above) the TB ends on a 32-bit boundary,
* then the next TB may be accessed with the wrong
* address.
* To work around it, copy the data elsewhere and make
* a new mapping for it so the device will not fail.
*/
if (WARN_ON(len > PAGE_SIZE - sizeof(void *))) {
ret = -ENOBUFS;
goto unmap;
}
page = get_workaround_page(trans, skb);
if (!page) {
ret = -ENOMEM;
goto unmap;
}
memcpy(page_address(page), virt, len);
phys = dma_map_single(trans->dev, page_address(page), len,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, phys)))
return -ENOMEM;
ret = iwl_pcie_gen2_set_tb(trans, tfd, phys, len);
if (ret < 0) {
/* unmap the new allocation as single */
oldphys = phys;
meta = NULL;
goto unmap;
}
IWL_WARN(trans,
"TB bug workaround: copied %d bytes from 0x%llx to 0x%llx\n",
len, (unsigned long long)oldphys, (unsigned long long)phys);
ret = 0;
unmap:
if (meta)
dma_unmap_page(trans->dev, oldphys, len, DMA_TO_DEVICE);
else
dma_unmap_single(trans->dev, oldphys, len, DMA_TO_DEVICE);
trace:
trace_iwlwifi_dev_tx_tb(trans->dev, skb, virt, phys, len);
return ret;
}
static int iwl_pcie_gen2_build_amsdu(struct iwl_trans *trans,
struct sk_buff *skb,
struct iwl_tfh_tfd *tfd, int start_len,
u8 hdr_len,
struct iwl_device_tx_cmd *dev_cmd)
{
#ifdef CONFIG_INET
struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload;
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
unsigned int mss = skb_shinfo(skb)->gso_size;
u16 length, amsdu_pad;
u8 *start_hdr;
struct iwl_tso_hdr_page *hdr_page;
struct tso_t tso;
trace_iwlwifi_dev_tx(trans->dev, skb, tfd, sizeof(*tfd),
&dev_cmd->hdr, start_len, 0);
ip_hdrlen = skb_transport_header(skb) - skb_network_header(skb);
snap_ip_tcp_hdrlen = 8 + ip_hdrlen + tcp_hdrlen(skb);
total_len = skb->len - snap_ip_tcp_hdrlen - hdr_len;
amsdu_pad = 0;
/* total amount of header we may need for this A-MSDU */
hdr_room = DIV_ROUND_UP(total_len, mss) *
(3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr));
/* Our device supports 9 segments at most, it will fit in 1 page */
hdr_page = get_page_hdr(trans, hdr_room, skb);
if (!hdr_page)
return -ENOMEM;
start_hdr = hdr_page->pos;
/*
* Pull the ieee80211 header to be able to use TSO core,
* we will restore it for the tx_status flow.
*/
skb_pull(skb, hdr_len);
/*
* Remove the length of all the headers that we don't actually
* have in the MPDU by themselves, but that we duplicate into
* all the different MSDUs inside the A-MSDU.
*/
le16_add_cpu(&tx_cmd->len, -snap_ip_tcp_hdrlen);
tso_start(skb, &tso);
while (total_len) {
/* this is the data left for this subframe */
unsigned int data_left = min_t(unsigned int, mss, total_len);
struct sk_buff *csum_skb = NULL;
unsigned int tb_len;
dma_addr_t tb_phys;
u8 *subf_hdrs_start = hdr_page->pos;
total_len -= data_left;
memset(hdr_page->pos, 0, amsdu_pad);
hdr_page->pos += amsdu_pad;
amsdu_pad = (4 - (sizeof(struct ethhdr) + snap_ip_tcp_hdrlen +
data_left)) & 0x3;
ether_addr_copy(hdr_page->pos, ieee80211_get_DA(hdr));
hdr_page->pos += ETH_ALEN;
ether_addr_copy(hdr_page->pos, ieee80211_get_SA(hdr));
hdr_page->pos += ETH_ALEN;
length = snap_ip_tcp_hdrlen + data_left;
*((__be16 *)hdr_page->pos) = cpu_to_be16(length);
hdr_page->pos += sizeof(length);
/*
* This will copy the SNAP as well which will be considered
* as MAC header.
*/
tso_build_hdr(skb, hdr_page->pos, &tso, data_left, !total_len);
hdr_page->pos += snap_ip_tcp_hdrlen;
tb_len = hdr_page->pos - start_hdr;
tb_phys = dma_map_single(trans->dev, start_hdr,
tb_len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
dev_kfree_skb(csum_skb);
goto out_err;
}
/*
* No need for _with_wa, this is from the TSO page and
* we leave some space at the end of it so can't hit
* the buggy scenario.
*/
iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, tb_len);
trace_iwlwifi_dev_tx_tb(trans->dev, skb, start_hdr,
tb_phys, tb_len);
/* add this subframe's headers' length to the tx_cmd */
le16_add_cpu(&tx_cmd->len, hdr_page->pos - subf_hdrs_start);
/* prepare the start_hdr for the next subframe */
start_hdr = hdr_page->pos;
/* put the payload */
while (data_left) {
int ret;
tb_len = min_t(unsigned int, tso.size, data_left);
tb_phys = dma_map_single(trans->dev, tso.data,
tb_len, DMA_TO_DEVICE);
ret = iwl_pcie_gen2_set_tb_with_wa(trans, skb, tfd,
tb_phys, tso.data,
tb_len, NULL);
if (ret) {
dev_kfree_skb(csum_skb);
goto out_err;
}
data_left -= tb_len;
tso_build_data(skb, &tso, tb_len);
}
}
/* re -add the WiFi header */
skb_push(skb, hdr_len);
return 0;
out_err:
#endif
return -EINVAL;
}
static struct
iwl_tfh_tfd *iwl_pcie_gen2_build_tx_amsdu(struct iwl_trans *trans,
struct iwl_txq *txq,
struct iwl_device_tx_cmd *dev_cmd,
struct sk_buff *skb,
struct iwl_cmd_meta *out_meta,
int hdr_len,
int tx_cmd_len)
{
int idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr);
struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans, txq, idx);
dma_addr_t tb_phys;
int len;
void *tb1_addr;
tb_phys = iwl_pcie_get_first_tb_dma(txq, idx);
/*
* No need for _with_wa, the first TB allocation is aligned up
* to a 64-byte boundary and thus can't be at the end or cross
* a page boundary (much less a 2^32 boundary).
*/
iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, IWL_FIRST_TB_SIZE);
/*
* The second TB (tb1) points to the remainder of the TX command
* and the 802.11 header - dword aligned size
* (This calculation modifies the TX command, so do it before the
* setup of the first TB)
*/
len = tx_cmd_len + sizeof(struct iwl_cmd_header) + hdr_len -
IWL_FIRST_TB_SIZE;
/* do not align A-MSDU to dword as the subframe header aligns it */
/* map the data for TB1 */
tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE;
tb_phys = dma_map_single(trans->dev, tb1_addr, len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, tb_phys)))
goto out_err;
/*
* No need for _with_wa(), we ensure (via alignment) that the data
* here can never cross or end at a page boundary.
*/
iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, len);
if (iwl_pcie_gen2_build_amsdu(trans, skb, tfd,
len + IWL_FIRST_TB_SIZE,
hdr_len, dev_cmd))
goto out_err;
/* building the A-MSDU might have changed this data, memcpy it now */
memcpy(&txq->first_tb_bufs[idx], dev_cmd, IWL_FIRST_TB_SIZE);
return tfd;
out_err:
iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd);
return NULL;
}
static int iwl_pcie_gen2_tx_add_frags(struct iwl_trans *trans,
struct sk_buff *skb,
struct iwl_tfh_tfd *tfd,
struct iwl_cmd_meta *out_meta)
{
int i;
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;
unsigned int fragsz = skb_frag_size(frag);
int ret;
if (!fragsz)
continue;
tb_phys = skb_frag_dma_map(trans->dev, frag, 0,
fragsz, DMA_TO_DEVICE);
ret = iwl_pcie_gen2_set_tb_with_wa(trans, skb, tfd, tb_phys,
skb_frag_address(frag),
fragsz, out_meta);
if (ret)
return ret;
}
return 0;
}
static struct
iwl_tfh_tfd *iwl_pcie_gen2_build_tx(struct iwl_trans *trans,
struct iwl_txq *txq,
struct iwl_device_tx_cmd *dev_cmd,
struct sk_buff *skb,
struct iwl_cmd_meta *out_meta,
int hdr_len,
int tx_cmd_len,
bool pad)
{
int idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr);
struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans, txq, idx);
dma_addr_t tb_phys;
int len, tb1_len, tb2_len;
void *tb1_addr;
struct sk_buff *frag;
tb_phys = iwl_pcie_get_first_tb_dma(txq, idx);
/* The first TB points to bi-directional DMA data */
memcpy(&txq->first_tb_bufs[idx], dev_cmd, IWL_FIRST_TB_SIZE);
/*
* No need for _with_wa, the first TB allocation is aligned up
* to a 64-byte boundary and thus can't be at the end or cross
* a page boundary (much less a 2^32 boundary).
*/
iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, IWL_FIRST_TB_SIZE);
/*
* The second TB (tb1) points to the remainder of the TX command
* and the 802.11 header - dword aligned size
* (This calculation modifies the TX command, so do it before the
* setup of the first TB)
*/
len = tx_cmd_len + sizeof(struct iwl_cmd_header) + hdr_len -
IWL_FIRST_TB_SIZE;
if (pad)
tb1_len = ALIGN(len, 4);
else
tb1_len = len;
/* map the data for TB1 */
tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE;
tb_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, tb_phys)))
goto out_err;
/*
* No need for _with_wa(), we ensure (via alignment) that the data
* here can never cross or end at a page boundary.
*/
iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, tb1_len);
trace_iwlwifi_dev_tx(trans->dev, skb, tfd, sizeof(*tfd), &dev_cmd->hdr,
IWL_FIRST_TB_SIZE + tb1_len, hdr_len);
/* set up TFD's third entry to point to remainder of skb's head */
tb2_len = skb_headlen(skb) - hdr_len;
if (tb2_len > 0) {
int ret;
tb_phys = dma_map_single(trans->dev, skb->data + hdr_len,
tb2_len, DMA_TO_DEVICE);
ret = iwl_pcie_gen2_set_tb_with_wa(trans, skb, tfd, tb_phys,
skb->data + hdr_len, tb2_len,
NULL);
if (ret)
goto out_err;
}
if (iwl_pcie_gen2_tx_add_frags(trans, skb, tfd, out_meta))
goto out_err;
skb_walk_frags(skb, frag) {
int ret;
tb_phys = dma_map_single(trans->dev, frag->data,
skb_headlen(frag), DMA_TO_DEVICE);
ret = iwl_pcie_gen2_set_tb_with_wa(trans, skb, tfd, tb_phys,
frag->data,
skb_headlen(frag), NULL);
if (ret)
goto out_err;
if (iwl_pcie_gen2_tx_add_frags(trans, frag, tfd, out_meta))
goto out_err;
}
return tfd;
out_err:
iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd);
return NULL;
}
static
struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans,
struct iwl_txq *txq,
struct iwl_device_tx_cmd *dev_cmd,
struct sk_buff *skb,
struct iwl_cmd_meta *out_meta)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
int idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr);
struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans, txq, idx);
int len, hdr_len;
bool amsdu;
/* There must be data left over for TB1 or this code must be changed */
BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen2) < IWL_FIRST_TB_SIZE);
memset(tfd, 0, sizeof(*tfd));
if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
len = sizeof(struct iwl_tx_cmd_gen2);
else
len = sizeof(struct iwl_tx_cmd_gen3);
amsdu = ieee80211_is_data_qos(hdr->frame_control) &&
(*ieee80211_get_qos_ctl(hdr) &
IEEE80211_QOS_CTL_A_MSDU_PRESENT);
hdr_len = ieee80211_hdrlen(hdr->frame_control);
/*
* Only build A-MSDUs here if doing so by GSO, otherwise it may be
* an A-MSDU for other reasons, e.g. NAN or an A-MSDU having been
* built in the higher layers already.
*/
if (amsdu && skb_shinfo(skb)->gso_size)
return iwl_pcie_gen2_build_tx_amsdu(trans, txq, dev_cmd, skb,
out_meta, hdr_len, len);
return iwl_pcie_gen2_build_tx(trans, txq, dev_cmd, skb, out_meta,
hdr_len, len, !amsdu);
}
int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_tx_cmd *dev_cmd, int txq_id)
{
struct iwl_cmd_meta *out_meta;
struct iwl_txq *txq = trans->txqs.txq[txq_id];
u16 cmd_len;
int idx;
void *tfd;
if (WARN_ONCE(txq_id >= IWL_MAX_TVQM_QUEUES,
"queue %d out of range", txq_id))
return -EINVAL;
if (WARN_ONCE(!test_bit(txq_id, trans->txqs.queue_used),
"TX on unused queue %d\n", txq_id))
return -EINVAL;
if (skb_is_nonlinear(skb) &&
skb_shinfo(skb)->nr_frags > IWL_TRANS_MAX_FRAGS(trans) &&
__skb_linearize(skb))
return -ENOMEM;
spin_lock(&txq->lock);
if (iwl_queue_space(trans, txq) < txq->high_mark) {
iwl_stop_queue(trans, txq);
/* don't put the packet on the ring, if there is no room */
if (unlikely(iwl_queue_space(trans, txq) < 3)) {
struct iwl_device_tx_cmd **dev_cmd_ptr;
dev_cmd_ptr = (void *)((u8 *)skb->cb +
trans->txqs.dev_cmd_offs);
*dev_cmd_ptr = dev_cmd;
__skb_queue_tail(&txq->overflow_q, skb);
spin_unlock(&txq->lock);
return 0;
}
}
idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr);
/* Set up driver data for this TFD */
txq->entries[idx].skb = skb;
txq->entries[idx].cmd = dev_cmd;
dev_cmd->hdr.sequence =
cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |
INDEX_TO_SEQ(idx)));
/* Set up first empty entry in queue's array of Tx/cmd buffers */
out_meta = &txq->entries[idx].meta;
out_meta->flags = 0;
tfd = iwl_pcie_gen2_build_tfd(trans, txq, dev_cmd, skb, out_meta);
if (!tfd) {
spin_unlock(&txq->lock);
return -1;
}
if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
struct iwl_tx_cmd_gen3 *tx_cmd_gen3 =
(void *)dev_cmd->payload;
cmd_len = le16_to_cpu(tx_cmd_gen3->len);
} else {
struct iwl_tx_cmd_gen2 *tx_cmd_gen2 =
(void *)dev_cmd->payload;
cmd_len = le16_to_cpu(tx_cmd_gen2->len);
}
/* Set up entry for this TFD in Tx byte-count array */
iwl_pcie_gen2_update_byte_tbl(trans, txq, cmd_len,
iwl_pcie_gen2_get_num_tbs(trans, tfd));
/* start timer if queue currently empty */
if (txq->read_ptr == txq->write_ptr && txq->wd_timeout)
mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
/* Tell device the write index *just past* this latest filled TFD */
txq->write_ptr = iwl_queue_inc_wrap(trans, txq->write_ptr);
iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq);
/*
* At this point the frame is "transmitted" successfully
* and we will get a TX status notification eventually.
*/
spin_unlock(&txq->lock);
return 0;
}
/*************** HOST COMMAND QUEUE FUNCTIONS *****/ /*************** HOST COMMAND QUEUE FUNCTIONS *****/
...@@ -897,11 +158,11 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, ...@@ -897,11 +158,11 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
spin_lock_bh(&txq->lock); spin_lock_bh(&txq->lock);
idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); idx = iwl_txq_get_cmd_index(txq, txq->write_ptr);
tfd = iwl_pcie_get_tfd(trans, txq, txq->write_ptr); tfd = iwl_txq_get_tfd(trans, txq, txq->write_ptr);
memset(tfd, 0, sizeof(*tfd)); memset(tfd, 0, sizeof(*tfd));
if (iwl_queue_space(trans, txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { if (iwl_txq_space(trans, txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
spin_unlock_bh(&txq->lock); spin_unlock_bh(&txq->lock);
IWL_ERR(trans, "No space in command queue\n"); IWL_ERR(trans, "No space in command queue\n");
...@@ -979,8 +240,8 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, ...@@ -979,8 +240,8 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
/* start the TFD with the minimum copy bytes */ /* start the TFD with the minimum copy bytes */
tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE); tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE);
memcpy(&txq->first_tb_bufs[idx], out_cmd, tb0_size); memcpy(&txq->first_tb_bufs[idx], out_cmd, tb0_size);
iwl_pcie_gen2_set_tb(trans, tfd, iwl_pcie_get_first_tb_dma(txq, idx), iwl_txq_gen2_set_tb(trans, tfd, iwl_txq_get_first_tb_dma(txq, idx),
tb0_size); tb0_size);
/* map first command fragment, if any remains */ /* map first command fragment, if any remains */
if (copy_size > tb0_size) { if (copy_size > tb0_size) {
...@@ -990,11 +251,11 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, ...@@ -990,11 +251,11 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (dma_mapping_error(trans->dev, phys_addr)) { if (dma_mapping_error(trans->dev, phys_addr)) {
idx = -ENOMEM; idx = -ENOMEM;
iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd); iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd);
goto out; goto out;
} }
iwl_pcie_gen2_set_tb(trans, tfd, phys_addr, iwl_txq_gen2_set_tb(trans, tfd, phys_addr,
copy_size - tb0_size); copy_size - tb0_size);
} }
/* map the remaining (adjusted) nocopy/dup fragments */ /* map the remaining (adjusted) nocopy/dup fragments */
...@@ -1012,10 +273,10 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1012,10 +273,10 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
cmdlen[i], DMA_TO_DEVICE); cmdlen[i], DMA_TO_DEVICE);
if (dma_mapping_error(trans->dev, phys_addr)) { if (dma_mapping_error(trans->dev, phys_addr)) {
idx = -ENOMEM; idx = -ENOMEM;
iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd); iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd);
goto out; goto out;
} }
iwl_pcie_gen2_set_tb(trans, tfd, phys_addr, cmdlen[i]); iwl_txq_gen2_set_tb(trans, tfd, phys_addr, cmdlen[i]);
} }
BUILD_BUG_ON(IWL_TFH_NUM_TBS > sizeof(out_meta->tbs) * BITS_PER_BYTE); BUILD_BUG_ON(IWL_TFH_NUM_TBS > sizeof(out_meta->tbs) * BITS_PER_BYTE);
...@@ -1032,8 +293,8 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1032,8 +293,8 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
spin_lock_irqsave(&trans_pcie->reg_lock, flags); spin_lock_irqsave(&trans_pcie->reg_lock, flags);
/* Increment and update queue's write index */ /* Increment and update queue's write index */
txq->write_ptr = iwl_queue_inc_wrap(trans, txq->write_ptr); txq->write_ptr = iwl_txq_inc_wrap(trans, txq->write_ptr);
iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq); iwl_txq_inc_wr_ptr(trans, txq);
spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
out: out:
...@@ -1164,316 +425,3 @@ int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, ...@@ -1164,316 +425,3 @@ int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans,
return iwl_pcie_gen2_send_hcmd_sync(trans, cmd); return iwl_pcie_gen2_send_hcmd_sync(trans, cmd);
} }
/*
* iwl_pcie_gen2_txq_unmap - Unmap any remaining DMA mappings and free skb's
*/
void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id)
{
struct iwl_txq *txq = trans->txqs.txq[txq_id];
spin_lock_bh(&txq->lock);
while (txq->write_ptr != txq->read_ptr) {
IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
txq_id, txq->read_ptr);
if (txq_id != trans->txqs.cmd.q_id) {
int idx = iwl_pcie_get_cmd_index(txq, txq->read_ptr);
struct sk_buff *skb = txq->entries[idx].skb;
if (WARN_ON_ONCE(!skb))
continue;
iwl_pcie_free_tso_page(trans, skb);
}
iwl_pcie_gen2_free_tfd(trans, txq);
txq->read_ptr = iwl_queue_inc_wrap(trans, txq->read_ptr);
}
while (!skb_queue_empty(&txq->overflow_q)) {
struct sk_buff *skb = __skb_dequeue(&txq->overflow_q);
iwl_op_mode_free_skb(trans->op_mode, skb);
}
spin_unlock_bh(&txq->lock);
/* just in case - this queue may have been stopped */
iwl_wake_queue(trans, txq);
}
void iwl_pcie_gen2_txq_free_memory(struct iwl_trans *trans,
struct iwl_txq *txq)
{
struct device *dev = trans->dev;
/* De-alloc circular buffer of TFDs */
if (txq->tfds) {
dma_free_coherent(dev,
trans->txqs.tfd.size * txq->n_window,
txq->tfds, txq->dma_addr);
dma_free_coherent(dev,
sizeof(*txq->first_tb_bufs) * txq->n_window,
txq->first_tb_bufs, txq->first_tb_dma);
}
kfree(txq->entries);
if (txq->bc_tbl.addr)
dma_pool_free(trans->txqs.bc_pool, txq->bc_tbl.addr,
txq->bc_tbl.dma);
kfree(txq);
}
/*
* iwl_pcie_txq_free - Deallocate DMA queue.
* @txq: Transmit queue to deallocate.
*
* Empty queue by removing and destroying all BD's.
* Free all buffers.
* 0-fill, but do not free "txq" descriptor structure.
*/
static void iwl_pcie_gen2_txq_free(struct iwl_trans *trans, int txq_id)
{
struct iwl_txq *txq;
int i;
if (WARN_ONCE(txq_id >= IWL_MAX_TVQM_QUEUES,
"queue %d out of range", txq_id))
return;
txq = trans->txqs.txq[txq_id];
if (WARN_ON(!txq))
return;
iwl_pcie_gen2_txq_unmap(trans, txq_id);
/* De-alloc array of command/tx buffers */
if (txq_id == trans->txqs.cmd.q_id)
for (i = 0; i < txq->n_window; i++) {
kfree_sensitive(txq->entries[i].cmd);
kfree_sensitive(txq->entries[i].free_buf);
}
del_timer_sync(&txq->stuck_timer);
iwl_pcie_gen2_txq_free_memory(trans, txq);
trans->txqs.txq[txq_id] = NULL;
clear_bit(txq_id, trans->txqs.queue_used);
}
int iwl_trans_pcie_dyn_txq_alloc_dma(struct iwl_trans *trans,
struct iwl_txq **intxq, int size,
unsigned int timeout)
{
size_t bc_tbl_size, bc_tbl_entries;
struct iwl_txq *txq;
int ret;
WARN_ON(!trans->txqs.bc_tbl_size);
bc_tbl_size = trans->txqs.bc_tbl_size;
bc_tbl_entries = bc_tbl_size / sizeof(u16);
if (WARN_ON(size > bc_tbl_entries))
return -EINVAL;
txq = kzalloc(sizeof(*txq), GFP_KERNEL);
if (!txq)
return -ENOMEM;
txq->bc_tbl.addr = dma_pool_alloc(trans->txqs.bc_pool, GFP_KERNEL,
&txq->bc_tbl.dma);
if (!txq->bc_tbl.addr) {
IWL_ERR(trans, "Scheduler BC Table allocation failed\n");
kfree(txq);
return -ENOMEM;
}
ret = iwl_pcie_txq_alloc(trans, txq, size, false);
if (ret) {
IWL_ERR(trans, "Tx queue alloc failed\n");
goto error;
}
ret = iwl_pcie_txq_init(trans, txq, size, false);
if (ret) {
IWL_ERR(trans, "Tx queue init failed\n");
goto error;
}
txq->wd_timeout = msecs_to_jiffies(timeout);
*intxq = txq;
return 0;
error:
iwl_pcie_gen2_txq_free_memory(trans, txq);
return ret;
}
int iwl_trans_pcie_txq_alloc_response(struct iwl_trans *trans,
struct iwl_txq *txq,
struct iwl_host_cmd *hcmd)
{
struct iwl_tx_queue_cfg_rsp *rsp;
int ret, qid;
u32 wr_ptr;
if (WARN_ON(iwl_rx_packet_payload_len(hcmd->resp_pkt) !=
sizeof(*rsp))) {
ret = -EINVAL;
goto error_free_resp;
}
rsp = (void *)hcmd->resp_pkt->data;
qid = le16_to_cpu(rsp->queue_number);
wr_ptr = le16_to_cpu(rsp->write_pointer);
if (qid >= ARRAY_SIZE(trans->txqs.txq)) {
WARN_ONCE(1, "queue index %d unsupported", qid);
ret = -EIO;
goto error_free_resp;
}
if (test_and_set_bit(qid, trans->txqs.queue_used)) {
WARN_ONCE(1, "queue %d already used", qid);
ret = -EIO;
goto error_free_resp;
}
txq->id = qid;
trans->txqs.txq[qid] = txq;
wr_ptr &= (trans->trans_cfg->base_params->max_tfd_queue_size - 1);
/* Place first TFD at index corresponding to start sequence number */
txq->read_ptr = wr_ptr;
txq->write_ptr = wr_ptr;
IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d\n", qid);
iwl_free_resp(hcmd);
return qid;
error_free_resp:
iwl_free_resp(hcmd);
iwl_pcie_gen2_txq_free_memory(trans, txq);
return ret;
}
int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
__le16 flags, u8 sta_id, u8 tid,
int cmd_id, int size,
unsigned int timeout)
{
struct iwl_txq *txq = NULL;
struct iwl_tx_queue_cfg_cmd cmd = {
.flags = flags,
.sta_id = sta_id,
.tid = tid,
};
struct iwl_host_cmd hcmd = {
.id = cmd_id,
.len = { sizeof(cmd) },
.data = { &cmd, },
.flags = CMD_WANT_SKB,
};
int ret;
ret = iwl_trans_pcie_dyn_txq_alloc_dma(trans, &txq, size, timeout);
if (ret)
return ret;
cmd.tfdq_addr = cpu_to_le64(txq->dma_addr);
cmd.byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma);
cmd.cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size));
ret = iwl_trans_send_cmd(trans, &hcmd);
if (ret)
goto error;
return iwl_trans_pcie_txq_alloc_response(trans, txq, &hcmd);
error:
iwl_pcie_gen2_txq_free_memory(trans, txq);
return ret;
}
void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue)
{
if (WARN(queue >= IWL_MAX_TVQM_QUEUES,
"queue %d out of range", queue))
return;
/*
* Upon HW Rfkill - we stop the device, and then stop the queues
* in the op_mode. Just for the sake of the simplicity of the op_mode,
* allow the op_mode to call txq_disable after it already called
* stop_device.
*/
if (!test_and_clear_bit(queue, trans->txqs.queue_used)) {
WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status),
"queue %d not used", queue);
return;
}
iwl_pcie_gen2_txq_unmap(trans, queue);
iwl_pcie_gen2_txq_free_memory(trans, trans->txqs.txq[queue]);
trans->txqs.txq[queue] = NULL;
IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", queue);
}
void iwl_pcie_gen2_tx_free(struct iwl_trans *trans)
{
int i;
memset(trans->txqs.queue_used, 0, sizeof(trans->txqs.queue_used));
/* Free all TX queues */
for (i = 0; i < ARRAY_SIZE(trans->txqs.txq); i++) {
if (!trans->txqs.txq[i])
continue;
iwl_pcie_gen2_txq_free(trans, i);
}
}
int iwl_pcie_gen2_tx_init(struct iwl_trans *trans, int txq_id, int queue_size)
{
struct iwl_txq *queue;
int ret;
/* alloc and init the tx queue */
if (!trans->txqs.txq[txq_id]) {
queue = kzalloc(sizeof(*queue), GFP_KERNEL);
if (!queue) {
IWL_ERR(trans, "Not enough memory for tx queue\n");
return -ENOMEM;
}
trans->txqs.txq[txq_id] = queue;
ret = iwl_pcie_txq_alloc(trans, queue, queue_size, true);
if (ret) {
IWL_ERR(trans, "Tx %d queue init failed\n", txq_id);
goto error;
}
} else {
queue = trans->txqs.txq[txq_id];
}
ret = iwl_pcie_txq_init(trans, queue, queue_size,
(txq_id == trans->txqs.cmd.q_id));
if (ret) {
IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id);
goto error;
}
trans->txqs.txq[txq_id]->id = txq_id;
set_bit(txq_id, trans->txqs.queue_used);
return 0;
error:
iwl_pcie_gen2_tx_free(trans);
return ret;
}
...@@ -102,60 +102,6 @@ ...@@ -102,60 +102,6 @@
* *
***************************************************/ ***************************************************/
int iwl_queue_space(struct iwl_trans *trans, const struct iwl_txq *q)
{
unsigned int max;
unsigned int used;
/*
* To avoid ambiguity between empty and completely full queues, there
* should always be less than max_tfd_queue_size elements in the queue.
* If q->n_window is smaller than max_tfd_queue_size, there is no need
* to reserve any queue entries for this purpose.
*/
if (q->n_window < trans->trans_cfg->base_params->max_tfd_queue_size)
max = q->n_window;
else
max = trans->trans_cfg->base_params->max_tfd_queue_size - 1;
/*
* max_tfd_queue_size is a power of 2, so the following is equivalent to
* modulo by max_tfd_queue_size and is well defined.
*/
used = (q->write_ptr - q->read_ptr) &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1);
if (WARN_ON(used > max))
return 0;
return max - used;
}
/*
* iwl_queue_init - Initialize queue's high/low-water and read/write indexes
*/
static int iwl_queue_init(struct iwl_txq *q, int slots_num)
{
q->n_window = slots_num;
/* slots_num must be power-of-two size, otherwise
* iwl_pcie_get_cmd_index is broken. */
if (WARN_ON(!is_power_of_2(slots_num)))
return -EINVAL;
q->low_mark = q->n_window / 4;
if (q->low_mark < 4)
q->low_mark = 4;
q->high_mark = q->n_window / 8;
if (q->high_mark < 2)
q->high_mark = 2;
q->write_ptr = 0;
q->read_ptr = 0;
return 0;
}
int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans,
struct iwl_dma_ptr *ptr, size_t size) struct iwl_dma_ptr *ptr, size_t size)
...@@ -180,24 +126,6 @@ void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr) ...@@ -180,24 +126,6 @@ void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr)
memset(ptr, 0, sizeof(*ptr)); memset(ptr, 0, sizeof(*ptr));
} }
static void iwl_pcie_txq_stuck_timer(struct timer_list *t)
{
struct iwl_txq *txq = from_timer(txq, t, stuck_timer);
struct iwl_trans *trans = txq->trans;
spin_lock(&txq->lock);
/* check if triggered erroneously */
if (txq->read_ptr == txq->write_ptr) {
spin_unlock(&txq->lock);
return;
}
spin_unlock(&txq->lock);
iwl_trans_pcie_log_scd_error(trans, txq);
iwl_force_nmi(trans);
}
/* /*
* iwl_pcie_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array * iwl_pcie_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
*/ */
...@@ -402,7 +330,7 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, ...@@ -402,7 +330,7 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans,
struct iwl_txq *txq, int index) struct iwl_txq *txq, int index)
{ {
int i, num_tbs; int i, num_tbs;
void *tfd = iwl_pcie_get_tfd(trans, txq, index); void *tfd = iwl_txq_get_tfd(trans, txq, index);
/* Sanity check on number of chunks */ /* Sanity check on number of chunks */
num_tbs = iwl_pcie_tfd_get_num_tbs(trans, tfd); num_tbs = iwl_pcie_tfd_get_num_tbs(trans, tfd);
...@@ -459,7 +387,7 @@ void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) ...@@ -459,7 +387,7 @@ void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
* idx is bounded by n_window * idx is bounded by n_window
*/ */
int rd_ptr = txq->read_ptr; int rd_ptr = txq->read_ptr;
int idx = iwl_pcie_get_cmd_index(txq, rd_ptr); int idx = iwl_txq_get_cmd_index(txq, rd_ptr);
lockdep_assert_held(&txq->lock); lockdep_assert_held(&txq->lock);
...@@ -514,125 +442,6 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, ...@@ -514,125 +442,6 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
return num_tbs; return num_tbs;
} }
int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq,
int slots_num, bool cmd_queue)
{
size_t tfd_sz = trans->txqs.tfd.size *
trans->trans_cfg->base_params->max_tfd_queue_size;
size_t tb0_buf_sz;
int i;
if (WARN_ON(txq->entries || txq->tfds))
return -EINVAL;
if (trans->trans_cfg->use_tfh)
tfd_sz = trans->txqs.tfd.size * slots_num;
timer_setup(&txq->stuck_timer, iwl_pcie_txq_stuck_timer, 0);
txq->trans = trans;
txq->n_window = slots_num;
txq->entries = kcalloc(slots_num,
sizeof(struct iwl_pcie_txq_entry),
GFP_KERNEL);
if (!txq->entries)
goto error;
if (cmd_queue)
for (i = 0; i < slots_num; i++) {
txq->entries[i].cmd =
kmalloc(sizeof(struct iwl_device_cmd),
GFP_KERNEL);
if (!txq->entries[i].cmd)
goto error;
}
/* Circular buffer of transmit frame descriptors (TFDs),
* shared with device */
txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz,
&txq->dma_addr, GFP_KERNEL);
if (!txq->tfds)
goto error;
BUILD_BUG_ON(IWL_FIRST_TB_SIZE_ALIGN != sizeof(*txq->first_tb_bufs));
tb0_buf_sz = sizeof(*txq->first_tb_bufs) * slots_num;
txq->first_tb_bufs = dma_alloc_coherent(trans->dev, tb0_buf_sz,
&txq->first_tb_dma,
GFP_KERNEL);
if (!txq->first_tb_bufs)
goto err_free_tfds;
return 0;
err_free_tfds:
dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->dma_addr);
error:
if (txq->entries && cmd_queue)
for (i = 0; i < slots_num; i++)
kfree(txq->entries[i].cmd);
kfree(txq->entries);
txq->entries = NULL;
return -ENOMEM;
}
int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
int slots_num, bool cmd_queue)
{
int ret;
u32 tfd_queue_max_size =
trans->trans_cfg->base_params->max_tfd_queue_size;
txq->need_update = false;
/* max_tfd_queue_size must be power-of-two size, otherwise
* iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
if (WARN_ONCE(tfd_queue_max_size & (tfd_queue_max_size - 1),
"Max tfd queue size must be a power of two, but is %d",
tfd_queue_max_size))
return -EINVAL;
/* Initialize queue's high/low-water marks, and head/tail indexes */
ret = iwl_queue_init(txq, slots_num);
if (ret)
return ret;
spin_lock_init(&txq->lock);
if (cmd_queue) {
static struct lock_class_key iwl_pcie_cmd_queue_lock_class;
lockdep_set_class(&txq->lock, &iwl_pcie_cmd_queue_lock_class);
}
__skb_queue_head_init(&txq->overflow_q);
return 0;
}
void iwl_pcie_free_tso_page(struct iwl_trans *trans,
struct sk_buff *skb)
{
struct page **page_ptr;
struct page *next;
page_ptr = (void *)((u8 *)skb->cb + trans->txqs.page_offs);
next = *page_ptr;
*page_ptr = NULL;
while (next) {
struct page *tmp = next;
next = *(void **)(page_address(next) + PAGE_SIZE -
sizeof(void *));
__free_page(tmp);
}
}
static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
...@@ -668,10 +477,10 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) ...@@ -668,10 +477,10 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
if (WARN_ON_ONCE(!skb)) if (WARN_ON_ONCE(!skb))
continue; continue;
iwl_pcie_free_tso_page(trans, skb); iwl_txq_free_tso_page(trans, skb);
} }
iwl_pcie_txq_free_tfd(trans, txq); iwl_pcie_txq_free_tfd(trans, txq);
txq->read_ptr = iwl_queue_inc_wrap(trans, txq->read_ptr); txq->read_ptr = iwl_txq_inc_wrap(trans, txq->read_ptr);
if (txq->read_ptr == txq->write_ptr) { if (txq->read_ptr == txq->write_ptr) {
unsigned long flags; unsigned long flags;
...@@ -996,8 +805,8 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) ...@@ -996,8 +805,8 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans)
slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE, slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE,
trans->cfg->min_256_ba_txq_size); trans->cfg->min_256_ba_txq_size);
trans->txqs.txq[txq_id] = &trans_pcie->txq_memory[txq_id]; trans->txqs.txq[txq_id] = &trans_pcie->txq_memory[txq_id];
ret = iwl_pcie_txq_alloc(trans, trans->txqs.txq[txq_id], ret = iwl_txq_alloc(trans, trans->txqs.txq[txq_id], slots_num,
slots_num, cmd_queue); cmd_queue);
if (ret) { if (ret) {
IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id);
goto error; goto error;
...@@ -1049,8 +858,8 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) ...@@ -1049,8 +858,8 @@ int iwl_pcie_tx_init(struct iwl_trans *trans)
else else
slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE, slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE,
trans->cfg->min_256_ba_txq_size); trans->cfg->min_256_ba_txq_size);
ret = iwl_pcie_txq_init(trans, trans->txqs.txq[txq_id], ret = iwl_txq_init(trans, trans->txqs.txq[txq_id], slots_num,
slots_num, cmd_queue); cmd_queue);
if (ret) { if (ret) {
IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); IWL_ERR(trans, "Tx %d queue init failed\n", txq_id);
goto error; goto error;
...@@ -1108,8 +917,8 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -1108,8 +917,8 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
struct sk_buff_head *skbs) struct sk_buff_head *skbs)
{ {
struct iwl_txq *txq = trans->txqs.txq[txq_id]; struct iwl_txq *txq = trans->txqs.txq[txq_id];
int tfd_num = iwl_pcie_get_cmd_index(txq, ssn); int tfd_num = iwl_txq_get_cmd_index(txq, ssn);
int read_ptr = iwl_pcie_get_cmd_index(txq, txq->read_ptr); int read_ptr = iwl_txq_get_cmd_index(txq, txq->read_ptr);
int last_to_free; int last_to_free;
/* This function is not meant to release cmd queue*/ /* This function is not meant to release cmd queue*/
...@@ -1132,9 +941,9 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -1132,9 +941,9 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
/*Since we free until index _not_ inclusive, the one before index is /*Since we free until index _not_ inclusive, the one before index is
* the last we will free. This one must be used */ * the last we will free. This one must be used */
last_to_free = iwl_queue_dec_wrap(trans, tfd_num); last_to_free = iwl_txq_dec_wrap(trans, tfd_num);
if (!iwl_queue_used(txq, last_to_free)) { if (!iwl_txq_used(txq, last_to_free)) {
IWL_ERR(trans, IWL_ERR(trans,
"%s: Read index for txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", "%s: Read index for txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
__func__, txq_id, last_to_free, __func__, txq_id, last_to_free,
...@@ -1148,14 +957,14 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -1148,14 +957,14 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
for (; for (;
read_ptr != tfd_num; read_ptr != tfd_num;
txq->read_ptr = iwl_queue_inc_wrap(trans, txq->read_ptr), txq->read_ptr = iwl_txq_inc_wrap(trans, txq->read_ptr),
read_ptr = iwl_pcie_get_cmd_index(txq, txq->read_ptr)) { read_ptr = iwl_txq_get_cmd_index(txq, txq->read_ptr)) {
struct sk_buff *skb = txq->entries[read_ptr].skb; struct sk_buff *skb = txq->entries[read_ptr].skb;
if (WARN_ON_ONCE(!skb)) if (WARN_ON_ONCE(!skb))
continue; continue;
iwl_pcie_free_tso_page(trans, skb); iwl_txq_free_tso_page(trans, skb);
__skb_queue_tail(skbs, skb); __skb_queue_tail(skbs, skb);
...@@ -1169,7 +978,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -1169,7 +978,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
iwl_pcie_txq_progress(txq); iwl_pcie_txq_progress(txq);
if (iwl_queue_space(trans, txq) > txq->low_mark && if (iwl_txq_space(trans, txq) > txq->low_mark &&
test_bit(txq_id, trans->txqs.queue_stopped)) { test_bit(txq_id, trans->txqs.queue_stopped)) {
struct sk_buff_head overflow_skbs; struct sk_buff_head overflow_skbs;
...@@ -1203,13 +1012,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -1203,13 +1012,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
/* /*
* Note that we can very well be overflowing again. * Note that we can very well be overflowing again.
* In that case, iwl_queue_space will be small again * In that case, iwl_txq_space will be small again
* and we won't wake mac80211's queue. * and we won't wake mac80211's queue.
*/ */
iwl_trans_tx(trans, skb, dev_cmd_ptr, txq_id); iwl_trans_tx(trans, skb, dev_cmd_ptr, txq_id);
} }
if (iwl_queue_space(trans, txq) > txq->low_mark) if (iwl_txq_space(trans, txq) > txq->low_mark)
iwl_wake_queue(trans, txq); iwl_wake_queue(trans, txq);
spin_lock_bh(&txq->lock); spin_lock_bh(&txq->lock);
...@@ -1290,11 +1099,11 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) ...@@ -1290,11 +1099,11 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
lockdep_assert_held(&txq->lock); lockdep_assert_held(&txq->lock);
idx = iwl_pcie_get_cmd_index(txq, idx); idx = iwl_txq_get_cmd_index(txq, idx);
r = iwl_pcie_get_cmd_index(txq, txq->read_ptr); r = iwl_txq_get_cmd_index(txq, txq->read_ptr);
if (idx >= trans->trans_cfg->base_params->max_tfd_queue_size || if (idx >= trans->trans_cfg->base_params->max_tfd_queue_size ||
(!iwl_queue_used(txq, idx))) { (!iwl_txq_used(txq, idx))) {
WARN_ONCE(test_bit(txq_id, trans->txqs.queue_used), WARN_ONCE(test_bit(txq_id, trans->txqs.queue_used),
"%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
__func__, txq_id, idx, __func__, txq_id, idx,
...@@ -1303,9 +1112,9 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) ...@@ -1303,9 +1112,9 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
return; return;
} }
for (idx = iwl_queue_inc_wrap(trans, idx); r != idx; for (idx = iwl_txq_inc_wrap(trans, idx); r != idx;
r = iwl_queue_inc_wrap(trans, r)) { r = iwl_txq_inc_wrap(trans, r)) {
txq->read_ptr = iwl_queue_inc_wrap(trans, txq->read_ptr); txq->read_ptr = iwl_txq_inc_wrap(trans, txq->read_ptr);
if (nfreed++ > 0) { if (nfreed++ > 0) {
IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
...@@ -1617,7 +1426,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1617,7 +1426,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
spin_lock_bh(&txq->lock); spin_lock_bh(&txq->lock);
if (iwl_queue_space(trans, txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { if (iwl_txq_space(trans, txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
spin_unlock_bh(&txq->lock); spin_unlock_bh(&txq->lock);
IWL_ERR(trans, "No space in command queue\n"); IWL_ERR(trans, "No space in command queue\n");
...@@ -1626,7 +1435,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1626,7 +1435,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
goto free_dup_buf; goto free_dup_buf;
} }
idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); idx = iwl_txq_get_cmd_index(txq, txq->write_ptr);
out_cmd = txq->entries[idx].cmd; out_cmd = txq->entries[idx].cmd;
out_meta = &txq->entries[idx].meta; out_meta = &txq->entries[idx].meta;
...@@ -1709,7 +1518,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1709,7 +1518,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE); tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE);
memcpy(&txq->first_tb_bufs[idx], &out_cmd->hdr, tb0_size); memcpy(&txq->first_tb_bufs[idx], &out_cmd->hdr, tb0_size);
iwl_pcie_txq_build_tfd(trans, txq, iwl_pcie_txq_build_tfd(trans, txq,
iwl_pcie_get_first_tb_dma(txq, idx), iwl_txq_get_first_tb_dma(txq, idx),
tb0_size, true); tb0_size, true);
/* map first command fragment, if any remains */ /* map first command fragment, if any remains */
...@@ -1773,7 +1582,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1773,7 +1582,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
} }
/* Increment and update queue's write index */ /* Increment and update queue's write index */
txq->write_ptr = iwl_queue_inc_wrap(trans, txq->write_ptr); txq->write_ptr = iwl_txq_inc_wrap(trans, txq->write_ptr);
iwl_pcie_txq_inc_wr_ptr(trans, txq); iwl_pcie_txq_inc_wr_ptr(trans, txq);
spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
...@@ -1818,7 +1627,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, ...@@ -1818,7 +1627,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
spin_lock_bh(&txq->lock); spin_lock_bh(&txq->lock);
cmd_index = iwl_pcie_get_cmd_index(txq, index); cmd_index = iwl_txq_get_cmd_index(txq, index);
cmd = txq->entries[cmd_index].cmd; cmd = txq->entries[cmd_index].cmd;
meta = &txq->entries[cmd_index].meta; meta = &txq->entries[cmd_index].meta;
group_id = cmd->hdr.group_id; group_id = cmd->hdr.group_id;
...@@ -2045,51 +1854,6 @@ static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -2045,51 +1854,6 @@ static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb,
} }
#ifdef CONFIG_INET #ifdef CONFIG_INET
struct iwl_tso_hdr_page *get_page_hdr(struct iwl_trans *trans, size_t len,
struct sk_buff *skb)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_tso_hdr_page *p = this_cpu_ptr(trans_pcie->tso_hdr_page);
struct page **page_ptr;
page_ptr = (void *)((u8 *)skb->cb + trans->txqs.page_offs);
if (WARN_ON(*page_ptr))
return NULL;
if (!p->page)
goto alloc;
/*
* Check if there's enough room on this page
*
* Note that we put a page chaining pointer *last* in the
* page - we need it somewhere, and if it's there then we
* avoid DMA mapping the last bits of the page which may
* trigger the 32-bit boundary hardware bug.
*
* (see also get_workaround_page() in tx-gen2.c)
*/
if (p->pos + len < (u8 *)page_address(p->page) + PAGE_SIZE -
sizeof(void *))
goto out;
/* We don't have enough room on this page, get a new one. */
__free_page(p->page);
alloc:
p->page = alloc_page(GFP_ATOMIC);
if (!p->page)
return NULL;
p->pos = page_address(p->page);
/* set the chaining pointer to NULL */
*(void **)(page_address(p->page) + PAGE_SIZE - sizeof(void *)) = NULL;
out:
*page_ptr = p->page;
get_page(p->page);
return p;
}
static void iwl_compute_pseudo_hdr_csum(void *iph, struct tcphdr *tcph, static void iwl_compute_pseudo_hdr_csum(void *iph, struct tcphdr *tcph,
bool ipv6, unsigned int len) bool ipv6, unsigned int len)
{ {
...@@ -2132,7 +1896,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -2132,7 +1896,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
IEEE80211_CCMP_HDR_LEN : 0; IEEE80211_CCMP_HDR_LEN : 0;
trace_iwlwifi_dev_tx(trans->dev, skb, trace_iwlwifi_dev_tx(trans->dev, skb,
iwl_pcie_get_tfd(trans, txq, txq->write_ptr), iwl_txq_get_tfd(trans, txq, txq->write_ptr),
trans->txqs.tfd.size, trans->txqs.tfd.size,
&dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, 0); &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, 0);
...@@ -2355,11 +2119,11 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -2355,11 +2119,11 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
spin_lock(&txq->lock); spin_lock(&txq->lock);
if (iwl_queue_space(trans, txq) < txq->high_mark) { if (iwl_txq_space(trans, txq) < txq->high_mark) {
iwl_stop_queue(trans, txq); iwl_txq_stop(trans, txq);
/* don't put the packet on the ring, if there is no room */ /* don't put the packet on the ring, if there is no room */
if (unlikely(iwl_queue_space(trans, txq) < 3)) { if (unlikely(iwl_txq_space(trans, txq) < 3)) {
struct iwl_device_tx_cmd **dev_cmd_ptr; struct iwl_device_tx_cmd **dev_cmd_ptr;
dev_cmd_ptr = (void *)((u8 *)skb->cb + dev_cmd_ptr = (void *)((u8 *)skb->cb +
...@@ -2392,7 +2156,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -2392,7 +2156,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |
INDEX_TO_SEQ(txq->write_ptr))); INDEX_TO_SEQ(txq->write_ptr)));
tb0_phys = iwl_pcie_get_first_tb_dma(txq, txq->write_ptr); tb0_phys = iwl_txq_get_first_tb_dma(txq, txq->write_ptr);
scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) +
offsetof(struct iwl_tx_cmd, scratch); offsetof(struct iwl_tx_cmd, scratch);
...@@ -2442,8 +2206,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -2442,8 +2206,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false); iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
trace_iwlwifi_dev_tx(trans->dev, skb, trace_iwlwifi_dev_tx(trans->dev, skb,
iwl_pcie_get_tfd(trans, txq, iwl_txq_get_tfd(trans, txq, txq->write_ptr),
txq->write_ptr),
trans->txqs.tfd.size, trans->txqs.tfd.size,
&dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len,
hdr_len); hdr_len);
...@@ -2476,7 +2239,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -2476,7 +2239,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
/* building the A-MSDU might have changed this data, so memcpy it now */ /* building the A-MSDU might have changed this data, so memcpy it now */
memcpy(&txq->first_tb_bufs[txq->write_ptr], dev_cmd, IWL_FIRST_TB_SIZE); memcpy(&txq->first_tb_bufs[txq->write_ptr], dev_cmd, IWL_FIRST_TB_SIZE);
tfd = iwl_pcie_get_tfd(trans, txq, txq->write_ptr); tfd = iwl_txq_get_tfd(trans, txq, txq->write_ptr);
/* 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),
iwl_pcie_tfd_get_num_tbs(trans, tfd)); iwl_pcie_tfd_get_num_tbs(trans, tfd));
...@@ -2499,7 +2262,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -2499,7 +2262,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
} }
/* Tell device the write index *just past* this latest filled TFD */ /* Tell device the write index *just past* this latest filled TFD */
txq->write_ptr = iwl_queue_inc_wrap(trans, txq->write_ptr); txq->write_ptr = iwl_txq_inc_wrap(trans, txq->write_ptr);
if (!wait_write_ptr) if (!wait_write_ptr)
iwl_pcie_txq_inc_wr_ptr(trans, txq); iwl_pcie_txq_inc_wr_ptr(trans, txq);
......
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2020 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* BSD LICENSE
*
* Copyright(c) 2020 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <net/tso.h>
#include <linux/tcp.h>
#include "iwl-debug.h"
#include "iwl-io.h"
#include "fw/api/tx.h"
#include "queue/tx.h"
#include "iwl-fh.h"
#include "iwl-scd.h"
#include <linux/dmapool.h>
/*
* iwl_txq_gen2_tx_stop - Stop all Tx DMA channels
*/
void iwl_txq_gen2_tx_stop(struct iwl_trans *trans)
{
int txq_id;
/*
* This function can be called before the op_mode disabled the
* queues. This happens when we have an rfkill interrupt.
* Since we stop Tx altogether - mark the queues as stopped.
*/
memset(trans->txqs.queue_stopped, 0,
sizeof(trans->txqs.queue_stopped));
memset(trans->txqs.queue_used, 0, sizeof(trans->txqs.queue_used));
/* Unmap DMA from host system and free skb's */
for (txq_id = 0; txq_id < ARRAY_SIZE(trans->txqs.txq); txq_id++) {
if (!trans->txqs.txq[txq_id])
continue;
iwl_txq_gen2_unmap(trans, txq_id);
}
}
/*
* iwl_txq_update_byte_tbl - Set up entry in Tx byte-count array
*/
static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans,
struct iwl_txq *txq, u16 byte_cnt,
int num_tbs)
{
int idx = iwl_txq_get_cmd_index(txq, txq->write_ptr);
u8 filled_tfd_size, num_fetch_chunks;
u16 len = byte_cnt;
__le16 bc_ent;
if (WARN(idx >= txq->n_window, "%d >= %d\n", idx, txq->n_window))
return;
filled_tfd_size = offsetof(struct iwl_tfh_tfd, tbs) +
num_tbs * sizeof(struct iwl_tfh_tb);
/*
* filled_tfd_size contains the number of filled bytes in the TFD.
* Dividing it by 64 will give the number of chunks to fetch
* to SRAM- 0 for one chunk, 1 for 2 and so on.
* If, for example, TFD contains only 3 TBs then 32 bytes
* of the TFD are used, and only one chunk of 64 bytes should
* be fetched
*/
num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1;
if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
struct iwl_gen3_bc_tbl *scd_bc_tbl_gen3 = txq->bc_tbl.addr;
/* Starting from AX210, the HW expects bytes */
WARN_ON(trans->txqs.bc_table_dword);
WARN_ON(len > 0x3FFF);
bc_ent = cpu_to_le16(len | (num_fetch_chunks << 14));
scd_bc_tbl_gen3->tfd_offset[idx] = bc_ent;
} else {
struct iwlagn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.addr;
/* Before AX210, the HW expects DW */
WARN_ON(!trans->txqs.bc_table_dword);
len = DIV_ROUND_UP(len, 4);
WARN_ON(len > 0xFFF);
bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12));
scd_bc_tbl->tfd_offset[idx] = bc_ent;
}
}
/*
* iwl_txq_inc_wr_ptr - Send new write index to hardware
*/
void iwl_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
{
lockdep_assert_held(&txq->lock);
IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq->id, txq->write_ptr);
/*
* if not in power-save mode, uCode will never sleep when we're
* trying to tx (during RFKILL, we're not trying to tx).
*/
iwl_write32(trans, HBUS_TARG_WRPTR, txq->write_ptr | (txq->id << 16));
}
static u8 iwl_txq_gen2_get_num_tbs(struct iwl_trans *trans,
struct iwl_tfh_tfd *tfd)
{
return le16_to_cpu(tfd->num_tbs) & 0x1f;
}
void iwl_txq_gen2_tfd_unmap(struct iwl_trans *trans, struct iwl_cmd_meta *meta,
struct iwl_tfh_tfd *tfd)
{
int i, num_tbs;
/* Sanity check on number of chunks */
num_tbs = iwl_txq_gen2_get_num_tbs(trans, tfd);
if (num_tbs > trans->txqs.tfd.max_tbs) {
IWL_ERR(trans, "Too many chunks: %i\n", num_tbs);
return;
}
/* first TB is never freed - it's the bidirectional DMA data */
for (i = 1; i < num_tbs; i++) {
if (meta->tbs & BIT(i))
dma_unmap_page(trans->dev,
le64_to_cpu(tfd->tbs[i].addr),
le16_to_cpu(tfd->tbs[i].tb_len),
DMA_TO_DEVICE);
else
dma_unmap_single(trans->dev,
le64_to_cpu(tfd->tbs[i].addr),
le16_to_cpu(tfd->tbs[i].tb_len),
DMA_TO_DEVICE);
}
tfd->num_tbs = 0;
}
void iwl_txq_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
{
/* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and
* idx is bounded by n_window
*/
int idx = iwl_txq_get_cmd_index(txq, txq->read_ptr);
lockdep_assert_held(&txq->lock);
iwl_txq_gen2_tfd_unmap(trans, &txq->entries[idx].meta,
iwl_txq_get_tfd(trans, txq, idx));
/* free SKB */
if (txq->entries) {
struct sk_buff *skb;
skb = txq->entries[idx].skb;
/* Can be called from irqs-disabled context
* If skb is not NULL, it means that the whole queue is being
* freed and that the queue is not empty - free the skb
*/
if (skb) {
iwl_op_mode_free_skb(trans->op_mode, skb);
txq->entries[idx].skb = NULL;
}
}
}
int iwl_txq_gen2_set_tb(struct iwl_trans *trans, struct iwl_tfh_tfd *tfd,
dma_addr_t addr, u16 len)
{
int idx = iwl_txq_gen2_get_num_tbs(trans, tfd);
struct iwl_tfh_tb *tb;
/*
* Only WARN here so we know about the issue, but we mess up our
* unmap path because not every place currently checks for errors
* returned from this function - it can only return an error if
* there's no more space, and so when we know there is enough we
* don't always check ...
*/
WARN(iwl_txq_crosses_4g_boundary(addr, len),
"possible DMA problem with iova:0x%llx, len:%d\n",
(unsigned long long)addr, len);
if (WARN_ON(idx >= IWL_TFH_NUM_TBS))
return -EINVAL;
tb = &tfd->tbs[idx];
/* Each TFD can point to a maximum max_tbs Tx buffers */
if (le16_to_cpu(tfd->num_tbs) >= trans->txqs.tfd.max_tbs) {
IWL_ERR(trans, "Error can not send more than %d chunks\n",
trans->txqs.tfd.max_tbs);
return -EINVAL;
}
put_unaligned_le64(addr, &tb->addr);
tb->tb_len = cpu_to_le16(len);
tfd->num_tbs = cpu_to_le16(idx + 1);
return idx;
}
static struct page *get_workaround_page(struct iwl_trans *trans,
struct sk_buff *skb)
{
struct page **page_ptr;
struct page *ret;
page_ptr = (void *)((u8 *)skb->cb + trans->txqs.page_offs);
ret = alloc_page(GFP_ATOMIC);
if (!ret)
return NULL;
/* set the chaining pointer to the previous page if there */
*(void **)(page_address(ret) + PAGE_SIZE - sizeof(void *)) = *page_ptr;
*page_ptr = ret;
return ret;
}
/*
* Add a TB and if needed apply the FH HW bug workaround;
* meta != NULL indicates that it's a page mapping and we
* need to dma_unmap_page() and set the meta->tbs bit in
* this case.
*/
static int iwl_txq_gen2_set_tb_with_wa(struct iwl_trans *trans,
struct sk_buff *skb,
struct iwl_tfh_tfd *tfd,
dma_addr_t phys, void *virt,
u16 len, struct iwl_cmd_meta *meta)
{
dma_addr_t oldphys = phys;
struct page *page;
int ret;
if (unlikely(dma_mapping_error(trans->dev, phys)))
return -ENOMEM;
if (likely(!iwl_txq_crosses_4g_boundary(phys, len))) {
ret = iwl_txq_gen2_set_tb(trans, tfd, phys, len);
if (ret < 0)
goto unmap;
if (meta)
meta->tbs |= BIT(ret);
ret = 0;
goto trace;
}
/*
* Work around a hardware bug. If (as expressed in the
* condition above) the TB ends on a 32-bit boundary,
* then the next TB may be accessed with the wrong
* address.
* To work around it, copy the data elsewhere and make
* a new mapping for it so the device will not fail.
*/
if (WARN_ON(len > PAGE_SIZE - sizeof(void *))) {
ret = -ENOBUFS;
goto unmap;
}
page = get_workaround_page(trans, skb);
if (!page) {
ret = -ENOMEM;
goto unmap;
}
memcpy(page_address(page), virt, len);
phys = dma_map_single(trans->dev, page_address(page), len,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, phys)))
return -ENOMEM;
ret = iwl_txq_gen2_set_tb(trans, tfd, phys, len);
if (ret < 0) {
/* unmap the new allocation as single */
oldphys = phys;
meta = NULL;
goto unmap;
}
IWL_WARN(trans,
"TB bug workaround: copied %d bytes from 0x%llx to 0x%llx\n",
len, (unsigned long long)oldphys, (unsigned long long)phys);
ret = 0;
unmap:
if (meta)
dma_unmap_page(trans->dev, oldphys, len, DMA_TO_DEVICE);
else
dma_unmap_single(trans->dev, oldphys, len, DMA_TO_DEVICE);
trace:
trace_iwlwifi_dev_tx_tb(trans->dev, skb, virt, phys, len);
return ret;
}
#ifdef CONFIG_INET
struct iwl_tso_hdr_page *get_page_hdr(struct iwl_trans *trans, size_t len,
struct sk_buff *skb)
{
struct iwl_tso_hdr_page *p = this_cpu_ptr(trans->txqs.tso_hdr_page);
struct page **page_ptr;
page_ptr = (void *)((u8 *)skb->cb + trans->txqs.page_offs);
if (WARN_ON(*page_ptr))
return NULL;
if (!p->page)
goto alloc;
/*
* Check if there's enough room on this page
*
* Note that we put a page chaining pointer *last* in the
* page - we need it somewhere, and if it's there then we
* avoid DMA mapping the last bits of the page which may
* trigger the 32-bit boundary hardware bug.
*
* (see also get_workaround_page() in tx-gen2.c)
*/
if (p->pos + len < (u8 *)page_address(p->page) + PAGE_SIZE -
sizeof(void *))
goto out;
/* We don't have enough room on this page, get a new one. */
__free_page(p->page);
alloc:
p->page = alloc_page(GFP_ATOMIC);
if (!p->page)
return NULL;
p->pos = page_address(p->page);
/* set the chaining pointer to NULL */
*(void **)(page_address(p->page) + PAGE_SIZE - sizeof(void *)) = NULL;
out:
*page_ptr = p->page;
get_page(p->page);
return p;
}
#endif
static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans,
struct sk_buff *skb,
struct iwl_tfh_tfd *tfd, int start_len,
u8 hdr_len,
struct iwl_device_tx_cmd *dev_cmd)
{
#ifdef CONFIG_INET
struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload;
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
unsigned int mss = skb_shinfo(skb)->gso_size;
u16 length, amsdu_pad;
u8 *start_hdr;
struct iwl_tso_hdr_page *hdr_page;
struct tso_t tso;
trace_iwlwifi_dev_tx(trans->dev, skb, tfd, sizeof(*tfd),
&dev_cmd->hdr, start_len, 0);
ip_hdrlen = skb_transport_header(skb) - skb_network_header(skb);
snap_ip_tcp_hdrlen = 8 + ip_hdrlen + tcp_hdrlen(skb);
total_len = skb->len - snap_ip_tcp_hdrlen - hdr_len;
amsdu_pad = 0;
/* total amount of header we may need for this A-MSDU */
hdr_room = DIV_ROUND_UP(total_len, mss) *
(3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr));
/* Our device supports 9 segments at most, it will fit in 1 page */
hdr_page = get_page_hdr(trans, hdr_room, skb);
if (!hdr_page)
return -ENOMEM;
start_hdr = hdr_page->pos;
/*
* Pull the ieee80211 header to be able to use TSO core,
* we will restore it for the tx_status flow.
*/
skb_pull(skb, hdr_len);
/*
* Remove the length of all the headers that we don't actually
* have in the MPDU by themselves, but that we duplicate into
* all the different MSDUs inside the A-MSDU.
*/
le16_add_cpu(&tx_cmd->len, -snap_ip_tcp_hdrlen);
tso_start(skb, &tso);
while (total_len) {
/* this is the data left for this subframe */
unsigned int data_left = min_t(unsigned int, mss, total_len);
struct sk_buff *csum_skb = NULL;
unsigned int tb_len;
dma_addr_t tb_phys;
u8 *subf_hdrs_start = hdr_page->pos;
total_len -= data_left;
memset(hdr_page->pos, 0, amsdu_pad);
hdr_page->pos += amsdu_pad;
amsdu_pad = (4 - (sizeof(struct ethhdr) + snap_ip_tcp_hdrlen +
data_left)) & 0x3;
ether_addr_copy(hdr_page->pos, ieee80211_get_DA(hdr));
hdr_page->pos += ETH_ALEN;
ether_addr_copy(hdr_page->pos, ieee80211_get_SA(hdr));
hdr_page->pos += ETH_ALEN;
length = snap_ip_tcp_hdrlen + data_left;
*((__be16 *)hdr_page->pos) = cpu_to_be16(length);
hdr_page->pos += sizeof(length);
/*
* This will copy the SNAP as well which will be considered
* as MAC header.
*/
tso_build_hdr(skb, hdr_page->pos, &tso, data_left, !total_len);
hdr_page->pos += snap_ip_tcp_hdrlen;
tb_len = hdr_page->pos - start_hdr;
tb_phys = dma_map_single(trans->dev, start_hdr,
tb_len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
dev_kfree_skb(csum_skb);
goto out_err;
}
/*
* No need for _with_wa, this is from the TSO page and
* we leave some space at the end of it so can't hit
* the buggy scenario.
*/
iwl_txq_gen2_set_tb(trans, tfd, tb_phys, tb_len);
trace_iwlwifi_dev_tx_tb(trans->dev, skb, start_hdr,
tb_phys, tb_len);
/* add this subframe's headers' length to the tx_cmd */
le16_add_cpu(&tx_cmd->len, hdr_page->pos - subf_hdrs_start);
/* prepare the start_hdr for the next subframe */
start_hdr = hdr_page->pos;
/* put the payload */
while (data_left) {
int ret;
tb_len = min_t(unsigned int, tso.size, data_left);
tb_phys = dma_map_single(trans->dev, tso.data,
tb_len, DMA_TO_DEVICE);
ret = iwl_txq_gen2_set_tb_with_wa(trans, skb, tfd,
tb_phys, tso.data,
tb_len, NULL);
if (ret) {
dev_kfree_skb(csum_skb);
goto out_err;
}
data_left -= tb_len;
tso_build_data(skb, &tso, tb_len);
}
}
/* re -add the WiFi header */
skb_push(skb, hdr_len);
return 0;
out_err:
#endif
return -EINVAL;
}
static struct
iwl_tfh_tfd *iwl_txq_gen2_build_tx_amsdu(struct iwl_trans *trans,
struct iwl_txq *txq,
struct iwl_device_tx_cmd *dev_cmd,
struct sk_buff *skb,
struct iwl_cmd_meta *out_meta,
int hdr_len,
int tx_cmd_len)
{
int idx = iwl_txq_get_cmd_index(txq, txq->write_ptr);
struct iwl_tfh_tfd *tfd = iwl_txq_get_tfd(trans, txq, idx);
dma_addr_t tb_phys;
int len;
void *tb1_addr;
tb_phys = iwl_txq_get_first_tb_dma(txq, idx);
/*
* No need for _with_wa, the first TB allocation is aligned up
* to a 64-byte boundary and thus can't be at the end or cross
* a page boundary (much less a 2^32 boundary).
*/
iwl_txq_gen2_set_tb(trans, tfd, tb_phys, IWL_FIRST_TB_SIZE);
/*
* The second TB (tb1) points to the remainder of the TX command
* and the 802.11 header - dword aligned size
* (This calculation modifies the TX command, so do it before the
* setup of the first TB)
*/
len = tx_cmd_len + sizeof(struct iwl_cmd_header) + hdr_len -
IWL_FIRST_TB_SIZE;
/* do not align A-MSDU to dword as the subframe header aligns it */
/* map the data for TB1 */
tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE;
tb_phys = dma_map_single(trans->dev, tb1_addr, len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, tb_phys)))
goto out_err;
/*
* No need for _with_wa(), we ensure (via alignment) that the data
* here can never cross or end at a page boundary.
*/
iwl_txq_gen2_set_tb(trans, tfd, tb_phys, len);
if (iwl_txq_gen2_build_amsdu(trans, skb, tfd, len + IWL_FIRST_TB_SIZE,
hdr_len, dev_cmd))
goto out_err;
/* building the A-MSDU might have changed this data, memcpy it now */
memcpy(&txq->first_tb_bufs[idx], dev_cmd, IWL_FIRST_TB_SIZE);
return tfd;
out_err:
iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd);
return NULL;
}
static int iwl_txq_gen2_tx_add_frags(struct iwl_trans *trans,
struct sk_buff *skb,
struct iwl_tfh_tfd *tfd,
struct iwl_cmd_meta *out_meta)
{
int i;
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;
unsigned int fragsz = skb_frag_size(frag);
int ret;
if (!fragsz)
continue;
tb_phys = skb_frag_dma_map(trans->dev, frag, 0,
fragsz, DMA_TO_DEVICE);
ret = iwl_txq_gen2_set_tb_with_wa(trans, skb, tfd, tb_phys,
skb_frag_address(frag),
fragsz, out_meta);
if (ret)
return ret;
}
return 0;
}
static struct
iwl_tfh_tfd *iwl_txq_gen2_build_tx(struct iwl_trans *trans,
struct iwl_txq *txq,
struct iwl_device_tx_cmd *dev_cmd,
struct sk_buff *skb,
struct iwl_cmd_meta *out_meta,
int hdr_len,
int tx_cmd_len,
bool pad)
{
int idx = iwl_txq_get_cmd_index(txq, txq->write_ptr);
struct iwl_tfh_tfd *tfd = iwl_txq_get_tfd(trans, txq, idx);
dma_addr_t tb_phys;
int len, tb1_len, tb2_len;
void *tb1_addr;
struct sk_buff *frag;
tb_phys = iwl_txq_get_first_tb_dma(txq, idx);
/* The first TB points to bi-directional DMA data */
memcpy(&txq->first_tb_bufs[idx], dev_cmd, IWL_FIRST_TB_SIZE);
/*
* No need for _with_wa, the first TB allocation is aligned up
* to a 64-byte boundary and thus can't be at the end or cross
* a page boundary (much less a 2^32 boundary).
*/
iwl_txq_gen2_set_tb(trans, tfd, tb_phys, IWL_FIRST_TB_SIZE);
/*
* The second TB (tb1) points to the remainder of the TX command
* and the 802.11 header - dword aligned size
* (This calculation modifies the TX command, so do it before the
* setup of the first TB)
*/
len = tx_cmd_len + sizeof(struct iwl_cmd_header) + hdr_len -
IWL_FIRST_TB_SIZE;
if (pad)
tb1_len = ALIGN(len, 4);
else
tb1_len = len;
/* map the data for TB1 */
tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE;
tb_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, tb_phys)))
goto out_err;
/*
* No need for _with_wa(), we ensure (via alignment) that the data
* here can never cross or end at a page boundary.
*/
iwl_txq_gen2_set_tb(trans, tfd, tb_phys, tb1_len);
trace_iwlwifi_dev_tx(trans->dev, skb, tfd, sizeof(*tfd), &dev_cmd->hdr,
IWL_FIRST_TB_SIZE + tb1_len, hdr_len);
/* set up TFD's third entry to point to remainder of skb's head */
tb2_len = skb_headlen(skb) - hdr_len;
if (tb2_len > 0) {
int ret;
tb_phys = dma_map_single(trans->dev, skb->data + hdr_len,
tb2_len, DMA_TO_DEVICE);
ret = iwl_txq_gen2_set_tb_with_wa(trans, skb, tfd, tb_phys,
skb->data + hdr_len, tb2_len,
NULL);
if (ret)
goto out_err;
}
if (iwl_txq_gen2_tx_add_frags(trans, skb, tfd, out_meta))
goto out_err;
skb_walk_frags(skb, frag) {
int ret;
tb_phys = dma_map_single(trans->dev, frag->data,
skb_headlen(frag), DMA_TO_DEVICE);
ret = iwl_txq_gen2_set_tb_with_wa(trans, skb, tfd, tb_phys,
frag->data,
skb_headlen(frag), NULL);
if (ret)
goto out_err;
if (iwl_txq_gen2_tx_add_frags(trans, frag, tfd, out_meta))
goto out_err;
}
return tfd;
out_err:
iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd);
return NULL;
}
static
struct iwl_tfh_tfd *iwl_txq_gen2_build_tfd(struct iwl_trans *trans,
struct iwl_txq *txq,
struct iwl_device_tx_cmd *dev_cmd,
struct sk_buff *skb,
struct iwl_cmd_meta *out_meta)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
int idx = iwl_txq_get_cmd_index(txq, txq->write_ptr);
struct iwl_tfh_tfd *tfd = iwl_txq_get_tfd(trans, txq, idx);
int len, hdr_len;
bool amsdu;
/* There must be data left over for TB1 or this code must be changed */
BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen2) < IWL_FIRST_TB_SIZE);
memset(tfd, 0, sizeof(*tfd));
if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
len = sizeof(struct iwl_tx_cmd_gen2);
else
len = sizeof(struct iwl_tx_cmd_gen3);
amsdu = ieee80211_is_data_qos(hdr->frame_control) &&
(*ieee80211_get_qos_ctl(hdr) &
IEEE80211_QOS_CTL_A_MSDU_PRESENT);
hdr_len = ieee80211_hdrlen(hdr->frame_control);
/*
* Only build A-MSDUs here if doing so by GSO, otherwise it may be
* an A-MSDU for other reasons, e.g. NAN or an A-MSDU having been
* built in the higher layers already.
*/
if (amsdu && skb_shinfo(skb)->gso_size)
return iwl_txq_gen2_build_tx_amsdu(trans, txq, dev_cmd, skb,
out_meta, hdr_len, len);
return iwl_txq_gen2_build_tx(trans, txq, dev_cmd, skb, out_meta,
hdr_len, len, !amsdu);
}
int iwl_txq_space(struct iwl_trans *trans, const struct iwl_txq *q)
{
unsigned int max;
unsigned int used;
/*
* To avoid ambiguity between empty and completely full queues, there
* should always be less than max_tfd_queue_size elements in the queue.
* If q->n_window is smaller than max_tfd_queue_size, there is no need
* to reserve any queue entries for this purpose.
*/
if (q->n_window < trans->trans_cfg->base_params->max_tfd_queue_size)
max = q->n_window;
else
max = trans->trans_cfg->base_params->max_tfd_queue_size - 1;
/*
* max_tfd_queue_size is a power of 2, so the following is equivalent to
* modulo by max_tfd_queue_size and is well defined.
*/
used = (q->write_ptr - q->read_ptr) &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1);
if (WARN_ON(used > max))
return 0;
return max - used;
}
int iwl_txq_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_tx_cmd *dev_cmd, int txq_id)
{
struct iwl_cmd_meta *out_meta;
struct iwl_txq *txq = trans->txqs.txq[txq_id];
u16 cmd_len;
int idx;
void *tfd;
if (WARN_ONCE(txq_id >= IWL_MAX_TVQM_QUEUES,
"queue %d out of range", txq_id))
return -EINVAL;
if (WARN_ONCE(!test_bit(txq_id, trans->txqs.queue_used),
"TX on unused queue %d\n", txq_id))
return -EINVAL;
if (skb_is_nonlinear(skb) &&
skb_shinfo(skb)->nr_frags > IWL_TRANS_MAX_FRAGS(trans) &&
__skb_linearize(skb))
return -ENOMEM;
spin_lock(&txq->lock);
if (iwl_txq_space(trans, txq) < txq->high_mark) {
iwl_txq_stop(trans, txq);
/* don't put the packet on the ring, if there is no room */
if (unlikely(iwl_txq_space(trans, txq) < 3)) {
struct iwl_device_tx_cmd **dev_cmd_ptr;
dev_cmd_ptr = (void *)((u8 *)skb->cb +
trans->txqs.dev_cmd_offs);
*dev_cmd_ptr = dev_cmd;
__skb_queue_tail(&txq->overflow_q, skb);
spin_unlock(&txq->lock);
return 0;
}
}
idx = iwl_txq_get_cmd_index(txq, txq->write_ptr);
/* Set up driver data for this TFD */
txq->entries[idx].skb = skb;
txq->entries[idx].cmd = dev_cmd;
dev_cmd->hdr.sequence =
cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |
INDEX_TO_SEQ(idx)));
/* Set up first empty entry in queue's array of Tx/cmd buffers */
out_meta = &txq->entries[idx].meta;
out_meta->flags = 0;
tfd = iwl_txq_gen2_build_tfd(trans, txq, dev_cmd, skb, out_meta);
if (!tfd) {
spin_unlock(&txq->lock);
return -1;
}
if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
struct iwl_tx_cmd_gen3 *tx_cmd_gen3 =
(void *)dev_cmd->payload;
cmd_len = le16_to_cpu(tx_cmd_gen3->len);
} else {
struct iwl_tx_cmd_gen2 *tx_cmd_gen2 =
(void *)dev_cmd->payload;
cmd_len = le16_to_cpu(tx_cmd_gen2->len);
}
/* Set up entry for this TFD in Tx byte-count array */
iwl_pcie_gen2_update_byte_tbl(trans, txq, cmd_len,
iwl_txq_gen2_get_num_tbs(trans, tfd));
/* start timer if queue currently empty */
if (txq->read_ptr == txq->write_ptr && txq->wd_timeout)
mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
/* Tell device the write index *just past* this latest filled TFD */
txq->write_ptr = iwl_txq_inc_wrap(trans, txq->write_ptr);
iwl_txq_inc_wr_ptr(trans, txq);
/*
* At this point the frame is "transmitted" successfully
* and we will get a TX status notification eventually.
*/
spin_unlock(&txq->lock);
return 0;
}
/*************** HOST COMMAND QUEUE FUNCTIONS *****/
/*
* iwl_txq_gen2_unmap - Unmap any remaining DMA mappings and free skb's
*/
void iwl_txq_gen2_unmap(struct iwl_trans *trans, int txq_id)
{
struct iwl_txq *txq = trans->txqs.txq[txq_id];
spin_lock_bh(&txq->lock);
while (txq->write_ptr != txq->read_ptr) {
IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
txq_id, txq->read_ptr);
if (txq_id != trans->txqs.cmd.q_id) {
int idx = iwl_txq_get_cmd_index(txq, txq->read_ptr);
struct sk_buff *skb = txq->entries[idx].skb;
if (WARN_ON_ONCE(!skb))
continue;
iwl_txq_free_tso_page(trans, skb);
}
iwl_txq_gen2_free_tfd(trans, txq);
txq->read_ptr = iwl_txq_inc_wrap(trans, txq->read_ptr);
}
while (!skb_queue_empty(&txq->overflow_q)) {
struct sk_buff *skb = __skb_dequeue(&txq->overflow_q);
iwl_op_mode_free_skb(trans->op_mode, skb);
}
spin_unlock_bh(&txq->lock);
/* just in case - this queue may have been stopped */
iwl_wake_queue(trans, txq);
}
static void iwl_txq_gen2_free_memory(struct iwl_trans *trans,
struct iwl_txq *txq)
{
struct device *dev = trans->dev;
/* De-alloc circular buffer of TFDs */
if (txq->tfds) {
dma_free_coherent(dev,
trans->txqs.tfd.size * txq->n_window,
txq->tfds, txq->dma_addr);
dma_free_coherent(dev,
sizeof(*txq->first_tb_bufs) * txq->n_window,
txq->first_tb_bufs, txq->first_tb_dma);
}
kfree(txq->entries);
if (txq->bc_tbl.addr)
dma_pool_free(trans->txqs.bc_pool,
txq->bc_tbl.addr, txq->bc_tbl.dma);
kfree(txq);
}
/*
* iwl_pcie_txq_free - Deallocate DMA queue.
* @txq: Transmit queue to deallocate.
*
* Empty queue by removing and destroying all BD's.
* Free all buffers.
* 0-fill, but do not free "txq" descriptor structure.
*/
static void iwl_txq_gen2_free(struct iwl_trans *trans, int txq_id)
{
struct iwl_txq *txq;
int i;
if (WARN_ONCE(txq_id >= IWL_MAX_TVQM_QUEUES,
"queue %d out of range", txq_id))
return;
txq = trans->txqs.txq[txq_id];
if (WARN_ON(!txq))
return;
iwl_txq_gen2_unmap(trans, txq_id);
/* De-alloc array of command/tx buffers */
if (txq_id == trans->txqs.cmd.q_id)
for (i = 0; i < txq->n_window; i++) {
kfree_sensitive(txq->entries[i].cmd);
kfree_sensitive(txq->entries[i].free_buf);
}
del_timer_sync(&txq->stuck_timer);
iwl_txq_gen2_free_memory(trans, txq);
trans->txqs.txq[txq_id] = NULL;
clear_bit(txq_id, trans->txqs.queue_used);
}
/*
* iwl_queue_init - Initialize queue's high/low-water and read/write indexes
*/
static int iwl_queue_init(struct iwl_txq *q, int slots_num)
{
q->n_window = slots_num;
/* slots_num must be power-of-two size, otherwise
* iwl_txq_get_cmd_index is broken. */
if (WARN_ON(!is_power_of_2(slots_num)))
return -EINVAL;
q->low_mark = q->n_window / 4;
if (q->low_mark < 4)
q->low_mark = 4;
q->high_mark = q->n_window / 8;
if (q->high_mark < 2)
q->high_mark = 2;
q->write_ptr = 0;
q->read_ptr = 0;
return 0;
}
int iwl_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num,
bool cmd_queue)
{
int ret;
u32 tfd_queue_max_size =
trans->trans_cfg->base_params->max_tfd_queue_size;
txq->need_update = false;
/* max_tfd_queue_size must be power-of-two size, otherwise
* iwl_txq_inc_wrap and iwl_txq_dec_wrap are broken. */
if (WARN_ONCE(tfd_queue_max_size & (tfd_queue_max_size - 1),
"Max tfd queue size must be a power of two, but is %d",
tfd_queue_max_size))
return -EINVAL;
/* Initialize queue's high/low-water marks, and head/tail indexes */
ret = iwl_queue_init(txq, slots_num);
if (ret)
return ret;
spin_lock_init(&txq->lock);
if (cmd_queue) {
static struct lock_class_key iwl_txq_cmd_queue_lock_class;
lockdep_set_class(&txq->lock, &iwl_txq_cmd_queue_lock_class);
}
__skb_queue_head_init(&txq->overflow_q);
return 0;
}
void iwl_txq_free_tso_page(struct iwl_trans *trans, struct sk_buff *skb)
{
struct page **page_ptr;
struct page *next;
page_ptr = (void *)((u8 *)skb->cb + trans->txqs.page_offs);
next = *page_ptr;
*page_ptr = NULL;
while (next) {
struct page *tmp = next;
next = *(void **)(page_address(next) + PAGE_SIZE -
sizeof(void *));
__free_page(tmp);
}
}
void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq)
{
u32 txq_id = txq->id;
u32 status;
bool active;
u8 fifo;
if (trans->trans_cfg->use_tfh) {
IWL_ERR(trans, "Queue %d is stuck %d %d\n", txq_id,
txq->read_ptr, txq->write_ptr);
/* TODO: access new SCD registers and dump them */
return;
}
status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id));
fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
IWL_ERR(trans,
"Queue %d is %sactive on fifo %d and stuck for %u ms. SW [%d, %d] HW [%d, %d] FH TRB=0x0%x\n",
txq_id, active ? "" : "in", fifo,
jiffies_to_msecs(txq->wd_timeout),
txq->read_ptr, txq->write_ptr,
iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id)) &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1),
iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq_id)) &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1),
iwl_read_direct32(trans, FH_TX_TRB_REG(fifo)));
}
static void iwl_txq_stuck_timer(struct timer_list *t)
{
struct iwl_txq *txq = from_timer(txq, t, stuck_timer);
struct iwl_trans *trans = txq->trans;
spin_lock(&txq->lock);
/* check if triggered erroneously */
if (txq->read_ptr == txq->write_ptr) {
spin_unlock(&txq->lock);
return;
}
spin_unlock(&txq->lock);
iwl_txq_log_scd_error(trans, txq);
iwl_force_nmi(trans);
}
int iwl_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num,
bool cmd_queue)
{
size_t tfd_sz = trans->txqs.tfd.size *
trans->trans_cfg->base_params->max_tfd_queue_size;
size_t tb0_buf_sz;
int i;
if (WARN_ON(txq->entries || txq->tfds))
return -EINVAL;
if (trans->trans_cfg->use_tfh)
tfd_sz = trans->txqs.tfd.size * slots_num;
timer_setup(&txq->stuck_timer, iwl_txq_stuck_timer, 0);
txq->trans = trans;
txq->n_window = slots_num;
txq->entries = kcalloc(slots_num,
sizeof(struct iwl_pcie_txq_entry),
GFP_KERNEL);
if (!txq->entries)
goto error;
if (cmd_queue)
for (i = 0; i < slots_num; i++) {
txq->entries[i].cmd =
kmalloc(sizeof(struct iwl_device_cmd),
GFP_KERNEL);
if (!txq->entries[i].cmd)
goto error;
}
/* Circular buffer of transmit frame descriptors (TFDs),
* shared with device */
txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz,
&txq->dma_addr, GFP_KERNEL);
if (!txq->tfds)
goto error;
BUILD_BUG_ON(sizeof(*txq->first_tb_bufs) != IWL_FIRST_TB_SIZE_ALIGN);
tb0_buf_sz = sizeof(*txq->first_tb_bufs) * slots_num;
txq->first_tb_bufs = dma_alloc_coherent(trans->dev, tb0_buf_sz,
&txq->first_tb_dma,
GFP_KERNEL);
if (!txq->first_tb_bufs)
goto err_free_tfds;
return 0;
err_free_tfds:
dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->dma_addr);
error:
if (txq->entries && cmd_queue)
for (i = 0; i < slots_num; i++)
kfree(txq->entries[i].cmd);
kfree(txq->entries);
txq->entries = NULL;
return -ENOMEM;
}
static int iwl_txq_dyn_alloc_dma(struct iwl_trans *trans,
struct iwl_txq **intxq, int size,
unsigned int timeout)
{
size_t bc_tbl_size, bc_tbl_entries;
struct iwl_txq *txq;
int ret;
WARN_ON(!trans->txqs.bc_tbl_size);
bc_tbl_size = trans->txqs.bc_tbl_size;
bc_tbl_entries = bc_tbl_size / sizeof(u16);
if (WARN_ON(size > bc_tbl_entries))
return -EINVAL;
txq = kzalloc(sizeof(*txq), GFP_KERNEL);
if (!txq)
return -ENOMEM;
txq->bc_tbl.addr = dma_pool_alloc(trans->txqs.bc_pool, GFP_KERNEL,
&txq->bc_tbl.dma);
if (!txq->bc_tbl.addr) {
IWL_ERR(trans, "Scheduler BC Table allocation failed\n");
kfree(txq);
return -ENOMEM;
}
ret = iwl_txq_alloc(trans, txq, size, false);
if (ret) {
IWL_ERR(trans, "Tx queue alloc failed\n");
goto error;
}
ret = iwl_txq_init(trans, txq, size, false);
if (ret) {
IWL_ERR(trans, "Tx queue init failed\n");
goto error;
}
txq->wd_timeout = msecs_to_jiffies(timeout);
*intxq = txq;
return 0;
error:
iwl_txq_gen2_free_memory(trans, txq);
return ret;
}
static int iwl_txq_alloc_response(struct iwl_trans *trans, struct iwl_txq *txq,
struct iwl_host_cmd *hcmd)
{
struct iwl_tx_queue_cfg_rsp *rsp;
int ret, qid;
u32 wr_ptr;
if (WARN_ON(iwl_rx_packet_payload_len(hcmd->resp_pkt) !=
sizeof(*rsp))) {
ret = -EINVAL;
goto error_free_resp;
}
rsp = (void *)hcmd->resp_pkt->data;
qid = le16_to_cpu(rsp->queue_number);
wr_ptr = le16_to_cpu(rsp->write_pointer);
if (qid >= ARRAY_SIZE(trans->txqs.txq)) {
WARN_ONCE(1, "queue index %d unsupported", qid);
ret = -EIO;
goto error_free_resp;
}
if (test_and_set_bit(qid, trans->txqs.queue_used)) {
WARN_ONCE(1, "queue %d already used", qid);
ret = -EIO;
goto error_free_resp;
}
txq->id = qid;
trans->txqs.txq[qid] = txq;
wr_ptr &= (trans->trans_cfg->base_params->max_tfd_queue_size - 1);
/* Place first TFD at index corresponding to start sequence number */
txq->read_ptr = wr_ptr;
txq->write_ptr = wr_ptr;
IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d\n", qid);
iwl_free_resp(hcmd);
return qid;
error_free_resp:
iwl_free_resp(hcmd);
iwl_txq_gen2_free_memory(trans, txq);
return ret;
}
int iwl_txq_dyn_alloc(struct iwl_trans *trans, __le16 flags, u8 sta_id, u8 tid,
int cmd_id, int size, unsigned int timeout)
{
struct iwl_txq *txq = NULL;
struct iwl_tx_queue_cfg_cmd cmd = {
.flags = flags,
.sta_id = sta_id,
.tid = tid,
};
struct iwl_host_cmd hcmd = {
.id = cmd_id,
.len = { sizeof(cmd) },
.data = { &cmd, },
.flags = CMD_WANT_SKB,
};
int ret;
ret = iwl_txq_dyn_alloc_dma(trans, &txq, size, timeout);
if (ret)
return ret;
cmd.tfdq_addr = cpu_to_le64(txq->dma_addr);
cmd.byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma);
cmd.cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size));
ret = iwl_trans_send_cmd(trans, &hcmd);
if (ret)
goto error;
return iwl_txq_alloc_response(trans, txq, &hcmd);
error:
iwl_txq_gen2_free_memory(trans, txq);
return ret;
}
void iwl_txq_dyn_free(struct iwl_trans *trans, int queue)
{
if (WARN(queue >= IWL_MAX_TVQM_QUEUES,
"queue %d out of range", queue))
return;
/*
* Upon HW Rfkill - we stop the device, and then stop the queues
* in the op_mode. Just for the sake of the simplicity of the op_mode,
* allow the op_mode to call txq_disable after it already called
* stop_device.
*/
if (!test_and_clear_bit(queue, trans->txqs.queue_used)) {
WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status),
"queue %d not used", queue);
return;
}
iwl_txq_gen2_unmap(trans, queue);
iwl_txq_gen2_free_memory(trans, trans->txqs.txq[queue]);
trans->txqs.txq[queue] = NULL;
IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", queue);
}
void iwl_txq_gen2_tx_free(struct iwl_trans *trans)
{
int i;
memset(trans->txqs.queue_used, 0, sizeof(trans->txqs.queue_used));
/* Free all TX queues */
for (i = 0; i < ARRAY_SIZE(trans->txqs.txq); i++) {
if (!trans->txqs.txq[i])
continue;
iwl_txq_gen2_free(trans, i);
}
}
int iwl_txq_gen2_init(struct iwl_trans *trans, int txq_id, int queue_size)
{
struct iwl_txq *queue;
int ret;
/* alloc and init the tx queue */
if (!trans->txqs.txq[txq_id]) {
queue = kzalloc(sizeof(*queue), GFP_KERNEL);
if (!queue) {
IWL_ERR(trans, "Not enough memory for tx queue\n");
return -ENOMEM;
}
trans->txqs.txq[txq_id] = queue;
ret = iwl_txq_alloc(trans, queue, queue_size, true);
if (ret) {
IWL_ERR(trans, "Tx %d queue init failed\n", txq_id);
goto error;
}
} else {
queue = trans->txqs.txq[txq_id];
}
ret = iwl_txq_init(trans, queue, queue_size,
(txq_id == trans->txqs.cmd.q_id));
if (ret) {
IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id);
goto error;
}
trans->txqs.txq[txq_id]->id = txq_id;
set_bit(txq_id, trans->txqs.queue_used);
return 0;
error:
iwl_txq_gen2_tx_free(trans);
return ret;
}
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2020 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2020 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __iwl_trans_queue_tx_h__
#define __iwl_trans_queue_tx_h__
#include "iwl-fh.h"
#include "fw/api/tx.h"
struct iwl_tso_hdr_page {
struct page *page;
u8 *pos;
};
static inline dma_addr_t
iwl_txq_get_first_tb_dma(struct iwl_txq *txq, int idx)
{
return txq->first_tb_dma +
sizeof(struct iwl_pcie_first_tb_buf) * idx;
}
static inline u16 iwl_txq_get_cmd_index(const struct iwl_txq *q, u32 index)
{
return index & (q->n_window - 1);
}
void iwl_txq_gen2_unmap(struct iwl_trans *trans, int txq_id);
static inline void iwl_wake_queue(struct iwl_trans *trans,
struct iwl_txq *txq)
{
if (test_and_clear_bit(txq->id, trans->txqs.queue_stopped)) {
IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->id);
iwl_op_mode_queue_not_full(trans->op_mode, txq->id);
}
}
static inline void *iwl_txq_get_tfd(struct iwl_trans *trans,
struct iwl_txq *txq, int idx)
{
if (trans->trans_cfg->use_tfh)
idx = iwl_txq_get_cmd_index(txq, idx);
return txq->tfds + trans->txqs.tfd.size * idx;
}
int iwl_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num,
bool cmd_queue);
/*
* We need this inline in case dma_addr_t is only 32-bits - since the
* hardware is always 64-bit, the issue can still occur in that case,
* so use u64 for 'phys' here to force the addition in 64-bit.
*/
static inline bool iwl_txq_crosses_4g_boundary(u64 phys, u16 len)
{
return upper_32_bits(phys) != upper_32_bits(phys + len);
}
int iwl_txq_space(struct iwl_trans *trans, const struct iwl_txq *q);
static inline void iwl_txq_stop(struct iwl_trans *trans, struct iwl_txq *txq)
{
if (!test_and_set_bit(txq->id, trans->txqs.queue_stopped)) {
iwl_op_mode_queue_full(trans->op_mode, txq->id);
IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->id);
} else {
IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n",
txq->id);
}
}
/**
* iwl_txq_inc_wrap - increment queue index, wrap back to beginning
* @index -- current index
*/
static inline int iwl_txq_inc_wrap(struct iwl_trans *trans, int index)
{
return ++index &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1);
}
/**
* iwl_txq_dec_wrap - decrement queue index, wrap back to end
* @index -- current index
*/
static inline int iwl_txq_dec_wrap(struct iwl_trans *trans, int index)
{
return --index &
(trans->trans_cfg->base_params->max_tfd_queue_size - 1);
}
static inline bool iwl_txq_used(const struct iwl_txq *q, int i)
{
int index = iwl_txq_get_cmd_index(q, i);
int r = iwl_txq_get_cmd_index(q, q->read_ptr);
int w = iwl_txq_get_cmd_index(q, q->write_ptr);
return w >= r ?
(index >= r && index < w) :
!(index < r && index >= w);
}
void iwl_txq_free_tso_page(struct iwl_trans *trans, struct sk_buff *skb);
void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq);
int iwl_txq_gen2_set_tb(struct iwl_trans *trans,
struct iwl_tfh_tfd *tfd, dma_addr_t addr,
u16 len);
void iwl_txq_gen2_tfd_unmap(struct iwl_trans *trans,
struct iwl_cmd_meta *meta,
struct iwl_tfh_tfd *tfd);
int iwl_txq_dyn_alloc(struct iwl_trans *trans,
__le16 flags, u8 sta_id, u8 tid,
int cmd_id, int size,
unsigned int timeout);
int iwl_txq_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_tx_cmd *dev_cmd, int txq_id);
void iwl_txq_dyn_free(struct iwl_trans *trans, int queue);
void iwl_txq_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq);
void iwl_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq);
void iwl_txq_gen2_tx_stop(struct iwl_trans *trans);
void iwl_txq_gen2_tx_free(struct iwl_trans *trans);
int iwl_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num,
bool cmd_queue);
int iwl_txq_gen2_init(struct iwl_trans *trans, int txq_id, int queue_size);
#ifdef CONFIG_INET
struct iwl_tso_hdr_page *get_page_hdr(struct iwl_trans *trans, size_t len,
struct sk_buff *skb);
#endif
#endif /* __iwl_trans_queue_tx_h__ */
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