Commit 22b9153f authored by Dan Williams's avatar Dan Williams Committed by James Bottomley

[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: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent f8fc75dc
...@@ -205,8 +205,7 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) ...@@ -205,8 +205,7 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)
static void sas_probe_devices(struct work_struct *work) static void sas_probe_devices(struct work_struct *work)
{ {
struct domain_device *dev, *n; struct domain_device *dev, *n;
struct sas_discovery_event *ev = struct sas_discovery_event *ev = to_sas_discovery_event(work);
container_of(work, struct sas_discovery_event, work);
struct asd_sas_port *port = ev->port; struct asd_sas_port *port = ev->port;
clear_bit(DISCE_PROBE, &port->disc.pending); clear_bit(DISCE_PROBE, &port->disc.pending);
...@@ -291,8 +290,7 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d ...@@ -291,8 +290,7 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d
static void sas_destruct_devices(struct work_struct *work) static void sas_destruct_devices(struct work_struct *work)
{ {
struct domain_device *dev, *n; struct domain_device *dev, *n;
struct sas_discovery_event *ev = struct sas_discovery_event *ev = to_sas_discovery_event(work);
container_of(work, struct sas_discovery_event, work);
struct asd_sas_port *port = ev->port; struct asd_sas_port *port = ev->port;
clear_bit(DISCE_DESTRUCT, &port->disc.pending); clear_bit(DISCE_DESTRUCT, &port->disc.pending);
...@@ -377,8 +375,7 @@ static void sas_discover_domain(struct work_struct *work) ...@@ -377,8 +375,7 @@ static void sas_discover_domain(struct work_struct *work)
{ {
struct domain_device *dev; struct domain_device *dev;
int error = 0; int error = 0;
struct sas_discovery_event *ev = struct sas_discovery_event *ev = to_sas_discovery_event(work);
container_of(work, struct sas_discovery_event, work);
struct asd_sas_port *port = ev->port; struct asd_sas_port *port = ev->port;
clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending); clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending);
...@@ -437,8 +434,7 @@ static void sas_discover_domain(struct work_struct *work) ...@@ -437,8 +434,7 @@ static void sas_discover_domain(struct work_struct *work)
static void sas_revalidate_domain(struct work_struct *work) static void sas_revalidate_domain(struct work_struct *work)
{ {
int res = 0; int res = 0;
struct sas_discovery_event *ev = struct sas_discovery_event *ev = to_sas_discovery_event(work);
container_of(work, struct sas_discovery_event, work);
struct asd_sas_port *port = ev->port; struct asd_sas_port *port = ev->port;
struct sas_ha_struct *ha = port->ha; struct sas_ha_struct *ha = port->ha;
...@@ -466,21 +462,25 @@ static void sas_revalidate_domain(struct work_struct *work) ...@@ -466,21 +462,25 @@ static void sas_revalidate_domain(struct work_struct *work)
/* ---------- Events ---------- */ /* ---------- Events ---------- */
static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work) static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw)
{ {
/* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */ /* chained work is not subject to SA_HA_DRAINING or
scsi_queue_work(ha->core.shost, work); * SAS_HA_REGISTERED, because it is either submitted in the
* workqueue, or known to be submitted from a context that is
* not racing against draining
*/
scsi_queue_work(ha->core.shost, &sw->work);
} }
static void sas_chain_event(int event, unsigned long *pending, static void sas_chain_event(int event, unsigned long *pending,
struct work_struct *work, struct sas_work *sw,
struct sas_ha_struct *ha) struct sas_ha_struct *ha)
{ {
if (!test_and_set_bit(event, pending)) { if (!test_and_set_bit(event, pending)) {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&ha->state_lock, flags); spin_lock_irqsave(&ha->state_lock, flags);
sas_chain_work(ha, work); sas_chain_work(ha, sw);
spin_unlock_irqrestore(&ha->state_lock, flags); spin_unlock_irqrestore(&ha->state_lock, flags);
} }
} }
...@@ -519,7 +519,7 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) ...@@ -519,7 +519,7 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
disc->pending = 0; disc->pending = 0;
for (i = 0; i < DISC_NUM_EVENTS; i++) { for (i = 0; i < DISC_NUM_EVENTS; i++) {
INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]); INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]);
disc->disc_work[i].port = port; disc->disc_work[i].port = port;
} }
} }
...@@ -27,19 +27,21 @@ ...@@ -27,19 +27,21 @@
#include "sas_internal.h" #include "sas_internal.h"
#include "sas_dump.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)) if (!test_bit(SAS_HA_REGISTERED, &ha->state))
return; return;
if (test_bit(SAS_HA_DRAINING, &ha->state)) if (test_bit(SAS_HA_DRAINING, &ha->state)) {
list_add(&work->entry, &ha->defer_q); /* add it to the defer list, if not already pending */
else if (list_empty(&sw->drain_node))
scsi_queue_work(ha->core.shost, work); 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, static void sas_queue_event(int event, unsigned long *pending,
struct work_struct *work, struct sas_work *work,
struct sas_ha_struct *ha) struct sas_ha_struct *ha)
{ {
if (!test_and_set_bit(event, pending)) { if (!test_and_set_bit(event, pending)) {
...@@ -55,7 +57,7 @@ static void sas_queue_event(int event, unsigned long *pending, ...@@ -55,7 +57,7 @@ static void sas_queue_event(int event, unsigned long *pending,
void __sas_drain_work(struct sas_ha_struct *ha) void __sas_drain_work(struct sas_ha_struct *ha)
{ {
struct workqueue_struct *wq = ha->core.shost->work_q; 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); set_bit(SAS_HA_DRAINING, &ha->state);
/* flush submitters */ /* flush submitters */
...@@ -66,9 +68,9 @@ void __sas_drain_work(struct sas_ha_struct *ha) ...@@ -66,9 +68,9 @@ void __sas_drain_work(struct sas_ha_struct *ha)
spin_lock_irq(&ha->state_lock); spin_lock_irq(&ha->state_lock);
clear_bit(SAS_HA_DRAINING, &ha->state); clear_bit(SAS_HA_DRAINING, &ha->state);
list_for_each_entry_safe(w, _w, &ha->defer_q, entry) { list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
list_del_init(&w->entry); list_del_init(&sw->drain_node);
sas_queue_work(ha, w); sas_queue_work(ha, sw);
} }
spin_unlock_irq(&ha->state_lock); spin_unlock_irq(&ha->state_lock);
} }
...@@ -151,7 +153,7 @@ int sas_init_events(struct sas_ha_struct *sas_ha) ...@@ -151,7 +153,7 @@ int sas_init_events(struct sas_ha_struct *sas_ha)
int i; int i;
for (i = 0; i < HA_NUM_EVENTS; 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; sas_ha->ha_events[i].ha = sas_ha;
} }
......
...@@ -94,8 +94,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) ...@@ -94,8 +94,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr)
void sas_hae_reset(struct work_struct *work) void sas_hae_reset(struct work_struct *work)
{ {
struct sas_ha_event *ev = struct sas_ha_event *ev = to_sas_ha_event(work);
container_of(work, struct sas_ha_event, work);
struct sas_ha_struct *ha = ev->ha; struct sas_ha_struct *ha = ev->ha;
clear_bit(HAE_RESET, &ha->pending); clear_bit(HAE_RESET, &ha->pending);
...@@ -369,14 +368,14 @@ static void sas_phy_release(struct sas_phy *phy) ...@@ -369,14 +368,14 @@ static void sas_phy_release(struct sas_phy *phy)
static void phy_reset_work(struct work_struct *work) static void phy_reset_work(struct work_struct *work)
{ {
struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work);
d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset);
} }
static void phy_enable_work(struct work_struct *work) static void phy_enable_work(struct work_struct *work)
{ {
struct sas_phy_data *d = container_of(work, typeof(*d), enable_work); struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work);
d->enable_result = sas_phy_enable(d->phy, d->enable); d->enable_result = sas_phy_enable(d->phy, d->enable);
} }
...@@ -389,8 +388,8 @@ static int sas_phy_setup(struct sas_phy *phy) ...@@ -389,8 +388,8 @@ static int sas_phy_setup(struct sas_phy *phy)
return -ENOMEM; return -ENOMEM;
mutex_init(&d->event_lock); mutex_init(&d->event_lock);
INIT_WORK(&d->reset_work, phy_reset_work); INIT_SAS_WORK(&d->reset_work, phy_reset_work);
INIT_WORK(&d->enable_work, phy_enable_work); INIT_SAS_WORK(&d->enable_work, phy_enable_work);
d->phy = phy; d->phy = phy;
phy->hostdata = d; phy->hostdata = d;
......
...@@ -45,10 +45,10 @@ struct sas_phy_data { ...@@ -45,10 +45,10 @@ struct sas_phy_data {
struct mutex event_lock; struct mutex event_lock;
int hard_reset; int hard_reset;
int reset_result; int reset_result;
struct work_struct reset_work; struct sas_work reset_work;
int enable; int enable;
int enable_result; int enable_result;
struct work_struct enable_work; struct sas_work enable_work;
}; };
void sas_scsi_recover_host(struct Scsi_Host *shost); void sas_scsi_recover_host(struct Scsi_Host *shost);
...@@ -80,7 +80,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work); ...@@ -80,7 +80,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work);
void sas_porte_link_reset_err(struct work_struct *work); void sas_porte_link_reset_err(struct work_struct *work);
void sas_porte_timer_event(struct work_struct *work); void sas_porte_timer_event(struct work_struct *work);
void sas_porte_hard_reset(struct work_struct *work); void sas_porte_hard_reset(struct work_struct *work);
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);
int sas_notify_lldd_dev_found(struct domain_device *); int sas_notify_lldd_dev_found(struct domain_device *);
void sas_notify_lldd_dev_gone(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *);
......
...@@ -32,8 +32,7 @@ ...@@ -32,8 +32,7 @@
static void sas_phye_loss_of_signal(struct work_struct *work) static void sas_phye_loss_of_signal(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending); clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending);
...@@ -43,8 +42,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) ...@@ -43,8 +42,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work)
static void sas_phye_oob_done(struct work_struct *work) static void sas_phye_oob_done(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending); clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending);
...@@ -53,8 +51,7 @@ static void sas_phye_oob_done(struct work_struct *work) ...@@ -53,8 +51,7 @@ static void sas_phye_oob_done(struct work_struct *work)
static void sas_phye_oob_error(struct work_struct *work) static void sas_phye_oob_error(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
struct sas_ha_struct *sas_ha = phy->ha; struct sas_ha_struct *sas_ha = phy->ha;
struct asd_sas_port *port = phy->port; struct asd_sas_port *port = phy->port;
...@@ -85,8 +82,7 @@ static void sas_phye_oob_error(struct work_struct *work) ...@@ -85,8 +82,7 @@ static void sas_phye_oob_error(struct work_struct *work)
static void sas_phye_spinup_hold(struct work_struct *work) static void sas_phye_spinup_hold(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
struct sas_ha_struct *sas_ha = phy->ha; struct sas_ha_struct *sas_ha = phy->ha;
struct sas_internal *i = struct sas_internal *i =
...@@ -127,14 +123,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) ...@@ -127,14 +123,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
phy->error = 0; phy->error = 0;
INIT_LIST_HEAD(&phy->port_phy_el); INIT_LIST_HEAD(&phy->port_phy_el);
for (k = 0; k < PORT_NUM_EVENTS; k++) { for (k = 0; k < PORT_NUM_EVENTS; k++) {
INIT_WORK(&phy->port_events[k].work, INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]);
sas_port_event_fns[k]);
phy->port_events[k].phy = phy; phy->port_events[k].phy = phy;
} }
for (k = 0; k < PHY_NUM_EVENTS; k++) { for (k = 0; k < PHY_NUM_EVENTS; k++) {
INIT_WORK(&phy->phy_events[k].work, INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]);
sas_phy_event_fns[k]);
phy->phy_events[k].phy = phy; phy->phy_events[k].phy = phy;
} }
...@@ -144,8 +138,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) ...@@ -144,8 +138,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
spin_lock_init(&phy->sas_prim_lock); spin_lock_init(&phy->sas_prim_lock);
phy->frame_rcvd_size = 0; phy->frame_rcvd_size = 0;
phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i);
i);
if (!phy->phy) if (!phy->phy)
return -ENOMEM; return -ENOMEM;
......
...@@ -208,8 +208,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) ...@@ -208,8 +208,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
void sas_porte_bytes_dmaed(struct work_struct *work) void sas_porte_bytes_dmaed(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending); clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending);
...@@ -219,8 +218,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work) ...@@ -219,8 +218,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work)
void sas_porte_broadcast_rcvd(struct work_struct *work) void sas_porte_broadcast_rcvd(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
unsigned long flags; unsigned long flags;
u32 prim; u32 prim;
...@@ -237,8 +235,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) ...@@ -237,8 +235,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work)
void sas_porte_link_reset_err(struct work_struct *work) void sas_porte_link_reset_err(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending);
...@@ -248,8 +245,7 @@ void sas_porte_link_reset_err(struct work_struct *work) ...@@ -248,8 +245,7 @@ void sas_porte_link_reset_err(struct work_struct *work)
void sas_porte_timer_event(struct work_struct *work) void sas_porte_timer_event(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending);
...@@ -259,8 +255,7 @@ void sas_porte_timer_event(struct work_struct *work) ...@@ -259,8 +255,7 @@ void sas_porte_timer_event(struct work_struct *work)
void sas_porte_hard_reset(struct work_struct *work) void sas_porte_hard_reset(struct work_struct *work)
{ {
struct asd_sas_event *ev = struct asd_sas_event *ev = to_asd_sas_event(work);
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy; struct asd_sas_phy *phy = ev->phy;
clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); clear_bit(PORTE_HARD_RESET, &phy->port_events_pending);
......
...@@ -217,11 +217,29 @@ struct domain_device { ...@@ -217,11 +217,29 @@ struct domain_device {
struct kref kref; struct kref kref;
}; };
struct sas_discovery_event { struct sas_work {
struct list_head drain_node;
struct work_struct work; 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; 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 {
struct sas_discovery_event disc_work[DISC_NUM_EVENTS]; struct sas_discovery_event disc_work[DISC_NUM_EVENTS];
unsigned long pending; unsigned long pending;
...@@ -244,7 +262,7 @@ struct asd_sas_port { ...@@ -244,7 +262,7 @@ struct asd_sas_port {
struct list_head destroy_list; struct list_head destroy_list;
enum sas_linkrate linkrate; enum sas_linkrate linkrate;
struct work_struct work; struct sas_work work;
/* public: */ /* public: */
int id; int id;
...@@ -270,10 +288,17 @@ struct asd_sas_port { ...@@ -270,10 +288,17 @@ struct asd_sas_port {
}; };
struct asd_sas_event { struct asd_sas_event {
struct work_struct work; struct sas_work work;
struct asd_sas_phy *phy; 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 phy pretty much is controlled by the LLDD.
* The class only reads those fields. * The class only reads those fields.
*/ */
...@@ -333,10 +358,17 @@ struct scsi_core { ...@@ -333,10 +358,17 @@ struct scsi_core {
}; };
struct sas_ha_event { struct sas_ha_event {
struct work_struct work; struct sas_work work;
struct sas_ha_struct *ha; 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 { enum sas_ha_state {
SAS_HA_REGISTERED, SAS_HA_REGISTERED,
SAS_HA_DRAINING, SAS_HA_DRAINING,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment