linux-mmc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] mmc: Convert from tasklet to BH workqueue
@ 2024-06-18 22:52 Allen Pais
  2024-06-20 15:14 ` Ulf Hansson
  2024-06-21  9:53 ` Wolfram Sang
  0 siblings, 2 replies; 10+ messages in thread
From: Allen Pais @ 2024-06-18 22:52 UTC (permalink / raw)
  To: Aubin Constans, Ulf Hansson, Nicolas Ferre, Alexandre Belloni,
	Claudiu Beznea, Manuel Lauss, Michał Mirosław,
	Jaehoon Chung, Aaro Koskinen, Wolfram Sang, Adrian Hunter,
	Florian Fainelli, Ray Jui, Scott Branden,
	Broadcom internal kernel review list, Alex Dubov,
	Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang, Harald Welte,
	Pierre Ossman
  Cc: Allen Pais, Christian Loehle, linux-mmc, linux-arm-kernel,
	linux-kernel, linux-omap, linux-renesas-soc

The only generic interface to execute asynchronously in the BH context is
tasklet; however, it's marked deprecated and has some design flaws. To
replace tasklets, BH workqueue support was recently added. A BH workqueue
behaves similarly to regular workqueues except that the queued work items
are executed in the BH context.

This patch converts drivers/mmc/* from tasklet to BH workqueue.

Based on the work done by Tejun Heo <tj@kernel.org>

Tested-by: Christian Loehle <christian.loehle@arm.com>
Tested-by: Aubin Constans <aubin.constans@microchip.com>
Acked-by: Aubin Constans <aubin.constans@microchip.com>
Acked-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Reviewed-by: Christian Loehle <christian.loehle@arm.com>
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
v3:
   - fixed a comment - [Adrian Hunter]
v2:
   - fixed patch styling issues
   - rename work to bh_work

Link to v1:
https://lore.kernel.org/all/20240327160314.9982-10-apais@linux.microsoft.com/

 drivers/mmc/host/atmel-mci.c                  | 35 ++++-----
 drivers/mmc/host/au1xmmc.c                    | 37 +++++-----
 drivers/mmc/host/cb710-mmc.c                  | 14 ++--
 drivers/mmc/host/cb710-mmc.h                  |  3 +-
 drivers/mmc/host/dw_mmc.c                     | 24 +++---
 drivers/mmc/host/dw_mmc.h                     |  9 ++-
 drivers/mmc/host/omap.c                       | 17 +++--
 drivers/mmc/host/renesas_sdhi.h               |  3 +-
 drivers/mmc/host/renesas_sdhi_internal_dmac.c | 24 +++---
 drivers/mmc/host/renesas_sdhi_sys_dmac.c      |  9 +--
 drivers/mmc/host/sdhci-bcm-kona.c             |  2 +-
 drivers/mmc/host/tifm_sd.c                    | 15 ++--
 drivers/mmc/host/tmio_mmc.h                   |  3 +-
 drivers/mmc/host/tmio_mmc_core.c              |  4 +-
 drivers/mmc/host/uniphier-sd.c                | 12 +--
 drivers/mmc/host/via-sdmmc.c                  | 25 ++++---
 drivers/mmc/host/wbsd.c                       | 74 +++++++++----------
 drivers/mmc/host/wbsd.h                       | 10 +--
 18 files changed, 163 insertions(+), 157 deletions(-)

diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 8199d9620075..6490df54a6f5 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -33,6 +33,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/workqueue.h>
 
 #include <asm/cacheflush.h>
 #include <asm/io.h>
@@ -272,12 +273,12 @@ struct atmel_mci_dma {
  *	EVENT_DATA_ERROR is pending.
  * @stop_cmdr: Value to be loaded into CMDR when the stop command is
  *	to be sent.
- * @tasklet: Tasklet running the request state machine.
+ * @bh_work: Work running the request state machine.
  * @pending_events: Bitmask of events flagged by the interrupt handler
- *	to be processed by the tasklet.
+ *	to be processed by the work.
  * @completed_events: Bitmask of events which the state machine has
  *	processed.
- * @state: Tasklet state.
+ * @state: Work state.
  * @queue: List of slots waiting for access to the controller.
  * @need_clock_update: Update the clock rate before the next request.
  * @need_reset: Reset controller before next request.
@@ -352,7 +353,7 @@ struct atmel_mci {
 	u32			data_status;
 	u32			stop_cmdr;
 
-	struct tasklet_struct	tasklet;
+	struct work_struct	bh_work;
 	unsigned long		pending_events;
 	unsigned long		completed_events;
 	enum atmel_mci_state	state;
@@ -735,7 +736,7 @@ static void atmci_timeout_timer(struct timer_list *t)
 	host->need_reset = 1;
 	host->state = STATE_END_REQUEST;
 	smp_wmb();
-	tasklet_schedule(&host->tasklet);
+	queue_work(system_bh_wq, &host->bh_work);
 }
 
 static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
@@ -958,7 +959,7 @@ static void atmci_pdc_complete(struct atmel_mci *host)
 
 	dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
 	atmci_set_pending(host, EVENT_XFER_COMPLETE);
-	tasklet_schedule(&host->tasklet);
+	queue_work(system_bh_wq, &host->bh_work);
 }
 
 static void atmci_dma_cleanup(struct atmel_mci *host)
@@ -972,7 +973,7 @@ static void atmci_dma_cleanup(struct atmel_mci *host)
 }
 
 /*
- * This function is called by the DMA driver from tasklet context.
+ * This function is called by the DMA driver from bh context.
  */
 static void atmci_dma_complete(void *arg)
 {
@@ -995,7 +996,7 @@ static void atmci_dma_complete(void *arg)
 	if (data) {
 		dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
 		atmci_set_pending(host, EVENT_XFER_COMPLETE);
-		tasklet_schedule(&host->tasklet);
+		queue_work(system_bh_wq, &host->bh_work);
 
 		/*
 		 * Regardless of what the documentation says, we have
@@ -1008,7 +1009,7 @@ static void atmci_dma_complete(void *arg)
 		 * haven't seen all the potential error bits yet.
 		 *
 		 * The interrupt handler will schedule a different
-		 * tasklet to finish things up when the data transfer
+		 * bh work to finish things up when the data transfer
 		 * is completely done.
 		 *
 		 * We may not complete the mmc request here anyway
@@ -1745,9 +1746,9 @@ static void atmci_detect_change(struct timer_list *t)
 	}
 }
 
-static void atmci_tasklet_func(struct tasklet_struct *t)
+static void atmci_work_func(struct work_struct *t)
 {
-	struct atmel_mci        *host = from_tasklet(host, t, tasklet);
+	struct atmel_mci        *host = from_work(host, t, bh_work);
 	struct mmc_request	*mrq = host->mrq;
 	struct mmc_data		*data = host->data;
 	struct device		*dev = host->dev;
@@ -1759,7 +1760,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
 
 	state = host->state;
 
-	dev_vdbg(dev, "tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
+	dev_vdbg(dev, "bh_work: state %u pending/completed/mask %lx/%lx/%x\n",
 		state, host->pending_events, host->completed_events,
 		atmci_readl(host, ATMCI_IMR));
 
@@ -2118,7 +2119,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
 			dev_dbg(dev, "set pending data error\n");
 			smp_wmb();
 			atmci_set_pending(host, EVENT_DATA_ERROR);
-			tasklet_schedule(&host->tasklet);
+			queue_work(system_bh_wq, &host->bh_work);
 		}
 
 		if (pending & ATMCI_TXBUFE) {
@@ -2187,7 +2188,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
 			smp_wmb();
 			dev_dbg(dev, "set pending notbusy\n");
 			atmci_set_pending(host, EVENT_NOTBUSY);
-			tasklet_schedule(&host->tasklet);
+			queue_work(system_bh_wq, &host->bh_work);
 		}
 
 		if (pending & ATMCI_NOTBUSY) {
@@ -2196,7 +2197,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
 			smp_wmb();
 			dev_dbg(dev, "set pending notbusy\n");
 			atmci_set_pending(host, EVENT_NOTBUSY);
-			tasklet_schedule(&host->tasklet);
+			queue_work(system_bh_wq, &host->bh_work);
 		}
 
 		if (pending & ATMCI_RXRDY)
@@ -2211,7 +2212,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
 			smp_wmb();
 			dev_dbg(dev, "set pending cmd rdy\n");
 			atmci_set_pending(host, EVENT_CMD_RDY);
-			tasklet_schedule(&host->tasklet);
+			queue_work(system_bh_wq, &host->bh_work);
 		}
 
 		if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
@@ -2487,7 +2488,7 @@ static int atmci_probe(struct platform_device *pdev)
 
 	host->mapbase = regs->start;
 
-	tasklet_setup(&host->tasklet, atmci_tasklet_func);
+	INIT_WORK(&host->bh_work, atmci_work_func);
 
 	ret = request_irq(irq, atmci_interrupt, 0, dev_name(dev), host);
 	if (ret) {
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index b5a5c6a2fe8b..6e80bcb668ec 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -42,6 +42,7 @@
 #include <linux/leds.h>
 #include <linux/mmc/host.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 
 #include <asm/io.h>
 #include <asm/mach-au1x00/au1000.h>
@@ -113,8 +114,8 @@ struct au1xmmc_host {
 
 	int irq;
 
-	struct tasklet_struct finish_task;
-	struct tasklet_struct data_task;
+	struct work_struct finish_bh_work;
+	struct work_struct data_bh_work;
 	struct au1xmmc_platform_data *platdata;
 	struct platform_device *pdev;
 	struct resource *ioarea;
@@ -253,9 +254,9 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host)
 	mmc_request_done(host->mmc, mrq);
 }
 
-static void au1xmmc_tasklet_finish(struct tasklet_struct *t)
+static void au1xmmc_finish_bh_work(struct work_struct *t)
 {
-	struct au1xmmc_host *host = from_tasklet(host, t, finish_task);
+	struct au1xmmc_host *host = from_work(host, t, finish_bh_work);
 	au1xmmc_finish_request(host);
 }
 
@@ -363,9 +364,9 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
 	au1xmmc_finish_request(host);
 }
 
-static void au1xmmc_tasklet_data(struct tasklet_struct *t)
+static void au1xmmc_data_bh_work(struct work_struct *t)
 {
-	struct au1xmmc_host *host = from_tasklet(host, t, data_task);
+	struct au1xmmc_host *host = from_work(host, t, data_bh_work);
 
 	u32 status = __raw_readl(HOST_STATUS(host));
 	au1xmmc_data_complete(host, status);
@@ -425,7 +426,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
 		if (host->flags & HOST_F_STOP)
 			SEND_STOP(host);
 
-		tasklet_schedule(&host->data_task);
+		queue_work(system_bh_wq, &host->data_bh_work);
 	}
 }
 
@@ -505,7 +506,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
 		if (host->flags & HOST_F_STOP)
 			SEND_STOP(host);
 
-		tasklet_schedule(&host->data_task);
+		queue_work(system_bh_wq, &host->data_bh_work);
 	}
 }
 
@@ -561,7 +562,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
 
 	if (!trans || cmd->error) {
 		IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF);
-		tasklet_schedule(&host->finish_task);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 		return;
 	}
 
@@ -797,7 +798,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
 		IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
 
 		/* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */
-		tasklet_schedule(&host->finish_task);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 	}
 #if 0
 	else if (status & SD_STATUS_DD) {
@@ -806,7 +807,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
 			au1xmmc_receive_pio(host);
 		else {
 			au1xmmc_data_complete(host, status);
-			/* tasklet_schedule(&host->data_task); */
+			/* queue_work(system_bh_wq, &host->data_bh_work); */
 		}
 	}
 #endif
@@ -854,7 +855,7 @@ static void au1xmmc_dbdma_callback(int irq, void *dev_id)
 	if (host->flags & HOST_F_STOP)
 		SEND_STOP(host);
 
-	tasklet_schedule(&host->data_task);
+	queue_work(system_bh_wq, &host->data_bh_work);
 }
 
 static int au1xmmc_dbdma_init(struct au1xmmc_host *host)
@@ -1039,9 +1040,9 @@ static int au1xmmc_probe(struct platform_device *pdev)
 	if (host->platdata)
 		mmc->caps &= ~(host->platdata->mask_host_caps);
 
-	tasklet_setup(&host->data_task, au1xmmc_tasklet_data);
+	INIT_WORK(&host->data_bh_work, au1xmmc_data_bh_work);
 
-	tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish);
+	INIT_WORK(&host->finish_bh_work, au1xmmc_finish_bh_work);
 
 	if (has_dbdma()) {
 		ret = au1xmmc_dbdma_init(host);
@@ -1091,8 +1092,8 @@ static int au1xmmc_probe(struct platform_device *pdev)
 	if (host->flags & HOST_F_DBDMA)
 		au1xmmc_dbdma_shutdown(host);
 
-	tasklet_kill(&host->data_task);
-	tasklet_kill(&host->finish_task);
+	cancel_work_sync(&host->data_bh_work);
+	cancel_work_sync(&host->finish_bh_work);
 
 	if (host->platdata && host->platdata->cd_setup &&
 	    !(mmc->caps & MMC_CAP_NEEDS_POLL))
@@ -1135,8 +1136,8 @@ static void au1xmmc_remove(struct platform_device *pdev)
 		__raw_writel(0, HOST_CONFIG2(host));
 		wmb(); /* drain writebuffer */
 
-		tasklet_kill(&host->data_task);
-		tasklet_kill(&host->finish_task);
+		cancel_work_sync(&host->data_bh_work);
+		cancel_work_sync(&host->finish_bh_work);
 
 		if (host->flags & HOST_F_DBDMA)
 			au1xmmc_dbdma_shutdown(host);
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index 0aec33b88bef..902f7f20abaa 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -493,7 +493,7 @@ static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop)
 		cb710_mmc_command(mmc, mrq->stop);
 
-	tasklet_schedule(&reader->finish_req_tasklet);
+	queue_work(system_bh_wq, &reader->finish_req_bh_work);
 }
 
 static int cb710_mmc_powerup(struct cb710_slot *slot)
@@ -646,10 +646,10 @@ static int cb710_mmc_irq_handler(struct cb710_slot *slot)
 	return 1;
 }
 
