Message ID | 20240327160314.9982-10-apais@linux.microsoft.com |
---|---|
State | New |
Headers | show |
Series | Convert Tasklets to BH Workqueues | expand |
Dne sreda, 27. marec 2024 ob 17:03:14 CET je Allen Pais napisal(a): > 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/infiniband/* from tasklet to BH workqueue. infiniband -> mmc Best regards, Jernej > > Based on the work done by Tejun Heo <tj@kernel.org> > Branch: https://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-6.10 > > Signed-off-by: Allen Pais <allen.lkml@gmail.com>
On 27/03/2024 16:03, 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/infiniband/* from tasklet to BH workqueue. s/infiniband/mmc > > Based on the work done by Tejun Heo <tj@kernel.org> > Branch: https://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-6.10 > > Signed-off-by: Allen Pais <allen.lkml@gmail.com> > --- > drivers/mmc/host/atmel-mci.c | 35 ++++----- > drivers/mmc/host/au1xmmc.c | 37 ++++----- > drivers/mmc/host/cb710-mmc.c | 15 ++-- > drivers/mmc/host/cb710-mmc.h | 3 +- > drivers/mmc/host/dw_mmc.c | 25 ++++--- > drivers/mmc/host/dw_mmc.h | 9 ++- For dw_mmc: Performance numbers look good FWIW. for i in $(seq 0 5); do echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor; done for i in $(seq 0 4); do fio --name=test --rw=randread --bs=4k --runtime=30 --time_based --filename=/dev/mmcblk1 --minimal --numjobs=6 --iodepth=32 --group_reporting | awk -F ";" '{print $8}'; sleep 30; done Baseline: 1758 1773 1619 1835 1639 to: 1743 1643 1860 1638 1872 (I'd call that equivalent). This is on a RK3399. I would prefer most of the naming to change from "work" to "workqueue" in the driver code. Apart from that: Reviewed-by: Christian Loehle <christian.loehle@arm.com> Tested-by: Christian Loehle <christian.loehle@arm.com> > drivers/mmc/host/omap.c | 17 +++-- > drivers/mmc/host/renesas_sdhi.h | 3 +- > drivers/mmc/host/renesas_sdhi_internal_dmac.c | 24 +++--- See inline > 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 | 13 ++-- > drivers/mmc/host/via-sdmmc.c | 25 ++++--- > drivers/mmc/host/wbsd.c | 75 ++++++++++--------- > drivers/mmc/host/wbsd.h | 10 +-- > 18 files changed, 167 insertions(+), 157 deletions(-) > > diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c > index dba826db739a..0a92a7fd020f 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> > @@ -284,12 +285,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. > + * @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. > @@ -363,7 +364,7 @@ struct atmel_mci { > u32 data_status; > u32 stop_cmdr; > > - struct tasklet_struct tasklet; > + struct work_struct work; > unsigned long pending_events; > unsigned long completed_events; > enum atmel_mci_state state; > @@ -761,7 +762,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->work); > } > > static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host, > @@ -983,7 +984,7 @@ static void atmci_pdc_complete(struct atmel_mci *host) > > dev_dbg(&host->pdev->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->work); > } > > static void atmci_dma_cleanup(struct atmel_mci *host) > @@ -997,7 +998,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 work context. > */ > static void atmci_dma_complete(void *arg) > { > @@ -1020,7 +1021,7 @@ static void atmci_dma_complete(void *arg) > dev_dbg(&host->pdev->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->work); > > /* > * Regardless of what the documentation says, we have > @@ -1033,7 +1034,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 > + * work to finish things up when the data transfer > * is completely done. > * > * We may not complete the mmc request here anyway > @@ -1765,9 +1766,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, work); > struct mmc_request *mrq = host->mrq; > struct mmc_data *data = host->data; > enum atmel_mci_state state = host->state; > @@ -1779,7 +1780,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t) > state = host->state; > > dev_vdbg(&host->pdev->dev, > - "tasklet: state %u pending/completed/mask %lx/%lx/%x\n", > + "work: state %u pending/completed/mask %lx/%lx/%x\n", > state, host->pending_events, host->completed_events, > atmci_readl(host, ATMCI_IMR)); > > @@ -2141,7 +2142,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) > dev_dbg(&host->pdev->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->work); > } > > if (pending & ATMCI_TXBUFE) { > @@ -2210,7 +2211,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) > smp_wmb(); > dev_dbg(&host->pdev->dev, "set pending notbusy\n"); > atmci_set_pending(host, EVENT_NOTBUSY); > - tasklet_schedule(&host->tasklet); > + queue_work(system_bh_wq, &host->work); > } > > if (pending & ATMCI_NOTBUSY) { > @@ -2219,7 +2220,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) > smp_wmb(); > dev_dbg(&host->pdev->dev, "set pending notbusy\n"); > atmci_set_pending(host, EVENT_NOTBUSY); > - tasklet_schedule(&host->tasklet); > + queue_work(system_bh_wq, &host->work); > } > > if (pending & ATMCI_RXRDY) > @@ -2234,7 +2235,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) > smp_wmb(); > dev_dbg(&host->pdev->dev, "set pending cmd rdy\n"); > atmci_set_pending(host, EVENT_CMD_RDY); > - tasklet_schedule(&host->tasklet); > + queue_work(system_bh_wq, &host->work); > } > > if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB)) > @@ -2530,7 +2531,7 @@ static int atmci_probe(struct platform_device *pdev) > > host->mapbase = regs->start; > > - tasklet_setup(&host->tasklet, atmci_tasklet_func); > + INIT_WORK(&host->work, atmci_work_func); > > ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); > if (ret) { > diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c > index b5a5c6a2fe8b..c86fa7d2ebb7 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_task; > + struct work_struct data_task; > 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_work_finish(struct work_struct *t) > { > - struct au1xmmc_host *host = from_tasklet(host, t, finish_task); > + struct au1xmmc_host *host = from_work(host, t, finish_task); > 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_work_data(struct work_struct *t) > { > - struct au1xmmc_host *host = from_tasklet(host, t, data_task); > + struct au1xmmc_host *host = from_work(host, t, data_task); > > 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_task); > } > } > > @@ -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_task); > } > } > > @@ -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_task); > 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_task); > } > #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_task); */ > } > } > #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_task); > } > > 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_task, au1xmmc_work_data); > > - tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish); > + INIT_WORK(&host->finish_task, au1xmmc_work_finish); > > 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_task); > + cancel_work_sync(&host->finish_task); > > 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_task); > + cancel_work_sync(&host->finish_task); > > 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..eebb6797e785 100644 > --- a/drivers/mmc/host/cb710-mmc.c > +++ b/drivers/mmc/host/cb710-mmc.c > @@ -8,6 +8,7 @@ > #include <linux/module.h> > #include <linux/pci.h> > #include <linux/delay.h> > +#include <linux/workqueue.h> > #include "cb710-mmc.h" > > #define CB710_MMC_REQ_TIMEOUT_MS 2000 > @@ -493,7 +494,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_work); > } > > static int cb710_mmc_powerup(struct cb710_slot *slot) > @@ -646,10 +647,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_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_work); > struct mmc_request *mrq = reader->mrq; > > reader->mrq = NULL; > @@ -718,8 +719,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_work, > + cb710_mmc_finish_request_work); > spin_lock_init(&reader->irq_lock); > cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); > > @@ -763,7 +764,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_work); > > mmc_free_host(mmc); > } > diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h > index 5e053077dbed..b35ab8736374 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_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..ee6f892bc0d8 100644 > --- a/drivers/mmc/host/dw_mmc.c > +++ b/drivers/mmc/host/dw_mmc.c > @@ -36,6 +36,7 @@ > #include <linux/regulator/consumer.h> > #include <linux/of.h> > #include <linux/mmc/slot-gpio.h> > +#include <linux/workqueue.h> > > #include "dw_mmc.h" > > @@ -493,7 +494,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->work); > } > } > > @@ -1834,7 +1835,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->work); > } > > spin_unlock_irqrestore(&host->irq_lock, flags); > @@ -2056,9 +2057,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, work); > struct mmc_data *data; > struct mmc_command *cmd; > struct mmc_request *mrq; > @@ -2113,7 +2114,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 work got > * delayed. Allowing the transfer to take place > * avoids races and keeps things simple. > */ > @@ -2706,7 +2707,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->work); > > dw_mci_start_fault_timer(host); > } > @@ -2774,7 +2775,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->work); > > spin_unlock(&host->irq_lock); > } > @@ -2793,7 +2794,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->work); > > spin_unlock(&host->irq_lock); > } > @@ -3098,7 +3099,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->work); > } > > static void dw_mci_cto_timer(struct timer_list *t) > @@ -3144,7 +3145,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->work); > break; > default: > dev_warn(host->dev, "Unexpected command timeout, state %d\n", > @@ -3195,7 +3196,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->work); > break; > default: > dev_warn(host->dev, "Unexpected data timeout, state %d\n", > @@ -3435,7 +3436,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->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..d17f398a0432 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. > + * @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. > * @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 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 088f8ed4fdc4..d85bae7b9cba 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_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_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_work); > } > > -static void mmc_omap_cover_handler(struct tasklet_struct *t) > +static void mmc_omap_cover_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_work); > int cover_open = mmc_omap_cover_is_open(slot); > > mmc_detect_change(slot->mmc, 0); > @@ -1299,7 +1300,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_work, mmc_omap_cover_handler); > } > > r = mmc_add_host(mmc); > @@ -1318,7 +1319,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_work); > } > > return 0; > @@ -1341,7 +1342,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_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 53d34c3eddce..f175f8898516 100644 > --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c > +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c > @@ -336,7 +336,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; > @@ -351,7 +351,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); > } > > /* > @@ -439,9 +439,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); > @@ -453,7 +453,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); > } > } > > @@ -483,9 +483,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, dam_complete); That doesn't compile, even if I fix the typo to dma_complete. I have something that does compile, but don't have the platform to test it. > > spin_lock_irq(&host->lock); > if (!renesas_sdhi_internal_dmac_complete(host)) > @@ -543,12 +543,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..974f205d479b 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 which schedules card_work. > */ > 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..c6285c577db0 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_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_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_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_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_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_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..bee13acaa80f 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..d1964111c393 100644 > --- a/drivers/mmc/host/uniphier-sd.c > +++ b/drivers/mmc/host/uniphier-sd.c > @@ -17,6 +17,7 @@ > #include <linux/platform_device.h> > #include <linux/regmap.h> > #include <linux/reset.h> > +#include <linux/workqueue.h> > > #include "tmio_mmc.h" > > @@ -90,9 +91,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 +200,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 +237,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 +318,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..2777b773086b 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_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_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_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_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_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_work); > } > } > > spin_unlock_irqrestore(&sdhost->lock, flags); > } > > -static void via_sdc_tasklet_finish(struct tasklet_struct *t) > +static void via_sdc_work_finish(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_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_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_work, via_sdc_work_finish); > > 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_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_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..984e380abc71 100644 > --- a/drivers/mmc/host/wbsd.c > +++ b/drivers/mmc/host/wbsd.c > @@ -32,6 +32,7 @@ > #include <linux/mmc/sd.h> > #include <linux/scatterlist.h> > #include <linux/slab.h> > +#include <linux/workqueue.h> > > #include <asm/io.h> > #include <asm/dma.h> > @@ -459,7 +460,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_work); > } > > static void wbsd_fill_fifo(struct wbsd_host *host) > @@ -524,7 +525,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_work); > } > > static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) > @@ -746,7 +747,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) > struct mmc_command *cmd; > > /* > - * Disable tasklets to avoid a deadlock. > + * Disable works to avoid a deadlock. > */ > spin_lock_bh(&host->lock); > > @@ -821,7 +822,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_work); > > spin_unlock_bh(&host->lock); > > @@ -961,13 +962,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_work); > > spin_unlock_bh(&host->lock); > } > > /* > - * Tasklets > + * Works > */ > > static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) > @@ -987,9 +988,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_work_card(struct work_struct *t) > { > - struct wbsd_host *host = from_tasklet(host, t, card_tasklet); > + struct wbsd_host *host = from_work(host, t, card_work); > u8 csr; > int delay = -1; > > @@ -1020,7 +1021,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_work); > } > > delay = 0; > @@ -1036,9 +1037,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_work_fifo(struct work_struct *t) > { > - struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet); > + struct wbsd_host *host = from_work(host, t, fifo_work); > struct mmc_data *data; > > spin_lock(&host->lock); > @@ -1060,16 +1061,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_work); > } > > end: > spin_unlock(&host->lock); > } > > -static void wbsd_tasklet_crc(struct tasklet_struct *t) > +static void wbsd_work_crc(struct work_struct *t) > { > - struct wbsd_host *host = from_tasklet(host, t, crc_tasklet); > + struct wbsd_host *host = from_work(host, t, crc_work); > struct mmc_data *data; > > spin_lock(&host->lock); > @@ -1085,15 +1086,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_work); > > end: > spin_unlock(&host->lock); > } > > -static void wbsd_tasklet_timeout(struct tasklet_struct *t) > +static void wbsd_work_timeout(struct work_struct *t) > { > - struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet); > + struct wbsd_host *host = from_work(host, t, timeout_work); > struct mmc_data *data; > > spin_lock(&host->lock); > @@ -1109,15 +1110,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_work); > > end: > spin_unlock(&host->lock); > } > > -static void wbsd_tasklet_finish(struct tasklet_struct *t) > +static void wbsd_work_finish(struct work_struct *t) > { > - struct wbsd_host *host = from_tasklet(host, t, finish_tasklet); > + struct wbsd_host *host = from_work(host, t, finish_work); > struct mmc_data *data; > > spin_lock(&host->lock); > @@ -1156,18 +1157,18 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id) > host->isr |= isr; > > /* > - * Schedule tasklets as needed. > + * Schedule works as needed. > */ > if (isr & WBSD_INT_CARD) > - tasklet_schedule(&host->card_tasklet); > + queue_work(system_bh_wq, &host->card_work); > if (isr & WBSD_INT_FIFO_THRE) > - tasklet_schedule(&host->fifo_tasklet); > + queue_work(system_bh_wq, &host->fifo_work); > if (isr & WBSD_INT_CRC) > - tasklet_hi_schedule(&host->crc_tasklet); > + queue_work(system_bh_highpri_wq, &host->crc_work); > if (isr & WBSD_INT_TIMEOUT) > - tasklet_hi_schedule(&host->timeout_tasklet); > + queue_work(system_bh_highpri_wq, &host->timeout_work); > if (isr & WBSD_INT_TC) > - tasklet_schedule(&host->finish_tasklet); > + queue_work(system_bh_wq, &host->finish_work); > > return IRQ_HANDLED; > } > @@ -1443,13 +1444,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 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_work, wbsd_work_card); > + INIT_WORK(&host->fifo_work, wbsd_work_fifo); > + INIT_WORK(&host->crc_work, wbsd_work_crc); > + INIT_WORK(&host->timeout_work, wbsd_work_timeout); > + INIT_WORK(&host->finish_work, wbsd_work_finish); > > /* > * Allocate interrupt. > @@ -1472,11 +1473,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_work); > + cancel_work_sync(&host->fifo_work); > + cancel_work_sync(&host->crc_work); > + cancel_work_sync(&host->timeout_work); > + cancel_work_sync(&host->finish_work); > } > > /* > diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h > index be30b4d8ce4c..942a64a724e4 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_work; /* Work structures */ > + struct work_struct fifo_work; > + struct work_struct crc_work; > + struct work_struct timeout_work; > + struct work_struct finish_work; > > struct timer_list ignore_timer; /* Ignore detection timer */ > };
On Thu, Mar 28, 2024 at 1:54 PM Ulf Hansson <ulf.hansson@linaro.org> wrote: > At this point we have suggested to drivers to switch to use threaded > irq handlers (and regular work queues if needed too). That said, > what's the benefit of using the BH work queue? Context: https://lwn.net/Articles/960041/ "Tasklets, in particular, remain because they offer lower latency than workqueues which, since they must go through the CPU scheduler, can take longer to execute a deferred-work item." The BH WQ is controlled by a software IRQ and quicker than an ordinary work item. I don't know if this little latency could actually affect any MMC device, I doubt it. The other benefit IIUC is that it is easy to mechanically rewrite tasklets to BH workqueues and be sure that it is as fast as the tasklet, if you want to switch to threaded IRQ handlers or proper work, you need to write a lot of elaborate code and test it (preferably on real hardware). Yours, Linus Walleij
On Thu, Mar 28, 2024 at 3:16 AM Christian Loehle <christian.loehle@arm.com> wrote: > > On 27/03/2024 16:03, 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/infiniband/* from tasklet to BH workqueue. > s/infiniband/mmc Will fix it in v2. > > > > Based on the work done by Tejun Heo <tj@kernel.org> > > Branch: https://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-6.10 > > > > Signed-off-by: Allen Pais <allen.lkml@gmail.com> > > --- > > drivers/mmc/host/atmel-mci.c | 35 ++++----- > > drivers/mmc/host/au1xmmc.c | 37 ++++----- > > drivers/mmc/host/cb710-mmc.c | 15 ++-- > > drivers/mmc/host/cb710-mmc.h | 3 +- > > drivers/mmc/host/dw_mmc.c | 25 ++++--- > > drivers/mmc/host/dw_mmc.h | 9 ++- > For dw_mmc: > Performance numbers look good FWIW. > for i in $(seq 0 5); do echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor; done > for i in $(seq 0 4); do fio --name=test --rw=randread --bs=4k --runtime=30 --time_based --filename=/dev/mmcblk1 --minimal --numjobs=6 --iodepth=32 --group_reporting | awk -F ";" '{print $8}'; sleep 30; done > Baseline: > 1758 > 1773 > 1619 > 1835 > 1639 > to: > 1743 > 1643 > 1860 > 1638 > 1872 > (I'd call that equivalent). > This is on a RK3399. > I would prefer most of the naming to change from "work" to "workqueue" in the driver > code. > Apart from that: > Reviewed-by: Christian Loehle <christian.loehle@arm.com> > Tested-by: Christian Loehle <christian.loehle@arm.com> Thank you very much for testing and the review. Will have your concerns addressed in v2. - Allen
On Thu, 28 Mar 2024 at 17:21, Tejun Heo <tj@kernel.org> wrote: > > Hello, > > On Thu, Mar 28, 2024 at 01:53:25PM +0100, Ulf Hansson wrote: > > At this point we have suggested to drivers to switch to use threaded > > irq handlers (and regular work queues if needed too). That said, > > what's the benefit of using the BH work queue? > > BH workqueues should behave about the same as tasklets which have more > limited interface and is subtly broken in an expensive-to-fix way (around > freeing in-flight work item), so the plan is to replace tasklets with BH > workqueues and remove tasklets from the kernel. Seems like a good approach! > > The [dis]advantages of BH workqueues over threaded IRQs or regular threaded > workqueues are the same as when you compare them to tasklets. No thread > switching overhead, so latencies will be a bit tighter. Wheteher that > actually matters really depends on the use case. Here, the biggest advantage > is that it's mostly interchangeable with tasklets and can thus be swapped > easily. Right, thanks for clarifying! However, the main question is then - if/when it makes sense to use the BH workqueue for an mmc host driver. Unless there are some HW limitations, a threaded irq handler should be sufficient, I think. That said, moving to threaded irq handlers is a different topic and doesn't prevent us from moving to BH workqueues as it seems like a step in the right direction. Kind regards Uffe
On 27/03/2024 17:03, Allen Pais wrote: > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe > > 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/infiniband/* from tasklet to BH workqueue. > > Based on the work done by Tejun Heo <tj@kernel.org> > Branch: https://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-6.10 > > Signed-off-by: Allen Pais <allen.lkml@gmail.com> > --- > drivers/mmc/host/atmel-mci.c | 35 ++++----- [...] For atmel-mci, judging from a few simple tests, performance is preserved. E.g. writing to a SD Card on the SAMA5D3-Xplained board: time dd if=/dev/zero of=/opt/_del_me bs=4k count=64k Base 6.9.0 : 0.07user 5.05system 0:18.92elapsed 27%CPU Patched 6.9.0+: 0.12user 4.92system 0:18.76elapsed 26%CPU However, please resolve what checkpatch is complaining about: scripts/checkpatch.pl --strict PATCH-9-9-mmc-Convert-from-tasklet-to-BH-workqueue.mbox WARNING: please, no space before tabs #72: FILE: drivers/mmc/host/atmel-mci.c:367: +^Istruct work_struct ^Iwork;$ Same as discussions on the USB patch[1] and others in this series, I am also in favour of "workqueue" or similar in the comments, rather than just "work". Apart from that: Tested-by: Aubin Constans <aubin.constans@microchip.com> Acked-by: Aubin Constans <aubin.constans@microchip.com> Thanks. [1]: https://lore.kernel.org/linux-mmc/CAOMdWSLipPfm3OZTpjZz4uF4M+E_8QAoTeMcKBXawLnkTQx6Jg@mail.gmail.com/
> On Jun 3, 2024, at 5:38 AM, Aubin Constans <aubin.constans@microchip.com> wrote: > > On 27/03/2024 17:03, Allen Pais wrote: >> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe >> 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/infiniband/* from tasklet to BH workqueue. >> Based on the work done by Tejun Heo <tj@kernel.org> >> Branch: https://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-6.10 >> Signed-off-by: Allen Pais <allen.lkml@gmail.com> >> --- >> drivers/mmc/host/atmel-mci.c | 35 ++++----- > [...] > > For atmel-mci, judging from a few simple tests, performance is preserved. > E.g. writing to a SD Card on the SAMA5D3-Xplained board: > time dd if=/dev/zero of=/opt/_del_me bs=4k count=64k > > Base 6.9.0 : 0.07user 5.05system 0:18.92elapsed 27%CPU > Patched 6.9.0+: 0.12user 4.92system 0:18.76elapsed 26%CPU > > However, please resolve what checkpatch is complaining about: > scripts/checkpatch.pl --strict PATCH-9-9-mmc-Convert-from-tasklet-to-BH-workqueue.mbox > > WARNING: please, no space before tabs > #72: FILE: drivers/mmc/host/atmel-mci.c:367: > +^Istruct work_struct ^Iwork;$ > > Same as discussions on the USB patch[1] and others in this series, I am also in favour of "workqueue" or similar in the comments, rather than just "work". Will send out a new version. Thank you very much for testing and providing your review. - Allen > > Apart from that: > Tested-by: Aubin Constans <aubin.constans@microchip.com> > Acked-by: Aubin Constans <aubin.constans@microchip.com> > > Thanks. > > [1]: https://lore.kernel.org/linux-mmc/CAOMdWSLipPfm3OZTpjZz4uF4M+E_8QAoTeMcKBXawLnkTQx6Jg@mail.gmail.com/
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index dba826db739a..0a92a7fd020f 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> @@ -284,12 +285,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. + * @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. @@ -363,7 +364,7 @@ struct atmel_mci { u32 data_status; u32 stop_cmdr; - struct tasklet_struct tasklet; + struct work_struct work; unsigned long pending_events; unsigned long completed_events; enum atmel_mci_state state; @@ -761,7 +762,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->work); } static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host, @@ -983,7 +984,7 @@ static void atmci_pdc_complete(struct atmel_mci *host) dev_dbg(&host->pdev->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->work); } static void atmci_dma_cleanup(struct atmel_mci *host) @@ -997,7 +998,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 work context. */ static void atmci_dma_complete(void *arg) { @@ -1020,7 +1021,7 @@ static void atmci_dma_complete(void *arg) dev_dbg(&host->pdev->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->work); /* * Regardless of what the documentation says, we have @@ -1033,7 +1034,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 + * work to finish things up when the data transfer * is completely done. * * We may not complete the mmc request here anyway @@ -1765,9 +1766,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, work); struct mmc_request *mrq = host->mrq; struct mmc_data *data = host->data; enum atmel_mci_state state = host->state; @@ -1779,7 +1780,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t) state = host->state; dev_vdbg(&host->pdev->dev, - "tasklet: state %u pending/completed/mask %lx/%lx/%x\n", + "work: state %u pending/completed/mask %lx/%lx/%x\n", state, host->pending_events, host->completed_events, atmci_readl(host, ATMCI_IMR)); @@ -2141,7 +2142,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) dev_dbg(&host->pdev->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->work); } if (pending & ATMCI_TXBUFE) { @@ -2210,7 +2211,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) smp_wmb(); dev_dbg(&host->pdev->dev, "set pending notbusy\n"); atmci_set_pending(host, EVENT_NOTBUSY); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->work); } if (pending & ATMCI_NOTBUSY) { @@ -2219,7 +2220,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) smp_wmb(); dev_dbg(&host->pdev->dev, "set pending notbusy\n"); atmci_set_pending(host, EVENT_NOTBUSY); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->work); } if (pending & ATMCI_RXRDY) @@ -2234,7 +2235,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) smp_wmb(); dev_dbg(&host->pdev->dev, "set pending cmd rdy\n"); atmci_set_pending(host, EVENT_CMD_RDY); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->work); } if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB)) @@ -2530,7 +2531,7 @@ static int atmci_probe(struct platform_device *pdev) host->mapbase = regs->start; - tasklet_setup(&host->tasklet, atmci_tasklet_func); + INIT_WORK(&host->work, atmci_work_func); ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); if (ret) { diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index b5a5c6a2fe8b..c86fa7d2ebb7 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_task; + struct work_struct data_task; 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_work_finish(struct work_struct *t) { - struct au1xmmc_host *host = from_tasklet(host, t, finish_task); + struct au1xmmc_host *host = from_work(host, t, finish_task); 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_work_data(struct work_struct *t) { - struct au1xmmc_host *host = from_tasklet(host, t, data_task); + struct au1xmmc_host *host = from_work(host, t, data_task); 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_task); } } @@ -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_task); } } @@ -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_task); 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_task); } #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_task); */ } } #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_task); } 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_task, au1xmmc_work_data); - tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish); + INIT_WORK(&host->finish_task, au1xmmc_work_finish); 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_task); + cancel_work_sync(&host->finish_task); 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_task); + cancel_work_sync(&host->finish_task); 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..eebb6797e785 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/delay.h> +#include <linux/workqueue.h> #include "cb710-mmc.h" #define CB710_MMC_REQ_TIMEOUT_MS 2000 @@ -493,7 +494,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_work); } static int cb710_mmc_powerup(struct cb710_slot *slot) @@ -646,10 +647,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_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_work); struct mmc_request *mrq = reader->mrq; reader->mrq = NULL; @@ -718,8 +719,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_work, + cb710_mmc_finish_request_work); spin_lock_init(&reader->irq_lock); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); @@ -763,7 +764,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_work); mmc_free_host(mmc); } diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h index 5e053077dbed..b35ab8736374 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_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..ee6f892bc0d8 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -36,6 +36,7 @@ #include <linux/regulator/consumer.h> #include <linux/of.h> #include <linux/mmc/slot-gpio.h> +#include <linux/workqueue.h> #include "dw_mmc.h" @@ -493,7 +494,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->work); } } @@ -1834,7 +1835,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->work); } spin_unlock_irqrestore(&host->irq_lock, flags); @@ -2056,9 +2057,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, work); struct mmc_data *data; struct mmc_command *cmd; struct mmc_request *mrq; @@ -2113,7 +2114,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 work got * delayed. Allowing the transfer to take place * avoids races and keeps things simple. */ @@ -2706,7 +2707,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->work); dw_mci_start_fault_timer(host); } @@ -2774,7 +2775,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->work); spin_unlock(&host->irq_lock); } @@ -2793,7 +2794,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->work); spin_unlock(&host->irq_lock); } @@ -3098,7 +3099,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->work); } static void dw_mci_cto_timer(struct timer_list *t) @@ -3144,7 +3145,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->work); break; default: dev_warn(host->dev, "Unexpected command timeout, state %d\n", @@ -3195,7 +3196,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->work); break; default: dev_warn(host->dev, "Unexpected data timeout, state %d\n", @@ -3435,7 +3436,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->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..d17f398a0432 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. + * @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. * @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 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 088f8ed4fdc4..d85bae7b9cba 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_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_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_work); } -static void mmc_omap_cover_handler(struct tasklet_struct *t) +static void mmc_omap_cover_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_work); int cover_open = mmc_omap_cover_is_open(slot); mmc_detect_change(slot->mmc, 0); @@ -1299,7 +1300,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_work, mmc_omap_cover_handler); } r = mmc_add_host(mmc); @@ -1318,7 +1319,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_work); } return 0; @@ -1341,7 +1342,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_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 53d34c3eddce..f175f8898516 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -336,7 +336,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; @@ -351,7 +351,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); } /* @@ -439,9 +439,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); @@ -453,7 +453,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); } } @@ -483,9 +483,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, dam_complete); spin_lock_irq(&host->lock); if (!renesas_sdhi_internal_dmac_complete(host)) @@ -543,12 +543,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..974f205d479b 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 which schedules card_work. */ 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..c6285c577db0 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_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_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_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_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_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_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..bee13acaa80f 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..d1964111c393 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/reset.h> +#include <linux/workqueue.h> #include "tmio_mmc.h" @@ -90,9 +91,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 +200,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 +237,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 +318,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..2777b773086b 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_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_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_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_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_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_work); } } spin_unlock_irqrestore(&sdhost->lock, flags); } -static void via_sdc_tasklet_finish(struct tasklet_struct *t) +static void via_sdc_work_finish(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_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_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_work, via_sdc_work_finish); 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_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_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..984e380abc71 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -32,6 +32,7 @@ #include <linux/mmc/sd.h> #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/workqueue.h> #include <asm/io.h> #include <asm/dma.h> @@ -459,7 +460,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_work); } static void wbsd_fill_fifo(struct wbsd_host *host) @@ -524,7 +525,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_work); } static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) @@ -746,7 +747,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd; /* - * Disable tasklets to avoid a deadlock. + * Disable works to avoid a deadlock. */ spin_lock_bh(&host->lock); @@ -821,7 +822,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_work); spin_unlock_bh(&host->lock); @@ -961,13 +962,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_work); spin_unlock_bh(&host->lock); } /* - * Tasklets + * Works */ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) @@ -987,9 +988,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_work_card(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, card_tasklet); + struct wbsd_host *host = from_work(host, t, card_work); u8 csr; int delay = -1; @@ -1020,7 +1021,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_work); } delay = 0; @@ -1036,9 +1037,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_work_fifo(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet); + struct wbsd_host *host = from_work(host, t, fifo_work); struct mmc_data *data; spin_lock(&host->lock); @@ -1060,16 +1061,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_work); } end: spin_unlock(&host->lock); } -static void wbsd_tasklet_crc(struct tasklet_struct *t) +static void wbsd_work_crc(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, crc_tasklet); + struct wbsd_host *host = from_work(host, t, crc_work); struct mmc_data *data; spin_lock(&host->lock); @@ -1085,15 +1086,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_work); end: spin_unlock(&host->lock); } -static void wbsd_tasklet_timeout(struct tasklet_struct *t) +static void wbsd_work_timeout(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet); + struct wbsd_host *host = from_work(host, t, timeout_work); struct mmc_data *data; spin_lock(&host->lock); @@ -1109,15 +1110,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_work); end: spin_unlock(&host->lock); } -static void wbsd_tasklet_finish(struct tasklet_struct *t) +static void wbsd_work_finish(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, finish_tasklet); + struct wbsd_host *host = from_work(host, t, finish_work); struct mmc_data *data; spin_lock(&host->lock); @@ -1156,18 +1157,18 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id) host->isr |= isr; /* - * Schedule tasklets as needed. + * Schedule works as needed. */ if (isr & WBSD_INT_CARD) - tasklet_schedule(&host->card_tasklet); + queue_work(system_bh_wq, &host->card_work); if (isr & WBSD_INT_FIFO_THRE) - tasklet_schedule(&host->fifo_tasklet); + queue_work(system_bh_wq, &host->fifo_work); if (isr & WBSD_INT_CRC) - tasklet_hi_schedule(&host->crc_tasklet); + queue_work(system_bh_highpri_wq, &host->crc_work); if (isr & WBSD_INT_TIMEOUT) - tasklet_hi_schedule(&host->timeout_tasklet); + queue_work(system_bh_highpri_wq, &host->timeout_work); if (isr & WBSD_INT_TC) - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_work); return IRQ_HANDLED; } @@ -1443,13 +1444,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 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_work, wbsd_work_card); + INIT_WORK(&host->fifo_work, wbsd_work_fifo); + INIT_WORK(&host->crc_work, wbsd_work_crc); + INIT_WORK(&host->timeout_work, wbsd_work_timeout); + INIT_WORK(&host->finish_work, wbsd_work_finish); /* * Allocate interrupt. @@ -1472,11 +1473,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_work); + cancel_work_sync(&host->fifo_work); + cancel_work_sync(&host->crc_work); + cancel_work_sync(&host->timeout_work); + cancel_work_sync(&host->finish_work); } /* diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h index be30b4d8ce4c..942a64a724e4 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_work; /* Work structures */ + struct work_struct fifo_work; + struct work_struct crc_work; + struct work_struct timeout_work; + struct work_struct finish_work; struct timer_list ignore_timer; /* Ignore detection timer */ };
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/infiniband/* from tasklet to BH workqueue. Based on the work done by Tejun Heo <tj@kernel.org> Branch: https://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-6.10 Signed-off-by: Allen Pais <allen.lkml@gmail.com> --- drivers/mmc/host/atmel-mci.c | 35 ++++----- drivers/mmc/host/au1xmmc.c | 37 ++++----- drivers/mmc/host/cb710-mmc.c | 15 ++-- drivers/mmc/host/cb710-mmc.h | 3 +- drivers/mmc/host/dw_mmc.c | 25 ++++--- 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 | 13 ++-- drivers/mmc/host/via-sdmmc.c | 25 ++++--- drivers/mmc/host/wbsd.c | 75 ++++++++++--------- drivers/mmc/host/wbsd.h | 10 +-- 18 files changed, 167 insertions(+), 157 deletions(-)