[PATCH] mmc: rtsx_pci: Cope with hot-removal of MMC host

Lukas Wunner <[email protected]>
Newsgroups org.kernel.vger.linux-mmc
Message-ID <f20e08ef831f0bad278adcec44046742b54ab648.1786781008.git.lukas@wunner.de>
Derek reports a lockup on hot-removal of a PCI-attached Realtek RTS525A
MMC host if a card is inserted.  He has root-caused it to the block
layer being unaware of the hot-removal and waiting indefinitely in
sync_filesystem().

For comparison, the NVMe subsystem copes with hot-removal by setting the
controller state to NVME_CTRL_DEAD in nvme_remove(), which in turn leads
to blk_mark_disk_dead() being called from nvme_mark_namespaces_dead().
That avoids the indefinite wait in sync_filesystem().

Adopt this approach:  Detect hot-removal in rtsx_pci_sdmmc_drv_remove()
and invoke a new mmc_host_set_removed() helper which in turn invokes
mmc_card_set_removed().  Before flushing outstanding requests to the
card in mmc_blk_remove_req(), check for its removal and call
blk_mark_disk_dead().

Note that mmc_card_set_removed() cannot be called from the MMC host
driver because it is private to the MMC core, hence the indirection
through the newly introduced mmc_host_set_removed() helper.

Other removable MMC hosts, in particular if attached via PCI or USB,
may need to be amended with a similar check for hot-removal.  The
present commit seeks to get that process going.

Reported-by: Derek J. Clark <[email protected]>
Tested-by: Derek J. Clark <[email protected]>
Closes: https://lore.kernel.org/r/[email protected]/
Link: https://github.com/ValveSoftware/SteamOS/issues/2473
Signed-off-by: Lukas Wunner <[email protected]>
Cc: Matthew Schwartz <[email protected]>
---
 drivers/mmc/core/block.c          | 11 +++++++----
 drivers/mmc/core/host.c           | 15 +++++++++++++++
 drivers/mmc/host/rtsx_pci_sdmmc.c |  3 +++
 include/linux/mmc/host.h          |  1 +
 4 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 0274e8d..1a82233 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -2978,8 +2978,11 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
 	return 0;
 }
 
-static void mmc_blk_remove_req(struct mmc_blk_data *md)
+static void mmc_blk_remove_req(struct mmc_card *card, struct mmc_blk_data *md)
 {
+	if (mmc_card_removed(card))
+		blk_mark_disk_dead(md->disk);
+
 	/*
 	 * Flush remaining requests and free queues. It is freeing the queue
 	 * that stops new requests from being accepted.
@@ -3006,7 +3009,7 @@ static void mmc_blk_remove_parts(struct mmc_card *card,
 	list_for_each_safe(pos, q, &md->part) {
 		part_md = list_entry(pos, struct mmc_blk_data, part);
 		list_del(pos);
-		mmc_blk_remove_req(part_md);
+		mmc_blk_remove_req(card, part_md);
 	}
 }
 
@@ -3252,7 +3255,7 @@ static int mmc_blk_probe(struct mmc_card *card)
 
 out:
 	mmc_blk_remove_parts(card, md);
-	mmc_blk_remove_req(md);
+	mmc_blk_remove_req(card, md);
 out_free:
 	destroy_workqueue(card->complete_wq);
 	return ret;
@@ -3273,7 +3276,7 @@ static void mmc_blk_remove(struct mmc_card *card)
 	if (!mmc_card_sd_combo(card))
 		pm_runtime_disable(&card->dev);
 	pm_runtime_put_noidle(&card->dev);
-	mmc_blk_remove_req(md);
+	mmc_blk_remove_req(card, md);
 	destroy_workqueue(card->complete_wq);
 }
 
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index b7ce313..2625d91 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -22,6 +22,7 @@
 #include <linux/mmc/card.h>
 #include <linux/mmc/slot-gpio.h>
 
+#include "card.h"
 #include "core.h"
 #include "crypto.h"
 #include "host.h"
@@ -703,3 +704,17 @@ void mmc_free_host(struct mmc_host *host)
 }
 
 EXPORT_SYMBOL(mmc_free_host);
+
+/**
+ *	mmc_host_set_removed - declare host removed
+ *	@host: mmc host
+ *
+ *	Declare the host (and any inserted card) removed and inaccessible.
+ */
+void mmc_host_set_removed(struct mmc_host *host)
+{
+	if (host->card)
+		mmc_card_set_removed(host->card);
+}
+
+EXPORT_SYMBOL(mmc_host_set_removed);
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 8dfbc62..3f97659f 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -1511,6 +1511,9 @@ static void rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
 	pcr->slots[RTSX_SD_CARD].card_event = NULL;
 	mmc = host->mmc;
 
+	if (pci_dev_is_disconnected(pcr->pci))
+		mmc_host_set_removed(mmc);
+
 	cancel_work_sync(&host->work);
 
 	mutex_lock(&host->host_mutex);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index ba84f02..a240306 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -588,6 +588,7 @@ struct mmc_host {
 int mmc_add_host(struct mmc_host *);
 void mmc_remove_host(struct mmc_host *);
 void mmc_free_host(struct mmc_host *);
+void mmc_host_set_removed(struct mmc_host *host);
 void mmc_of_parse_clk_phase(struct device *dev,
 			    struct mmc_clk_phase_map *map);
 int mmc_of_parse(struct mmc_host *host);
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.