[SCSI] libata, libsas: introduce sched_eh and end_eh port ops
When managing shost->host_eh_scheduled libata assumes that there is a 1:1 shost-to-ata_port relationship. libsas creates a 1:N relationship so it needs to manage host_eh_scheduled cumulatively at the host level. The sched_eh and end_eh port port ops allow libsas to track when domain devices enter/leave the "eh-pending" state under ha->lock (previously named ha->state_lock, but it is no longer just a lock for ha->state changes). Since host_eh_scheduled indicates eh without backing commands pinning the device it can be deallocated at any time. Move the taking of the domain_device reference under the port_lock to guarantee that the ata_port stays around for the duration of eh. Reviewed-by: Jacek Danecki <jacek.danecki@intel.com> Acked-by: Jeff Garzik <jgarzik@redhat.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:

committed by
James Bottomley

parent
3b661a92e8
commit
e4a9c3732c
@@ -523,6 +523,31 @@ static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev)
|
||||
i->dft->lldd_ata_set_dmamode(dev);
|
||||
}
|
||||
|
||||
static void sas_ata_sched_eh(struct ata_port *ap)
|
||||
{
|
||||
struct domain_device *dev = ap->private_data;
|
||||
struct sas_ha_struct *ha = dev->port->ha;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ha->lock, flags);
|
||||
if (!test_and_set_bit(SAS_DEV_EH_PENDING, &dev->state))
|
||||
ha->eh_active++;
|
||||
ata_std_sched_eh(ap);
|
||||
spin_unlock_irqrestore(&ha->lock, flags);
|
||||
}
|
||||
|
||||
void sas_ata_end_eh(struct ata_port *ap)
|
||||
{
|
||||
struct domain_device *dev = ap->private_data;
|
||||
struct sas_ha_struct *ha = dev->port->ha;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ha->lock, flags);
|
||||
if (test_and_clear_bit(SAS_DEV_EH_PENDING, &dev->state))
|
||||
ha->eh_active--;
|
||||
spin_unlock_irqrestore(&ha->lock, flags);
|
||||
}
|
||||
|
||||
static struct ata_port_operations sas_sata_ops = {
|
||||
.prereset = ata_std_prereset,
|
||||
.hardreset = sas_ata_hard_reset,
|
||||
@@ -536,6 +561,8 @@ static struct ata_port_operations sas_sata_ops = {
|
||||
.port_start = ata_sas_port_start,
|
||||
.port_stop = ata_sas_port_stop,
|
||||
.set_dmamode = sas_ata_set_dmamode,
|
||||
.sched_eh = sas_ata_sched_eh,
|
||||
.end_eh = sas_ata_end_eh,
|
||||
};
|
||||
|
||||
static struct ata_port_info sata_port_info = {
|
||||
@@ -708,10 +735,6 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie)
|
||||
struct ata_port *ap = dev->sata_dev.ap;
|
||||
struct sas_ha_struct *ha = dev->port->ha;
|
||||
|
||||
/* hold a reference over eh since we may be racing with final
|
||||
* remove once all commands are completed
|
||||
*/
|
||||
kref_get(&dev->kref);
|
||||
sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n");
|
||||
ata_scsi_port_error_handler(ha->core.shost, ap);
|
||||
sas_put_device(dev);
|
||||
@@ -742,6 +765,13 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)
|
||||
list_for_each_entry(dev, &port->dev_list, dev_list_node) {
|
||||
if (!dev_is_sata(dev))
|
||||
continue;
|
||||
|
||||
/* hold a reference over eh since we may be
|
||||
* racing with final remove once all commands
|
||||
* are completed
|
||||
*/
|
||||
kref_get(&dev->kref);
|
||||
|
||||
async_schedule_domain(async_sas_ata_eh, dev, &async);
|
||||
}
|
||||
spin_unlock(&port->dev_list_lock);
|
||||
|
Reference in New Issue
Block a user