Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4a9c373 authored by Dan Williams's avatar Dan Williams Committed by James Bottomley
Browse files

[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: default avatarJacek Danecki <jacek.danecki@intel.com>
Acked-by: default avatarJeff Garzik <jgarzik@redhat.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 3b661a92
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -80,6 +80,8 @@ const struct ata_port_operations ata_base_port_ops = {
	.prereset		= ata_std_prereset,
	.prereset		= ata_std_prereset,
	.postreset		= ata_std_postreset,
	.postreset		= ata_std_postreset,
	.error_handler		= ata_std_error_handler,
	.error_handler		= ata_std_error_handler,
	.sched_eh		= ata_std_sched_eh,
	.end_eh			= ata_std_end_eh,
};
};


const struct ata_port_operations sata_port_ops = {
const struct ata_port_operations sata_port_ops = {
@@ -6642,6 +6644,8 @@ struct ata_port_operations ata_dummy_port_ops = {
	.qc_prep		= ata_noop_qc_prep,
	.qc_prep		= ata_noop_qc_prep,
	.qc_issue		= ata_dummy_qc_issue,
	.qc_issue		= ata_dummy_qc_issue,
	.error_handler		= ata_dummy_error_handler,
	.error_handler		= ata_dummy_error_handler,
	.sched_eh		= ata_std_sched_eh,
	.end_eh			= ata_std_end_eh,
};
};


const struct ata_port_info ata_dummy_port_info = {
const struct ata_port_info ata_dummy_port_info = {
+46 −11
Original line number Original line Diff line number Diff line
@@ -793,12 +793,12 @@ void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap)
		ata_for_each_link(link, ap, HOST_FIRST)
		ata_for_each_link(link, ap, HOST_FIRST)
			memset(&link->eh_info, 0, sizeof(link->eh_info));
			memset(&link->eh_info, 0, sizeof(link->eh_info));


		/* Clear host_eh_scheduled while holding ap->lock such
		/* end eh (clear host_eh_scheduled) while holding
		 * that if exception occurs after this point but
		 * ap->lock such that if exception occurs after this
		 * before EH completion, SCSI midlayer will
		 * point but before EH completion, SCSI midlayer will
		 * re-initiate EH.
		 * re-initiate EH.
		 */
		 */
		host->host_eh_scheduled = 0;
		ap->ops->end_eh(ap);


		spin_unlock_irqrestore(ap->lock, flags);
		spin_unlock_irqrestore(ap->lock, flags);
		ata_eh_release(ap);
		ata_eh_release(ap);
@@ -986,16 +986,13 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc)
}
}


/**
/**
 *	ata_port_schedule_eh - schedule error handling without a qc
 * ata_std_sched_eh - non-libsas ata_ports issue eh with this common routine
 * @ap: ATA port to schedule EH for
 * @ap: ATA port to schedule EH for
 *
 *
 *	Schedule error handling for @ap.  EH will kick in as soon as
 *	LOCKING: inherited from ata_port_schedule_eh
 *	all commands are drained.
 *
 *	LOCKING:
 *	spin_lock_irqsave(host lock)
 *	spin_lock_irqsave(host lock)
 */
 */
void ata_port_schedule_eh(struct ata_port *ap)
void ata_std_sched_eh(struct ata_port *ap)
{
{
	WARN_ON(!ap->ops->error_handler);
	WARN_ON(!ap->ops->error_handler);


@@ -1007,6 +1004,44 @@ void ata_port_schedule_eh(struct ata_port *ap)


	DPRINTK("port EH scheduled\n");
	DPRINTK("port EH scheduled\n");
}
}
EXPORT_SYMBOL_GPL(ata_std_sched_eh);

/**
 * ata_std_end_eh - non-libsas ata_ports complete eh with this common routine
 * @ap: ATA port to end EH for
 *
 * In the libata object model there is a 1:1 mapping of ata_port to
 * shost, so host fields can be directly manipulated under ap->lock, in
 * the libsas case we need to hold a lock at the ha->level to coordinate
 * these events.
 *
 *	LOCKING:
 *	spin_lock_irqsave(host lock)
 */
void ata_std_end_eh(struct ata_port *ap)
{
	struct Scsi_Host *host = ap->scsi_host;

	host->host_eh_scheduled = 0;
}
EXPORT_SYMBOL(ata_std_end_eh);


/**
 *	ata_port_schedule_eh - schedule error handling without a qc
 *	@ap: ATA port to schedule EH for
 *
 *	Schedule error handling for @ap.  EH will kick in as soon as
 *	all commands are drained.
 *
 *	LOCKING:
 *	spin_lock_irqsave(host lock)
 */
void ata_port_schedule_eh(struct ata_port *ap)
{
	/* see: ata_std_sched_eh, unless you know better */
	ap->ops->sched_eh(ap);
}


static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link)
static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link)
{
{
+34 −4
Original line number Original line Diff line number Diff line
@@ -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);
		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 = {
static struct ata_port_operations sas_sata_ops = {
	.prereset		= ata_std_prereset,
	.prereset		= ata_std_prereset,
	.hardreset		= sas_ata_hard_reset,
	.hardreset		= sas_ata_hard_reset,
@@ -536,6 +561,8 @@ static struct ata_port_operations sas_sata_ops = {
	.port_start		= ata_sas_port_start,
	.port_start		= ata_sas_port_start,
	.port_stop		= ata_sas_port_stop,
	.port_stop		= ata_sas_port_stop,
	.set_dmamode		= sas_ata_set_dmamode,
	.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 = {
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 ata_port *ap = dev->sata_dev.ap;
	struct sas_ha_struct *ha = dev->port->ha;
	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");
	sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n");
	ata_scsi_port_error_handler(ha->core.shost, ap);
	ata_scsi_port_error_handler(ha->core.shost, ap);
	sas_put_device(dev);
	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) {
		list_for_each_entry(dev, &port->dev_list, dev_list_node) {
			if (!dev_is_sata(dev))
			if (!dev_is_sata(dev))
				continue;
				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);
			async_schedule_domain(async_sas_ata_eh, dev, &async);
		}
		}
		spin_unlock(&port->dev_list_lock);
		spin_unlock(&port->dev_list_lock);
+4 −2
Original line number Original line Diff line number Diff line
@@ -294,6 +294,8 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d


	spin_lock_irq(&port->dev_list_lock);
	spin_lock_irq(&port->dev_list_lock);
	list_del_init(&dev->dev_list_node);
	list_del_init(&dev->dev_list_node);
	if (dev_is_sata(dev))
		sas_ata_end_eh(dev->sata_dev.ap);
	spin_unlock_irq(&port->dev_list_lock);
	spin_unlock_irq(&port->dev_list_lock);


	sas_put_device(dev);
	sas_put_device(dev);
@@ -488,9 +490,9 @@ static void sas_chain_event(int event, unsigned long *pending,
	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->lock, flags);
		sas_chain_work(ha, sw);
		sas_chain_work(ha, sw);
		spin_unlock_irqrestore(&ha->state_lock, flags);
		spin_unlock_irqrestore(&ha->lock, flags);
	}
	}
}
}


+6 −6
Original line number Original line Diff line number Diff line
@@ -47,9 +47,9 @@ static void sas_queue_event(int event, unsigned long *pending,
	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->lock, flags);
		sas_queue_work(ha, work);
		sas_queue_work(ha, work);
		spin_unlock_irqrestore(&ha->state_lock, flags);
		spin_unlock_irqrestore(&ha->lock, flags);
	}
	}
}
}


@@ -61,18 +61,18 @@ void __sas_drain_work(struct sas_ha_struct *ha)


	set_bit(SAS_HA_DRAINING, &ha->state);
	set_bit(SAS_HA_DRAINING, &ha->state);
	/* flush submitters */
	/* flush submitters */
	spin_lock_irq(&ha->state_lock);
	spin_lock_irq(&ha->lock);
	spin_unlock_irq(&ha->state_lock);
	spin_unlock_irq(&ha->lock);


	drain_workqueue(wq);
	drain_workqueue(wq);


	spin_lock_irq(&ha->state_lock);
	spin_lock_irq(&ha->lock);
	clear_bit(SAS_HA_DRAINING, &ha->state);
	clear_bit(SAS_HA_DRAINING, &ha->state);
	list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
	list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
		list_del_init(&sw->drain_node);
		list_del_init(&sw->drain_node);
		sas_queue_work(ha, sw);
		sas_queue_work(ha, sw);
	}
	}
	spin_unlock_irq(&ha->state_lock);
	spin_unlock_irq(&ha->lock);
}
}


int sas_drain_work(struct sas_ha_struct *ha)
int sas_drain_work(struct sas_ha_struct *ha)
Loading