diff mbox series

[RFC] scsi: Convert from tasklet to BH workqueue

Message ID 20240502203433.15811-2-apais@linux.microsoft.com
State New
Headers show
Series Convert tasklets to bottom half workqueues | expand

Commit Message

Allen Pais May 2, 2024, 8:34 p.m. UTC
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/scsi/* 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/scsi/aic7xxx/aic7xxx_osm.c          |  2 +-
 drivers/scsi/aic94xx/aic94xx_hwi.c          | 14 ++--
 drivers/scsi/aic94xx/aic94xx_hwi.h          |  5 +-
 drivers/scsi/aic94xx/aic94xx_scb.c          | 36 +++++-----
 drivers/scsi/aic94xx/aic94xx_task.c         | 14 ++--
 drivers/scsi/aic94xx/aic94xx_tmf.c          | 34 ++++-----
 drivers/scsi/esas2r/esas2r.h                | 12 ++--
 drivers/scsi/esas2r/esas2r_init.c           | 14 ++--
 drivers/scsi/esas2r/esas2r_int.c            | 18 ++---
 drivers/scsi/esas2r/esas2r_io.c             |  2 +-
 drivers/scsi/esas2r/esas2r_main.c           | 16 ++---
 drivers/scsi/ibmvscsi/ibmvfc.c              | 16 ++---
 drivers/scsi/ibmvscsi/ibmvfc.h              |  3 +-
 drivers/scsi/ibmvscsi/ibmvscsi.c            | 16 ++---
 drivers/scsi/ibmvscsi/ibmvscsi.h            |  3 +-
 drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c    | 15 ++--
 drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h    |  3 +-
 drivers/scsi/isci/host.c                    | 12 ++--
 drivers/scsi/isci/host.h                    |  8 +--
 drivers/scsi/isci/init.c                    |  4 +-
 drivers/scsi/megaraid/mega_common.h         |  5 +-
 drivers/scsi/megaraid/megaraid_mbox.c       | 21 +++---
 drivers/scsi/megaraid/megaraid_sas.h        |  4 +-
 drivers/scsi/megaraid/megaraid_sas_base.c   | 32 ++++-----
 drivers/scsi/megaraid/megaraid_sas_fusion.c | 16 ++---
 drivers/scsi/mvsas/mv_init.c                | 27 ++++---
 drivers/scsi/mvsas/mv_sas.h                 |  9 +--
 drivers/scsi/pm8001/pm8001_init.c           | 55 ++++++++-------
 drivers/scsi/pm8001/pm8001_sas.h            |  2 +-
 drivers/scsi/pmcraid.c                      | 78 +++++++++++----------
 drivers/scsi/pmcraid.h                      |  5 +-
 31 files changed, 251 insertions(+), 250 deletions(-)

Comments

Michael Ellerman May 3, 2024, 2:03 a.m. UTC | #1
Allen Pais <apais@linux.microsoft.com> writes:
> 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/scsi/* 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/scsi/aic7xxx/aic7xxx_osm.c          |  2 +-
>  drivers/scsi/aic94xx/aic94xx_hwi.c          | 14 ++--
>  drivers/scsi/aic94xx/aic94xx_hwi.h          |  5 +-
>  drivers/scsi/aic94xx/aic94xx_scb.c          | 36 +++++-----
>  drivers/scsi/aic94xx/aic94xx_task.c         | 14 ++--
>  drivers/scsi/aic94xx/aic94xx_tmf.c          | 34 ++++-----
>  drivers/scsi/esas2r/esas2r.h                | 12 ++--
>  drivers/scsi/esas2r/esas2r_init.c           | 14 ++--
>  drivers/scsi/esas2r/esas2r_int.c            | 18 ++---
>  drivers/scsi/esas2r/esas2r_io.c             |  2 +-
>  drivers/scsi/esas2r/esas2r_main.c           | 16 ++---
>  drivers/scsi/ibmvscsi/ibmvfc.c              | 16 ++---
>  drivers/scsi/ibmvscsi/ibmvfc.h              |  3 +-
>  drivers/scsi/ibmvscsi/ibmvscsi.c            | 16 ++---
>  drivers/scsi/ibmvscsi/ibmvscsi.h            |  3 +-
>  drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c    | 15 ++--
>  drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h    |  3 +-

Something there is giving me a build failure (ppc64le_guest_defconfig):

  + make -s 'CC=ccache powerpc64le-linux-gnu-gcc' -j 4
  /linux/drivers/scsi/ibmvscsi/ibmvscsi.c: In function 'ibmvscsi_init_crq_queue':
  Error: /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:370:331: error: 'ibmvscsi_work' undeclared (first use in this function)
  /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:370:331: note: each undeclared identifier is reported only once for each function it appears in
  /linux/scripts/Makefile.build:244: recipe for target 'drivers/scsi/ibmvscsi/ibmvscsi.o' failed
  /linux/scripts/Makefile.build:485: recipe for target 'drivers/scsi/ibmvscsi' failed
  /linux/scripts/Makefile.build:485: recipe for target 'drivers/scsi' failed
  /linux/scripts/Makefile.build:485: recipe for target 'drivers' failed
  /linux/drivers/scsi/ibmvscsi/ibmvscsi.c: In function 'ibmvscsi_probe':
  Error: /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:2255:78: error: passing argument 1 of 'kthread_create_on_node' from incompatible pointer type [-Werror=incompatible-pointer-types]
  In file included from /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:56:0:
  /linux/include/linux/kthread.h:11:21: note: expected 'int (*)(void *)' but argument is of type 'int (*)(struct work_struct *)'
   struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
                       ^
  /linux/drivers/scsi/ibmvscsi/ibmvscsi.c: At top level:
  Warning: /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:212:13: warning: 'ibmvscsi_task' defined but not used [-Wunused-function]
   static void ibmvscsi_task(void *data)
               ^
  Warning: cc1: warning: unrecognized command line option '-Wno-shift-negative-value'
  Warning: cc1: warning: unrecognized command line option '-Wno-stringop-overflow'
  cc1: some warnings being treated as errors
  make[6]: *** [drivers/scsi/ibmvscsi/ibmvscsi.o] Error 1
  make[5]: *** [drivers/scsi/ibmvscsi] Error 2
  make[4]: *** [drivers/scsi] Error 2
  make[3]: *** [drivers] Error 2
  make[3]: *** Waiting for unfinished jobs....

Full log here: https://github.com/linuxppc/linux-snowpatch/actions/runs/8930174372/job/24529645923

Cross compile instructions if you're keen: https://github.com/linuxppc/wiki/wiki/Building-powerpc-kernels

cheers
Allen Pais May 3, 2024, 3:32 p.m. UTC | #2
> On May 2, 2024, at 7:03 PM, Michael Ellerman <mpe@ellerman.id.au> wrote:
> 
> Allen Pais <apais@linux.microsoft.com> writes:
>> 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/scsi/* 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/scsi/aic7xxx/aic7xxx_osm.c          |  2 +-
>> drivers/scsi/aic94xx/aic94xx_hwi.c          | 14 ++--
>> drivers/scsi/aic94xx/aic94xx_hwi.h          |  5 +-
>> drivers/scsi/aic94xx/aic94xx_scb.c          | 36 +++++-----
>> drivers/scsi/aic94xx/aic94xx_task.c         | 14 ++--
>> drivers/scsi/aic94xx/aic94xx_tmf.c          | 34 ++++-----
>> drivers/scsi/esas2r/esas2r.h                | 12 ++--
>> drivers/scsi/esas2r/esas2r_init.c           | 14 ++--
>> drivers/scsi/esas2r/esas2r_int.c            | 18 ++---
>> drivers/scsi/esas2r/esas2r_io.c             |  2 +-
>> drivers/scsi/esas2r/esas2r_main.c           | 16 ++---
>> drivers/scsi/ibmvscsi/ibmvfc.c              | 16 ++---
>> drivers/scsi/ibmvscsi/ibmvfc.h              |  3 +-
>> drivers/scsi/ibmvscsi/ibmvscsi.c            | 16 ++---
>> drivers/scsi/ibmvscsi/ibmvscsi.h            |  3 +-
>> drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c    | 15 ++--
>> drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h    |  3 +-
> 
> Something there is giving me a build failure (ppc64le_guest_defconfig):
> 
>  + make -s 'CC=ccache powerpc64le-linux-gnu-gcc' -j 4
>  /linux/drivers/scsi/ibmvscsi/ibmvscsi.c: In function 'ibmvscsi_init_crq_queue':
>  Error: /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:370:331: error: 'ibmvscsi_work' undeclared (first use in this function)
>  /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:370:331: note: each undeclared identifier is reported only once for each function it appears in
>  /linux/scripts/Makefile.build:244: recipe for target 'drivers/scsi/ibmvscsi/ibmvscsi.o' failed
>  /linux/scripts/Makefile.build:485: recipe for target 'drivers/scsi/ibmvscsi' failed
>  /linux/scripts/Makefile.build:485: recipe for target 'drivers/scsi' failed
>  /linux/scripts/Makefile.build:485: recipe for target 'drivers' failed
>  /linux/drivers/scsi/ibmvscsi/ibmvscsi.c: In function 'ibmvscsi_probe':
>  Error: /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:2255:78: error: passing argument 1 of 'kthread_create_on_node' from incompatible pointer type [-Werror=incompatible-pointer-types]
>  In file included from /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:56:0:
>  /linux/include/linux/kthread.h:11:21: note: expected 'int (*)(void *)' but argument is of type 'int (*)(struct work_struct *)'
>   struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
>                       ^
>  /linux/drivers/scsi/ibmvscsi/ibmvscsi.c: At top level:
>  Warning: /linux/drivers/scsi/ibmvscsi/ibmvscsi.c:212:13: warning: 'ibmvscsi_task' defined but not used [-Wunused-function]
>   static void ibmvscsi_task(void *data)
>               ^
>  Warning: cc1: warning: unrecognized command line option '-Wno-shift-negative-value'
>  Warning: cc1: warning: unrecognized command line option '-Wno-stringop-overflow'
>  cc1: some warnings being treated as errors
>  make[6]: *** [drivers/scsi/ibmvscsi/ibmvscsi.o] Error 1
>  make[5]: *** [drivers/scsi/ibmvscsi] Error 2
>  make[4]: *** [drivers/scsi] Error 2
>  make[3]: *** [drivers] Error 2
>  make[3]: *** Waiting for unfinished jobs....
> 
> Full log here: https://github.com/linuxppc/linux-snowpatch/actions/runs/8930174372/job/24529645923

 Thank you for testing it out. Unfortunately, I did not cross-compile it.
Will fix this in v2.

- Allen

> 
> Cross compile instructions if you're keen: https://github.com/linuxppc/wiki/wiki/Building-powerpc-kernels
> 
> cheers
diff mbox series

Patch

diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
index b0c4f2345321..42f76391f589 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -797,7 +797,7 @@  struct scsi_host_template aic7xxx_driver_template = {
 	.target_destroy		= ahc_linux_target_destroy,
 };
 
-/**************************** Tasklet Handler *********************************/
+/**************************** Work Handler *********************************/
 
 
 static inline unsigned int ahc_build_scsiid(struct ahc_softc *ahc,
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c
index 9dda296c0152..b08f0231e562 100644
--- a/drivers/scsi/aic94xx/aic94xx_hwi.c
+++ b/drivers/scsi/aic94xx/aic94xx_hwi.c
@@ -246,7 +246,7 @@  static void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha)
 
 /* ---------- Done List initialization ---------- */
 
