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

Commit 0563b54d authored by Ram Prakash Gupta's avatar Ram Prakash Gupta
Browse files

mmc: sdhci-msm: Remove bus vote without delay when gating clocks



Whenever a job in workqueue with WQ_MEM_RECLAIM flag set tries
to execute another job without WQ_MEM_RECLAIM flag set, kernel
dumps below warning call stack of potential deadlock.

WQ_MEM_RECLAIM kblockd:blk_mq_run_work_fn is flushing
!WQ_MEM_RECLAIM events:sdhci_msm_bus_work

check_flush_dependency+0x118/0x120
start_flush_work+0x88/0x2d0
__cancel_work_timer+0x140/0x1e8
cancel_delayed_work_sync+0x20/0x30
sdhci_msm_bus_cancel_work_and_set_vote+0x34/0x148
sdhci_msm_enable_controller_clock+0x88/0x918
sdhci_msm_runtime_resume+0x34/0x260
pm_generic_runtime_resume+0x34/0x48
__rpm_callback+0x178/0x280
rpm_resume+0x564/0x7f8
__pm_runtime_resume+0x7c/0xa0
__mmc_claim_host+0x2b4/0x2f0
mmc_get_card+0x34/0x50
mmc_mq_queue_rq+0x1a4/0x280
blk_mq_dispatch_rq_list+0x264/0x748
blk_mq_do_dispatch_sched+0xc4/0x118
blk_mq_sched_dispatch_requests+0x130/0x190
__blk_mq_run_hw_queue+0xcc/0x148
blk_mq_run_work_fn+0x24/0x30
process_one_work+0x328/0x6b0
worker_thread+0x330/0x4d0
kthread+0x128/0x138

Above is observed when sdcc clocks are ungated in request context
which have WQ_MEM_RECLAIM flag set, but delayed work invoked in
this context do not have WQ_MEM_RECLAIM flag set.

To fix this issue, remove bus vote without deferring any job
when gating sdcc clocks, as gating and ungating of sdcc clock is
now part of runtime pm.

Change-Id: I7b7fb1a7b60029fb5a3c2afd0e8eefeb3f480f4c
Signed-off-by: default avatarRam Prakash Gupta <rampraka@codeaurora.org>
parent f08e34e4
Loading
Loading
Loading
Loading
+9 −83
Original line number Diff line number Diff line
@@ -2225,33 +2225,6 @@ static inline int sdhci_msm_bus_set_vote(struct sdhci_msm_host *msm_host,
	return rc;
}

/*
 * Internal work. Work to set 0 bandwidth for msm bus.
 */
static void sdhci_msm_bus_work(struct work_struct *work)
{
	struct sdhci_msm_host *msm_host;
	struct sdhci_host *host;
	unsigned long flags;

	msm_host = container_of(work, struct sdhci_msm_host,
				msm_bus_vote.vote_work.work);
	host =  platform_get_drvdata(msm_host->pdev);

	if (!msm_host->msm_bus_vote.client_handle)
		return;

	spin_lock_irqsave(&host->lock, flags);
	/* don't vote for 0 bandwidth if any request is in progress */
	if (!host->mrq) {
		sdhci_msm_bus_set_vote(msm_host,
			msm_host->msm_bus_vote.min_bw_vote, &flags);
	} else
		pr_warn("%s: %s: Transfer in progress. skipping bus voting to 0 bandwidth\n",
			   mmc_hostname(host->mmc), __func__);
	spin_unlock_irqrestore(&host->lock, flags);
}

/*****************************************************************************\
 *                                                                           *
 * MSM Command Queue Engine (CQE)                                            *
@@ -2437,7 +2410,7 @@ static void sdhci_msm_cqe_add_host(struct sdhci_host *host,
 * This function cancels any scheduled delayed work and sets the bus
 * vote based on bw (bandwidth) argument.
 */