-static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t)
+static void cb710_mmc_finish_request_bh_work(struct work_struct *t)
 {
-	struct cb710_mmc_reader *reader = from_tasklet(reader, t,
-						       finish_req_tasklet);
+	struct cb710_mmc_reader *reader = from_work(reader, t,
+						       finish_req_bh_work);
 	struct mmc_request *mrq = reader->mrq;
 
 	reader->mrq = NULL;
@@ -718,8 +718,8 @@ static int cb710_mmc_init(struct platform_device *pdev)
 
 	reader = mmc_priv(mmc);
 
-	tasklet_setup(&reader->finish_req_tasklet,
-		      cb710_mmc_finish_request_tasklet);
+	INIT_WORK(&reader->finish_req_bh_work,
+			cb710_mmc_finish_request_bh_work);
 	spin_lock_init(&reader->irq_lock);
 	cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
 
@@ -763,7 +763,7 @@ static void cb710_mmc_exit(struct platform_device *pdev)
 	cb710_write_port_32(slot, CB710_MMC_CONFIG_PORT, 0);
 	cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0);
 
-	tasklet_kill(&reader->finish_req_tasklet);
+	cancel_work_sync(&reader->finish_req_bh_work);
 
 	mmc_free_host(mmc);
 }
diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h
index 5e053077dbed..59abaccaad10 100644
--- a/drivers/mmc/host/cb710-mmc.h
+++ b/drivers/mmc/host/cb710-mmc.h
@@ -8,10 +8,11 @@
 #define LINUX_CB710_MMC_H
 
 #include <linux/cb710.h>
+#include <linux/workqueue.h>
 
 /* per-MMC-reader structure */
 struct cb710_mmc_reader {
-	struct tasklet_struct finish_req_tasklet;
+	struct work_struct finish_req_bh_work;
 	struct mmc_request *mrq;
 	spinlock_t irq_lock;
 	unsigned char last_power_mode;
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 8e2d676b9239..7195de2cde44 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -493,7 +493,7 @@ static void dw_mci_dmac_complete_dma(void *arg)
 	 */
 	if (data) {
 		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
-		tasklet_schedule(&host->tasklet);
+		queue_work(system_bh_wq, &host->bh_work);
 	}
 }
 
@@ -1834,7 +1834,7 @@ static enum hrtimer_restart dw_mci_fault_timer(struct hrtimer *t)
 	if (!host->data_status) {
 		host->data_status = SDMMC_INT_DCRC;
 		set_bit(EVENT_DATA_ERROR, &host->pending_events);
-		tasklet_schedule(&host->tasklet);
+		queue_work(system_bh_wq, &host->bh_work);
 	}
 
 	spin_unlock_irqrestore(&host->irq_lock, flags);
@@ -2056,9 +2056,9 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
 	return true;
 }
 
-static void dw_mci_tasklet_func(struct tasklet_struct *t)
+static void dw_mci_work_func(struct work_struct *t)
 {
-	struct dw_mci *host = from_tasklet(host, t, tasklet);
+	struct dw_mci *host = from_work(host, t, bh_work);
 	struct mmc_data	*data;
 	struct mmc_command *cmd;
 	struct mmc_request *mrq;
@@ -2113,7 +2113,7 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t)
 				 * will waste a bit of time (we already know
 				 * the command was bad), it can't cause any
 				 * errors since it's possible it would have
-				 * taken place anyway if this tasklet got
+				 * taken place anyway if this bh work got
 				 * delayed. Allowing the transfer to take place
 				 * avoids races and keeps things simple.
 				 */
@@ -2706,7 +2706,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
 	smp_wmb(); /* drain writebuffer */
 
 	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
-	tasklet_schedule(&host->tasklet);
+	queue_work(system_bh_wq, &host->bh_work);
 
 	dw_mci_start_fault_timer(host);
 }
@@ -2774,7 +2774,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 				set_bit(EVENT_DATA_COMPLETE,
 					&host->pending_events);
 
-			tasklet_schedule(&host->tasklet);
+			queue_work(system_bh_wq, &host->bh_work);
 
 			spin_unlock(&host->irq_lock);
 		}
@@ -2793,7 +2793,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 					dw_mci_read_data_pio(host, true);
 			}
 			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
-			tasklet_schedule(&host->tasklet);
+			queue_work(system_bh_wq, &host->bh_work);
 
 			spin_unlock(&host->irq_lock);
 		}
@@ -3098,7 +3098,7 @@ static void dw_mci_cmd11_timer(struct timer_list *t)
 
 	host->cmd_status = SDMMC_INT_RTO;
 	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
-	tasklet_schedule(&host->tasklet);
+	queue_work(system_bh_wq, &host->bh_work);
 }
 
 static void dw_mci_cto_timer(struct timer_list *t)
@@ -3144,7 +3144,7 @@ static void dw_mci_cto_timer(struct timer_list *t)
 		 */
 		host->cmd_status = SDMMC_INT_RTO;
 		set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
-		tasklet_schedule(&host->tasklet);
+		queue_work(system_bh_wq, &host->bh_work);
 		break;
 	default:
 		dev_warn(host->dev, "Unexpected command timeout, state %d\n",
@@ -3195,7 +3195,7 @@ static void dw_mci_dto_timer(struct timer_list *t)
 		host->data_status = SDMMC_INT_DRTO;
 		set_bit(EVENT_DATA_ERROR, &host->pending_events);
 		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
-		tasklet_schedule(&host->tasklet);
+		queue_work(system_bh_wq, &host->bh_work);
 		break;
 	default:
 		dev_warn(host->dev, "Unexpected data timeout, state %d\n",
@@ -3435,7 +3435,7 @@ int dw_mci_probe(struct dw_mci *host)
 	else
 		host->fifo_reg = host->regs + DATA_240A_OFFSET;
 
-	tasklet_setup(&host->tasklet, dw_mci_tasklet_func);
+	INIT_WORK(&host->bh_work, dw_mci_work_func);
 	ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
 			       host->irq_flags, "dw-mci", host);
 	if (ret)
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 4ed81f94f7ca..b504f7ae513a 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -17,6 +17,7 @@
 #include <linux/fault-inject.h>
 #include <linux/hrtimer.h>
 #include <linux/interrupt.h>
+#include <linux/workqueue.h>
 
 enum dw_mci_state {
 	STATE_IDLE = 0,
@@ -89,12 +90,12 @@ struct dw_mci_dma_slave {
  * @stop_cmdr: Value to be loaded into CMDR when the stop command is
  *	to be sent.
  * @dir_status: Direction of current transfer.
- * @tasklet: Tasklet running the request state machine.
+ * @bh_work: Work running the request state machine.
  * @pending_events: Bitmask of events flagged by the interrupt handler
- *	to be processed by the tasklet.
+ *	to be processed by bh work.
  * @completed_events: Bitmask of events which the state machine has
  *	processed.
- * @state: Tasklet state.
+ * @state: BH work state.
  * @queue: List of slots waiting for access to the controller.
  * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
  *	rate and timeout calculations.
@@ -194,7 +195,7 @@ struct dw_mci {
 	u32			data_status;
 	u32			stop_cmdr;
 	u32			dir_status;
-	struct tasklet_struct	tasklet;
+	struct work_struct	bh_work;
 	unsigned long		pending_events;
 	unsigned long		completed_events;
 	enum dw_mci_state	state;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index a8ee0df47148..335350a4e99a 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/gpio/consumer.h>
 #include <linux/platform_data/mmc-omap.h>
+#include <linux/workqueue.h>
 
 
 #define	OMAP_MMC_REG_CMD	0x00
@@ -105,7 +106,7 @@ struct mmc_omap_slot {
 	u16			power_mode;
 	unsigned int		fclk_freq;
 
-	struct tasklet_struct	cover_tasklet;
+	struct work_struct	cover_bh_work;
 	struct timer_list       cover_timer;
 	unsigned		cover_open;
 
@@ -873,18 +874,18 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
 		sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
 	}
 
-	tasklet_hi_schedule(&slot->cover_tasklet);
+	queue_work(system_bh_highpri_wq, &slot->cover_bh_work);
 }
 
 static void mmc_omap_cover_timer(struct timer_list *t)
 {
 	struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer);
-	tasklet_schedule(&slot->cover_tasklet);
+	queue_work(system_bh_wq, &slot->cover_bh_work);
 }
 
-static void mmc_omap_cover_handler(struct tasklet_struct *t)
+static void mmc_omap_cover_bh_handler(struct work_struct *t)
 {
-	struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet);
+	struct mmc_omap_slot *slot = from_work(slot, t, cover_bh_work);
 	int cover_open = mmc_omap_cover_is_open(slot);
 
 	mmc_detect_change(slot->mmc, 0);
@@ -1314,7 +1315,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
 
 	if (slot->pdata->get_cover_state != NULL) {
 		timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0);
-		tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler);
+		INIT_WORK(&slot->cover_bh_work, mmc_omap_cover_bh_handler);
 	}
 
 	r = mmc_add_host(mmc);
@@ -1333,7 +1334,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
 					&dev_attr_cover_switch);
 		if (r < 0)
 			goto err_remove_slot_name;
-		tasklet_schedule(&slot->cover_tasklet);
+		queue_work(system_bh_wq, &slot->cover_bh_work);
 	}
 
 	return 0;
@@ -1356,7 +1357,7 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
 	if (slot->pdata->get_cover_state != NULL)
 		device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
 
-	tasklet_kill(&slot->cover_tasklet);
+	cancel_work_sync(&slot->cover_bh_work);
 	del_timer_sync(&slot->cover_timer);
 	flush_workqueue(slot->host->mmc_omap_wq);
 
diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index 586f94d4dbfd..4fd2bfcacd76 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -11,6 +11,7 @@
 
 #include <linux/dmaengine.h>
 #include <linux/platform_device.h>
+#include <linux/workqueue.h>
 #include "tmio_mmc.h"
 
 struct renesas_sdhi_scc {
@@ -67,7 +68,7 @@ struct renesas_sdhi_dma {
 	dma_filter_fn filter;
 	void (*enable)(struct tmio_mmc_host *host, bool enable);
 	struct completion dma_dataend;
-	struct tasklet_struct dma_complete;
+	struct work_struct dma_complete;
 };
 
 struct renesas_sdhi {
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 422fa63a2e99..5fd276492f80 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -337,7 +337,7 @@ static bool renesas_sdhi_internal_dmac_dma_irq(struct tmio_mmc_host *host)
 		writel(status ^ dma_irqs, host->ctl + DM_CM_INFO1);
 		set_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags);
 		if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags))
-			tasklet_schedule(&dma_priv->dma_complete);
+			queue_work(system_bh_wq, &dma_priv->dma_complete);
 	}
 
 	return status & dma_irqs;
@@ -352,7 +352,7 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host)
 	set_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags);
 	if (test_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags) ||
 	    host->data->error)
-		tasklet_schedule(&dma_priv->dma_complete);
+		queue_work(system_bh_wq, &dma_priv->dma_complete);
 }
 
 /*
@@ -440,9 +440,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
 	renesas_sdhi_internal_dmac_enable_dma(host, false);
 }
 
-static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
+static void renesas_sdhi_internal_dmac_issue_work_fn(struct work_struct *t)
 {
-	struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+	struct tmio_mmc_host *host = from_work(host, t, dma_issue);
 	struct renesas_sdhi *priv = host_to_priv(host);
 
 	tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
@@ -454,7 +454,7 @@ static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
 		/* on CMD errors, simulate DMA end immediately */
 		set_bit(SDHI_DMA_END_FLAG_DMA, &priv->dma_priv.end_flags);
 		if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &priv->dma_priv.end_flags))
-			tasklet_schedule(&priv->dma_priv.dma_complete);
+			queue_work(system_bh_wq, &priv->dma_priv.dma_complete);
 	}
 }
 
@@ -484,9 +484,9 @@ static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
 	return true;
 }
 
-static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
+static void renesas_sdhi_internal_dmac_complete_work_fn(struct work_struct *t)
 {
-	struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+	struct tmio_mmc_host *host = from_work(host, t, dma_complete);
 
 	spin_lock_irq(&host->lock);
 	if (!renesas_sdhi_internal_dmac_complete(host))
@@ -544,12 +544,10 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
 	/* Each value is set to non-zero to assume "enabling" each DMA */
 	host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
 
-	tasklet_init(&priv->dma_priv.dma_complete,
-		     renesas_sdhi_internal_dmac_complete_tasklet_fn,
-		     (unsigned long)host);
-	tasklet_init(&host->dma_issue,
-		     renesas_sdhi_internal_dmac_issue_tasklet_fn,
-		     (unsigned long)host);
+	INIT_WORK(&priv->dma_priv.dma_complete,
+			renesas_sdhi_internal_dmac_complete_work_fn);
+	INIT_WORK(&host->dma_issue,
+			renesas_sdhi_internal_dmac_issue_work_fn);
 
 	/* Add pre_req and post_req */
 	host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req;
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 9cf7f9feab72..793595ad6d02 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -312,9 +312,9 @@ static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host,
 	}
 }
 
-static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv)
+static void renesas_sdhi_sys_dmac_issue_work_fn(struct work_struct *t)
 {
-	struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv;
+	struct tmio_mmc_host *host = from_work(host, t, dma_issue);
 	struct dma_chan *chan = NULL;
 
 	spin_lock_irq(&host->lock);
@@ -401,9 +401,8 @@ static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host,
 			goto ebouncebuf;
 
 		init_completion(&priv->dma_priv.dma_dataend);
-		tasklet_init(&host->dma_issue,
-			     renesas_sdhi_sys_dmac_issue_tasklet_fn,
-			     (unsigned long)host);
+		INIT_WORK(&host->dma_issue,
+				renesas_sdhi_sys_dmac_issue_work_fn);
 	}
 
 	renesas_sdhi_sys_dmac_enable_dma(host, true);
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index cb9152c6a65d..e067c7f5c537 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -107,7 +107,7 @@ static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
  * Software emulation of the SD card insertion/removal. Set insert=1 for insert
  * and insert=0 for removal. The card detection is done by GPIO. For Broadcom
  * IP to function properly the bit 0 of CORESTAT register needs to be set/reset
- * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
+* to generate the CD IRQ handled in sdhci.c
  */
 static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
 {
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index b5a2f2f25ad9..aea14bf3e2e8 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -13,6 +13,7 @@
 #include <linux/highmem.h>
 #include <linux/scatterlist.h>
 #include <linux/module.h>
+#include <linux/workqueue.h>
 #include <asm/io.h>
 
 #define DRIVER_NAME "tifm_sd"
@@ -97,7 +98,7 @@ struct tifm_sd {
 	unsigned int          clk_div;
 	unsigned long         timeout_jiffies;
 
-	struct tasklet_struct finish_tasklet;
+	struct work_struct    finish_bh_work;
 	struct timer_list     timer;
 	struct mmc_request    *req;
 
@@ -463,7 +464,7 @@ static void tifm_sd_check_status(struct tifm_sd *host)
 		}
 	}
 finish_request:
