[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>
This commit is contained in:
Dan Williams 2012-03-09 11:00:06 -08:00 committed by James Bottomley
parent f8fc75dc57
commit 22b9153faa
7 changed files with 83 additions and 62 deletions

View file

@ -217,11 +217,29 @@ struct domain_device {
struct kref kref;
};
struct sas_discovery_event {
struct sas_work {
struct list_head drain_node;
struct work_struct work;
};
static inline void INIT_SAS_WORK(struct sas_work *sw, void (*fn)(struct work_struct *))
{
INIT_WORK(&sw->work, fn);
INIT_LIST_HEAD(&sw->drain_node);
}
struct sas_discovery_event {
struct sas_work work;
struct asd_sas_port *port;
};
static inline struct sas_discovery_event *to_sas_discovery_event(struct work_struct *work)
{
struct sas_discovery_event *ev = container_of(work, typeof(*ev), work.work);
return ev;
}
struct sas_discovery {
struct sas_discovery_event disc_work[DISC_NUM_EVENTS];
unsigned long pending;
@ -244,7 +262,7 @@ struct asd_sas_port {
struct list_head destroy_list;
enum sas_linkrate linkrate;
struct work_struct work;
struct sas_work work;
/* public: */
int id;
@ -270,10 +288,17 @@ struct asd_sas_port {
};
struct asd_sas_event {
struct work_struct work;
struct sas_work work;
struct asd_sas_phy *phy;
};
static inline struct asd_sas_event *to_asd_sas_event(struct work_struct *work)
{
struct asd_sas_event *ev = container_of(work, typeof(*ev), work.work);
return ev;
}
/* The phy pretty much is controlled by the LLDD.
* The class only reads those fields.
*/
@ -333,10 +358,17 @@ struct scsi_core {
};
struct sas_ha_event {
struct work_struct work;
struct sas_work work;
struct sas_ha_struct *ha;
};
static inline struct sas_ha_event *to_sas_ha_event(struct work_struct *work)
{
struct sas_ha_event *ev = container_of(work, typeof(*ev), work.work);
return ev;
}
enum sas_ha_state {
SAS_HA_REGISTERED,
SAS_HA_DRAINING,