iwlwifi: trans/pcie: defer transport initialisation
In a few PCIe devices we may have to swap out the configuration after we allocate/initialise some parts of the device because we only know the correct one after reading some registers. This causes some things such as the byte-count table allocations to be incorrect, since the configuration is swapped for one with a bigger queue size. Fix this by initialising most of the transport much later, only after the configuration has finally been determined. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Link: https://lore.kernel.org/r/iwlwifi.20210411132130.8f5db97db1e4.Ic622da559b586a04ca536a0ec49ed5ecf03a9354@changeid Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
This commit is contained in:
parent
7db67f6839
commit
d12455fdbf
@ -2,7 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 Intel Mobile Communications GmbH
|
* Copyright (C) 2015 Intel Mobile Communications GmbH
|
||||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||||
* Copyright (C) 2019-2020 Intel Corporation
|
* Copyright (C) 2019-2021 Intel Corporation
|
||||||
*/
|
*/
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/bsearch.h>
|
#include <linux/bsearch.h>
|
||||||
@ -21,7 +21,6 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
|||||||
const struct iwl_cfg_trans_params *cfg_trans)
|
const struct iwl_cfg_trans_params *cfg_trans)
|
||||||
{
|
{
|
||||||
struct iwl_trans *trans;
|
struct iwl_trans *trans;
|
||||||
int txcmd_size, txcmd_align;
|
|
||||||
#ifdef CONFIG_LOCKDEP
|
#ifdef CONFIG_LOCKDEP
|
||||||
static struct lock_class_key __key;
|
static struct lock_class_key __key;
|
||||||
#endif
|
#endif
|
||||||
@ -31,23 +30,6 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
trans->trans_cfg = cfg_trans;
|
trans->trans_cfg = cfg_trans;
|
||||||
if (!cfg_trans->gen2) {
|
|
||||||
txcmd_size = sizeof(struct iwl_tx_cmd);
|
|
||||||
txcmd_align = sizeof(void *);
|
|
||||||
} else if (cfg_trans->device_family < IWL_DEVICE_FAMILY_AX210) {
|
|
||||||
txcmd_size = sizeof(struct iwl_tx_cmd_gen2);
|
|
||||||
txcmd_align = 64;
|
|
||||||
} else {
|
|
||||||
txcmd_size = sizeof(struct iwl_tx_cmd_gen3);
|
|
||||||
txcmd_align = 128;
|
|
||||||
}
|
|
||||||
|
|
||||||
txcmd_size += sizeof(struct iwl_cmd_header);
|
|
||||||
txcmd_size += 36; /* biggest possible 802.11 header */
|
|
||||||
|
|
||||||
/* Ensure device TX cmd cannot reach/cross a page boundary in gen2 */
|
|
||||||
if (WARN_ON(cfg_trans->gen2 && txcmd_size >= txcmd_align))
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
|
|
||||||
#ifdef CONFIG_LOCKDEP
|
#ifdef CONFIG_LOCKDEP
|
||||||
lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map",
|
lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map",
|
||||||
@ -58,22 +40,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
|||||||
trans->ops = ops;
|
trans->ops = ops;
|
||||||
trans->num_rx_queues = 1;
|
trans->num_rx_queues = 1;
|
||||||
|
|
||||||
if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
|
WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty);
|
||||||
trans->txqs.bc_tbl_size = sizeof(struct iwl_gen3_bc_tbl);
|
|
||||||
else
|
|
||||||
trans->txqs.bc_tbl_size = sizeof(struct iwlagn_scd_bc_tbl);
|
|
||||||
/*
|
|
||||||
* For gen2 devices, we use a single allocation for each byte-count
|
|
||||||
* table, but they're pretty small (1k) so use a DMA pool that we
|
|
||||||
* allocate here.
|
|
||||||
*/
|
|
||||||
if (trans->trans_cfg->gen2) {
|
|
||||||
trans->txqs.bc_pool = dmam_pool_create("iwlwifi:bc", dev,
|
|
||||||
trans->txqs.bc_tbl_size,
|
|
||||||
256, 0);
|
|
||||||
if (!trans->txqs.bc_pool)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trans->trans_cfg->use_tfh) {
|
if (trans->trans_cfg->use_tfh) {
|
||||||
trans->txqs.tfd.addr_size = 64;
|
trans->txqs.tfd.addr_size = 64;
|
||||||
@ -86,6 +53,52 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
|||||||
}
|
}
|
||||||
trans->max_skb_frags = IWL_TRANS_MAX_FRAGS(trans);
|
trans->max_skb_frags = IWL_TRANS_MAX_FRAGS(trans);
|
||||||
|
|
||||||
|
return trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
int iwl_trans_init(struct iwl_trans *trans)
|
||||||
|
{
|
||||||
|
int txcmd_size, txcmd_align;
|
||||||
|
|
||||||
|
if (!trans->trans_cfg->gen2) {
|
||||||
|
txcmd_size = sizeof(struct iwl_tx_cmd);
|
||||||
|
txcmd_align = sizeof(void *);
|
||||||
|
} else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
|
||||||
|
txcmd_size = sizeof(struct iwl_tx_cmd_gen2);
|
||||||
|
txcmd_align = 64;
|
||||||
|
} else {
|
||||||
|
txcmd_size = sizeof(struct iwl_tx_cmd_gen3);
|
||||||
|
txcmd_align = 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
txcmd_size += sizeof(struct iwl_cmd_header);
|
||||||
|
txcmd_size += 36; /* biggest possible 802.11 header */
|
||||||
|
|
||||||
|
/* Ensure device TX cmd cannot reach/cross a page boundary in gen2 */
|
||||||
|
if (WARN_ON(trans->trans_cfg->gen2 && txcmd_size >= txcmd_align))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
|
||||||
|
trans->txqs.bc_tbl_size = sizeof(struct iwl_gen3_bc_tbl);
|
||||||
|
else
|
||||||
|
trans->txqs.bc_tbl_size = sizeof(struct iwlagn_scd_bc_tbl);
|
||||||
|
/*
|
||||||
|
* For gen2 devices, we use a single allocation for each byte-count
|
||||||
|
* table, but they're pretty small (1k) so use a DMA pool that we
|
||||||
|
* allocate here.
|
||||||
|
*/
|
||||||
|
if (trans->trans_cfg->gen2) {
|
||||||
|
trans->txqs.bc_pool = dmam_pool_create("iwlwifi:bc", trans->dev,
|
||||||
|
trans->txqs.bc_tbl_size,
|
||||||
|
256, 0);
|
||||||
|
if (!trans->txqs.bc_pool)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Some things must not change even if the config does */
|
||||||
|
WARN_ON(trans->txqs.tfd.addr_size !=
|
||||||
|
(trans->trans_cfg->use_tfh ? 64 : 36));
|
||||||
|
|
||||||
snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
|
snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
|
||||||
"iwl_cmd_pool:%s", dev_name(trans->dev));
|
"iwl_cmd_pool:%s", dev_name(trans->dev));
|
||||||
trans->dev_cmd_pool =
|
trans->dev_cmd_pool =
|
||||||
@ -93,36 +106,36 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
|||||||
txcmd_size, txcmd_align,
|
txcmd_size, txcmd_align,
|
||||||
SLAB_HWCACHE_ALIGN, NULL);
|
SLAB_HWCACHE_ALIGN, NULL);
|
||||||
if (!trans->dev_cmd_pool)
|
if (!trans->dev_cmd_pool)
|
||||||
return NULL;
|
return -ENOMEM;
|
||||||
|
|
||||||
WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty);
|
|
||||||
|
|
||||||
trans->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
|
trans->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
|
||||||
if (!trans->txqs.tso_hdr_page) {
|
if (!trans->txqs.tso_hdr_page) {
|
||||||
kmem_cache_destroy(trans->dev_cmd_pool);
|
kmem_cache_destroy(trans->dev_cmd_pool);
|
||||||
return NULL;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the wait queue for commands */
|
/* Initialize the wait queue for commands */
|
||||||
init_waitqueue_head(&trans->wait_command_queue);
|
init_waitqueue_head(&trans->wait_command_queue);
|
||||||
|
|
||||||
return trans;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void iwl_trans_free(struct iwl_trans *trans)
|
void iwl_trans_free(struct iwl_trans *trans)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for_each_possible_cpu(i) {
|
if (trans->txqs.tso_hdr_page) {
|
||||||
struct iwl_tso_hdr_page *p =
|
for_each_possible_cpu(i) {
|
||||||
per_cpu_ptr(trans->txqs.tso_hdr_page, i);
|
struct iwl_tso_hdr_page *p =
|
||||||
|
per_cpu_ptr(trans->txqs.tso_hdr_page, i);
|
||||||
|
|
||||||
if (p->page)
|
if (p && p->page)
|
||||||
__free_page(p->page);
|
__free_page(p->page);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_percpu(trans->txqs.tso_hdr_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
free_percpu(trans->txqs.tso_hdr_page);
|
|
||||||
|
|
||||||
kmem_cache_destroy(trans->dev_cmd_pool);
|
kmem_cache_destroy(trans->dev_cmd_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1439,6 +1439,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
|||||||
struct device *dev,
|
struct device *dev,
|
||||||
const struct iwl_trans_ops *ops,
|
const struct iwl_trans_ops *ops,
|
||||||
const struct iwl_cfg_trans_params *cfg_trans);
|
const struct iwl_cfg_trans_params *cfg_trans);
|
||||||
|
int iwl_trans_init(struct iwl_trans *trans);
|
||||||
void iwl_trans_free(struct iwl_trans *trans);
|
void iwl_trans_free(struct iwl_trans *trans);
|
||||||
|
|
||||||
/*****************************************************
|
/*****************************************************
|
||||||
|
@ -1276,6 +1276,10 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
trans_pcie->num_rx_bufs = RX_QUEUE_SIZE;
|
trans_pcie->num_rx_bufs = RX_QUEUE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = iwl_trans_init(iwl_trans);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_trans;
|
||||||
|
|
||||||
pci_set_drvdata(pdev, iwl_trans);
|
pci_set_drvdata(pdev, iwl_trans);
|
||||||
iwl_trans->drv = iwl_drv_start(iwl_trans);
|
iwl_trans->drv = iwl_drv_start(iwl_trans);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user