Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7616f334 authored by Eliad Peller's avatar Eliad Peller Committed by Emmanuel Grumbach
Browse files

iwlwifi: pcie: add basic reference accounting



Implement the ref/unref trans ops and track both tx and
host command queues (and hold references while they
are not empty).

Signed-off-by: default avatarEliad Peller <eliadx.peller@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent a549b296
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1362,6 +1362,7 @@ struct iwl_mod_params iwlwifi_mod_params = {
	.bt_coex_active = true,
	.power_level = IWL_POWER_INDEX_1,
	.wd_disable = true,
	.d0i3_disable = true,
#ifndef CONFIG_IWLWIFI_UAPSD
	.uapsd_disable = true,
#endif /* CONFIG_IWLWIFI_UAPSD */
@@ -1478,6 +1479,10 @@ MODULE_PARM_DESC(wd_disable,
module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
MODULE_PARM_DESC(nvm_file, "NVM file name");

module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable,
		   bool, S_IRUGO);
MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)");

module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
		   bool, S_IRUGO);
#ifdef CONFIG_IWLWIFI_UAPSD
+2 −0
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ enum iwl_disable_11n {
 * @power_level: power level, default = 1
 * @debug_level: levels are IWL_DL_*
 * @ant_coupling: antenna coupling in dB, default = 0
 * @d0i3_disable: disable d0i3, default = 1,
 * @fw_monitor: allow to use firmware monitor
 */
struct iwl_mod_params {
@@ -121,6 +122,7 @@ struct iwl_mod_params {
	int ant_coupling;
	char *nvm_file;
	bool uapsd_disable;
	bool d0i3_disable;
	bool fw_monitor;
};

+8 −0
Original line number Diff line number Diff line
@@ -318,6 +318,11 @@ struct iwl_trans_pcie {
	/*protect hw register */
	spinlock_t reg_lock;
	bool cmd_in_flight;
	bool ref_cmd_in_flight;

	/* protect ref counter */
	spinlock_t ref_lock;
	u32 ref_count;

	dma_addr_t fw_mon_phys;
	struct page *fw_mon_page;
@@ -381,6 +386,9 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
			    struct sk_buff_head *skbs);
void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);

void iwl_trans_pcie_ref(struct iwl_trans *trans);
void iwl_trans_pcie_unref(struct iwl_trans *trans);

static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
{
	struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+39 −0
Original line number Diff line number Diff line
@@ -931,6 +931,7 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
				   const struct fw_img *fw, bool run_in_rfkill)
{
	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
	int ret;
	bool hw_rfkill;

@@ -960,6 +961,9 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
		return ret;
	}

	/* init ref_count to 1 (should be cleared when ucode is loaded) */
	trans_pcie->ref_count = 1;

	/* make sure rfkill handshake bits are cleared */
	iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
	iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
@@ -1550,6 +1554,38 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
	spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
}

void iwl_trans_pcie_ref(struct iwl_trans *trans)
{
	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
	unsigned long flags;

	if (iwlwifi_mod_params.d0i3_disable)
		return;

	spin_lock_irqsave(&trans_pcie->ref_lock, flags);
	IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
	trans_pcie->ref_count++;
	spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
}

void iwl_trans_pcie_unref(struct iwl_trans *trans)
{
	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
	unsigned long flags;

	if (iwlwifi_mod_params.d0i3_disable)
		return;

	spin_lock_irqsave(&trans_pcie->ref_lock, flags);
	IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
	if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) {
		spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
		return;
	}
	trans_pcie->ref_count--;
	spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
}

static const char *get_csr_string(int cmd)
{
#define IWL_CMD(x) case x: return #x
@@ -2274,6 +2310,9 @@ static const struct iwl_trans_ops trans_ops_pcie = {
	.release_nic_access = iwl_trans_pcie_release_nic_access,
	.set_bits_mask = iwl_trans_pcie_set_bits_mask,

	.ref = iwl_trans_pcie_ref,
	.unref = iwl_trans_pcie_unref,

	.dump_data = iwl_trans_pcie_dump_data,
};

+29 −5
Original line number Diff line number Diff line
@@ -985,17 +985,31 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,

	if (iwl_queue_space(&txq->q) > txq->q.low_mark)
		iwl_wake_queue(trans, txq);

	if (q->read_ptr == q->write_ptr) {
		IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id);
		iwl_trans_pcie_unref(trans);
	}

out:
	spin_unlock_bh(&txq->lock);
}

static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans)
static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
				      const struct iwl_host_cmd *cmd)
{
	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
	int ret;

	lockdep_assert_held(&trans_pcie->reg_lock);

	if (!(cmd->flags & CMD_SEND_IN_IDLE) &&
	    !trans_pcie->ref_cmd_in_flight) {
		trans_pcie->ref_cmd_in_flight = true;
		IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n");
		iwl_trans_pcie_ref(trans);
	}

	if (trans_pcie->cmd_in_flight)
		return 0;

@@ -1036,6 +1050,12 @@ static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)

	lockdep_assert_held(&trans_pcie->reg_lock);

	if (trans_pcie->ref_cmd_in_flight) {
		trans_pcie->ref_cmd_in_flight = false;
		IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n");
		iwl_trans_pcie_unref(trans);
	}

	if (WARN_ON(!trans_pcie->cmd_in_flight))
		return 0;

@@ -1473,7 +1493,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
		mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);

	spin_lock_irqsave(&trans_pcie->reg_lock, flags);
	ret = iwl_pcie_set_cmd_in_flight(trans);
	ret = iwl_pcie_set_cmd_in_flight(trans, cmd);
	if (ret < 0) {
		idx = ret;
		spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
@@ -1819,9 +1839,13 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
	wait_write_ptr = ieee80211_has_morefrags(fc);

	/* start timer if queue currently empty */
	if (txq->need_update && q->read_ptr == q->write_ptr &&
	    trans_pcie->wd_timeout)
		mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
	if (q->read_ptr == q->write_ptr) {
		if (txq->need_update && trans_pcie->wd_timeout)
			mod_timer(&txq->stuck_timer,
				  jiffies + trans_pcie->wd_timeout);
		IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id);
		iwl_trans_pcie_ref(trans);
	}

	/* Tell device the write index *just past* this latest filled TFD */
	q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);