summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libsas/sas_event.c
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2012-03-09 11:00:06 -0800
committerJames Bottomley <JBottomley@Parallels.com>2012-04-23 12:03:39 +0100
commit22b9153faa2263aa89625de25e71c7d44c8dbd16 (patch)
tree1157d64a9c63c5b7b48b4a5b3610965d8d4c9624 /drivers/scsi/libsas/sas_event.c
parentf8fc75dc576eac0c996e4a792a4701819d999260 (diff)
[SCSI] libsas: introduce sas_work to fix sas_drain_work vs sas_queue_work
When requeuing work to a draining workqueue the last work instance may not be idle, so sas_queue_work() must not touch work->entry. Introduce sas_work with a drain_node list_head to have a private list for collecting work deferred due to drain collision. Fixes reports like: BUG: unable to handle kernel NULL pointer dereference at (null) IP: [<ffffffff810410d4>] process_one_work+0x2e/0x338 Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/libsas/sas_event.c')
-rw-r--r--drivers/scsi/libsas/sas_event.c24
1 files changed, 13 insertions, 11 deletions
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c
index 16639bbae62..4e4292d210c 100644
--- a/drivers/scsi/libsas/sas_event.c
+++ b/drivers/scsi/libsas/sas_event.c
@@ -27,19 +27,21 @@
#include "sas_internal.h"
#include "sas_dump.h"
-void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work)
+void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
{
if (!test_bit(SAS_HA_REGISTERED, &ha->state))
return;
- if (test_bit(SAS_HA_DRAINING, &ha->state))
- list_add(&work->entry, &ha->defer_q);
- else
- scsi_queue_work(ha->core.shost, work);
+ if (test_bit(SAS_HA_DRAINING, &ha->state)) {
+ /* add it to the defer list, if not already pending */
+ if (list_empty(&sw->drain_node))
+ list_add(&sw->drain_node, &ha->defer_q);
+ } else
+ scsi_queue_work(ha->core.shost, &sw->work);
}
static void sas_queue_event(int event, unsigned long *pending,
- struct work_struct *work,
+ struct sas_work *work,
struct sas_ha_struct *ha)
{
if (!test_and_set_bit(event, pending)) {
@@ -55,7 +57,7 @@ static void sas_queue_event(int event, unsigned long *pending,
void __sas_drain_work(struct sas_ha_struct *ha)
{
struct workqueue_struct *wq = ha->core.shost->work_q;
- struct work_struct *w, *_w;
+ struct sas_work *sw, *_sw;
set_bit(SAS_HA_DRAINING, &ha->state);
/* flush submitters */
@@ -66,9 +68,9 @@ void __sas_drain_work(struct sas_ha_struct *ha)
spin_lock_irq(&ha->state_lock);
clear_bit(SAS_HA_DRAINING, &ha->state);
- list_for_each_entry_safe(w, _w, &ha->defer_q, entry) {
- list_del_init(&w->entry);
- sas_queue_work(ha, w);
+ list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
+ list_del_init(&sw->drain_node);
+ sas_queue_work(ha, sw);
}
spin_unlock_irq(&ha->state_lock);
}
@@ -151,7 +153,7 @@ int sas_init_events(struct sas_ha_struct *sas_ha)
int i;
for (i = 0; i < HA_NUM_EVENTS; i++) {
- INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]);
+ INIT_SAS_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]);
sas_ha->ha_events[i].ha = sas_ha;
}