static void sdhci_msm_bus_cancel_work_and_set_vote(struct sdhci_host *host,
static void sdhci_msm_bus_get_and_set_vote(struct sdhci_host *host,
						unsigned int bw)
{
	int vote;
@@ -2445,31 +2418,12 @@ static void sdhci_msm_bus_cancel_work_and_set_vote(struct sdhci_host *host,
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_msm_host *msm_host = pltfm_host->priv;

	cancel_delayed_work_sync(&msm_host->msm_bus_vote.vote_work);
	spin_lock_irqsave(&host->lock, flags);
	vote = sdhci_msm_bus_get_vote_for_bw(msm_host, bw);
	sdhci_msm_bus_set_vote(msm_host, vote, &flags);
	spin_unlock_irqrestore(&host->lock, flags);
}

#define MSM_MMC_BUS_VOTING_DELAY	200 /* msecs */

/* This function queues a work which will set the bandwidth requiement to 0 */
static void sdhci_msm_bus_queue_work(struct sdhci_host *host)
{
	unsigned long flags;
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_msm_host *msm_host = pltfm_host->priv;

	spin_lock_irqsave(&host->lock, flags);
	if (msm_host->msm_bus_vote.min_bw_vote !=
		msm_host->msm_bus_vote.curr_vote)
		queue_delayed_work(system_wq,
				   &msm_host->msm_bus_vote.vote_work,
				   msecs_to_jiffies(MSM_MMC_BUS_VOTING_DELAY));
	spin_unlock_irqrestore(&host->lock, flags);
}

static int sdhci_msm_bus_register(struct sdhci_msm_host *host,
				struct platform_device *pdev)
{
@@ -2540,22 +2494,11 @@ static void sdhci_msm_bus_voting(struct sdhci_host *host, u32 enable)
	if (!msm_host->msm_bus_vote.client_handle)
		return;

	bw = sdhci_get_bw_required(host, ios);
	if (enable) {
		sdhci_msm_bus_cancel_work_and_set_vote(host, bw);
		bw = sdhci_get_bw_required(host, ios);
		sdhci_msm_bus_get_and_set_vote(host, bw);
	} else {
		/*
		 * If clock gating is enabled, then remove the vote
		 * immediately because clocks will be disabled only
		 * after SDHCI_MSM_MMC_CLK_GATE_DELAY and thus no
		 * additional delay is required to remove the bus vote.
		 */
#ifdef CONFIG_MMC_CLKGATE
		if (host->mmc->clkgate_delay)
			sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
		else
#endif
			sdhci_msm_bus_queue_work(host);
		sdhci_msm_bus_get_and_set_vote(host, 0);
	}
}

@@ -3485,8 +3428,7 @@ static int sdhci_msm_enable_controller_clock(struct sdhci_host *host)
	if (!IS_ERR(msm_host->pclk))
		clk_disable_unprepare(msm_host->pclk);
remove_vote:
	if (msm_host->msm_bus_vote.client_handle)
		sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
	sdhci_msm_bus_voting(host, 0);
out:
	return rc;
}
@@ -3615,8 +3557,7 @@ static int sdhci_msm_prepare_clocks(struct sdhci_host *host, bool enable)
		clk_disable_unprepare(msm_host->pclk);
	atomic_set(&msm_host->controller_clock, 0);
remove_vote:
	if (msm_host->msm_bus_vote.client_handle)
		sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
		sdhci_msm_bus_voting(host, 0);
out:
	return rc;
}
@@ -5157,9 +5098,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
	if (ret)
		goto sleep_clk_disable;

	if (msm_host->msm_bus_vote.client_handle)
		INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work,
				  sdhci_msm_bus_work);
	sdhci_msm_bus_voting(host, 1);

	/* Setup regulators */
@@ -5482,8 +5420,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
vreg_deinit:
	sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
bus_unregister:
	if (msm_host->msm_bus_vote.client_handle)
		sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
	sdhci_msm_bus_voting(host, 0);
	sdhci_msm_bus_unregister(msm_host);
sleep_clk_disable:
	if (!IS_ERR(msm_host->sleep_clk))
@@ -5568,10 +5505,9 @@ static int sdhci_msm_remove(struct platform_device *pdev)
	sdhci_msm_setup_pins(pdata, true);
	sdhci_msm_setup_pins(pdata, false);

	if (msm_host->msm_bus_vote.client_handle) {
		sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
	sdhci_msm_bus_voting(host, 0);
	if (msm_host->msm_bus_vote.client_handle)
		sdhci_msm_bus_unregister(msm_host);
	}

	sdhci_pltfm_free(pdev);

@@ -5643,16 +5579,6 @@ static int sdhci_msm_runtime_suspend(struct device *dev)
defer_disable_host_irq:
	disable_irq(msm_host->pwr_irq);

	/*
	 * Remove the vote immediately only if clocks are off in which
	 * case we might have queued work to remove vote but it may not
	 * be completed before runtime suspend or system suspend.
	 */
	if (!atomic_read(&msm_host->clks_on)) {
		if (msm_host->msm_bus_vote.client_handle)
			sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
	}

	if (host->is_crypto_en) {
		ret = sdhci_msm_ice_suspend(host);
		if (ret < 0)
+0 −1
Original line number Diff line number Diff line
@@ -158,7 +158,6 @@ struct sdhci_msm_bus_vote {
	int min_bw_vote;
	int max_bw_vote;
	bool is_max_bw_needed;
	struct delayed_work vote_work;
	struct device_attribute max_bus_bw;
};