-	tasklet_schedule(&host->finish_tasklet);
+	queue_work(system_bh_wq, &host->finish_bh_work);
 }
 
 /* Called from interrupt handler */
@@ -723,9 +724,9 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	mmc_request_done(mmc, mrq);
 }
 
-static void tifm_sd_end_cmd(struct tasklet_struct *t)
+static void tifm_sd_end_cmd(struct work_struct *t)
 {
-	struct tifm_sd *host = from_tasklet(host, t, finish_tasklet);
+	struct tifm_sd *host = from_work(host, t, finish_bh_work);
 	struct tifm_dev *sock = host->dev;
 	struct mmc_host *mmc = tifm_get_drvdata(sock);
 	struct mmc_request *mrq;
@@ -960,7 +961,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
 	 */
 	mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
 
-	tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd);
+	INIT_WORK(&host->finish_bh_work, tifm_sd_end_cmd);
 	timer_setup(&host->timer, tifm_sd_abort, 0);
 
 	mmc->ops = &tifm_sd_ops;
@@ -999,7 +1000,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
 	writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
 	spin_unlock_irqrestore(&sock->lock, flags);
 
-	tasklet_kill(&host->finish_tasklet);
+	cancel_work_sync(&host->finish_bh_work);
 
 	spin_lock_irqsave(&sock->lock, flags);
 	if (host->req) {
@@ -1009,7 +1010,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
 		host->req->cmd->error = -ENOMEDIUM;
 		if (host->req->stop)
 			host->req->stop->error = -ENOMEDIUM;
-		tasklet_schedule(&host->finish_tasklet);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 	}
 	spin_unlock_irqrestore(&sock->lock, flags);
 	mmc_remove_host(mmc);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index de56e6534aea..63000d9c7030 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -21,6 +21,7 @@
 #include <linux/scatterlist.h>
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
+#include <linux/workqueue.h>
 
 #define CTL_SD_CMD 0x00
 #define CTL_ARG_REG 0x04
@@ -156,7 +157,7 @@ struct tmio_mmc_host {
 	bool			dma_on;
 	struct dma_chan		*chan_rx;
 	struct dma_chan		*chan_tx;
-	struct tasklet_struct	dma_issue;
+	struct work_struct	dma_issue;
 	struct scatterlist	bounce_sg;
 	u8			*bounce_buf;
 
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 93e912afd3ae..51bd2365795b 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -608,7 +608,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
 			} else {
 				tmio_mmc_disable_mmc_irqs(host,
 							  TMIO_MASK_READOP);
-				tasklet_schedule(&host->dma_issue);
+				queue_work(system_bh_wq, &host->dma_issue);
 			}
 		} else {
 			if (!host->dma_on) {
@@ -616,7 +616,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
 			} else {
 				tmio_mmc_disable_mmc_irqs(host,
 							  TMIO_MASK_WRITEOP);
-				tasklet_schedule(&host->dma_issue);
+				queue_work(system_bh_wq, &host->dma_issue);
 			}
 		}
 	} else {
diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c
index 1404989e6151..417fd13efdfd 100644
--- a/drivers/mmc/host/uniphier-sd.c
+++ b/drivers/mmc/host/uniphier-sd.c
@@ -90,9 +90,9 @@ static void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable)
 }
 
 /* external DMA engine */
-static void uniphier_sd_external_dma_issue(struct tasklet_struct *t)
+static void uniphier_sd_external_dma_issue(struct work_struct *t)
 {
-	struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
+	struct tmio_mmc_host *host = from_work(host, t, dma_issue);
 	struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
 
 	uniphier_sd_dma_endisable(host, 1);
@@ -199,7 +199,7 @@ static void uniphier_sd_external_dma_request(struct tmio_mmc_host *host,
 	host->chan_rx = chan;
 	host->chan_tx = chan;
 
-	tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue);
+	INIT_WORK(&host->dma_issue, uniphier_sd_external_dma_issue);
 }
 
 static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host)
@@ -236,9 +236,9 @@ static const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = {
 	.dataend = uniphier_sd_external_dma_dataend,
 };
 
-static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t)
+static void uniphier_sd_internal_dma_issue(struct work_struct *t)
 {
-	struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
+	struct tmio_mmc_host *host = from_work(host, t, dma_issue);
 	unsigned long flags;
 
 	spin_lock_irqsave(&host->lock, flags);
@@ -317,7 +317,7 @@ static void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host,
 
 	host->chan_tx = (void *)0xdeadbeaf;
 
-	tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue);
+	INIT_WORK(&host->dma_issue, uniphier_sd_internal_dma_issue);
 }
 
 static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host)
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index ba6044b16e07..f77457105ec3 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -12,6 +12,7 @@
 #include <linux/interrupt.h>
 
 #include <linux/mmc/host.h>
+#include <linux/workqueue.h>
 
 #define DRV_NAME	"via_sdmmc"
 
@@ -307,7 +308,7 @@ struct via_crdr_mmc_host {
 	struct sdhcreg pm_sdhc_reg;
 
 	struct work_struct carddet_work;
-	struct tasklet_struct finish_tasklet;
+	struct work_struct finish_bh_work;
 
 	struct timer_list timer;
 	spinlock_t lock;
@@ -643,7 +644,7 @@ static void via_sdc_finish_data(struct via_crdr_mmc_host *host)
 	if (data->stop)
 		via_sdc_send_command(host, data->stop);
 	else
-		tasklet_schedule(&host->finish_tasklet);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 }
 
 static void via_sdc_finish_command(struct via_crdr_mmc_host *host)
@@ -653,7 +654,7 @@ static void via_sdc_finish_command(struct via_crdr_mmc_host *host)
 	host->cmd->error = 0;
 
 	if (!host->cmd->data)
-		tasklet_schedule(&host->finish_tasklet);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 
 	host->cmd = NULL;
 }
@@ -682,7 +683,7 @@ static void via_sdc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	status = readw(host->sdhc_mmiobase + VIA_CRDR_SDSTATUS);
 	if (!(status & VIA_CRDR_SDSTS_SLOTG) || host->reject) {
 		host->mrq->cmd->error = -ENOMEDIUM;
-		tasklet_schedule(&host->finish_tasklet);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 	} else {
 		via_sdc_send_command(host, mrq->cmd);
 	}
@@ -848,7 +849,7 @@ static void via_sdc_cmd_isr(struct via_crdr_mmc_host *host, u16 intmask)
 		host->cmd->error = -EILSEQ;
 
 	if (host->cmd->error)
-		tasklet_schedule(&host->finish_tasklet);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 	else if (intmask & VIA_CRDR_SDSTS_CRD)
 		via_sdc_finish_command(host);
 }
@@ -955,16 +956,16 @@ static void via_sdc_timeout(struct timer_list *t)
 				sdhost->cmd->error = -ETIMEDOUT;
 			else
 				sdhost->mrq->cmd->error = -ETIMEDOUT;
-			tasklet_schedule(&sdhost->finish_tasklet);
+			queue_work(system_bh_wq, &sdhost->finish_bh_work);
 		}
 	}
 
 	spin_unlock_irqrestore(&sdhost->lock, flags);
 }
 
-static void via_sdc_tasklet_finish(struct tasklet_struct *t)
+static void via_sdc_finish_bh_work(struct work_struct *t)
 {
-	struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet);
+	struct via_crdr_mmc_host *host = from_work(host, t, finish_bh_work);
 	unsigned long flags;
 	struct mmc_request *mrq;
 
@@ -1005,7 +1006,7 @@ static void via_sdc_card_detect(struct work_struct *work)
 			pr_err("%s: Card removed during transfer!\n",
 			       mmc_hostname(host->mmc));
 			host->mrq->cmd->error = -ENOMEDIUM;
-			tasklet_schedule(&host->finish_tasklet);
+			queue_work(system_bh_wq, &host->finish_bh_work);
 		}
 
 		spin_unlock_irqrestore(&host->lock, flags);
@@ -1051,7 +1052,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
 
 	INIT_WORK(&host->carddet_work, via_sdc_card_detect);
 
-	tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish);
+	INIT_WORK(&host->finish_bh_work, via_sdc_finish_bh_work);
 
 	addrbase = host->sdhc_mmiobase;
 	writel(0x0, addrbase + VIA_CRDR_SDINTMASK);
@@ -1193,7 +1194,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
 		sdhost->mrq->cmd->error = -ENOMEDIUM;
 		if (sdhost->mrq->stop)
 			sdhost->mrq->stop->error = -ENOMEDIUM;
-		tasklet_schedule(&sdhost->finish_tasklet);
+		queue_work(system_bh_wq, &sdhost->finish_bh_work);
 	}
 	spin_unlock_irqrestore(&sdhost->lock, flags);
 
@@ -1203,7 +1204,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
 
 	del_timer_sync(&sdhost->timer);
 
-	tasklet_kill(&sdhost->finish_tasklet);
+	cancel_work_sync(&sdhost->finish_bh_work);
 
 	/* switch off power */
 	gatt = readb(sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT);
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index f0562f712d98..6e20405d0430 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -459,7 +459,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
 	 * FIFO threshold interrupts properly.
 	 */
 	if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
-		tasklet_schedule(&host->fifo_tasklet);
+		queue_work(system_bh_wq, &host->fifo_bh_work);
 }
 
 static void wbsd_fill_fifo(struct wbsd_host *host)
@@ -524,7 +524,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
 	 * 'FIFO empty' under certain conditions. So we
 	 * need to be a bit more pro-active.
 	 */
-	tasklet_schedule(&host->fifo_tasklet);
+	queue_work(system_bh_wq, &host->fifo_bh_work);
 }
 
 static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
@@ -746,7 +746,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	struct mmc_command *cmd;
 
 	/*
-	 * Disable tasklets to avoid a deadlock.
+	 * Disable bh works to avoid a deadlock.
 	 */
 	spin_lock_bh(&host->lock);
 
@@ -821,7 +821,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
 		 * Dirty fix for hardware bug.
 		 */
 		if (host->dma == -1)
-			tasklet_schedule(&host->fifo_tasklet);
+			queue_work(system_bh_wq, &host->fifo_bh_work);
 
 		spin_unlock_bh(&host->lock);
 
@@ -961,13 +961,13 @@ static void wbsd_reset_ignore(struct timer_list *t)
 	 * Card status might have changed during the
 	 * blackout.
 	 */
-	tasklet_schedule(&host->card_tasklet);
+	queue_work(system_bh_wq, &host->card_bh_work);
 
 	spin_unlock_bh(&host->lock);
 }
 
 /*
- * Tasklets
+ * BH Works
  */
 
 static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
@@ -987,9 +987,9 @@ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
 	return host->mrq->cmd->data;
 }
 
-static void wbsd_tasklet_card(struct tasklet_struct *t)
+static void wbsd_card_bh_work(struct work_struct *t)
 {
-	struct wbsd_host *host = from_tasklet(host, t, card_tasklet);
+	struct wbsd_host *host = from_work(host, t, card_bh_work);
 	u8 csr;
 	int delay = -1;
 
@@ -1020,7 +1020,7 @@ static void wbsd_tasklet_card(struct tasklet_struct *t)
 			wbsd_reset(host);
 
 			host->mrq->cmd->error = -ENOMEDIUM;
-			tasklet_schedule(&host->finish_tasklet);
+			queue_work(system_bh_wq, &host->finish_bh_work);
 		}
 
 		delay = 0;
@@ -1036,9 +1036,9 @@ static void wbsd_tasklet_card(struct tasklet_struct *t)
 		mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
 }
 
-static void wbsd_tasklet_fifo(struct tasklet_struct *t)
+static void wbsd_fifo_bh_work(struct work_struct *t)
 {
-	struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet);
+	struct wbsd_host *host = from_work(host, t, fifo_bh_work);
 	struct mmc_data *data;
 
 	spin_lock(&host->lock);
@@ -1060,16 +1060,16 @@ static void wbsd_tasklet_fifo(struct tasklet_struct *t)
 	 */
 	if (host->num_sg == 0) {
 		wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
-		tasklet_schedule(&host->finish_tasklet);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 	}
 
 end:
 	spin_unlock(&host->lock);
 }
 
-static void wbsd_tasklet_crc(struct tasklet_struct *t)
+static void wbsd_crc_bh_work(struct work_struct *t)
 {
-	struct wbsd_host *host = from_tasklet(host, t, crc_tasklet);
+	struct wbsd_host *host = from_work(host, t, crc_bh_work);
 	struct mmc_data *data;
 
 	spin_lock(&host->lock);
@@ -1085,15 +1085,15 @@ static void wbsd_tasklet_crc(struct tasklet_struct *t)
 
 	data->error = -EILSEQ;
 
-	tasklet_schedule(&host->finish_tasklet);
+	queue_work(system_bh_wq, &host->finish_bh_work);
 
 end:
 	spin_unlock(&host->lock);
 }
 
-static void wbsd_tasklet_timeout(struct tasklet_struct *t)
+static void wbsd_timeout_bh_work(struct work_struct *t)
 {
-	struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet);
+	struct wbsd_host *host = from_work(host, t, timeout_bh_work);
 	struct mmc_data *data;
 
 	spin_lock(&host->lock);
@@ -1109,15 +1109,15 @@ static void wbsd_tasklet_timeout(struct tasklet_struct *t)
 
 	data->error = -ETIMEDOUT;
 
-	tasklet_schedule(&host->finish_tasklet);
+	queue_work(system_bh_wq, &host->finish_bh_work);
 
 end:
 	spin_unlock(&host->lock);
 }
 
-static void wbsd_tasklet_finish(struct tasklet_struct *t)
+static void wbsd_finish_bh_work(struct work_struct *t)
 {
-	struct wbsd_host *host = from_tasklet(host, t, finish_tasklet);
+	struct wbsd_host *host = from_work(host, t, finish_bh_work);
 	struct mmc_data *data;
 
 	spin_lock(&host->lock);
@@ -1156,18 +1156,18 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id)
 	host->isr |= isr;
 
 	/*
-	 * Schedule tasklets as needed.
+	 * Schedule bh work as needed.
 	 */
 	if (isr & WBSD_INT_CARD)