-static void asd_dl_tasklet_handler(unsigned long);
+static void asd_dl_work_handler(struct work_struct *);
 
 static int asd_init_dl(struct asd_ha_struct *asd_ha)
 {
@@ -259,8 +259,7 @@  static int asd_init_dl(struct asd_ha_struct *asd_ha)
 	asd_ha->seq.dl = asd_ha->seq.actual_dl->vaddr;
 	asd_ha->seq.dl_toggle = ASD_DEF_DL_TOGGLE;
 	asd_ha->seq.dl_next = 0;
-	tasklet_init(&asd_ha->seq.dl_tasklet, asd_dl_tasklet_handler,
-		     (unsigned long) asd_ha);
+	INIT_WORK(&asd_ha->seq.dl_work, asd_dl_work_handler);
 
 	return 0;
 }
@@ -709,10 +708,9 @@  static void asd_chip_reset(struct asd_ha_struct *asd_ha)
 
 /* ---------- Done List Routines ---------- */
 
-static void asd_dl_tasklet_handler(unsigned long data)
+static void asd_dl_work_handler(struct work_struct *t)
 {
-	struct asd_ha_struct *asd_ha = (struct asd_ha_struct *) data;
-	struct asd_seq_data *seq = &asd_ha->seq;
+	struct asd_seq_data *seq = from_work(seq, t, dl_work);
 	unsigned long flags;
 
 	while (1) {
@@ -739,7 +737,7 @@  static void asd_dl_tasklet_handler(unsigned long data)
 		seq->pending--;
 		spin_unlock_irqrestore(&seq->pend_q_lock, flags);
 	out:
-		ascb->tasklet_complete(ascb, dl);
+		ascb->work_complete(ascb, dl);
 
 	next_1:
 		seq->dl_next = (seq->dl_next + 1) & (ASD_DL_SIZE-1);
@@ -756,7 +754,7 @@  static void asd_dl_tasklet_handler(unsigned long data)
  */
 static void asd_process_donelist_isr(struct asd_ha_struct *asd_ha)
 {
-	tasklet_schedule(&asd_ha->seq.dl_tasklet);
+	queue_work(system_bh_wq, &asd_ha->seq.dl_work);
 }
 
 /**
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h
index 930e192b1cd4..2cc6fb7aa1a7 100644
--- a/drivers/scsi/aic94xx/aic94xx_hwi.h
+++ b/drivers/scsi/aic94xx/aic94xx_hwi.h
@@ -12,6 +12,7 @@ 
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/dma-mapping.h>
+#include <linux/workqueue.h>
 
 #include <scsi/libsas.h>
 
@@ -117,7 +118,7 @@  struct asd_ascb {
 	struct asd_dma_tok dma_scb;
 	struct asd_dma_tok *sg_arr;
 
-	void (*tasklet_complete)(struct asd_ascb *, struct done_list_struct *);
+	void (*work_complete)(struct asd_ascb *, struct done_list_struct *);
 	u8     uldd_timer:1;
 
 	/* internally generated command */
@@ -152,7 +153,7 @@  struct asd_seq_data {
 	void *tc_index_bitmap;
 	int   tc_index_bitmap_bits;
 
-	struct tasklet_struct dl_tasklet;
+	struct work_struct dl_work;
 	struct done_list_struct *dl; /* array of done list entries, equals */
 	struct asd_dma_tok *actual_dl; /* actual_dl->vaddr */
 	int    dl_toggle;
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c
index 68214a58b160..256800811553 100644
--- a/drivers/scsi/aic94xx/aic94xx_scb.c
+++ b/drivers/scsi/aic94xx/aic94xx_scb.c
@@ -64,7 +64,7 @@  static void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
 		phy->sas_phy.oob_mode = SATA_OOB_MODE;
 }
 
-static void asd_phy_event_tasklet(struct asd_ascb *ascb,
+static void asd_phy_event_work(struct asd_ascb *ascb,
 					 struct done_list_struct *dl)
 {
 	struct asd_ha_struct *asd_ha = ascb->ha;
@@ -215,7 +215,7 @@  static void asd_deform_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
 	spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags);
 }
 
-static void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
+static void asd_bytes_dmaed_work(struct asd_ascb *ascb,
 				    struct done_list_struct *dl,
 				    int edb_id, int phy_id)
 {
@@ -237,7 +237,7 @@  static void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
 	sas_notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED, GFP_ATOMIC);
 }
 
-static void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
+static void asd_link_reset_err_work(struct asd_ascb *ascb,
 				       struct done_list_struct *dl,
 				       int phy_id)
 {
@@ -290,7 +290,7 @@  static void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
 	;
 }
 
-static void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
+static void asd_primitive_rcvd_work(struct asd_ascb *ascb,
 				       struct done_list_struct *dl,
 				       int phy_id)
 {
@@ -361,7 +361,7 @@  static void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
  *
  * After an EDB has been invalidated, if all EDBs in this ESCB have been
  * invalidated, the ESCB is posted back to the sequencer.
- * Context is tasklet/IRQ.
+ * Context is BH work/IRQ.
  */
 void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
 {
@@ -396,7 +396,7 @@  void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
 	}
 }
 
-static void escb_tasklet_complete(struct asd_ascb *ascb,
+static void escb_work_complete(struct asd_ascb *ascb,
 				  struct done_list_struct *dl)
 {
 	struct asd_ha_struct *asd_ha = ascb->ha;
@@ -546,21 +546,21 @@  static void escb_tasklet_complete(struct asd_ascb *ascb,
 	switch (sb_opcode) {
 	case BYTES_DMAED:
 		ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __func__, phy_id);
-		asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id);
+		asd_bytes_dmaed_work(ascb, dl, edb, phy_id);
 		break;
 	case PRIMITIVE_RECVD:
 		ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __func__,
 			    phy_id);
-		asd_primitive_rcvd_tasklet(ascb, dl, phy_id);
+		asd_primitive_rcvd_work(ascb, dl, phy_id);
 		break;
 	case PHY_EVENT:
 		ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __func__, phy_id);
-		asd_phy_event_tasklet(ascb, dl);
+		asd_phy_event_work(ascb, dl);
 		break;
 	case LINK_RESET_ERROR:
 		ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __func__,
 			    phy_id);
-		asd_link_reset_err_tasklet(ascb, dl, phy_id);
+		asd_link_reset_err_work(ascb, dl, phy_id);
 		break;
 	case TIMER_EVENT:
 		ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n",
@@ -600,7 +600,7 @@  int asd_init_post_escbs(struct asd_ha_struct *asd_ha)
 	int i;
 
 	for (i = 0; i < seq->num_escbs; i++)
-		seq->escb_arr[i]->tasklet_complete = escb_tasklet_complete;
+		seq->escb_arr[i]->work_complete = escb_work_complete;
 
 	ASD_DPRINTK("posting %d escbs\n", i);
 	return asd_post_escb_list(asd_ha, seq->escb_arr[0], seq->num_escbs);
@@ -613,7 +613,7 @@  int asd_init_post_escbs(struct asd_ha_struct *asd_ha)
 			    | CURRENT_OOB_ERROR)
 
 /**
- * control_phy_tasklet_complete -- tasklet complete for CONTROL PHY ascb
+ * control_phy_work_complete -- BH work complete for CONTROL PHY ascb
  * @ascb: pointer to an ascb
  * @dl: pointer to the done list entry
  *
@@ -623,7 +623,7 @@  int asd_init_post_escbs(struct asd_ha_struct *asd_ha)
  *  - if a device is connected to the LED, it is lit,
  *  - if no device is connected to the LED, is is dimmed (off).
  */
-static void control_phy_tasklet_complete(struct asd_ascb *ascb,
+static void control_phy_work_complete(struct asd_ascb *ascb,
 					 struct done_list_struct *dl)
 {
 	struct asd_ha_struct *asd_ha = ascb->ha;
@@ -758,9 +758,9 @@  static void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
  *
  * This function builds a CONTROL PHY scb.  No allocation of any kind
  * is performed. @ascb is allocated with the list function.
- * The caller can override the ascb->tasklet_complete to point
+ * The caller can override the ascb->work_complete to point
  * to its own callback function.  It must call asd_ascb_free()
- * at its tasklet complete function.
+ * at its BH work complete function.
  * See the default implementation.
  */
 void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
@@ -806,14 +806,14 @@  void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
 
 	control_phy->conn_handle = cpu_to_le16(0xFFFF);
 
-	ascb->tasklet_complete = control_phy_tasklet_complete;
+	ascb->work_complete = control_phy_work_complete;
 }
 
 /* ---------- INITIATE LINK ADM TASK ---------- */
 
 #if 0
 
-static void link_adm_tasklet_complete(struct asd_ascb *ascb,
+static void link_adm_work_complete(struct asd_ascb *ascb,
 				      struct done_list_struct *dl)
 {
 	u8 opcode = dl->opcode;
@@ -842,7 +842,7 @@  void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
 	link_adm->sub_func = subfunc;
 	link_adm->conn_handle = cpu_to_le16(0xFFFF);
 
-	ascb->tasklet_complete = link_adm_tasklet_complete;
+	ascb->work_complete = link_adm_work_complete;
 }
 
 #endif  /*  0  */
diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c
index 4bfd03724ad6..2e1e30ba5555 100644
--- a/drivers/scsi/aic94xx/aic94xx_task.c
+++ b/drivers/scsi/aic94xx/aic94xx_task.c
@@ -138,9 +138,9 @@  static void asd_unmap_scatterlist(struct asd_ascb *ascb)
 			     task->num_scatter, task->data_dir);
 }
 
-/* ---------- Task complete tasklet ---------- */
+/* ---------- Task complete BH work ---------- */
 