-		tasklet_schedule(&host->card_tasklet);
+		queue_work(system_bh_wq, &host->card_bh_work);
 	if (isr & WBSD_INT_FIFO_THRE)
-		tasklet_schedule(&host->fifo_tasklet);
+		queue_work(system_bh_wq, &host->fifo_bh_work);
 	if (isr & WBSD_INT_CRC)
-		tasklet_hi_schedule(&host->crc_tasklet);
+		queue_work(system_bh_highpri_wq, &host->crc_bh_work);
 	if (isr & WBSD_INT_TIMEOUT)
-		tasklet_hi_schedule(&host->timeout_tasklet);
+		queue_work(system_bh_highpri_wq, &host->timeout_bh_work);
 	if (isr & WBSD_INT_TC)
-		tasklet_schedule(&host->finish_tasklet);
+		queue_work(system_bh_wq, &host->finish_bh_work);
 
 	return IRQ_HANDLED;
 }
@@ -1443,13 +1443,13 @@ static int wbsd_request_irq(struct wbsd_host *host, int irq)
 	int ret;
 
 	/*
-	 * Set up tasklets. Must be done before requesting interrupt.
+	 * Set up bh works. Must be done before requesting interrupt.
 	 */
-	tasklet_setup(&host->card_tasklet, wbsd_tasklet_card);
-	tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo);
-	tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc);
-	tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout);
-	tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish);
+	INIT_WORK(&host->card_bh_work, wbsd_card_bh_work);
+	INIT_WORK(&host->fifo_bh_work, wbsd_fifo_bh_work);
+	INIT_WORK(&host->crc_bh_work, wbsd_crc_bh_work);
+	INIT_WORK(&host->timeout_bh_work, wbsd_timeout_bh_work);
+	INIT_WORK(&host->finish_bh_work, wbsd_finish_bh_work);
 
 	/*
 	 * Allocate interrupt.
@@ -1472,11 +1472,11 @@ static void  wbsd_release_irq(struct wbsd_host *host)
 
 	host->irq = 0;
 
-	tasklet_kill(&host->card_tasklet);
-	tasklet_kill(&host->fifo_tasklet);
-	tasklet_kill(&host->crc_tasklet);
-	tasklet_kill(&host->timeout_tasklet);
-	tasklet_kill(&host->finish_tasklet);
+	cancel_work_sync(&host->card_bh_work);
+	cancel_work_sync(&host->fifo_bh_work);
+	cancel_work_sync(&host->crc_bh_work);
+	cancel_work_sync(&host->timeout_bh_work);
+	cancel_work_sync(&host->finish_bh_work);
 }
 
 /*
diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h
index be30b4d8ce4c..970886831305 100644
--- a/drivers/mmc/host/wbsd.h
+++ b/drivers/mmc/host/wbsd.h
@@ -171,11 +171,11 @@ struct wbsd_host
 	int			irq;		/* Interrupt */
 	int			dma;		/* DMA channel */
 
-	struct tasklet_struct	card_tasklet;	/* Tasklet structures */
-	struct tasklet_struct	fifo_tasklet;
-	struct tasklet_struct	crc_tasklet;
-	struct tasklet_struct	timeout_tasklet;
-	struct tasklet_struct	finish_tasklet;
+	struct work_struct	card_bh_work;	/* Work structures */
+	struct work_struct	fifo_bh_work;
+	struct work_struct	crc_bh_work;
+	struct work_struct	timeout_bh_work;
+	struct work_struct	finish_bh_work;
 
 	struct timer_list	ignore_timer;	/* Ignore detection timer */
 };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-18 22:52 [PATCH v3] mmc: Convert from tasklet to BH workqueue Allen Pais
@ 2024-06-20 15:14 ` Ulf Hansson
  2024-06-21  9:53 ` Wolfram Sang
  1 sibling, 0 replies; 10+ messages in thread
From: Ulf Hansson @ 2024-06-20 15:14 UTC (permalink / raw)
  To: Allen Pais
  Cc: Aubin Constans, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Manuel Lauss, Michał Mirosław, Jaehoon Chung,
	Aaro Koskinen, Wolfram Sang, Adrian Hunter, Florian Fainelli,
	Ray Jui, Scott Branden, Broadcom internal kernel review list,
	Alex Dubov, Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang,
	Harald Welte, Pierre Ossman, Christian Loehle, linux-mmc,
	linux-arm-kernel, linux-kernel, linux-omap, linux-renesas-soc

On Wed, 19 Jun 2024 at 00:52, Allen Pais <allen.lkml@gmail.com> wrote:
>
> The only generic interface to execute asynchronously in the BH context is
> tasklet; however, it's marked deprecated and has some design flaws. To
> replace tasklets, BH workqueue support was recently added. A BH workqueue
> behaves similarly to regular workqueues except that the queued work items
> are executed in the BH context.
>
> This patch converts drivers/mmc/* from tasklet to BH workqueue.
>
> Based on the work done by Tejun Heo <tj@kernel.org>
>
> Tested-by: Christian Loehle <christian.loehle@arm.com>
> Tested-by: Aubin Constans <aubin.constans@microchip.com>
> Acked-by: Aubin Constans <aubin.constans@microchip.com>
> Acked-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
> Reviewed-by: Christian Loehle <christian.loehle@arm.com>
> Signed-off-by: Allen Pais <allen.lkml@gmail.com>

Applied for next to get it tested (waiting for Adrian's ack/nack in
the meantime), thanks!

Kind regards
Uffe


> ---
> v3:
>    - fixed a comment - [Adrian Hunter]
> v2:
>    - fixed patch styling issues
>    - rename work to bh_work
>
> Link to v1:
> https://lore.kernel.org/all/20240327160314.9982-10-apais@linux.microsoft.com/
>
>  drivers/mmc/host/atmel-mci.c                  | 35 ++++-----
>  drivers/mmc/host/au1xmmc.c                    | 37 +++++-----
>  drivers/mmc/host/cb710-mmc.c                  | 14 ++--
>  drivers/mmc/host/cb710-mmc.h                  |  3 +-
>  drivers/mmc/host/dw_mmc.c                     | 24 +++---
>  drivers/mmc/host/dw_mmc.h                     |  9 ++-
>  drivers/mmc/host/omap.c                       | 17 +++--
>  drivers/mmc/host/renesas_sdhi.h               |  3 +-
>  drivers/mmc/host/renesas_sdhi_internal_dmac.c | 24 +++---
>  drivers/mmc/host/renesas_sdhi_sys_dmac.c      |  9 +--
>  drivers/mmc/host/sdhci-bcm-kona.c             |  2 +-
>  drivers/mmc/host/tifm_sd.c                    | 15 ++--
>  drivers/mmc/host/tmio_mmc.h                   |  3 +-
>  drivers/mmc/host/tmio_mmc_core.c              |  4 +-
>  drivers/mmc/host/uniphier-sd.c                | 12 +--
>  drivers/mmc/host/via-sdmmc.c                  | 25 ++++---
>  drivers/mmc/host/wbsd.c                       | 74 +++++++++----------
>  drivers/mmc/host/wbsd.h                       | 10 +--
>  18 files changed, 163 insertions(+), 157 deletions(-)
>
> diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
> index 8199d9620075..6490df54a6f5 100644
> --- a/drivers/mmc/host/atmel-mci.c
> +++ b/drivers/mmc/host/atmel-mci.c
> @@ -33,6 +33,7 @@
>  #include <linux/pm.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/pinctrl/consumer.h>
> +#include <linux/workqueue.h>
>
>  #include <asm/cacheflush.h>
>  #include <asm/io.h>
> @@ -272,12 +273,12 @@ struct atmel_mci_dma {
>   *     EVENT_DATA_ERROR is pending.
>   * @stop_cmdr: Value to be loaded into CMDR when the stop command is
>   *     to be sent.
> - * @tasklet: Tasklet running the request state machine.
> + * @bh_work: Work running the request state machine.
>   * @pending_events: Bitmask of events flagged by the interrupt handler
> - *     to be processed by the tasklet.
> + *     to be processed by the work.
>   * @completed_events: Bitmask of events which the state machine has
>   *     processed.
> - * @state: Tasklet state.
> + * @state: Work state.
>   * @queue: List of slots waiting for access to the controller.
>   * @need_clock_update: Update the clock rate before the next request.
>   * @need_reset: Reset controller before next request.
> @@ -352,7 +353,7 @@ struct atmel_mci {
>         u32                     data_status;
>         u32                     stop_cmdr;
>
> -       struct tasklet_struct   tasklet;
> +       struct work_struct      bh_work;
>         unsigned long           pending_events;
>         unsigned long           completed_events;
>         enum atmel_mci_state    state;
> @@ -735,7 +736,7 @@ static void atmci_timeout_timer(struct timer_list *t)
>         host->need_reset = 1;
>         host->state = STATE_END_REQUEST;
>         smp_wmb();
> -       tasklet_schedule(&host->tasklet);
> +       queue_work(system_bh_wq, &host->bh_work);
>  }
>
>  static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
> @@ -958,7 +959,7 @@ static void atmci_pdc_complete(struct atmel_mci *host)
>
>         dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
>         atmci_set_pending(host, EVENT_XFER_COMPLETE);
> -       tasklet_schedule(&host->tasklet);
> +       queue_work(system_bh_wq, &host->bh_work);
>  }
>
>  static void atmci_dma_cleanup(struct atmel_mci *host)
> @@ -972,7 +973,7 @@ static void atmci_dma_cleanup(struct atmel_mci *host)
>  }
>
>  /*
> - * This function is called by the DMA driver from tasklet context.
> + * This function is called by the DMA driver from bh context.
>   */
>  static void atmci_dma_complete(void *arg)
>  {
> @@ -995,7 +996,7 @@ static void atmci_dma_complete(void *arg)
>         if (data) {
>                 dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
>                 atmci_set_pending(host, EVENT_XFER_COMPLETE);
> -               tasklet_schedule(&host->tasklet);
> +               queue_work(system_bh_wq, &host->bh_work);
>
>                 /*
>                  * Regardless of what the documentation says, we have
> @@ -1008,7 +1009,7 @@ static void atmci_dma_complete(void *arg)
>                  * haven't seen all the potential error bits yet.
>                  *
>                  * The interrupt handler will schedule a different
> -                * tasklet to finish things up when the data transfer
> +                * bh work to finish things up when the data transfer
>                  * is completely done.
>                  *
>                  * We may not complete the mmc request here anyway
> @@ -1745,9 +1746,9 @@ static void atmci_detect_change(struct timer_list *t)
>         }
>  }
>
> -static void atmci_tasklet_func(struct tasklet_struct *t)
> +static void atmci_work_func(struct work_struct *t)
>  {
> -       struct atmel_mci        *host = from_tasklet(host, t, tasklet);
> +       struct atmel_mci        *host = from_work(host, t, bh_work);
>         struct mmc_request      *mrq = host->mrq;
>         struct mmc_data         *data = host->data;
>         struct device           *dev = host->dev;
> @@ -1759,7 +1760,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
>
>         state = host->state;
>
> -       dev_vdbg(dev, "tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
> +       dev_vdbg(dev, "bh_work: state %u pending/completed/mask %lx/%lx/%x\n",
>                 state, host->pending_events, host->completed_events,
>                 atmci_readl(host, ATMCI_IMR));
>
> @@ -2118,7 +2119,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
>                         dev_dbg(dev, "set pending data error\n");
>                         smp_wmb();
>                         atmci_set_pending(host, EVENT_DATA_ERROR);
> -                       tasklet_schedule(&host->tasklet);
> +                       queue_work(system_bh_wq, &host->bh_work);
>                 }
>
>                 if (pending & ATMCI_TXBUFE) {
> @@ -2187,7 +2188,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
>                         smp_wmb();
>                         dev_dbg(dev, "set pending notbusy\n");
>                         atmci_set_pending(host, EVENT_NOTBUSY);
> -                       tasklet_schedule(&host->tasklet);
> +                       queue_work(system_bh_wq, &host->bh_work);
>                 }
>
>                 if (pending & ATMCI_NOTBUSY) {
> @@ -2196,7 +2197,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
>                         smp_wmb();
>                         dev_dbg(dev, "set pending notbusy\n");
>                         atmci_set_pending(host, EVENT_NOTBUSY);
> -                       tasklet_schedule(&host->tasklet);
> +                       queue_work(system_bh_wq, &host->bh_work);
>                 }
>
>                 if (pending & ATMCI_RXRDY)
> @@ -2211,7 +2212,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
>                         smp_wmb();
>                         dev_dbg(dev, "set pending cmd rdy\n");
>                         atmci_set_pending(host, EVENT_CMD_RDY);
> -                       tasklet_schedule(&host->tasklet);
> +                       queue_work(system_bh_wq, &host->bh_work);
>                 }
>
>                 if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
> @@ -2487,7 +2488,7 @@ static int atmci_probe(struct platform_device *pdev)
>
>         host->mapbase = regs->start;
>
> -       tasklet_setup(&host->tasklet, atmci_tasklet_func);
> +       INIT_WORK(&host->bh_work, atmci_work_func);
>
>         ret = request_irq(irq, atmci_interrupt, 0, dev_name(dev), host);
>         if (ret) {
> diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
> index b5a5c6a2fe8b..6e80bcb668ec 100644
> --- a/drivers/mmc/host/au1xmmc.c
> +++ b/drivers/mmc/host/au1xmmc.c
> @@ -42,6 +42,7 @@
>  #include <linux/leds.h>
>  #include <linux/mmc/host.h>
>  #include <linux/slab.h>
> +#include <linux/workqueue.h>
>
>  #include <asm/io.h>
>  #include <asm/mach-au1x00/au1000.h>
> @@ -113,8 +114,8 @@ struct au1xmmc_host {
>
>         int irq;
>
> -       struct tasklet_struct finish_task;
> -       struct tasklet_struct data_task;
> +       struct work_struct finish_bh_work;
> +       struct work_struct data_bh_work;
>         struct au1xmmc_platform_data *platdata;
>         struct platform_device *pdev;
>         struct resource *ioarea;
> @@ -253,9 +254,9 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host)
>         mmc_request_done(host->mmc, mrq);
>  }
>
> -static void au1xmmc_tasklet_finish(struct tasklet_struct *t)
> +static void au1xmmc_finish_bh_work(struct work_struct *t)
>  {
> -       struct au1xmmc_host *host = from_tasklet(host, t, finish_task);
> +       struct au1xmmc_host *host = from_work(host, t, finish_bh_work);
>         au1xmmc_finish_request(host);
>  }
>
> @@ -363,9 +364,9 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
>         au1xmmc_finish_request(host);
>  }
>
> -static void au1xmmc_tasklet_data(struct tasklet_struct *t)
> +static void au1xmmc_data_bh_work(struct work_struct *t)
>  {
> -       struct au1xmmc_host *host = from_tasklet(host, t, data_task);
> +       struct au1xmmc_host *host = from_work(host, t, data_bh_work);
>
>         u32 status = __raw_readl(HOST_STATUS(host));
>         au1xmmc_data_complete(host, status);
> @@ -425,7 +426,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
>                 if (host->flags & HOST_F_STOP)
>                         SEND_STOP(host);
>
> -               tasklet_schedule(&host->data_task);
> +               queue_work(system_bh_wq, &host->data_bh_work);
>         }
>  }
>
> @@ -505,7 +506,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
>                 if (host->flags & HOST_F_STOP)
>                         SEND_STOP(host);
>
> -               tasklet_schedule(&host->data_task);
> +               queue_work(system_bh_wq, &host->data_bh_work);
>         }
>  }
>
> @@ -561,7 +562,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
>
>         if (!trans || cmd->error) {
>                 IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF);
> -               tasklet_schedule(&host->finish_task);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>                 return;
>         }
>
> @@ -797,7 +798,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
>                 IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
>
>                 /* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */
> -               tasklet_schedule(&host->finish_task);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>         }
>  #if 0
>         else if (status & SD_STATUS_DD) {
> @@ -806,7 +807,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
>                         au1xmmc_receive_pio(host);
>                 else {
>                         au1xmmc_data_complete(host, status);
> -                       /* tasklet_schedule(&host->data_task); */
> +                       /* queue_work(system_bh_wq, &host->data_bh_work); */
>                 }
>         }
>  #endif
> @@ -854,7 +855,7 @@ static void au1xmmc_dbdma_callback(int irq, void *dev_id)
>         if (host->flags & HOST_F_STOP)
>                 SEND_STOP(host);
>
> -       tasklet_schedule(&host->data_task);
> +       queue_work(system_bh_wq, &host->data_bh_work);
>  }
>
>  static int au1xmmc_dbdma_init(struct au1xmmc_host *host)
> @@ -1039,9 +1040,9 @@ static int au1xmmc_probe(struct platform_device *pdev)
>         if (host->platdata)
>                 mmc->caps &= ~(host->platdata->mask_host_caps);
>
> -       tasklet_setup(&host->data_task, au1xmmc_tasklet_data);
> +       INIT_WORK(&host->data_bh_work, au1xmmc_data_bh_work);
>
> -       tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish);
> +       INIT_WORK(&host->finish_bh_work, au1xmmc_finish_bh_work);
>
>         if (has_dbdma()) {
>                 ret = au1xmmc_dbdma_init(host);
> @@ -1091,8 +1092,8 @@ static int au1xmmc_probe(struct platform_device *pdev)
>         if (host->flags & HOST_F_DBDMA)
>                 au1xmmc_dbdma_shutdown(host);
>
> -       tasklet_kill(&host->data_task);
> -       tasklet_kill(&host->finish_task);
> +       cancel_work_sync(&host->data_bh_work);
> +       cancel_work_sync(&host->finish_bh_work);
>
>         if (host->platdata && host->platdata->cd_setup &&
>             !(mmc->caps & MMC_CAP_NEEDS_POLL))
> @@ -1135,8 +1136,8 @@ static void au1xmmc_remove(struct platform_device *pdev)
>                 __raw_writel(0, HOST_CONFIG2(host));
>                 wmb(); /* drain writebuffer */
>
> -               tasklet_kill(&host->data_task);
> -               tasklet_kill(&host->finish_task);
> +               cancel_work_sync(&host->data_bh_work);
> +               cancel_work_sync(&host->finish_bh_work);
>
>                 if (host->flags & HOST_F_DBDMA)
>                         au1xmmc_dbdma_shutdown(host);
> diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
> index 0aec33b88bef..902f7f20abaa 100644
> --- a/drivers/mmc/host/cb710-mmc.c
> +++ b/drivers/mmc/host/cb710-mmc.c
> @@ -493,7 +493,7 @@ static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
>         if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop)
>                 cb710_mmc_command(mmc, mrq->stop);
>
> -       tasklet_schedule(&reader->finish_req_tasklet);
> +       queue_work(system_bh_wq, &reader->finish_req_bh_work);
>  }
>
>  static int cb710_mmc_powerup(struct cb710_slot *slot)
> @@ -646,10 +646,10 @@ static int cb710_mmc_irq_handler(struct cb710_slot *slot)
>         return 1;
>  }
>
> -static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t)
> +static void cb710_mmc_finish_request_bh_work(struct work_struct *t)
>  {
> -       struct cb710_mmc_reader *reader = from_tasklet(reader, t,
> -                                                      finish_req_tasklet);
> +       struct cb710_mmc_reader *reader = from_work(reader, t,
> +                                                      finish_req_bh_work);
>         struct mmc_request *mrq = reader->mrq;
>
>         reader->mrq = NULL;
> @@ -718,8 +718,8 @@ static int cb710_mmc_init(struct platform_device *pdev)
>
>         reader = mmc_priv(mmc);
>
> -       tasklet_setup(&reader->finish_req_tasklet,
> -                     cb710_mmc_finish_request_tasklet);
> +       INIT_WORK(&reader->finish_req_bh_work,
> +                       cb710_mmc_finish_request_bh_work);
>         spin_lock_init(&reader->irq_lock);
>         cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
>
> @@ -763,7 +763,7 @@ static void cb710_mmc_exit(struct platform_device *pdev)
>         cb710_write_port_32(slot, CB710_MMC_CONFIG_PORT, 0);
>         cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0);
>
> -       tasklet_kill(&reader->finish_req_tasklet);
> +       cancel_work_sync(&reader->finish_req_bh_work);
>
>         mmc_free_host(mmc);
>  }
> diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h
> index 5e053077dbed..59abaccaad10 100644
> --- a/drivers/mmc/host/cb710-mmc.h
> +++ b/drivers/mmc/host/cb710-mmc.h
> @@ -8,10 +8,11 @@
>  #define LINUX_CB710_MMC_H
>
>  #include <linux/cb710.h>
> +#include <linux/workqueue.h>
>
>  /* per-MMC-reader structure */
>  struct cb710_mmc_reader {
> -       struct tasklet_struct finish_req_tasklet;
> +       struct work_struct finish_req_bh_work;
>         struct mmc_request *mrq;
>         spinlock_t irq_lock;
>         unsigned char last_power_mode;
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index 8e2d676b9239..7195de2cde44 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -493,7 +493,7 @@ static void dw_mci_dmac_complete_dma(void *arg)
>          */
>         if (data) {
>                 set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> -               tasklet_schedule(&host->tasklet);
> +               queue_work(system_bh_wq, &host->bh_work);
>         }
>  }
>
> @@ -1834,7 +1834,7 @@ static enum hrtimer_restart dw_mci_fault_timer(struct hrtimer *t)
>         if (!host->data_status) {
>                 host->data_status = SDMMC_INT_DCRC;
>                 set_bit(EVENT_DATA_ERROR, &host->pending_events);
> -               tasklet_schedule(&host->tasklet);
> +               queue_work(system_bh_wq, &host->bh_work);
>         }
>
>         spin_unlock_irqrestore(&host->irq_lock, flags);
> @@ -2056,9 +2056,9 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
>         return true;
>  }
>
> -static void dw_mci_tasklet_func(struct tasklet_struct *t)
> +static void dw_mci_work_func(struct work_struct *t)
>  {
> -       struct dw_mci *host = from_tasklet(host, t, tasklet);
> +       struct dw_mci *host = from_work(host, t, bh_work);
>         struct mmc_data *data;
>         struct mmc_command *cmd;
>         struct mmc_request *mrq;
> @@ -2113,7 +2113,7 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t)
>                                  * will waste a bit of time (we already know
>                                  * the command was bad), it can't cause any
>                                  * errors since it's possible it would have
> -                                * taken place anyway if this tasklet got
> +                                * taken place anyway if this bh work got
>                                  * delayed. Allowing the transfer to take place
>                                  * avoids races and keeps things simple.
>                                  */
> @@ -2706,7 +2706,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
>         smp_wmb(); /* drain writebuffer */
>
>         set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
> -       tasklet_schedule(&host->tasklet);
> +       queue_work(system_bh_wq, &host->bh_work);
>
>         dw_mci_start_fault_timer(host);
>  }
> @@ -2774,7 +2774,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
>                                 set_bit(EVENT_DATA_COMPLETE,
>                                         &host->pending_events);
>
> -                       tasklet_schedule(&host->tasklet);
> +                       queue_work(system_bh_wq, &host->bh_work);
>
>                         spin_unlock(&host->irq_lock);
>                 }
> @@ -2793,7 +2793,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
>                                         dw_mci_read_data_pio(host, true);
>                         }
>                         set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
> -                       tasklet_schedule(&host->tasklet);
> +                       queue_work(system_bh_wq, &host->bh_work);
>
>                         spin_unlock(&host->irq_lock);
>                 }
> @@ -3098,7 +3098,7 @@ static void dw_mci_cmd11_timer(struct timer_list *t)
>
>         host->cmd_status = SDMMC_INT_RTO;
>         set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
> -       tasklet_schedule(&host->tasklet);
> +       queue_work(system_bh_wq, &host->bh_work);
>  }
>
>  static void dw_mci_cto_timer(struct timer_list *t)
> @@ -3144,7 +3144,7 @@ static void dw_mci_cto_timer(struct timer_list *t)
>                  */
>                 host->cmd_status = SDMMC_INT_RTO;
>                 set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
> -               tasklet_schedule(&host->tasklet);
> +               queue_work(system_bh_wq, &host->bh_work);
>                 break;
>         default:
>                 dev_warn(host->dev, "Unexpected command timeout, state %d\n",
> @@ -3195,7 +3195,7 @@ static void dw_mci_dto_timer(struct timer_list *t)
>                 host->data_status = SDMMC_INT_DRTO;
>                 set_bit(EVENT_DATA_ERROR, &host->pending_events);
>                 set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
> -               tasklet_schedule(&host->tasklet);
> +               queue_work(system_bh_wq, &host->bh_work);
>                 break;
>         default:
>                 dev_warn(host->dev, "Unexpected data timeout, state %d\n",
> @@ -3435,7 +3435,7 @@ int dw_mci_probe(struct dw_mci *host)
>         else
>                 host->fifo_reg = host->regs + DATA_240A_OFFSET;
>
> -       tasklet_setup(&host->tasklet, dw_mci_tasklet_func);
> +       INIT_WORK(&host->bh_work, dw_mci_work_func);
>         ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
>                                host->irq_flags, "dw-mci", host);
>         if (ret)
> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
> index 4ed81f94f7ca..b504f7ae513a 100644
> --- a/drivers/mmc/host/dw_mmc.h
> +++ b/drivers/mmc/host/dw_mmc.h
> @@ -17,6 +17,7 @@
>  #include <linux/fault-inject.h>
>  #include <linux/hrtimer.h>
>  #include <linux/interrupt.h>
> +#include <linux/workqueue.h>
>
>  enum dw_mci_state {
>         STATE_IDLE = 0,
> @@ -89,12 +90,12 @@ struct dw_mci_dma_slave {
>   * @stop_cmdr: Value to be loaded into CMDR when the stop command is
>   *     to be sent.
>   * @dir_status: Direction of current transfer.
> - * @tasklet: Tasklet running the request state machine.
> + * @bh_work: Work running the request state machine.
>   * @pending_events: Bitmask of events flagged by the interrupt handler
> - *     to be processed by the tasklet.
> + *     to be processed by bh work.
>   * @completed_events: Bitmask of events which the state machine has
>   *     processed.
> - * @state: Tasklet state.
> + * @state: BH work state.
>   * @queue: List of slots waiting for access to the controller.
>   * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
>   *     rate and timeout calculations.
> @@ -194,7 +195,7 @@ struct dw_mci {
>         u32                     data_status;
>         u32                     stop_cmdr;
>         u32                     dir_status;
> -       struct tasklet_struct   tasklet;
> +       struct work_struct      bh_work;
>         unsigned long           pending_events;
>         unsigned long           completed_events;
>         enum dw_mci_state       state;
> diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
> index a8ee0df47148..335350a4e99a 100644
> --- a/drivers/mmc/host/omap.c
> +++ b/drivers/mmc/host/omap.c
> @@ -28,6 +28,7 @@
>  #include <linux/slab.h>
>  #include <linux/gpio/consumer.h>
>  #include <linux/platform_data/mmc-omap.h>
> +#include <linux/workqueue.h>
>
>
>  #define        OMAP_MMC_REG_CMD        0x00
> @@ -105,7 +106,7 @@ struct mmc_omap_slot {
>         u16                     power_mode;
>         unsigned int            fclk_freq;
>
> -       struct tasklet_struct   cover_tasklet;
> +       struct work_struct      cover_bh_work;
>         struct timer_list       cover_timer;
>         unsigned                cover_open;
>
> @@ -873,18 +874,18 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
>                 sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
>         }
>
> -       tasklet_hi_schedule(&slot->cover_tasklet);
> +       queue_work(system_bh_highpri_wq, &slot->cover_bh_work);
>  }
>
>  static void mmc_omap_cover_timer(struct timer_list *t)
>  {
>         struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer);
> -       tasklet_schedule(&slot->cover_tasklet);
> +       queue_work(system_bh_wq, &slot->cover_bh_work);
>  }
>
> -static void mmc_omap_cover_handler(struct tasklet_struct *t)
> +static void mmc_omap_cover_bh_handler(struct work_struct *t)
>  {
> -       struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet);
> +       struct mmc_omap_slot *slot = from_work(slot, t, cover_bh_work);
>         int cover_open = mmc_omap_cover_is_open(slot);
>
>         mmc_detect_change(slot->mmc, 0);
> @@ -1314,7 +1315,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
>
>         if (slot->pdata->get_cover_state != NULL) {
>                 timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0);
> -               tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler);
> +               INIT_WORK(&slot->cover_bh_work, mmc_omap_cover_bh_handler);
>         }
>
>         r = mmc_add_host(mmc);
> @@ -1333,7 +1334,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
>                                         &dev_attr_cover_switch);
>                 if (r < 0)
>                         goto err_remove_slot_name;
> -               tasklet_schedule(&slot->cover_tasklet);
> +               queue_work(system_bh_wq, &slot->cover_bh_work);
>         }
>
>         return 0;
> @@ -1356,7 +1357,7 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
>         if (slot->pdata->get_cover_state != NULL)
>                 device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
>
> -       tasklet_kill(&slot->cover_tasklet);
> +       cancel_work_sync(&slot->cover_bh_work);
>         del_timer_sync(&slot->cover_timer);
>         flush_workqueue(slot->host->mmc_omap_wq);
>
> diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
> index 586f94d4dbfd..4fd2bfcacd76 100644
> --- a/drivers/mmc/host/renesas_sdhi.h
> +++ b/drivers/mmc/host/renesas_sdhi.h
> @@ -11,6 +11,7 @@
>
>  #include <linux/dmaengine.h>
>  #include <linux/platform_device.h>
> +#include <linux/workqueue.h>
>  #include "tmio_mmc.h"
>
>  struct renesas_sdhi_scc {
> @@ -67,7 +68,7 @@ struct renesas_sdhi_dma {
>         dma_filter_fn filter;
>         void (*enable)(struct tmio_mmc_host *host, bool enable);
>         struct completion dma_dataend;
> -       struct tasklet_struct dma_complete;
> +       struct work_struct dma_complete;
>  };
>
>  struct renesas_sdhi {
> diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> index 422fa63a2e99..5fd276492f80 100644
> --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> @@ -337,7 +337,7 @@ static bool renesas_sdhi_internal_dmac_dma_irq(struct tmio_mmc_host *host)
>                 writel(status ^ dma_irqs, host->ctl + DM_CM_INFO1);
>                 set_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags);
>                 if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags))
> -                       tasklet_schedule(&dma_priv->dma_complete);
> +                       queue_work(system_bh_wq, &dma_priv->dma_complete);
>         }
>
>         return status & dma_irqs;
> @@ -352,7 +352,7 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host)
>         set_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags);
>         if (test_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags) ||
>             host->data->error)
> -               tasklet_schedule(&dma_priv->dma_complete);
> +               queue_work(system_bh_wq, &dma_priv->dma_complete);
>  }
>
>  /*
> @@ -440,9 +440,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
>         renesas_sdhi_internal_dmac_enable_dma(host, false);
>  }
>
> -static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
> +static void renesas_sdhi_internal_dmac_issue_work_fn(struct work_struct *t)
>  {
> -       struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
> +       struct tmio_mmc_host *host = from_work(host, t, dma_issue);
>         struct renesas_sdhi *priv = host_to_priv(host);
>
>         tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
> @@ -454,7 +454,7 @@ static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
>                 /* on CMD errors, simulate DMA end immediately */
>                 set_bit(SDHI_DMA_END_FLAG_DMA, &priv->dma_priv.end_flags);
>                 if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &priv->dma_priv.end_flags))
> -                       tasklet_schedule(&priv->dma_priv.dma_complete);
> +                       queue_work(system_bh_wq, &priv->dma_priv.dma_complete);
>         }
>  }
>
> @@ -484,9 +484,9 @@ static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
>         return true;
>  }
>
> -static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
> +static void renesas_sdhi_internal_dmac_complete_work_fn(struct work_struct *t)
>  {
> -       struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
> +       struct tmio_mmc_host *host = from_work(host, t, dma_complete);
>
>         spin_lock_irq(&host->lock);
>         if (!renesas_sdhi_internal_dmac_complete(host))
> @@ -544,12 +544,10 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
>         /* Each value is set to non-zero to assume "enabling" each DMA */
>         host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
>
> -       tasklet_init(&priv->dma_priv.dma_complete,
> -                    renesas_sdhi_internal_dmac_complete_tasklet_fn,
> -                    (unsigned long)host);
> -       tasklet_init(&host->dma_issue,
> -                    renesas_sdhi_internal_dmac_issue_tasklet_fn,
> -                    (unsigned long)host);
> +       INIT_WORK(&priv->dma_priv.dma_complete,
> +                       renesas_sdhi_internal_dmac_complete_work_fn);
> +       INIT_WORK(&host->dma_issue,
> +                       renesas_sdhi_internal_dmac_issue_work_fn);
>
>         /* Add pre_req and post_req */
>         host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req;
> diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
> index 9cf7f9feab72..793595ad6d02 100644
> --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
> +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
> @@ -312,9 +312,9 @@ static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host,
>         }
>  }
>
> -static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv)
> +static void renesas_sdhi_sys_dmac_issue_work_fn(struct work_struct *t)
>  {
> -       struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv;
> +       struct tmio_mmc_host *host = from_work(host, t, dma_issue);
>         struct dma_chan *chan = NULL;
>
>         spin_lock_irq(&host->lock);
> @@ -401,9 +401,8 @@ static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host,
>                         goto ebouncebuf;
>
>                 init_completion(&priv->dma_priv.dma_dataend);
> -               tasklet_init(&host->dma_issue,
> -                            renesas_sdhi_sys_dmac_issue_tasklet_fn,
> -                            (unsigned long)host);
> +               INIT_WORK(&host->dma_issue,
> +                               renesas_sdhi_sys_dmac_issue_work_fn);
>         }
>
>         renesas_sdhi_sys_dmac_enable_dma(host, true);
> diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
> index cb9152c6a65d..e067c7f5c537 100644
> --- a/drivers/mmc/host/sdhci-bcm-kona.c
> +++ b/drivers/mmc/host/sdhci-bcm-kona.c
> @@ -107,7 +107,7 @@ static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
>   * Software emulation of the SD card insertion/removal. Set insert=1 for insert
>   * and insert=0 for removal. The card detection is done by GPIO. For Broadcom
>   * IP to function properly the bit 0 of CORESTAT register needs to be set/reset
> - * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
> +* to generate the CD IRQ handled in sdhci.c
>   */
>  static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
>  {
> diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
> index b5a2f2f25ad9..aea14bf3e2e8 100644
> --- a/drivers/mmc/host/tifm_sd.c
> +++ b/drivers/mmc/host/tifm_sd.c
> @@ -13,6 +13,7 @@
>  #include <linux/highmem.h>
>  #include <linux/scatterlist.h>
>  #include <linux/module.h>
> +#include <linux/workqueue.h>
>  #include <asm/io.h>
>
>  #define DRIVER_NAME "tifm_sd"
> @@ -97,7 +98,7 @@ struct tifm_sd {
>         unsigned int          clk_div;
>         unsigned long         timeout_jiffies;
>
> -       struct tasklet_struct finish_tasklet;
> +       struct work_struct    finish_bh_work;
>         struct timer_list     timer;
>         struct mmc_request    *req;
>
> @@ -463,7 +464,7 @@ static void tifm_sd_check_status(struct tifm_sd *host)
>                 }
>         }
>  finish_request:
> -       tasklet_schedule(&host->finish_tasklet);
> +       queue_work(system_bh_wq, &host->finish_bh_work);
>  }
>
>  /* Called from interrupt handler */
> @@ -723,9 +724,9 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
>         mmc_request_done(mmc, mrq);
>  }
>
> -static void tifm_sd_end_cmd(struct tasklet_struct *t)
> +static void tifm_sd_end_cmd(struct work_struct *t)
>  {
> -       struct tifm_sd *host = from_tasklet(host, t, finish_tasklet);
> +       struct tifm_sd *host = from_work(host, t, finish_bh_work);
>         struct tifm_dev *sock = host->dev;
>         struct mmc_host *mmc = tifm_get_drvdata(sock);
>         struct mmc_request *mrq;
> @@ -960,7 +961,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
>          */
>         mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
>
> -       tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd);
> +       INIT_WORK(&host->finish_bh_work, tifm_sd_end_cmd);
>         timer_setup(&host->timer, tifm_sd_abort, 0);
>
>         mmc->ops = &tifm_sd_ops;
> @@ -999,7 +1000,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
>         writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
>         spin_unlock_irqrestore(&sock->lock, flags);
>
> -       tasklet_kill(&host->finish_tasklet);
> +       cancel_work_sync(&host->finish_bh_work);
>
>         spin_lock_irqsave(&sock->lock, flags);
>         if (host->req) {
> @@ -1009,7 +1010,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
>                 host->req->cmd->error = -ENOMEDIUM;
>                 if (host->req->stop)
>                         host->req->stop->error = -ENOMEDIUM;
> -               tasklet_schedule(&host->finish_tasklet);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>         }
>         spin_unlock_irqrestore(&sock->lock, flags);
>         mmc_remove_host(mmc);
> diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
> index de56e6534aea..63000d9c7030 100644
> --- a/drivers/mmc/host/tmio_mmc.h
> +++ b/drivers/mmc/host/tmio_mmc.h
> @@ -21,6 +21,7 @@
>  #include <linux/scatterlist.h>
>  #include <linux/spinlock.h>
>  #include <linux/interrupt.h>
> +#include <linux/workqueue.h>
>
>  #define CTL_SD_CMD 0x00
>  #define CTL_ARG_REG 0x04
> @@ -156,7 +157,7 @@ struct tmio_mmc_host {
>         bool                    dma_on;
>         struct dma_chan         *chan_rx;
>         struct dma_chan         *chan_tx;
> -       struct tasklet_struct   dma_issue;
> +       struct work_struct      dma_issue;
>         struct scatterlist      bounce_sg;
>         u8                      *bounce_buf;
>
> diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
> index 93e912afd3ae..51bd2365795b 100644
> --- a/drivers/mmc/host/tmio_mmc_core.c
> +++ b/drivers/mmc/host/tmio_mmc_core.c
> @@ -608,7 +608,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
>                         } else {
>                                 tmio_mmc_disable_mmc_irqs(host,
>                                                           TMIO_MASK_READOP);
> -                               tasklet_schedule(&host->dma_issue);
> +                               queue_work(system_bh_wq, &host->dma_issue);
>                         }
>                 } else {
>                         if (!host->dma_on) {
> @@ -616,7 +616,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
>                         } else {
>                                 tmio_mmc_disable_mmc_irqs(host,
>                                                           TMIO_MASK_WRITEOP);
> -                               tasklet_schedule(&host->dma_issue);
> +                               queue_work(system_bh_wq, &host->dma_issue);
>                         }
>                 }
>         } else {
> diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c
> index 1404989e6151..417fd13efdfd 100644
> --- a/drivers/mmc/host/uniphier-sd.c
> +++ b/drivers/mmc/host/uniphier-sd.c
> @@ -90,9 +90,9 @@ static void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable)
>  }
>
>  /* external DMA engine */
> -static void uniphier_sd_external_dma_issue(struct tasklet_struct *t)
> +static void uniphier_sd_external_dma_issue(struct work_struct *t)
>  {
> -       struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
> +       struct tmio_mmc_host *host = from_work(host, t, dma_issue);
>         struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
>
>         uniphier_sd_dma_endisable(host, 1);
> @@ -199,7 +199,7 @@ static void uniphier_sd_external_dma_request(struct tmio_mmc_host *host,
>         host->chan_rx = chan;
>         host->chan_tx = chan;
>
> -       tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue);
> +       INIT_WORK(&host->dma_issue, uniphier_sd_external_dma_issue);
>  }
>
>  static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host)
> @@ -236,9 +236,9 @@ static const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = {
>         .dataend = uniphier_sd_external_dma_dataend,
>  };
>
> -static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t)
> +static void uniphier_sd_internal_dma_issue(struct work_struct *t)
>  {
> -       struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
> +       struct tmio_mmc_host *host = from_work(host, t, dma_issue);
>         unsigned long flags;
>
>         spin_lock_irqsave(&host->lock, flags);
> @@ -317,7 +317,7 @@ static void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host,
>
>         host->chan_tx = (void *)0xdeadbeaf;
>
> -       tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue);
> +       INIT_WORK(&host->dma_issue, uniphier_sd_internal_dma_issue);
>  }
>
>  static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host)
> diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
> index ba6044b16e07..f77457105ec3 100644
> --- a/drivers/mmc/host/via-sdmmc.c
> +++ b/drivers/mmc/host/via-sdmmc.c
> @@ -12,6 +12,7 @@
>  #include <linux/interrupt.h>
>
>  #include <linux/mmc/host.h>
> +#include <linux/workqueue.h>
>
>  #define DRV_NAME       "via_sdmmc"
>
> @@ -307,7 +308,7 @@ struct via_crdr_mmc_host {
>         struct sdhcreg pm_sdhc_reg;
>
>         struct work_struct carddet_work;
> -       struct tasklet_struct finish_tasklet;
> +       struct work_struct finish_bh_work;
>
>         struct timer_list timer;
>         spinlock_t lock;
> @@ -643,7 +644,7 @@ static void via_sdc_finish_data(struct via_crdr_mmc_host *host)
>         if (data->stop)
>                 via_sdc_send_command(host, data->stop);
>         else
> -               tasklet_schedule(&host->finish_tasklet);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>  }
>
>  static void via_sdc_finish_command(struct via_crdr_mmc_host *host)
> @@ -653,7 +654,7 @@ static void via_sdc_finish_command(struct via_crdr_mmc_host *host)
>         host->cmd->error = 0;
>
>         if (!host->cmd->data)
> -               tasklet_schedule(&host->finish_tasklet);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>
>         host->cmd = NULL;
>  }
> @@ -682,7 +683,7 @@ static void via_sdc_request(struct mmc_host *mmc, struct mmc_request *mrq)
>         status = readw(host->sdhc_mmiobase + VIA_CRDR_SDSTATUS);
>         if (!(status & VIA_CRDR_SDSTS_SLOTG) || host->reject) {
>                 host->mrq->cmd->error = -ENOMEDIUM;
> -               tasklet_schedule(&host->finish_tasklet);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>         } else {
>                 via_sdc_send_command(host, mrq->cmd);
>         }
> @@ -848,7 +849,7 @@ static void via_sdc_cmd_isr(struct via_crdr_mmc_host *host, u16 intmask)
>                 host->cmd->error = -EILSEQ;
>
>         if (host->cmd->error)
> -               tasklet_schedule(&host->finish_tasklet);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>         else if (intmask & VIA_CRDR_SDSTS_CRD)
>                 via_sdc_finish_command(host);
>  }
> @@ -955,16 +956,16 @@ static void via_sdc_timeout(struct timer_list *t)
>                                 sdhost->cmd->error = -ETIMEDOUT;
>                         else
>                                 sdhost->mrq->cmd->error = -ETIMEDOUT;
> -                       tasklet_schedule(&sdhost->finish_tasklet);
> +                       queue_work(system_bh_wq, &sdhost->finish_bh_work);
>                 }
>         }
>
>         spin_unlock_irqrestore(&sdhost->lock, flags);
>  }
>
> -static void via_sdc_tasklet_finish(struct tasklet_struct *t)
> +static void via_sdc_finish_bh_work(struct work_struct *t)
>  {
> -       struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet);
> +       struct via_crdr_mmc_host *host = from_work(host, t, finish_bh_work);
>         unsigned long flags;
>         struct mmc_request *mrq;
>
> @@ -1005,7 +1006,7 @@ static void via_sdc_card_detect(struct work_struct *work)
>                         pr_err("%s: Card removed during transfer!\n",
>                                mmc_hostname(host->mmc));
>                         host->mrq->cmd->error = -ENOMEDIUM;
> -                       tasklet_schedule(&host->finish_tasklet);
> +                       queue_work(system_bh_wq, &host->finish_bh_work);
>                 }
>
>                 spin_unlock_irqrestore(&host->lock, flags);
> @@ -1051,7 +1052,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
>
>         INIT_WORK(&host->carddet_work, via_sdc_card_detect);
>
> -       tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish);
> +       INIT_WORK(&host->finish_bh_work, via_sdc_finish_bh_work);
>
>         addrbase = host->sdhc_mmiobase;
>         writel(0x0, addrbase + VIA_CRDR_SDINTMASK);
> @@ -1193,7 +1194,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
>                 sdhost->mrq->cmd->error = -ENOMEDIUM;
>                 if (sdhost->mrq->stop)
>                         sdhost->mrq->stop->error = -ENOMEDIUM;
> -               tasklet_schedule(&sdhost->finish_tasklet);
> +               queue_work(system_bh_wq, &sdhost->finish_bh_work);
>         }
>         spin_unlock_irqrestore(&sdhost->lock, flags);
>
> @@ -1203,7 +1204,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
>
>         del_timer_sync(&sdhost->timer);
>
> -       tasklet_kill(&sdhost->finish_tasklet);
> +       cancel_work_sync(&sdhost->finish_bh_work);
>
>         /* switch off power */
>         gatt = readb(sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT);
> diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
> index f0562f712d98..6e20405d0430 100644
> --- a/drivers/mmc/host/wbsd.c
> +++ b/drivers/mmc/host/wbsd.c
> @@ -459,7 +459,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
>          * FIFO threshold interrupts properly.
>          */
>         if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
> -               tasklet_schedule(&host->fifo_tasklet);
> +               queue_work(system_bh_wq, &host->fifo_bh_work);
>  }
>
>  static void wbsd_fill_fifo(struct wbsd_host *host)
> @@ -524,7 +524,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
>          * 'FIFO empty' under certain conditions. So we
>          * need to be a bit more pro-active.
>          */
> -       tasklet_schedule(&host->fifo_tasklet);
> +       queue_work(system_bh_wq, &host->fifo_bh_work);
>  }
>
>  static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
> @@ -746,7 +746,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
>         struct mmc_command *cmd;
>
>         /*
> -        * Disable tasklets to avoid a deadlock.
> +        * Disable bh works to avoid a deadlock.
>          */
>         spin_lock_bh(&host->lock);
>
> @@ -821,7 +821,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
>                  * Dirty fix for hardware bug.
>                  */
>                 if (host->dma == -1)
> -                       tasklet_schedule(&host->fifo_tasklet);
> +                       queue_work(system_bh_wq, &host->fifo_bh_work);
>
>                 spin_unlock_bh(&host->lock);
>
> @@ -961,13 +961,13 @@ static void wbsd_reset_ignore(struct timer_list *t)
>          * Card status might have changed during the
>          * blackout.
>          */
> -       tasklet_schedule(&host->card_tasklet);
> +       queue_work(system_bh_wq, &host->card_bh_work);
>
>         spin_unlock_bh(&host->lock);
>  }
>
>  /*
> - * Tasklets
> + * BH Works
>   */
>
>  static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
> @@ -987,9 +987,9 @@ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
>         return host->mrq->cmd->data;
>  }
>
> -static void wbsd_tasklet_card(struct tasklet_struct *t)
> +static void wbsd_card_bh_work(struct work_struct *t)
>  {
> -       struct wbsd_host *host = from_tasklet(host, t, card_tasklet);
> +       struct wbsd_host *host = from_work(host, t, card_bh_work);
>         u8 csr;
>         int delay = -1;
>
> @@ -1020,7 +1020,7 @@ static void wbsd_tasklet_card(struct tasklet_struct *t)
>                         wbsd_reset(host);
>
>                         host->mrq->cmd->error = -ENOMEDIUM;
> -                       tasklet_schedule(&host->finish_tasklet);
> +                       queue_work(system_bh_wq, &host->finish_bh_work);
>                 }
>
>                 delay = 0;
> @@ -1036,9 +1036,9 @@ static void wbsd_tasklet_card(struct tasklet_struct *t)
>                 mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
>  }
>
> -static void wbsd_tasklet_fifo(struct tasklet_struct *t)
> +static void wbsd_fifo_bh_work(struct work_struct *t)
>  {
> -       struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet);
> +       struct wbsd_host *host = from_work(host, t, fifo_bh_work);
>         struct mmc_data *data;
>
>         spin_lock(&host->lock);
> @@ -1060,16 +1060,16 @@ static void wbsd_tasklet_fifo(struct tasklet_struct *t)
>          */
>         if (host->num_sg == 0) {
>                 wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
> -               tasklet_schedule(&host->finish_tasklet);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>         }
>
>  end:
>         spin_unlock(&host->lock);
>  }
>
> -static void wbsd_tasklet_crc(struct tasklet_struct *t)
> +static void wbsd_crc_bh_work(struct work_struct *t)
>  {
> -       struct wbsd_host *host = from_tasklet(host, t, crc_tasklet);
> +       struct wbsd_host *host = from_work(host, t, crc_bh_work);
>         struct mmc_data *data;
>
>         spin_lock(&host->lock);
> @@ -1085,15 +1085,15 @@ static void wbsd_tasklet_crc(struct tasklet_struct *t)
>
>         data->error = -EILSEQ;
>
> -       tasklet_schedule(&host->finish_tasklet);
> +       queue_work(system_bh_wq, &host->finish_bh_work);
>
>  end:
>         spin_unlock(&host->lock);
>  }
>
> -static void wbsd_tasklet_timeout(struct tasklet_struct *t)
> +static void wbsd_timeout_bh_work(struct work_struct *t)
>  {
> -       struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet);
> +       struct wbsd_host *host = from_work(host, t, timeout_bh_work);
>         struct mmc_data *data;
>
>         spin_lock(&host->lock);
> @@ -1109,15 +1109,15 @@ static void wbsd_tasklet_timeout(struct tasklet_struct *t)
>
>         data->error = -ETIMEDOUT;
>
> -       tasklet_schedule(&host->finish_tasklet);
> +       queue_work(system_bh_wq, &host->finish_bh_work);
>
>  end:
>         spin_unlock(&host->lock);
>  }
>
> -static void wbsd_tasklet_finish(struct tasklet_struct *t)
> +static void wbsd_finish_bh_work(struct work_struct *t)
>  {
> -       struct wbsd_host *host = from_tasklet(host, t, finish_tasklet);
> +       struct wbsd_host *host = from_work(host, t, finish_bh_work);
>         struct mmc_data *data;
>
>         spin_lock(&host->lock);
> @@ -1156,18 +1156,18 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id)
>         host->isr |= isr;
>
>         /*
> -        * Schedule tasklets as needed.
> +        * Schedule bh work as needed.
>          */
>         if (isr & WBSD_INT_CARD)
> -               tasklet_schedule(&host->card_tasklet);
> +               queue_work(system_bh_wq, &host->card_bh_work);
>         if (isr & WBSD_INT_FIFO_THRE)
> -               tasklet_schedule(&host->fifo_tasklet);
> +               queue_work(system_bh_wq, &host->fifo_bh_work);
>         if (isr & WBSD_INT_CRC)
> -               tasklet_hi_schedule(&host->crc_tasklet);
> +               queue_work(system_bh_highpri_wq, &host->crc_bh_work);
>         if (isr & WBSD_INT_TIMEOUT)
> -               tasklet_hi_schedule(&host->timeout_tasklet);
> +               queue_work(system_bh_highpri_wq, &host->timeout_bh_work);
>         if (isr & WBSD_INT_TC)
> -               tasklet_schedule(&host->finish_tasklet);
> +               queue_work(system_bh_wq, &host->finish_bh_work);
>
>         return IRQ_HANDLED;
>  }
> @@ -1443,13 +1443,13 @@ static int wbsd_request_irq(struct wbsd_host *host, int irq)
>         int ret;
>
>         /*
> -        * Set up tasklets. Must be done before requesting interrupt.
> +        * Set up bh works. Must be done before requesting interrupt.
>          */
> -       tasklet_setup(&host->card_tasklet, wbsd_tasklet_card);
> -       tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo);
> -       tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc);
> -       tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout);
> -       tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish);
> +       INIT_WORK(&host->card_bh_work, wbsd_card_bh_work);
> +       INIT_WORK(&host->fifo_bh_work, wbsd_fifo_bh_work);
> +       INIT_WORK(&host->crc_bh_work, wbsd_crc_bh_work);
> +       INIT_WORK(&host->timeout_bh_work, wbsd_timeout_bh_work);
> +       INIT_WORK(&host->finish_bh_work, wbsd_finish_bh_work);
>
>         /*
>          * Allocate interrupt.
> @@ -1472,11 +1472,11 @@ static void  wbsd_release_irq(struct wbsd_host *host)
>
>         host->irq = 0;
>
> -       tasklet_kill(&host->card_tasklet);
> -       tasklet_kill(&host->fifo_tasklet);
> -       tasklet_kill(&host->crc_tasklet);
> -       tasklet_kill(&host->timeout_tasklet);
> -       tasklet_kill(&host->finish_tasklet);
> +       cancel_work_sync(&host->card_bh_work);
> +       cancel_work_sync(&host->fifo_bh_work);
> +       cancel_work_sync(&host->crc_bh_work);
> +       cancel_work_sync(&host->timeout_bh_work);
> +       cancel_work_sync(&host->finish_bh_work);
>  }
>
>  /*
> diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h
> index be30b4d8ce4c..970886831305 100644
> --- a/drivers/mmc/host/wbsd.h
> +++ b/drivers/mmc/host/wbsd.h
> @@ -171,11 +171,11 @@ struct wbsd_host
>         int                     irq;            /* Interrupt */
>         int                     dma;            /* DMA channel */
>
> -       struct tasklet_struct   card_tasklet;   /* Tasklet structures */
> -       struct tasklet_struct   fifo_tasklet;
> -       struct tasklet_struct   crc_tasklet;
> -       struct tasklet_struct   timeout_tasklet;
> -       struct tasklet_struct   finish_tasklet;
> +       struct work_struct      card_bh_work;   /* Work structures */
> +       struct work_struct      fifo_bh_work;
> +       struct work_struct      crc_bh_work;
> +       struct work_struct      timeout_bh_work;
> +       struct work_struct      finish_bh_work;
>
>         struct timer_list       ignore_timer;   /* Ignore detection timer */
>  };
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-18 22:52 [PATCH v3] mmc: Convert from tasklet to BH workqueue Allen Pais
  2024-06-20 15:14 ` Ulf Hansson
@ 2024-06-21  9:53 ` Wolfram Sang
  2024-06-21 10:06   ` Christian Loehle
  2024-06-21 17:47   ` Allen
  1 sibling, 2 replies; 10+ messages in thread