-static void asd_get_response_tasklet(struct asd_ascb *ascb,
+static void asd_get_response_work(struct asd_ascb *ascb,
 				     struct done_list_struct *dl)
 {
 	struct asd_ha_struct *asd_ha = ascb->ha;
@@ -194,7 +194,7 @@  static void asd_get_response_tasklet(struct asd_ascb *ascb,
 	asd_invalidate_edb(escb, edb_id);
 }
 
-static void asd_task_tasklet_complete(struct asd_ascb *ascb,
+static void asd_task_work_complete(struct asd_ascb *ascb,
 				      struct done_list_struct *dl)
 {
 	struct sas_task *task = ascb->uldd_task;
@@ -224,7 +224,7 @@  static void asd_task_tasklet_complete(struct asd_ascb *ascb,
 	case TC_ATA_RESP:
 		ts->resp = SAS_TASK_COMPLETE;
 		ts->stat = SAS_PROTO_RESPONSE;
-		asd_get_response_tasklet(ascb, dl);
+		asd_get_response_work(ascb, dl);
 		break;
 	case TF_OPEN_REJECT:
 		ts->resp = SAS_TASK_UNDELIVERED;
@@ -392,7 +392,7 @@  static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task,
 
 		scb->ata_task.flags = 0;
 	}
-	ascb->tasklet_complete = asd_task_tasklet_complete;
+	ascb->work_complete = asd_task_work_complete;
 
 	if (likely(!task->ata_task.device_control_reg_update))
 		res = asd_map_scatterlist(task, scb->ata_task.sg_element,
@@ -440,7 +440,7 @@  static int asd_build_smp_ascb(struct asd_ascb *ascb, struct sas_task *task,
 	scb->smp_task.conn_handle = cpu_to_le16((u16)
 						(unsigned long)dev->lldd_dev);
 
-	ascb->tasklet_complete = asd_task_tasklet_complete;
+	ascb->work_complete = asd_task_work_complete;
 
 	return 0;
 }
@@ -490,7 +490,7 @@  static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task,
 	scb->ssp_task.data_dir = data_dir_flags[task->data_dir];
 	scb->ssp_task.retry_count = scb->ssp_task.retry_count;
 
-	ascb->tasklet_complete = asd_task_tasklet_complete;
+	ascb->work_complete = asd_task_work_complete;
 
 	res = asd_map_scatterlist(task, scb->ssp_task.sg_element, gfp_flags);
 
diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c
index 27d32b8c2987..5eb0cc57ed2a 100644
--- a/drivers/scsi/aic94xx/aic94xx_tmf.c
+++ b/drivers/scsi/aic94xx/aic94xx_tmf.c
@@ -15,13 +15,13 @@ 
 /* ---------- Internal enqueue ---------- */
 
 static int asd_enqueue_internal(struct asd_ascb *ascb,
-		void (*tasklet_complete)(struct asd_ascb *,
+		void (*work_complete)(struct asd_ascb *,
 					 struct done_list_struct *),
 				void (*timed_out)(struct timer_list *t))
 {
 	int res;
 
-	ascb->tasklet_complete = tasklet_complete;
+	ascb->work_complete = work_complete;
 	ascb->uldd_timer = 1;
 
 	ascb->timer.function = timed_out;
@@ -37,7 +37,7 @@  static int asd_enqueue_internal(struct asd_ascb *ascb,
 
 /* ---------- CLEAR NEXUS ---------- */
 
-struct tasklet_completion_status {
+struct work_completion_status {
 	int	dl_opcode;
 	int	tmf_state;
 	u8	tag_valid:1;
@@ -45,7 +45,7 @@  struct tasklet_completion_status {
 };
 
 #define DECLARE_TCS(tcs) \
-	struct tasklet_completion_status tcs = { \
+	struct work_completion_status tcs = { \
 		.dl_opcode = 0, \
 		.tmf_state = 0, \
 		.tag_valid = 0, \
@@ -53,10 +53,10 @@  struct tasklet_completion_status {
 	}
 
 
-static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
+static void asd_clear_nexus_work_complete(struct asd_ascb *ascb,
 					     struct done_list_struct *dl)
 {
-	struct tasklet_completion_status *tcs = ascb->uldd_task;
+	struct work_completion_status *tcs = ascb->uldd_task;
 	ASD_DPRINTK("%s: here\n", __func__);
 	if (!del_timer(&ascb->timer)) {
 		ASD_DPRINTK("%s: couldn't delete timer\n", __func__);
@@ -71,7 +71,7 @@  static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
 static void asd_clear_nexus_timedout(struct timer_list *t)
 {
 	struct asd_ascb *ascb = from_timer(ascb, t, timer);
-	struct tasklet_completion_status *tcs = ascb->uldd_task;
+	struct work_completion_status *tcs = ascb->uldd_task;
 
 	ASD_DPRINTK("%s: here\n", __func__);
 	tcs->dl_opcode = TMF_RESP_FUNC_FAILED;
@@ -98,7 +98,7 @@  static void asd_clear_nexus_timedout(struct timer_list *t)
 
 #define CLEAR_NEXUS_POST        \
 	ASD_DPRINTK("%s: POST\n", __func__); \
-	res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \
+	res = asd_enqueue_internal(ascb, asd_clear_nexus_work_complete, \
 				   asd_clear_nexus_timedout);              \
 	if (res)                \
 		goto out_err;   \
@@ -245,14 +245,14 @@  static int asd_clear_nexus_index(struct sas_task *task)
 static void asd_tmf_timedout(struct timer_list *t)
 {
 	struct asd_ascb *ascb = from_timer(ascb, t, timer);
-	struct tasklet_completion_status *tcs = ascb->uldd_task;
+	struct work_completion_status *tcs = ascb->uldd_task;
 
 	ASD_DPRINTK("tmf timed out\n");
 	tcs->tmf_state = TMF_RESP_FUNC_FAILED;
 	complete(ascb->completion);
 }
 
-static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
+static int asd_get_tmf_resp_work(struct asd_ascb *ascb,
 				    struct done_list_struct *dl)
 {
 	struct asd_ha_struct *asd_ha = ascb->ha;
@@ -270,7 +270,7 @@  static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
 	struct ssp_response_iu   *ru;
 	int res = TMF_RESP_FUNC_FAILED;
 
-	ASD_DPRINTK("tmf resp tasklet\n");
+	ASD_DPRINTK("tmf resp BH work\n");
 
 	spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
 	escb = asd_tc_index_find(&asd_ha->seq,
@@ -298,21 +298,21 @@  static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
 	return res;
 }
 
-static void asd_tmf_tasklet_complete(struct asd_ascb *ascb,
+static void asd_tmf_work_complete(struct asd_ascb *ascb,
 				     struct done_list_struct *dl)
 {
-	struct tasklet_completion_status *tcs;
+	struct work_completion_status *tcs;
 
 	if (!del_timer(&ascb->timer))
 		return;
 
 	tcs = ascb->uldd_task;
-	ASD_DPRINTK("tmf tasklet complete\n");
+	ASD_DPRINTK("tmf BH work complete\n");
 
 	tcs->dl_opcode = dl->opcode;
 
 	if (dl->opcode == TC_SSP_RESP) {
-		tcs->tmf_state = asd_get_tmf_resp_tasklet(ascb, dl);
+		tcs->tmf_state = asd_get_tmf_resp_work(ascb, dl);
 		tcs->tag_valid = ascb->tag_valid;
 		tcs->tag = ascb->tag;
 	}
@@ -452,7 +452,7 @@  int asd_abort_task(struct sas_task *task)
 	scb->abort_task.index = cpu_to_le16((u16)tascb->tc_index);
 	scb->abort_task.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
 
-	res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
+	res = asd_enqueue_internal(ascb, asd_tmf_work_complete,
 				   asd_tmf_timedout);
 	if (res)
 		goto out_free;
@@ -600,7 +600,7 @@  static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun,
 	if (tmf == TMF_QUERY_TASK)
 		scb->ssp_tmf.index = cpu_to_le16(index);
 
-	res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
+	res = asd_enqueue_internal(ascb, asd_tmf_work_complete,
 				   asd_tmf_timedout);
 	if (res)
 		goto out_err;
diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
index ed63f7a9ea54..7c9db9e80576 100644
--- a/drivers/scsi/esas2r/esas2r.h
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -900,7 +900,7 @@  struct esas2r_adapter {
 	struct esas2r_flash_context flash_context;
 	u32 num_targets_backend;
 	u32 ioctl_tunnel;
-	struct tasklet_struct tasklet;
+	struct work_struct work;
 	struct pci_dev *pcid;
 	struct Scsi_Host *host;
 	unsigned int index;
@@ -992,7 +992,7 @@  int esas2r_write_vda(struct esas2r_adapter *a, const char *buf, long off,
 int esas2r_read_fs(struct esas2r_adapter *a, char *buf, long off, int count);
 int esas2r_write_fs(struct esas2r_adapter *a, const char *buf, long off,
 		    int count);
-void esas2r_adapter_tasklet(unsigned long context);
+void esas2r_adapter_work(struct work_struct *work);
 irqreturn_t esas2r_interrupt(int irq, void *dev_id);
 irqreturn_t esas2r_msi_interrupt(int irq, void *dev_id);
 void esas2r_kickoff_timer(struct esas2r_adapter *a);
@@ -1022,7 +1022,7 @@  bool esas2r_init_adapter_hw(struct esas2r_adapter *a, bool init_poll);
 void esas2r_start_request(struct esas2r_adapter *a, struct esas2r_request *rq);
 bool esas2r_send_task_mgmt(struct esas2r_adapter *a,
 			   struct esas2r_request *rqaux, u8 task_mgt_func);
-void esas2r_do_tasklet_tasks(struct esas2r_adapter *a);
+void esas2r_do_work_tasks(struct esas2r_adapter *a);
 void esas2r_adapter_interrupt(struct esas2r_adapter *a);
 void esas2r_do_deferred_processes(struct esas2r_adapter *a);
 void esas2r_reset_bus(struct esas2r_adapter *a);
@@ -1283,7 +1283,7 @@  static inline void esas2r_rq_destroy_request(struct esas2r_request *rq,
 	rq->data_buf = NULL;
 }
 
-static inline bool esas2r_is_tasklet_pending(struct esas2r_adapter *a)
+static inline bool esas2r_is_work_pending(struct esas2r_adapter *a)
 {
 
 	return test_bit(AF_BUSRST_NEEDED, &a->flags) ||
@@ -1327,11 +1327,11 @@  static inline void esas2r_enable_chip_interrupts(struct esas2r_adapter *a)
 /* Schedule a TASKLET to perform non-interrupt tasks that may require delays
  * or long completion times.
  */
-static inline void esas2r_schedule_tasklet(struct esas2r_adapter *a)
+static inline void esas2r_schedule_work(struct esas2r_adapter *a)
 {
 	/* make sure we don't schedule twice */
 	if (!test_and_set_bit(AF_TASKLET_SCHEDULED, &a->flags))
-		tasklet_hi_schedule(&a->tasklet);
+		queue_work(system_bh_highpri_wq, &a->work);
 }
 
 static inline void esas2r_enable_heartbeat(struct esas2r_adapter *a)
diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c
index c1a5ab662dc8..cf149a69ec55 100644
--- a/drivers/scsi/esas2r/esas2r_init.c
+++ b/drivers/scsi/esas2r/esas2r_init.c
@@ -401,9 +401,7 @@  int esas2r_init_adapter(struct Scsi_Host *host, struct pci_dev *pcid,
 		return 0;
 	}
 
-	tasklet_init(&a->tasklet,
-		     esas2r_adapter_tasklet,
-		     (unsigned long)a);
+	INIT_WORK(&a->work, esas2r_adapter_work);
 
 	/*
 	 * Disable chip interrupts to prevent spurious interrupts
@@ -441,7 +439,7 @@  static void esas2r_adapter_power_down(struct esas2r_adapter *a,
 	    &&  (!test_bit(AF_DEGRADED_MODE, &a->flags))) {
 		if (!power_management) {
 			del_timer_sync(&a->timer);
-			tasklet_kill(&a->tasklet);
+			cancel_work_sync(&a->work);
 		}
 		esas2r_power_down(a);
 
@@ -1346,7 +1344,7 @@  bool esas2r_init_adapter_hw(struct esas2r_adapter *a, bool init_poll)
 		u32 deltatime;
 
 		/*
-		 * Block Tasklets from getting scheduled and indicate this is
+		 * Block Works from getting scheduled and indicate this is
 		 * polled discovery.
 		 */
 		set_bit(AF_TASKLET_SCHEDULED, &a->flags);
@@ -1394,8 +1392,8 @@  bool esas2r_init_adapter_hw(struct esas2r_adapter *a, bool init_poll)
 				nexttick -= deltatime;
 
 			/* Do any deferred processing */
-			if (esas2r_is_tasklet_pending(a))
-				esas2r_do_tasklet_tasks(a);
+			if (esas2r_is_work_pending(a))
+				esas2r_do_work_tasks(a);
 
 		}
 
@@ -1463,7 +1461,7 @@  void esas2r_reset_adapter(struct esas2r_adapter *a)
 {
 	set_bit(AF_OS_RESET, &a->flags);
 	esas2r_local_reset_adapter(a);
-	esas2r_schedule_tasklet(a);
+	esas2r_schedule_work(a);
 }
 
 void esas2r_reset_chip(struct esas2r_adapter *a)
diff --git a/drivers/scsi/esas2r/esas2r_int.c b/drivers/scsi/esas2r/esas2r_int.c
index 5281d9356327..54e6eea522f8 100644
--- a/drivers/scsi/esas2r/esas2r_int.c
+++ b/drivers/scsi/esas2r/esas2r_int.c
@@ -97,7 +97,7 @@  irqreturn_t esas2r_interrupt(int irq, void *dev_id)
 		return IRQ_NONE;
 
 	set_bit(AF2_INT_PENDING, &a->flags2);
-	esas2r_schedule_tasklet(a);
+	esas2r_schedule_work(a);
 
 	return IRQ_HANDLED;
 }
@@ -162,7 +162,7 @@  irqreturn_t esas2r_msi_interrupt(int irq, void *dev_id)
 	if (likely(atomic_read(&a->disable_cnt) == 0))
 		esas2r_do_deferred_processes(a);
 
-	esas2r_do_tasklet_tasks(a);
+	esas2r_do_work_tasks(a);
 
 	return 1;
 }
@@ -327,8 +327,8 @@  void esas2r_do_deferred_processes(struct esas2r_adapter *a)
 
 	/* Clear off the completed list to be processed later. */
 
-	if (esas2r_is_tasklet_pending(a)) {
-		esas2r_schedule_tasklet(a);
+	if (esas2r_is_work_pending(a)) {
+		esas2r_schedule_work(a);
 
 		startreqs = 0;
 	}
@@ -476,7 +476,7 @@  static void esas2r_process_bus_reset(struct esas2r_adapter *a)
 	esas2r_trace_exit();
 }
 
-static void esas2r_chip_rst_needed_during_tasklet(struct esas2r_adapter *a)
+static void esas2r_chip_rst_needed_during_work(struct esas2r_adapter *a)
 {
 
 	clear_bit(AF_CHPRST_NEEDED, &a->flags);
@@ -558,7 +558,7 @@  static void esas2r_chip_rst_needed_during_tasklet(struct esas2r_adapter *a)
 	}
 }
 
-static void esas2r_handle_chip_rst_during_tasklet(struct esas2r_adapter *a)
+static void esas2r_handle_chip_rst_during_work(struct esas2r_adapter *a)
 {
 	while (test_bit(AF_CHPRST_DETECTED, &a->flags)) {
 		/*
@@ -614,15 +614,15 @@  static void esas2r_handle_chip_rst_during_tasklet(struct esas2r_adapter *a)
 
 
 /* Perform deferred tasks when chip interrupts are disabled */
-void esas2r_do_tasklet_tasks(struct esas2r_adapter *a)
+void esas2r_do_work_tasks(struct esas2r_adapter *a)
 {
 
 	if (test_bit(AF_CHPRST_NEEDED, &a->flags) ||
 	    test_bit(AF_CHPRST_DETECTED, &a->flags)) {
 		if (test_bit(AF_CHPRST_NEEDED, &a->flags))
-			esas2r_chip_rst_needed_during_tasklet(a);
+			esas2r_chip_rst_needed_during_work(a);
 
-		esas2r_handle_chip_rst_during_tasklet(a);
+		esas2r_handle_chip_rst_during_work(a);
 	}
 
 	if (test_bit(AF_BUSRST_NEEDED, &a->flags)) {
diff --git a/drivers/scsi/esas2r/esas2r_io.c b/drivers/scsi/esas2r/esas2r_io.c
index a8df916cd57a..d45e6e16a858 100644
--- a/drivers/scsi/esas2r/esas2r_io.c
+++ b/drivers/scsi/esas2r/esas2r_io.c
@@ -851,7 +851,7 @@  void esas2r_reset_bus(struct esas2r_adapter *a)
 		set_bit(AF_BUSRST_PENDING, &a->flags);
 		set_bit(AF_OS_RESET, &a->flags);
 
-		esas2r_schedule_tasklet(a);
+		esas2r_schedule_work(a);
 	}
 }
 
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
index f700a16cd885..e4e378adf7ed 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -1543,10 +1543,10 @@  void esas2r_complete_request_cb(struct esas2r_adapter *a,
 	esas2r_free_request(a, rq);
 }
 
-/* Run tasklet to handle stuff outside of interrupt context. */
-void esas2r_adapter_tasklet(unsigned long context)
+/* Run BH work to handle stuff outside of interrupt context. */
+void esas2r_adapter_work(struct work_struct *t)
 {
-	struct esas2r_adapter *a = (struct esas2r_adapter *)context;
+	struct esas2r_adapter *a = from_work(a, t, work);
 
 	if (unlikely(test_bit(AF2_TIMER_TICK, &a->flags2))) {
 		clear_bit(AF2_TIMER_TICK, &a->flags2);
@@ -1558,14 +1558,14 @@  void esas2r_adapter_tasklet(unsigned long context)
 		esas2r_adapter_interrupt(a);
 	}
 
-	if (esas2r_is_tasklet_pending(a))
-		esas2r_do_tasklet_tasks(a);
+	if (esas2r_is_work_pending(a))
+		esas2r_do_work_tasks(a);
 
-	if (esas2r_is_tasklet_pending(a)
+	if (esas2r_is_work_pending(a)
 	    || (test_bit(AF2_INT_PENDING, &a->flags2))
 	    || (test_bit(AF2_TIMER_TICK, &a->flags2))) {
 		clear_bit(AF_TASKLET_SCHEDULED, &a->flags);
-		esas2r_schedule_tasklet(a);
+		esas2r_schedule_work(a);
 	} else {
 		clear_bit(AF_TASKLET_SCHEDULED, &a->flags);
 	}
@@ -1589,7 +1589,7 @@  static void esas2r_timer_callback(struct timer_list *t)
 
 	set_bit(AF2_TIMER_TICK, &a->flags2);
 
-	esas2r_schedule_tasklet(a);
+	esas2r_schedule_work(a);
 
 	esas2r_kickoff_timer(a);
 }
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 05b126bfd18b..6a8ecd3358c4 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -899,7 +899,7 @@  static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
 
 	ibmvfc_dbg(vhost, "Releasing CRQ\n");
 	free_irq(vdev->irq, vhost);
-	tasklet_kill(&vhost->tasklet);
+	cancel_work_sync(&vhost->work);
 	do {
 		if (rc)
 			msleep(100);
@@ -3767,21 +3767,21 @@  static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance)
 
 	spin_lock_irqsave(vhost->host->host_lock, flags);
 	vio_disable_interrupts(to_vio_dev(vhost->dev));
-	tasklet_schedule(&vhost->tasklet);
+	queue_work(system_bh_wq, &vhost->work);
 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
 	return IRQ_HANDLED;
 }
 
 /**
- * ibmvfc_tasklet - Interrupt handler tasklet
+ * ibmvfc_work - Interrupt handler work
  * @data:		ibmvfc host struct
  *
  * Returns:
  *	Nothing
  **/
-static void ibmvfc_tasklet(void *data)
+static void ibmvfc_work(struct work_struct *t)
 {
-	struct ibmvfc_host *vhost = data;
+	struct ibmvfc_host *vhost = from_work(vhost, t, work);
 	struct vio_dev *vdev = to_vio_dev(vhost->dev);
 	struct ibmvfc_crq *crq;
 	struct ibmvfc_async_crq *async;
@@ -5885,7 +5885,7 @@  static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
 
 	retrc = 0;
 
-	tasklet_init(&vhost->tasklet, (void *)ibmvfc_tasklet, (unsigned long)vhost);
+	INIT_WORK(&vhost->work, ibmvfc_work);
 
 	if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) {
 		dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc);
@@ -5901,7 +5901,7 @@  static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
 	return retrc;
 
 req_irq_failed:
-	tasklet_kill(&vhost->tasklet);
+	cancel_work_sync(&vhost->work);
 	do {
 		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
 	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
@@ -6474,7 +6474,7 @@  static int ibmvfc_resume(struct device *dev)
 
 	spin_lock_irqsave(vhost->host->host_lock, flags);
 	vio_disable_interrupts(vdev);
-	tasklet_schedule(&vhost->tasklet);
+	queue_work(system_bh_wq, &vhost->work);
 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
 	return 0;
 }
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 745ad5ac7251..42861ee62bf9 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -12,6 +12,7 @@ 
 
 #include <linux/list.h>
 #include <linux/types.h>
+#include <linux/workqueue.h>
 #include <scsi/viosrp.h>
 
 #define IBMVFC_NAME	"ibmvfc"
@@ -910,7 +911,7 @@  struct ibmvfc_host {
 	char partition_name[97];
 	void (*job_step) (struct ibmvfc_host *);
 	struct task_struct *work_thread;
-	struct tasklet_struct tasklet;
+	struct work_struct work;
 	struct work_struct rport_add_work_q;
 	wait_queue_head_t init_wait_q;
 	wait_queue_head_t work_wait_q;
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index 71f3e9563520..91e1600bf219 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -125,7 +125,7 @@  static irqreturn_t ibmvscsi_handle_event(int irq, void *dev_instance)
 	struct ibmvscsi_host_data *hostdata =
 	    (struct ibmvscsi_host_data *)dev_instance;
 	vio_disable_interrupts(to_vio_dev(hostdata->dev));
-	tasklet_schedule(&hostdata->srp_task);
+	queue_work(system_bh_wq, &hostdata->srp_task);
 	return IRQ_HANDLED;
 }
 
@@ -145,7 +145,7 @@  static void ibmvscsi_release_crq_queue(struct crq_queue *queue,
 	long rc = 0;
 	struct vio_dev *vdev = to_vio_dev(hostdata->dev);
 	free_irq(vdev->irq, (void *)hostdata);
-	tasklet_kill(&hostdata->srp_task);
+	cancel_work_sync(&hostdata->srp_task);
 	do {
 		if (rc)
 			msleep(100);
@@ -367,8 +367,7 @@  static int ibmvscsi_init_crq_queue(struct crq_queue *queue,
 	queue->cur = 0;
 	spin_lock_init(&queue->lock);
 
-	tasklet_init(&hostdata->srp_task, (void *)ibmvscsi_task,
-		     (unsigned long)hostdata);
+	INIT_WORK(&hostdata->srp_task, ibmvscsi_work);
 
 	if (request_irq(vdev->irq,
 			ibmvscsi_handle_event,
@@ -387,7 +386,7 @@  static int ibmvscsi_init_crq_queue(struct crq_queue *queue,
 	return retrc;
 
       req_irq_failed:
-	tasklet_kill(&hostdata->srp_task);
+	cancel_work_sync(&hostdata->srp_task);
 	rc = 0;
 	do {
 		if (rc)
@@ -2194,9 +2193,10 @@  static int ibmvscsi_work_to_do(struct ibmvscsi_host_data *hostdata)
 	return rc;
 }
 
-static int ibmvscsi_work(void *data)
+static int ibmvscsi_work(struct work_struct *t)
 {
-	struct ibmvscsi_host_data *hostdata = data;
+	struct ibmvscsi_host_data *hostdata =
+		from_work(hostdata, t, srp_task);
 	int rc;
 
 	set_user_nice(current, MIN_NICE);
@@ -2371,7 +2371,7 @@  static int ibmvscsi_resume(struct device *dev)
 {
 	struct ibmvscsi_host_data *hostdata = dev_get_drvdata(dev);
 	vio_disable_interrupts(to_vio_dev(hostdata->dev));
-	tasklet_schedule(&hostdata->srp_task);
+	queue_work(system_bh_wq, &hostdata->srp_task);
 
 	return 0;
 }
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h
index e60916ef7a49..cfc0a70c434c 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.h
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.h
@@ -19,6 +19,7 @@ 
 #include <linux/list.h>
 #include <linux/completion.h>
 #include <linux/interrupt.h>
+#include <linux/workqueue.h>
 #include <scsi/viosrp.h>
 
 struct scsi_cmnd;
@@ -90,7 +91,7 @@  struct ibmvscsi_host_data {
 	struct device *dev;
 	struct event_pool pool;
 	struct crq_queue queue;
-	struct tasklet_struct srp_task;
+	struct work_struct srp_task;
 	struct list_head sent;
 	struct Scsi_Host *host;
 	struct task_struct *work_thread;
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
index 68b99924ee4f..204975fb61ba 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
@@ -2948,7 +2948,7 @@  static irqreturn_t ibmvscsis_interrupt(int dummy, void *data)
 	struct scsi_info *vscsi = data;
 
 	vio_disable_interrupts(vscsi->dma_dev);
-	tasklet_schedule(&vscsi->work_task);
+	queue_work(system_bh_wq, &scsi->work_task);
 
 	return IRQ_HANDLED;
 }
@@ -3309,7 +3309,7 @@  static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg,
 
 /**
  * ibmvscsis_handle_crq() - Handle CRQ
- * @data:	Pointer to our adapter structure
+ * @t:	Pointer to work_struct
  *
  * Read the command elements from the command queue and copy the payloads
  * associated with the command elements to local memory and execute the
@@ -3317,9 +3317,9 @@  static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg,
  *
  * Note: this is an edge triggered interrupt. It can not be shared.
  */
-static void ibmvscsis_handle_crq(unsigned long data)
+static void ibmvscsis_handle_crq(struct work_struct *t)
 {
-	struct scsi_info *vscsi = (struct scsi_info *)data;
+	struct scsi_info *vscsi = from_work(scsi, t, work_task);
 	struct viosrp_crq *crq;
 	long rc;
 	bool ack = true;
@@ -3530,8 +3530,7 @@  static int ibmvscsis_probe(struct vio_dev *vdev,
 	dev_dbg(&vscsi->dev, "probe hrc %ld, client partition num %d\n",
 		hrc, vscsi->client_data.partition_number);
 
-	tasklet_init(&vscsi->work_task, ibmvscsis_handle_crq,
-		     (unsigned long)vscsi);
+	INIT_WORK(&vscsi->work_task, ibmvscsis_handle_crq);
 
 	init_completion(&vscsi->wait_idle);
 	init_completion(&vscsi->unconfig);
@@ -3565,7 +3564,7 @@  static int ibmvscsis_probe(struct vio_dev *vdev,
 free_buf:
 	kfree(vscsi->map_buf);
 destroy_queue:
-	tasklet_kill(&vscsi->work_task);
+	cancel_work_sync(&vscsi->work_task);
 	ibmvscsis_unregister_command_q(vscsi);
 	ibmvscsis_destroy_command_q(vscsi);
 free_timer:
@@ -3602,7 +3601,7 @@  static void ibmvscsis_remove(struct vio_dev *vdev)
 	dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE,
 			 DMA_BIDIRECTIONAL);
 	kfree(vscsi->map_buf);
-	tasklet_kill(&vscsi->work_task);
+	cancel_work_sync(&vscsi->work_task);
 	ibmvscsis_destroy_command_q(vscsi);
 	ibmvscsis_freetimer(vscsi);
 	ibmvscsis_free_cmds(vscsi);
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
index 7ae074e5d7a1..e7dea32e4dbc 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
@@ -18,6 +18,7 @@ 
 #define __H_IBMVSCSI_TGT
 
 #include <linux/interrupt.h>
+#include <linux/workqueue.h>
 #include "libsrp.h"
 
 #define SYS_ID_NAME_LEN		64
@@ -295,7 +296,7 @@  struct scsi_info {
 	struct vio_dev *dma_dev;
 	struct srp_target target;
 	struct ibmvscsis_tport tport;
-	struct tasklet_struct work_task;
+	struct work_struct work_task;
 	struct work_struct proc_work;
 };
 
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 35589b6af90d..d911dc159809 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -220,7 +220,7 @@  irqreturn_t isci_msix_isr(int vec, void *data)
 	struct isci_host *ihost = data;
 
 	if (sci_controller_isr(ihost))
-		tasklet_schedule(&ihost->completion_tasklet);
+		queue_work(system_bh_wq, &ihost->completion_work);
 
 	return IRQ_HANDLED;
 }
@@ -610,7 +610,7 @@  irqreturn_t isci_intx_isr(int vec, void *data)
 
 	if (sci_controller_isr(ihost)) {
 		writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status);
-		tasklet_schedule(&ihost->completion_tasklet);
+		queue_work(system_bh_wq, &ihost->completion_work);
 		ret = IRQ_HANDLED;
 	} else if (sci_controller_error_isr(ihost)) {
 		spin_lock(&ihost->scic_lock);
@@ -1106,14 +1106,14 @@  void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_ta
 /**
  * isci_host_completion_routine() - This function is the delayed service
  *    routine that calls the sci core library's completion handler. It's
- *    scheduled as a tasklet from the interrupt service routine when interrupts
+ *    scheduled as a BH work from the interrupt service routine when interrupts
  *    in use, or set as the timeout function in polled mode.
- * @data: This parameter specifies the ISCI host object
+ * @t: pointer to the work_struct
  *
  */
-void isci_host_completion_routine(unsigned long data)
+void isci_host_completion_routine(struct work_struct *t)
 {
-	struct isci_host *ihost = (struct isci_host *)data;
+	struct isci_host *ihost = from_work(ihost, t, completion_work);
 	u16 active;
 
 	spin_lock_irq(&ihost->scic_lock);
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index 52388374cf31..8350e70bfb3a 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -131,8 +131,8 @@  struct sci_port_configuration_agent {
  * @device_table: rni (hw remote node index) to remote device lookup table
  * @available_remote_nodes: rni allocator
  * @power_control: manage device spin up
- * @io_request_sequence: generation number for tci's (task contexts)
- * @task_context_table: hw task context table
+ * @io_request_sequence: generation number for tci's (bh contexts)
+ * @task_context_table: hw bh context table
  * @remote_node_context_table: hw remote node context table
  * @completion_queue: hw-producer driver-consumer communication ring
  * @completion_queue_get: tracks the driver 'head' of the ring to notify hw
@@ -203,7 +203,7 @@  struct isci_host {
 	#define IHOST_IRQ_ENABLED 2
 	unsigned long flags;
 	wait_queue_head_t eventq;
-	struct tasklet_struct completion_tasklet;
+	struct work_struct completion_work;
 	spinlock_t scic_lock;
 	struct isci_request *reqs[SCI_MAX_IO_REQUESTS];
 	struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES];
@@ -478,7 +478,7 @@  void isci_tci_free(struct isci_host *ihost, u16 tci);
 void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task);
 
 int isci_host_init(struct isci_host *);
-void isci_host_completion_routine(unsigned long data);
+void isci_host_completion_routine(struct work_struct *t);
 void isci_host_deinit(struct isci_host *);
 void sci_controller_disable_interrupts(struct isci_host *ihost);
 bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost);
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index c582a3932cea..605e4d965e04 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -510,8 +510,8 @@  static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
 	init_waitqueue_head(&ihost->eventq);
 	ihost->sas_ha.dev = &ihost->pdev->dev;
 	ihost->sas_ha.lldd_ha = ihost;
-	tasklet_init(&ihost->completion_tasklet,
-		     isci_host_completion_routine, (unsigned long)ihost);
+	INIT_WORK(&ihost->completion_work,
+		     isci_host_completion_routine);
 
 	/* validate module parameters */
 	/* TODO: kill struct sci_user_parameters and reference directly */
diff --git a/drivers/scsi/megaraid/mega_common.h b/drivers/scsi/megaraid/mega_common.h
index 2ad0aa2f837d..cff3e98dbe31 100644
--- a/drivers/scsi/megaraid/mega_common.h
+++ b/drivers/scsi/megaraid/mega_common.h
@@ -24,6 +24,7 @@ 
 #include <linux/list.h>
 #include <linux/moduleparam.h>
 #include <linux/dma-mapping.h>
+#include <linux/workqueue.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
@@ -95,7 +96,7 @@  typedef struct {
 
 /**
  * struct adapter_t - driver's initialization structure
- * @aram dpc_h			: tasklet handle
+ * @aram dpc_h			: work handle
  * @pdev			: pci configuration pointer for kernel
  * @host			: pointer to host structure of mid-layer
  * @lock			: synchronization lock for mid-layer and driver
@@ -149,7 +150,7 @@  typedef struct {
 #define VERSION_SIZE	16
 
 typedef struct {
-	struct tasklet_struct	dpc_h;
+	struct work_struct	dpc_h;
 	struct pci_dev		*pdev;
 	struct Scsi_Host	*host;
 	spinlock_t		lock;
diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c
index bc867da650b6..4ce033cb9554 100644
--- a/drivers/scsi/megaraid/megaraid_mbox.c
+++ b/drivers/scsi/megaraid/megaraid_mbox.c
@@ -119,7 +119,7 @@  static void megaraid_mbox_prepare_epthru(adapter_t *, scb_t *,
 
 static irqreturn_t megaraid_isr(int, void *);
 
-static void megaraid_mbox_dpc(unsigned long);
+static void megaraid_mbox_dpc(struct work_struct *);
 
 static ssize_t megaraid_mbox_app_hndl_show(struct device *, struct device_attribute *attr, char *);
 static ssize_t megaraid_mbox_ld_show(struct device *, struct device_attribute *attr, char *);
@@ -879,9 +879,8 @@  megaraid_init_mbox(adapter_t *adapter)
 		}
 	}
 
-	// setup tasklet for DPC
-	tasklet_init(&adapter->dpc_h, megaraid_mbox_dpc,
-			(unsigned long)adapter);
+	/* Initialize the work for DPC */
+	INIT_WORK(&adapter->dpc_h, megaraid_mbox_dpc);
 
 	con_log(CL_DLEVEL1, (KERN_INFO
 		"megaraid mbox hba successfully initialized\n"));
@@ -917,7 +916,7 @@  megaraid_fini_mbox(adapter_t *adapter)
 	// flush all caches
 	megaraid_mbox_flush_cache(adapter);
 
-	tasklet_kill(&adapter->dpc_h);
+	cancel_work_sync(&adapter->dpc_h);
 
 	megaraid_sysfs_free_resources(adapter);
 
@@ -2127,7 +2126,7 @@  megaraid_ack_sequence(adapter_t *adapter)
 
 	// schedule the DPC if there is some work for it
 	if (handled)
-		tasklet_schedule(&adapter->dpc_h);
+		queue_work(system_bh_wq, &adapter->dpc_h);
 
 	return handled;
 }
@@ -2158,17 +2157,17 @@  megaraid_isr(int irq, void *devp)
 
 
 /**
- * megaraid_mbox_dpc - the tasklet to complete the commands from completed list
- * @devp	: pointer to HBA soft state
+ * megaraid_mbox_dpc - the work handler to complete the commands from completed list
+ * @t	: pointer to work_struct
  *
  * Pick up the commands from the completed list and send back to the owners.
  * This is a reentrant function and does not assume any locks are held while
  * it is being called.
  */
 static void
-megaraid_mbox_dpc(unsigned long devp)
+megaraid_mbox_dpc(struct work_struct *t)
 {
-	adapter_t		*adapter = (adapter_t *)devp;
+	adapter_t		*adapter = from_work(adapter, t, dpc_h);
 	mraid_device_t		*raid_dev;
 	struct list_head	clist;
 	struct scatterlist	*sgl;
@@ -3812,7 +3811,7 @@  megaraid_sysfs_free_resources(adapter_t *adapter)
  * megaraid_sysfs_get_ldmap_done - callback for get ldmap
  * @uioc	: completed packet
  *
- * Callback routine called in the ISR/tasklet context for get ldmap call
+ * Callback routine called in the ISR/BH context for get ldmap call
  */
 static void
 megaraid_sysfs_get_ldmap_done(uioc_t *uioc)
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index 56624cbf7fa5..8de7a678e096 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -2389,7 +2389,7 @@  struct megasas_instance {
 	atomic64_t high_iops_outstanding;
 
 	struct megasas_instance_template *instancet;
-	struct tasklet_struct isr_tasklet;
+	struct work_struct isr_work;
 	struct work_struct work_init;
 	struct delayed_work fw_fault_work;
 	struct workqueue_struct *fw_fault_work_q;
@@ -2551,7 +2551,7 @@  struct megasas_instance_template {
 	int (*check_reset)(struct megasas_instance *, \
 		struct megasas_register_set __iomem *);
 	irqreturn_t (*service_isr)(int irq, void *devp);
-	void (*tasklet)(unsigned long);
+	void (*work)(struct work_struct *);
 	u32 (*init_adapter)(struct megasas_instance *);
 	u32 (*build_and_issue_cmd) (struct megasas_instance *,
 				    struct scsi_cmnd *);
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 3d4f13da1ae8..dd935943ae4f 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -234,7 +234,7 @@  megasas_init_adapter_mfi(struct megasas_instance *instance);
 u32
 megasas_build_and_issue_cmd(struct megasas_instance *instance,
 			    struct scsi_cmnd *scmd);
-static void megasas_complete_cmd_dpc(unsigned long instance_addr);
+static void megasas_complete_cmd_dpc(struct work_struct *t);
 int
 wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
 	int seconds);
@@ -615,7 +615,7 @@  static struct megasas_instance_template megasas_instance_template_xscale = {
 	.adp_reset = megasas_adp_reset_xscale,
 	.check_reset = megasas_check_reset_xscale,
 	.service_isr = megasas_isr,
-	.tasklet = megasas_complete_cmd_dpc,
+	.work = megasas_complete_cmd_dpc,
 	.init_adapter = megasas_init_adapter_mfi,
 	.build_and_issue_cmd = megasas_build_and_issue_cmd,
 	.issue_dcmd = megasas_issue_dcmd,
@@ -754,7 +754,7 @@  static struct megasas_instance_template megasas_instance_template_ppc = {
 	.adp_reset = megasas_adp_reset_xscale,
 	.check_reset = megasas_check_reset_ppc,
 	.service_isr = megasas_isr,
-	.tasklet = megasas_complete_cmd_dpc,
+	.work = megasas_complete_cmd_dpc,
 	.init_adapter = megasas_init_adapter_mfi,
 	.build_and_issue_cmd = megasas_build_and_issue_cmd,
 	.issue_dcmd = megasas_issue_dcmd,
@@ -895,7 +895,7 @@  static struct megasas_instance_template megasas_instance_template_skinny = {
 	.adp_reset = megasas_adp_reset_gen2,
 	.check_reset = megasas_check_reset_skinny,
 	.service_isr = megasas_isr,
-	.tasklet = megasas_complete_cmd_dpc,
+	.work = megasas_complete_cmd_dpc,
 	.init_adapter = megasas_init_adapter_mfi,
 	.build_and_issue_cmd = megasas_build_and_issue_cmd,
 	.issue_dcmd = megasas_issue_dcmd,
@@ -1095,7 +1095,7 @@  static struct megasas_instance_template megasas_instance_template_gen2 = {
 	.adp_reset = megasas_adp_reset_gen2,
 	.check_reset = megasas_check_reset_gen2,
 	.service_isr = megasas_isr,
-	.tasklet = megasas_complete_cmd_dpc,
+	.work = megasas_complete_cmd_dpc,
 	.init_adapter = megasas_init_adapter_mfi,
 	.build_and_issue_cmd = megasas_build_and_issue_cmd,
 	.issue_dcmd = megasas_issue_dcmd,
@@ -2269,18 +2269,18 @@  megasas_check_and_restore_queue_depth(struct megasas_instance *instance)
 
 /**
  * megasas_complete_cmd_dpc	 -	Returns FW's controller structure
- * @instance_addr:			Address of adapter soft state
+ * @t:					pointer to the work_struct
  *
- * Tasklet to complete cmds
+ * Work to complete cmds
  */
-static void megasas_complete_cmd_dpc(unsigned long instance_addr)
+static void megasas_complete_cmd_dpc(struct work_struct *t)
 {
 	u32 producer;
 	u32 consumer;
 	u32 context;
 	struct megasas_cmd *cmd;
 	struct megasas_instance *instance =
-				(struct megasas_instance *)instance_addr;
+				from_work(instance, t, isr_work);
 	unsigned long flags;
 
 	/* If we have already declared adapter dead, donot complete cmds */
@@ -2825,7 +2825,7 @@  static int megasas_wait_for_outstanding(struct megasas_instance *instance)
 			 * Call cmd completion routine. Cmd to be
 			 * be completed directly without depending on isr.
 			 */
-			megasas_complete_cmd_dpc((unsigned long)instance);
+			megasas_complete_cmd_dpc(&instance->isr_work);
 		}
 
 		msleep(1000);
@@ -4073,7 +4073,7 @@  megasas_deplete_reply_queue(struct megasas_instance *instance,
 		}
 	}
 
-	tasklet_schedule(&instance->isr_tasklet);
+	queue_work(system_bh_wq, &instance->isr_work);
 	return IRQ_HANDLED;
 }
 
@@ -6313,8 +6313,7 @@  static int megasas_init_fw(struct megasas_instance *instance)
 	dev_info(&instance->pdev->dev,
 		"RDPQ mode\t: (%s)\n", instance->is_rdpq ? "enabled" : "disabled");
 
-	tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
-		(unsigned long)instance);
+	INIT_WORK(&instance->isr_work, instance->instancet->work);
 
 	/*
 	 * Below are default value for legacy Firmware.
@@ -7757,7 +7756,7 @@  megasas_suspend(struct device *dev)
 		instance->ev = NULL;
 	}
 
-	tasklet_kill(&instance->isr_tasklet);
+	cancel_work_sync(&instance->isr_work);
 
 	pci_set_drvdata(instance->pdev, instance);
 	instance->instancet->disable_intr(instance);
@@ -7865,8 +7864,7 @@  megasas_resume(struct device *dev)
 	if (megasas_get_ctrl_info(instance) != DCMD_SUCCESS)
 		goto fail_init_mfi;
 
-	tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
-		     (unsigned long)instance);
+	INIT_WORK(&instance->isr_work, instance->instancet->work);
 
 	if (instance->msix_vectors ?
 			megasas_setup_irqs_msix(instance, 0) :
@@ -7997,7 +7995,7 @@  static void megasas_detach_one(struct pci_dev *pdev)
 	/* cancel all wait events */
 	wake_up_all(&instance->int_cmd_wait_q);
 
-	tasklet_kill(&instance->isr_tasklet);
+	cancel_work_sync(&instance->isr_work);
 
 	/*
 	 * Take the instance off the instance array. Note that we will not
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index c60014e07b44..7dd036b31a0c 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -3821,15 +3821,15 @@  int megasas_irqpoll(struct irq_poll *irqpoll, int budget)
 
 /**
  * megasas_complete_cmd_dpc_fusion -	Completes command
- * @instance_addr:			Adapter soft state address
+ * @t:					pointer to the work_struct
  *
- * Tasklet to complete cmds
+ * Work to complete cmds
  */
 static void
-megasas_complete_cmd_dpc_fusion(unsigned long instance_addr)
+megasas_complete_cmd_dpc_fusion(struct work_struct *t)
 {
 	struct megasas_instance *instance =
-		(struct megasas_instance *)instance_addr;
+		from_work(instance, t, isr_work);
 	struct megasas_irq_context *irq_ctx = NULL;
 	u32 count, MSIxIndex;
 
@@ -4180,7 +4180,7 @@  megasas_wait_for_outstanding_fusion(struct megasas_instance *instance,
 	if (reason == MFI_IO_TIMEOUT_OCR) {
 		dev_info(&instance->pdev->dev,
 			"MFI command is timed out\n");
-		megasas_complete_cmd_dpc_fusion((unsigned long)instance);
+		megasas_complete_cmd_dpc_fusion(&instance->isr_work);
 		if (instance->snapdump_wait_time)
 			megasas_trigger_snap_dump(instance);
 		retval = 1;
@@ -4196,7 +4196,7 @@  megasas_wait_for_outstanding_fusion(struct megasas_instance *instance,
 				   "FW in FAULT state Fault code:0x%x subcode:0x%x func:%s\n",
 				   abs_state & MFI_STATE_FAULT_CODE,
 				   abs_state & MFI_STATE_FAULT_SUBCODE, __func__);
-			megasas_complete_cmd_dpc_fusion((unsigned long)instance);
+			megasas_complete_cmd_dpc_fusion(&instance->isr_work);
 			if (instance->requestorId && reason) {
 				dev_warn(&instance->pdev->dev, "SR-IOV Found FW in FAULT"
 				" state while polling during"
@@ -4240,7 +4240,7 @@  megasas_wait_for_outstanding_fusion(struct megasas_instance *instance,
 			}
 		}
 
-		megasas_complete_cmd_dpc_fusion((unsigned long)instance);
+		megasas_complete_cmd_dpc_fusion(&instance->isr_work);
 		outstanding = atomic_read(&instance->fw_outstanding);
 		if (!outstanding)
 			goto out;
@@ -5371,7 +5371,7 @@  struct megasas_instance_template megasas_instance_template_fusion = {
 	.adp_reset = megasas_adp_reset_fusion,
 	.check_reset = megasas_check_reset_fusion,
 	.service_isr = megasas_isr_fusion,
-	.tasklet = megasas_complete_cmd_dpc_fusion,
+	.work = megasas_complete_cmd_dpc_fusion,
 	.init_adapter = megasas_init_adapter_fusion,
 	.build_and_issue_cmd = megasas_build_and_issue_cmd_fusion,
 	.issue_dcmd = megasas_issue_dcmd_fusion,
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 43ebb331e216..c8b3c18cfc6c 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -144,14 +144,14 @@  static void mvs_free(struct mvs_info *mvi)
 	kfree(mvi);
 }
 
-#ifdef CONFIG_SCSI_MVSAS_TASKLET
-static void mvs_tasklet(unsigned long opaque)
+#ifdef CONFIG_SCSI_MVSAS_WORK
+static void mvs_work(struct work_struct *t)
 {
 	u32 stat;
 	u16 core_nr, i = 0;
 
-	struct mvs_info *mvi;
-	struct sas_ha_struct *sha = (struct sas_ha_struct *)opaque;
+	struct mvs_info *mvi = from_work(mvi, t, mv_work);
+	struct sas_ha_struct *sha = mvi->sha;
 
 	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
 	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
@@ -178,7 +178,7 @@  static irqreturn_t mvs_interrupt(int irq, void *opaque)
 	u32 stat;
 	struct mvs_info *mvi;
 	struct sas_ha_struct *sha = opaque;
-#ifndef CONFIG_SCSI_MVSAS_TASKLET
+#ifndef CONFIG_SCSI_MVSAS_WORK
 	u32 i;
 	u32 core_nr;
 
@@ -189,20 +189,20 @@  static irqreturn_t mvs_interrupt(int irq, void *opaque)
 
 	if (unlikely(!mvi))
 		return IRQ_NONE;
-#ifdef CONFIG_SCSI_MVSAS_TASKLET
+#ifdef CONFIG_SCSI_MVSAS_WORK
 	MVS_CHIP_DISP->interrupt_disable(mvi);
 #endif
 
 	stat = MVS_CHIP_DISP->isr_status(mvi, irq);
 	if (!stat) {
-	#ifdef CONFIG_SCSI_MVSAS_TASKLET
+	#ifdef CONFIG_SCSI_MVSAS_WORK
 		MVS_CHIP_DISP->interrupt_enable(mvi);
 	#endif
 		return IRQ_NONE;
 	}
 
-#ifdef CONFIG_SCSI_MVSAS_TASKLET
-	tasklet_schedule(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
+#ifdef CONFIG_SCSI_MVSAS_WORK
+	queue_work(system_bh_wq, &((struct mvs_prv_info *)sha->lldd_ha)->mv_work);
 #else
 	for (i = 0; i < core_nr; i++) {
 		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
@@ -553,12 +553,11 @@  static int mvs_pci_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 		}
 		nhost++;
 	} while (nhost < chip->n_host);
-#ifdef CONFIG_SCSI_MVSAS_TASKLET
+#ifdef CONFIG_SCSI_MVSAS_WORK
 	{
 	struct mvs_prv_info *mpi = SHOST_TO_SAS_HA(shost)->lldd_ha;
 
-	tasklet_init(&(mpi->mv_tasklet), mvs_tasklet,
-		     (unsigned long)SHOST_TO_SAS_HA(shost));
+	INIT_WORK(&(mpi->mv_work), mvs_work);
 	}
 #endif
 
@@ -603,8 +602,8 @@  static void mvs_pci_remove(struct pci_dev *pdev)
 	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
 	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
 
-#ifdef CONFIG_SCSI_MVSAS_TASKLET
-	tasklet_kill(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
+#ifdef CONFIG_SCSI_MVSAS_WORK
+	cancel_work_sync(&((struct mvs_prv_info *)sha->lldd_ha)->mv_work);
 #endif
 
 	sas_unregister_ha(sha);
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
index 68df771e2975..2bf1af51e2a4 100644
--- a/drivers/scsi/mvsas/mv_sas.h
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -23,6 +23,7 @@ 
 #include <linux/irq.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/workqueue.h>
 #include <asm/unaligned.h>
 #include <scsi/libsas.h>
 #include <scsi/scsi.h>
@@ -402,7 +403,7 @@  struct mvs_prv_info{
 	u8 scan_finished;
 	u8 reserve;
 	struct mvs_info *mvi[2];
-	struct tasklet_struct mv_tasklet;
+	struct work_struct mv_work;
 };
 
 struct mvs_wq {
@@ -432,8 +433,8 @@  void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo,
 		      u32 off_hi, u64 sas_addr);
 void mvs_scan_start(struct Scsi_Host *shost);
 int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time);
-int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags);
-int mvs_abort_task(struct sas_task *task);
+int mvs_queue_command(struct sas_task *work, gfp_t gfp_flags);
+int mvs_abort_task(struct sas_task *work);
 void mvs_port_formed(struct asd_sas_phy *sas_phy);
 void mvs_port_deformed(struct asd_sas_phy *sas_phy);
 int mvs_dev_found(struct domain_device *dev);
@@ -441,7 +442,7 @@  void mvs_dev_gone(struct domain_device *dev);
 int mvs_lu_reset(struct domain_device *dev, u8 *lun);
 int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags);
 int mvs_I_T_nexus_reset(struct domain_device *dev);
-int mvs_query_task(struct sas_task *task);
+int mvs_query_task(struct sas_task *work);
 void mvs_release_task(struct mvs_info *mvi,
 			struct domain_device *dev);
 void mvs_do_release_task(struct mvs_info *mvi, int phy_no,
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index ed6b7d954dda..bda175682785 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -60,8 +60,8 @@  bool pm8001_use_msix = true;
 module_param_named(use_msix, pm8001_use_msix, bool, 0444);
 MODULE_PARM_DESC(zoned, "Use MSIX interrupts. Default: true");
 
-static bool pm8001_use_tasklet = true;
-module_param_named(use_tasklet, pm8001_use_tasklet, bool, 0444);
+static bool pm8001_use_bh_work = true;
+module_param_named(use_bh_work, pm8001_use_bh_work, bool, 0444);
 MODULE_PARM_DESC(zoned, "Use MSIX interrupts. Default: true");
 
 static bool pm8001_read_wwn = true;
@@ -213,14 +213,17 @@  static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
 }
 
 /**
- * pm8001_tasklet() - tasklet for 64 msi-x interrupt handler
- * @opaque: the passed general host adapter struct
- * Note: pm8001_tasklet is common for pm8001 & pm80xx
+ * pm8001_work() - BH work for 64 msi-x interrupt handler
+ * @t: pointer to work_struct
+ * Note: pm8001_work is common for pm8001 & pm80xx
  */
-static void pm8001_tasklet(unsigned long opaque)
+static void pm8001_work(struct work_struct *t)
 {
-	struct isr_param *irq_vector = (struct isr_param *)opaque;
-	struct pm8001_hba_info *pm8001_ha = irq_vector->drv_inst;
+	/*FIXME: Since we don't know the index, we need a
+	 * mechanism to determine it or always use index 0
+	 */
+	struct pm8001_hba_info *pm8001_ha = from_work(pm8001_ha, t, work[0]);
+	struct isr_param *irq_vector = pm8001_ha->irq_vector;
 
 	if (WARN_ON_ONCE(!pm8001_ha))
 		return;
@@ -228,41 +231,39 @@  static void pm8001_tasklet(unsigned long opaque)
 	PM8001_CHIP_DISP->isr(pm8001_ha, irq_vector->irq_id);
 }
 
-static void pm8001_init_tasklet(struct pm8001_hba_info *pm8001_ha)
+static void pm8001_init_work(struct pm8001_hba_info *pm8001_ha)
 {
 	int i;
 
-	if (!pm8001_use_tasklet)
+	if (!pm8001_use_bh_work)
 		return;
 
-	/*  Tasklet for non msi-x interrupt handler */
+	/*  Work for non msi-x interrupt handler */
 	if ((!pm8001_ha->pdev->msix_cap || !pci_msi_enabled()) ||
 	    (pm8001_ha->chip_id == chip_8001)) {
-		tasklet_init(&pm8001_ha->tasklet[0], pm8001_tasklet,
-			     (unsigned long)&(pm8001_ha->irq_vector[0]));
+		INIT_WORK(&pm8001_ha->work[0], pm8001_work);
 		return;
 	}
 	for (i = 0; i < PM8001_MAX_MSIX_VEC; i++)
-		tasklet_init(&pm8001_ha->tasklet[i], pm8001_tasklet,
-			     (unsigned long)&(pm8001_ha->irq_vector[i]));
+		INIT_WORK(&pm8001_ha->work[i], pm8001_work);
 }
 
-static void pm8001_kill_tasklet(struct pm8001_hba_info *pm8001_ha)
+static void pm8001_cancel_work(struct pm8001_hba_info *pm8001_ha)
 {
 	int i;
 
-	if (!pm8001_use_tasklet)
+	if (!pm8001_use_bh_work)
 		return;
 
 	/* For non-msix and msix interrupts */
 	if ((!pm8001_ha->pdev->msix_cap || !pci_msi_enabled()) ||
 	    (pm8001_ha->chip_id == chip_8001)) {
-		tasklet_kill(&pm8001_ha->tasklet[0]);
+		cancel_work_sync(&pm8001_ha->work[0]);
 		return;
 	}
 
 	for (i = 0; i < PM8001_MAX_MSIX_VEC; i++)
-		tasklet_kill(&pm8001_ha->tasklet[i]);
+		cancel_work_sync(&pm8001_ha->work[i]);
 }
 
 static irqreturn_t pm8001_handle_irq(struct pm8001_hba_info *pm8001_ha,
@@ -274,10 +275,10 @@  static irqreturn_t pm8001_handle_irq(struct pm8001_hba_info *pm8001_ha,
 	if (!PM8001_CHIP_DISP->is_our_interrupt(pm8001_ha))
 		return IRQ_NONE;
 
-	if (!pm8001_use_tasklet)
+	if (!pm8001_use_bh_work)
 		return PM8001_CHIP_DISP->isr(pm8001_ha, irq);
 
-	tasklet_schedule(&pm8001_ha->tasklet[irq]);
+	queue_work(system_bh_wq, &pm8001_ha->work[irq]);
 	return IRQ_HANDLED;
 }
 
@@ -580,7 +581,7 @@  static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
 	else
 		pm8001_ha->iomb_size = IOMB_SIZE_SPC;
 
-	pm8001_init_tasklet(pm8001_ha);
+	pm8001_init_work(pm8001_ha);
 
 	if (pm8001_ioremap(pm8001_ha))
 		goto failed_pci_alloc;
@@ -1318,7 +1319,7 @@  static void pm8001_pci_remove(struct pci_dev *pdev)
 	PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
 
 	pm8001_free_irq(pm8001_ha);
-	pm8001_kill_tasklet(pm8001_ha);
+	pm8001_cancel_work(pm8001_ha);
 	scsi_host_put(pm8001_ha->shost);
 
 	for (i = 0; i < pm8001_ha->ccb_count; i++) {
@@ -1361,7 +1362,7 @@  static int __maybe_unused pm8001_pci_suspend(struct device *dev)
 	PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
 
 	pm8001_free_irq(pm8001_ha);
-	pm8001_kill_tasklet(pm8001_ha);
+	pm8001_cancel_work(pm8001_ha);
 
 	pm8001_info(pm8001_ha, "pdev=0x%p, slot=%s, entering "
 		      "suspended state\n", pdev,
@@ -1410,7 +1411,7 @@  static int __maybe_unused pm8001_pci_resume(struct device *dev)
 	if (rc)
 		goto err_out_disable;
 
-	pm8001_init_tasklet(pm8001_ha);
+	pm8001_init_work(pm8001_ha);
 
 	PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0);
 	if (pm8001_ha->chip_id != chip_8001) {
@@ -1543,8 +1544,8 @@  static int __init pm8001_init(void)
 {
 	int rc = -ENOMEM;
 
-	if (pm8001_use_tasklet && !pm8001_use_msix)
-		pm8001_use_tasklet = false;
+	if (pm8001_use_bh_work && !pm8001_use_msix)
+		pm8001_use_bh_work = false;
 
 	pm8001_wq = alloc_workqueue("pm80xx", 0, 0);
 	if (!pm8001_wq)
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
index 3ccb7371902f..08ab597406c7 100644
--- a/drivers/scsi/pm8001/pm8001_sas.h
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -522,7 +522,7 @@  struct pm8001_hba_info {
 	int			number_of_intr;/*will be used in remove()*/
 	char			intr_drvname[PM8001_MAX_MSIX_VEC]
 				[PM8001_NAME_LENGTH+1+3+1];
-	struct tasklet_struct	tasklet[PM8001_MAX_MSIX_VEC];
+	struct work_struct	work[PM8001_MAX_MSIX_VEC];
 	u32			logging_level;
 	u32			link_rate;
 	u32			fw_status;
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index e8bcc3a88732..be21c0ffe002 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -859,7 +859,7 @@  static void _pmcraid_fire_command(struct pmcraid_cmd *cmd)
 	/* Add this command block to pending cmd pool. We do this prior to
 	 * writting IOARCB to ioarrin because IOA might complete the command
 	 * by the time we are about to add it to the list. Response handler
-	 * (isr/tasklet) looks for cmd block in the pending pending list.
+	 * (isr/BH work) looks for cmd block in the pending list.
 	 */
 	spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
 	list_add_tail(&cmd->free_list, &pinstance->pending_cmd_pool);
@@ -1077,7 +1077,7 @@  static void pmcraid_identify_hrrq(struct pmcraid_cmd *cmd)
 
 	/* Subsequent commands require HRRQ identification to be successful.
 	 * Note that this gets called even during reset from SCSI mid-layer
-	 * or tasklet
+	 * or BH work
 	 */
 	pmcraid_send_cmd(cmd, done_function,
 			 PMCRAID_INTERNAL_TIMEOUT,
@@ -1843,7 +1843,7 @@  static void pmcraid_unregister_hcams(struct pmcraid_cmd *cmd)
 {
 	struct pmcraid_instance *pinstance = cmd->drv_inst;
 
-	/* During IOA bringdown, HCAM gets fired and tasklet proceeds with
+	/* During IOA bringdown, HCAM gets fired and BH work proceeds with
 	 * handling hcam response though it is not necessary. In order to
 	 * prevent this, set 'ignore', so that bring-down sequence doesn't
 	 * re-send any more hcams
@@ -1916,7 +1916,7 @@  static void pmcraid_soft_reset(struct pmcraid_cmd *cmd)
 	u32 doorbell;
 
 	/* There will be an interrupt when Transition to Operational bit is
-	 * set so tasklet would execute next reset task. The timeout handler
+	 * set so BH work would execute next reset task. The timeout handler
 	 * would re-initiate a reset
 	 */
 	cmd->cmd_done = pmcraid_ioa_reset;
@@ -2039,7 +2039,7 @@  static void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance)
  * @cmd: pointer to the cmd block to be used for entire reset process
  *
  * This function executes most of the steps required for IOA reset. This gets
- * called by user threads (modprobe/insmod/rmmod) timer, tasklet and midlayer's
+ * called by user threads (modprobe/insmod/rmmod) timer, BH work and midlayer's
  * 'eh_' thread. Access to variables used for controlling the reset sequence is
  * synchronized using host lock. Various functions called during reset process
  * would make use of a single command block, pointer to which is also stored in
@@ -2199,7 +2199,7 @@  static void pmcraid_ioa_reset(struct pmcraid_cmd *cmd)
 		pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
 
 		/* Initialization commands start with HRRQ identification. From
-		 * now on tasklet completes most of the commands as IOA is up
+		 * now on BH work completes most of the commands as IOA is up
 		 * and intrs are enabled
 		 */
 		pmcraid_identify_hrrq(cmd);
@@ -2261,7 +2261,7 @@  static void pmcraid_ioa_reset(struct pmcraid_cmd *cmd)
 
 /**
  * pmcraid_initiate_reset - initiates reset sequence. This is called from
- * ISR/tasklet during error interrupts including IOA unit check. If reset
+ * ISR/BH work during error interrupts including IOA unit check. If reset
  * is already in progress, it just returns, otherwise initiates IOA reset
  * to bring IOA up to operational state.
  *
@@ -2303,7 +2303,7 @@  static void pmcraid_initiate_reset(struct pmcraid_instance *pinstance)
  * @target_state: expected target state after reset
  *
  * Note: This command initiates reset and waits for its completion. Hence this
- * should not be called from isr/timer/tasklet functions (timeout handlers,
+ * should not be called from isr/timer/BH work functions (timeout handlers,
  * error response handlers and interrupt handlers).
  *
  * Return Value
@@ -2449,7 +2449,7 @@  static void pmcraid_request_sense(struct pmcraid_cmd *cmd)
 	ioadl->flags = IOADL_FLAGS_LAST_DESC;
 
 	/* request sense might be called as part of error response processing
-	 * which runs in tasklets context. It is possible that mid-layer might
+	 * which runs in works context. It is possible that mid-layer might
 	 * schedule queuecommand during this time, hence, writting to IOARRIN
 	 * must be protect by host_lock
 	 */
@@ -2566,7 +2566,7 @@  static void pmcraid_frame_auto_sense(struct pmcraid_cmd *cmd)
  * @cmd: pointer to pmcraid_cmd that has failed
  *
  * This function determines whether or not to initiate ERP on the affected
- * device. This is called from a tasklet, which doesn't hold any locks.
+ * device. This is called from a BH work, which doesn't hold any locks.
  *
  * Return value:
  *	 0 it caller can complete the request, otherwise 1 where in error
@@ -2825,7 +2825,7 @@  static int _pmcraid_io_done(struct pmcraid_cmd *cmd, int reslen, int ioasc)
  *
  * @cmd: pointer to pmcraid command struct
  *
- * This function is invoked by tasklet/mid-layer error handler to completing
+ * This function is invoked by BH work/mid-layer error handler to completing
  * the SCSI ops sent from mid-layer.
  *
  * Return value
@@ -3743,7 +3743,7 @@  static irqreturn_t pmcraid_isr_msix(int irq, void *dev_id)
 		}
 	}
 
-	tasklet_schedule(&(pinstance->isr_tasklet[hrrq_id]));
+	queue_work(system_bh_wq, &(pinstance->isr_work[hrrq_id]));
 
 	return IRQ_HANDLED;
 }
@@ -3811,8 +3811,8 @@  static irqreturn_t pmcraid_isr(int irq, void *dev_id)
 			ioread32(
 				pinstance->int_regs.ioa_host_interrupt_clr_reg);
 
-			tasklet_schedule(
-					&(pinstance->isr_tasklet[hrrq_id]));
+			queue_work(system_bh_wq,
+					&(pinstance->isr_work[hrrq_id]));
 		}
 	}
 
@@ -3918,14 +3918,14 @@  static void pmcraid_worker_function(struct work_struct *workp)
 }
 
 /**
- * pmcraid_tasklet_function - Tasklet function
+ * pmcraid_work_function - Work function
  *
- * @instance: pointer to msix param structure
+ * @t:  pointer to work_struct
  *
  * Return Value
  *	None
  */
-static void pmcraid_tasklet_function(unsigned long instance)
+static void pmcraid_work_function(struct work_struct *t)
 {
 	struct pmcraid_isr_param *hrrq_vector;
 	struct pmcraid_instance *pinstance;
@@ -3936,14 +3936,17 @@  static void pmcraid_tasklet_function(unsigned long instance)
 	int id;
 	u32 resp;
 
-	hrrq_vector = (struct pmcraid_isr_param *)instance;
-	pinstance = hrrq_vector->drv_inst;
+	/* FIXME: Since we don't know the index, we need a
+	 * mechanism to determine it or always use index 0
+	 */
+	pinstance = from_work(pinstance, t, isr_work[0]);
+	hrrq_vector = pinstance->hrrq_vector;
 	id = hrrq_vector->hrrq_id;
 	lockp = &(pinstance->hrrq_lock[id]);
 
 	/* loop through each of the commands responded by IOA. Each HRRQ buf is
 	 * protected by its own lock. Traversals must be done within this lock
-	 * as there may be multiple tasklets running on multiple CPUs. Note
+	 * as there may be multiple works running on multiple CPUs. Note
 	 * that the lock is held just for picking up the response handle and
 	 * manipulating hrrq_curr/toggle_bit values.
 	 */
@@ -4416,35 +4419,34 @@  static int pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance)
 }
 
 /**
- * pmcraid_init_tasklets - registers tasklets for response handling
+ * pmcraid_init_works - registers works for response handling
  *
  * @pinstance: pointer adapter instance structure
  *
  * Return value
  *	none
  */
-static void pmcraid_init_tasklets(struct pmcraid_instance *pinstance)
+static void pmcraid_init_works(struct pmcraid_instance *pinstance)
 {
 	int i;
 	for (i = 0; i < pinstance->num_hrrq; i++)
-		tasklet_init(&pinstance->isr_tasklet[i],
-			     pmcraid_tasklet_function,
-			     (unsigned long)&pinstance->hrrq_vector[i]);
+		INIT_WORK(&pinstance->isr_work[i],
+			     pmcraid_work_function);
 }
 
 /**
- * pmcraid_kill_tasklets - destroys tasklets registered for response handling
+ * pmcraid_kill_works - destroys works registered for response handling
  *
  * @pinstance: pointer to adapter instance structure
  *
  * Return value
  *	none
  */
-static void pmcraid_kill_tasklets(struct pmcraid_instance *pinstance)
+static void pmcraid_kill_works(struct pmcraid_instance *pinstance)
 {
 	int i;
 	for (i = 0; i < pinstance->num_hrrq; i++)
-		tasklet_kill(&pinstance->isr_tasklet[i]);
+		cancel_work_sync(&pinstance->isr_work[i]);
 }
 
 /**
@@ -4770,7 +4772,7 @@  static void pmcraid_remove(struct pci_dev *pdev)
 	pmcraid_disable_interrupts(pinstance, ~0);
 	flush_work(&pinstance->worker_q);
 
-	pmcraid_kill_tasklets(pinstance);
+	pmcraid_kill_works(pinstance);
 	pmcraid_unregister_interrupt_handler(pinstance);
 	pmcraid_release_buffers(pinstance);
 	iounmap(pinstance->mapped_dma_addr);
@@ -4794,7 +4796,7 @@  static int __maybe_unused pmcraid_suspend(struct device *dev)
 
 	pmcraid_shutdown(pdev);
 	pmcraid_disable_interrupts(pinstance, ~0);
-	pmcraid_kill_tasklets(pinstance);
+	pmcraid_kill_works(pinstance);
 	pmcraid_unregister_interrupt_handler(pinstance);
 
 	return 0;
@@ -4836,7 +4838,7 @@  static int __maybe_unused pmcraid_resume(struct device *dev)
 		goto release_host;
 	}
 
-	pmcraid_init_tasklets(pinstance);
+	pmcraid_init_works(pinstance);
 	pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS);
 
 	/* Start with hard reset sequence which brings up IOA to operational
@@ -4850,14 +4852,14 @@  static int __maybe_unused pmcraid_resume(struct device *dev)
 	if (pmcraid_reset_bringup(pinstance)) {
 		dev_err(&pdev->dev, "couldn't initialize IOA\n");
 		rc = -ENODEV;
-		goto release_tasklets;
+		goto release_works;
 	}
 
 	return 0;
 
-release_tasklets:
+release_works:
 	pmcraid_disable_interrupts(pinstance, ~0);
-	pmcraid_kill_tasklets(pinstance);
+	pmcraid_kill_works(pinstance);
 	pmcraid_unregister_interrupt_handler(pinstance);
 
 release_host:
@@ -4869,7 +4871,7 @@  static int __maybe_unused pmcraid_resume(struct device *dev)
 }
 
 /**
- * pmcraid_complete_ioa_reset - Called by either timer or tasklet during
+ * pmcraid_complete_ioa_reset - Called by either timer or BH work during
  *				completion of the ioa reset
  * @cmd: pointer to reset command block
  */
@@ -5014,7 +5016,7 @@  static void pmcraid_init_res_table(struct pmcraid_cmd *cmd)
 
 	/* resource list is protected by pinstance->resource_lock.
 	 * init_res_table can be called from probe (user-thread) or runtime
-	 * reset (timer/tasklet)
+	 * reset (timer/BH work)
 	 */
 	spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
 
@@ -5281,7 +5283,7 @@  static int pmcraid_probe(struct pci_dev *pdev,
 		goto out_scsi_host_put;
 	}
 
-	pmcraid_init_tasklets(pinstance);
+	pmcraid_init_works(pinstance);
 
 	/* allocate verious buffers used by LLD.*/
 	rc = pmcraid_init_buffers(pinstance);
@@ -5337,7 +5339,7 @@  static int pmcraid_probe(struct pci_dev *pdev,
 	pmcraid_release_buffers(pinstance);
 
 out_unregister_isr:
-	pmcraid_kill_tasklets(pinstance);
+	pmcraid_kill_works(pinstance);
 	pmcraid_unregister_interrupt_handler(pinstance);
 
 out_scsi_host_put:
diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h
index 9f59930e8b4f..2c17b1f4b57e 100644
--- a/drivers/scsi/pmcraid.h
+++ b/drivers/scsi/pmcraid.h
@@ -20,6 +20,7 @@ 
 #include <net/netlink.h>
 #include <net/genetlink.h>
 #include <linux/connector.h>
+#include <linux/workqueue.h>
 /*
  * Driver name   : string representing the driver name
  * Device file   : /dev file to be used for management interfaces
@@ -752,8 +753,8 @@  struct pmcraid_instance {
 	spinlock_t free_pool_lock;		/* free pool lock */
 	spinlock_t pending_pool_lock;		/* pending pool lock */
 
-	/* Tasklet to handle deferred processing */
-	struct tasklet_struct isr_tasklet[PMCRAID_NUM_MSIX_VECTORS];
+	/* BH work to handle deferred processing */
+	struct work_struct isr_work[PMCRAID_NUM_MSIX_VECTORS];
 
 	/* Work-queue (Shared) for deferred reset processing */
 	struct work_struct worker_q;