From: Wolfram Sang @ 2024-06-21  9:53 UTC (permalink / raw)
  To: Allen Pais
  Cc: Aubin Constans, Ulf Hansson, Nicolas Ferre, Alexandre Belloni,
	Claudiu Beznea, Manuel Lauss, Michał Mirosław,
	Jaehoon Chung, Aaro Koskinen, Adrian Hunter, Florian Fainelli,
	Ray Jui, Scott Branden, Broadcom internal kernel review list,
	Alex Dubov, Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang,
	Harald Welte, Pierre Ossman, Christian Loehle, linux-mmc,
	linux-arm-kernel, linux-kernel, linux-omap, linux-renesas-soc

[-- Attachment #1: Type: text/plain, Size: 1506 bytes --]

Hi,

On Tue, Jun 18, 2024 at 03:52:07PM GMT, Allen Pais wrote:
> The only generic interface to execute asynchronously in the BH context is
> tasklet; however, it's marked deprecated and has some design flaws. To
> replace tasklets, BH workqueue support was recently added. A BH workqueue
> behaves similarly to regular workqueues except that the queued work items
> are executed in the BH context.
> 
> This patch converts drivers/mmc/* from tasklet to BH workqueue.
> 
> Based on the work done by Tejun Heo <tj@kernel.org>

Has this been fully build-tested?

===
drivers/mmc/host/renesas_sdhi_internal_dmac.c: In function ‘renesas_sdhi_internal_dmac_complete_work_fn’:
./include/linux/container_of.h:20:54: error: ‘struct tmio_mmc_host’ has no member named ‘dma_complete’
===

In deed, 'dma_complete' is only in 'struct renesas_sdhi_dma'. From
there, we can get to the parent 'struct renesas_sdhi' using
container_of. But then, I don't see a way to go to 'struct
tmio_mmc_host' from there. The other way around is possible because
there is the pointer 'struct tmio_mmc_data *pdata' in the TMIO struct
pointing to the data contained in 'struct renesas_sdhi'. 'host_to_priv()'
does the math. But I don't see a path the other way around.

So, it doesn't look like the workqueue interface can provide a
generic pointer like tasklets could do? This means we have to add a
pointer from 'struct renesas_sdhi' to 'struct tmio_mmc_host'?

All the best,

   Wolfram


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-21  9:53 ` Wolfram Sang
@ 2024-06-21 10:06   ` Christian Loehle
  2024-06-21 10:22     ` Wolfram Sang
  2024-06-21 17:47   ` Allen
  1 sibling, 1 reply; 10+ messages in thread
From: Christian Loehle @ 2024-06-21 10:06 UTC (permalink / raw)
  To: Wolfram Sang, Allen Pais, Aubin Constans, Ulf Hansson,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Manuel Lauss,
	Michał Mirosław, Jaehoon Chung, Aaro Koskinen,
	Adrian Hunter, Florian Fainelli, Ray Jui, Scott Branden,
	Broadcom internal kernel review list, Alex Dubov,
	Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang, Harald Welte,
	Pierre Ossman, linux-mmc, linux-arm-kernel, linux-kernel,
	linux-omap, linux-renesas-soc

On 6/21/24 10:53, Wolfram Sang wrote:
> Hi,
> 
> On Tue, Jun 18, 2024 at 03:52:07PM GMT, Allen Pais wrote:
>> The only generic interface to execute asynchronously in the BH context is
>> tasklet; however, it's marked deprecated and has some design flaws. To
>> replace tasklets, BH workqueue support was recently added. A BH workqueue
>> behaves similarly to regular workqueues except that the queued work items
>> are executed in the BH context.
>>
>> This patch converts drivers/mmc/* from tasklet to BH workqueue.
>>
>> Based on the work done by Tejun Heo <tj@kernel.org>
> 
> Has this been fully build-tested?

Obviously not, FWIW I noted this on v1:
https://lore.kernel.org/linux-mmc/9c31b697-3d80-407a-82b3-cfbb19fafb31@arm.com/
But hadn't looked at the patch since then, sorry.

> 
> ===
> drivers/mmc/host/renesas_sdhi_internal_dmac.c: In function ‘renesas_sdhi_internal_dmac_complete_work_fn’:
> ./include/linux/container_of.h:20:54: error: ‘struct tmio_mmc_host’ has no member named ‘dma_complete’
> ===
> 
> In deed, 'dma_complete' is only in 'struct renesas_sdhi_dma'. From
> there, we can get to the parent 'struct renesas_sdhi' using
> container_of. But then, I don't see a way to go to 'struct
> tmio_mmc_host' from there. The other way around is possible because
> there is the pointer 'struct tmio_mmc_data *pdata' in the TMIO struct
> pointing to the data contained in 'struct renesas_sdhi'. 'host_to_priv()'
> does the math. But I don't see a path the other way around.
> 
> So, it doesn't look like the workqueue interface can provide a
> generic pointer like tasklets could do? This means we have to add a
> pointer from 'struct renesas_sdhi' to 'struct tmio_mmc_host'?
> 
> All the best,
> 
>    Wolfram
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-21 10:06   ` Christian Loehle
@ 2024-06-21 10:22     ` Wolfram Sang
  0 siblings, 0 replies; 10+ messages in thread
From: Wolfram Sang @ 2024-06-21 10:22 UTC (permalink / raw)
  To: Christian Loehle
  Cc: Allen Pais, Aubin Constans, Ulf Hansson, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Manuel Lauss,
	Michał Mirosław, Jaehoon Chung, Aaro Koskinen,
	Adrian Hunter, Florian Fainelli, Ray Jui, Scott Branden,
	Broadcom internal kernel review list, Alex Dubov,
	Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang, Harald Welte,
	Pierre Ossman, linux-mmc, linux-arm-kernel, linux-kernel,
	linux-omap, linux-renesas-soc

[-- Attachment #1: Type: text/plain, Size: 334 bytes --]


> Obviously not, FWIW I noted this on v1:
> https://lore.kernel.org/linux-mmc/9c31b697-3d80-407a-82b3-cfbb19fafb31@arm.com/
> But hadn't looked at the patch since then, sorry.

No worries, but you should really remove unneeded context from mails you
quote. In this web-interface, I couldn't find this mentioning of the
build error.


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-21  9:53 ` Wolfram Sang
  2024-06-21 10:06   ` Christian Loehle
@ 2024-06-21 17:47   ` Allen
  2024-06-21 18:35     ` Allen
  2024-06-21 21:42     ` Wolfram Sang
  1 sibling, 2 replies; 10+ messages in thread
From: Allen @ 2024-06-21 17:47 UTC (permalink / raw)
  To: Wolfram Sang, Allen Pais, Aubin Constans, Ulf Hansson,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Manuel Lauss,
	Michał Mirosław, Jaehoon Chung, Aaro Koskinen,
	Adrian Hunter, Florian Fainelli, Ray Jui, Scott Branden,
	Broadcom internal kernel review list, Alex Dubov,
	Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang, Harald Welte,
	Pierre Ossman, Christian Loehle, linux-mmc, linux-arm-kernel,
	linux-kernel, linux-omap, linux-renesas-soc

> On Tue, Jun 18, 2024 at 03:52:07PM GMT, Allen Pais wrote:
> > The only generic interface to execute asynchronously in the BH context is
> > tasklet; however, it's marked deprecated and has some design flaws. To
> > replace tasklets, BH workqueue support was recently added. A BH workqueue
> > behaves similarly to regular workqueues except that the queued work items
> > are executed in the BH context.
> >
> > This patch converts drivers/mmc/* from tasklet to BH workqueue.
> >
> > Based on the work done by Tejun Heo <tj@kernel.org>
>
> Has this been fully build-tested?
>
> ===
> drivers/mmc/host/renesas_sdhi_internal_dmac.c: In function ‘renesas_sdhi_internal_dmac_complete_work_fn’:
> ./include/linux/container_of.h:20:54: error: ‘struct tmio_mmc_host’ has no member named ‘dma_complete’
> ===

 Yes, it does break. My bad, my local compile testing failed to catch this.

>
> In deed, 'dma_complete' is only in 'struct renesas_sdhi_dma'. From
> there, we can get to the parent 'struct renesas_sdhi' using
> container_of. But then, I don't see a way to go to 'struct
> tmio_mmc_host' from there. The other way around is possible because
> there is the pointer 'struct tmio_mmc_data *pdata' in the TMIO struct
> pointing to the data contained in 'struct renesas_sdhi'. 'host_to_priv()'
> does the math. But I don't see a path the other way around.
>

 I have been looking at this code since the issue was reported. Yes it
is a bit tricky and so far, the only way I found was to introduce a new pointer.
But, I am not very familiar with the code and have asked Ulf for pointers.

If introducing the pointer is the only way forward and is an
acceptable solution,
I can send out a draft.

Thanks,
Allen

> So, it doesn't look like the workqueue interface can provide a
> generic pointer like tasklets could do? This means we have to add a
> pointer from 'struct renesas_sdhi' to 'struct tmio_mmc_host'?
>
> All the best,
>
>    Wolfram
>


-- 
       - Allen

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-21 17:47   ` Allen
@ 2024-06-21 18:35     ` Allen
  2024-06-21 21:42     ` Wolfram Sang
  1 sibling, 0 replies; 10+ messages in thread
From: Allen @ 2024-06-21 18:35 UTC (permalink / raw)
  To: Wolfram Sang, Allen Pais, Aubin Constans, Ulf Hansson,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Manuel Lauss,
	Michał Mirosław, Jaehoon Chung, Aaro Koskinen,
	Adrian Hunter, Florian Fainelli, Ray Jui, Scott Branden,
	Broadcom internal kernel review list, Alex Dubov,
	Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang, Harald Welte,
	Pierre Ossman, Christian Loehle, linux-mmc, linux-arm-kernel,
	linux-kernel, linux-omap, linux-renesas-soc

> > > The only generic interface to execute asynchronously in the BH context is
> > > tasklet; however, it's marked deprecated and has some design flaws. To
> > > replace tasklets, BH workqueue support was recently added. A BH workqueue
> > > behaves similarly to regular workqueues except that the queued work items
> > > are executed in the BH context.
> > >
> > > This patch converts drivers/mmc/* from tasklet to BH workqueue.
> > >
> > > Based on the work done by Tejun Heo <tj@kernel.org>
> >
> > Has this been fully build-tested?
> >
> > ===
> > drivers/mmc/host/renesas_sdhi_internal_dmac.c: In function ‘renesas_sdhi_internal_dmac_complete_work_fn’:
> > ./include/linux/container_of.h:20:54: error: ‘struct tmio_mmc_host’ has no member named ‘dma_complete’
> > ===
>
>  Yes, it does break. My bad, my local compile testing failed to catch this.
>
> >
> > In deed, 'dma_complete' is only in 'struct renesas_sdhi_dma'. From
> > there, we can get to the parent 'struct renesas_sdhi' using
> > container_of. But then, I don't see a way to go to 'struct
> > tmio_mmc_host' from there. The other way around is possible because
> > there is the pointer 'struct tmio_mmc_data *pdata' in the TMIO struct
> > pointing to the data contained in 'struct renesas_sdhi'. 'host_to_priv()'
> > does the math. But I don't see a path the other way around.
> >
>
>  I have been looking at this code since the issue was reported. Yes it
> is a bit tricky and so far, the only way I found was to introduce a new pointer.
> But, I am not very familiar with the code and have asked Ulf for pointers.
>
> If introducing the pointer is the only way forward and is an
> acceptable solution,
> I can send out a draft.
>
> Thanks,
> Allen
>
> > So, it doesn't look like the workqueue interface can provide a
> > generic pointer like tasklets could do? This means we have to add a
> > pointer from 'struct renesas_sdhi' to 'struct tmio_mmc_host'?
> >


How about the following?

diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 5fd276492f80..9a63b78837e2 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -484,9 +484,14 @@ static bool
renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
        return true;
 }

-static void renesas_sdhi_internal_dmac_complete_work_fn(struct work_struct *t)
+static void renesas_sdhi_internal_dmac_complete_work_fn(struct
work_struct *work)
 {
-       struct tmio_mmc_host *host = from_work(host, t, dma_complete);
+       struct renesas_sdhi_dma *dma_priv = container_of(work,
+                                                        struct
renesas_sdhi_dma,
+                                                        dma_complete);
+       struct renesas_sdhi *sdhi = container_of(dma_priv,
+                                                struct renesas_sdhi, dma_priv);
+       struct tmio_mmc_host *host = sdhi->mmc_data->host;

        spin_lock_irq(&host->lock);
        if (!renesas_sdhi_internal_dmac_complete(host))
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index eace8ea6cda0..cf2cca0e96b5 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -100,6 +100,7 @@ struct tmio_mmc_data {
        dma_addr_t                      dma_rx_offset;
        unsigned int                    max_blk_count;
        unsigned short                  max_segs;
+       struct tmio_mmc_host            *host;          /* back
pointer to the parent struct */
        void (*set_pwr)(struct platform_device *host, int state);
        void (*set_clk_div)(struct platform_device *host, int state);
 };

Thanks.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-21 17:47   ` Allen
  2024-06-21 18:35     ` Allen
@ 2024-06-21 21:42     ` Wolfram Sang
  2024-06-21 23:09       ` Allen
  1 sibling, 1 reply; 10+ messages in thread
From: Wolfram Sang @ 2024-06-21 21:42 UTC (permalink / raw)
  To: Allen
  Cc: Aubin Constans, Ulf Hansson, Nicolas Ferre, Alexandre Belloni,
	Claudiu Beznea, Manuel Lauss, Michał Mirosław,
	Jaehoon Chung, Aaro Koskinen, Adrian Hunter, Florian Fainelli,
	Ray Jui, Scott Branden, Broadcom internal kernel review list,
	Alex Dubov, Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang,
	Harald Welte, Pierre Ossman, Christian Loehle, linux-mmc,
	linux-arm-kernel, linux-kernel, linux-omap, linux-renesas-soc

[-- Attachment #1: Type: text/plain, Size: 384 bytes --]


> If introducing the pointer is the only way forward and is an
> acceptable solution,
> I can send out a draft.

My proposal is that I take over the SDHI part of your series. I know the
code pretty well and I can test the solution right away. Your draft
seems in the wrong layer (conceptually, technically it should work), but
I need to play around a little. Is that okay with you?


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-21 21:42     ` Wolfram Sang
@ 2024-06-21 23:09       ` Allen
  2024-06-24 14:38         ` Ulf Hansson
  0 siblings, 1 reply; 10+ messages in thread
From: Allen @ 2024-06-21 23:09 UTC (permalink / raw)
  To: Wolfram Sang, Allen, Aubin Constans, Ulf Hansson, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Manuel Lauss,
	Michał Mirosław, Jaehoon Chung, Aaro Koskinen,
	Adrian Hunter, Florian Fainelli, Ray Jui, Scott Branden,
	Broadcom internal kernel review list, Alex Dubov,
	Kunihiko Hayashi, Masami Hiramatsu, Bruce Chang, Harald Welte,
	Pierre Ossman, Christian Loehle, linux-mmc, linux-arm-kernel,
	linux-kernel, linux-omap, linux-renesas-soc

>
> > If introducing the pointer is the only way forward and is an
> > acceptable solution,
> > I can send out a draft.
>
> My proposal is that I take over the SDHI part of your series. I know the
> code pretty well and I can test the solution right away. Your draft
> seems in the wrong layer (conceptually, technically it should work), but
> I need to play around a little. Is that okay with you?
>


  Sure, happy to help if needed.

Thanks.

-- 
       - Allen

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] mmc: Convert from tasklet to BH workqueue
  2024-06-21 23:09       ` Allen
@ 2024-06-24 14:38         ` Ulf Hansson
  0 siblings, 0 replies; 10+ messages in thread
From: Ulf Hansson @ 2024-06-24 14:38 UTC (permalink / raw)
  To: Allen, Wolfram Sang; +Cc: Christian Loehle, linux-mmc

On Sat, 22 Jun 2024 at 01:09, Allen <allen.lkml@gmail.com> wrote:
>
> >
> > > If introducing the pointer is the only way forward and is an
> > > acceptable solution,
> > > I can send out a draft.
> >
> > My proposal is that I take over the SDHI part of your series. I know the
> > code pretty well and I can test the solution right away. Your draft
> > seems in the wrong layer (conceptually, technically it should work), but
> > I need to play around a little. Is that okay with you?
> >
>
>
>   Sure, happy to help if needed.
>
> Thanks.

Okay, so I have dropped the patch for now.

Awaiting a patch from Allen for everything but sdhi, which I then
expect Wolfram is going to send a patch for.

Kind regards
Uffe

>
> --
>        - Allen

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-06-24 14:38 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-18 22:52 [PATCH v3] mmc: Convert from tasklet to BH workqueue Allen Pais
2024-06-20 15:14 ` Ulf Hansson
2024-06-21  9:53 ` Wolfram Sang
2024-06-21 10:06   ` Christian Loehle
2024-06-21 10:22     ` Wolfram Sang
2024-06-21 17:47   ` Allen
2024-06-21 18:35     ` Allen
2024-06-21 21:42     ` Wolfram Sang
2024-06-21 23:09       ` Allen
2024-06-24 14:38         ` Ulf Hansson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).