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

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

[SCSI] libsas, libata: fix start of life for a sas ata_port



This changes the ordering of initialization and probing events from:
  1/ allocate rphy in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN
  2/ allocate ata_port and schedule port probe in DISCE_PROBE
...to:
  1/ allocate ata_port in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN
  2/ allocate rphy in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN
  3/ schedule port probe in DISCE_PROBE

This ordering prevents PHYE_SIGNAL_LOSS_EVENTS from sneaking in to
destrory ata devices before they have been fully initialized:

  BUG: unable to handle kernel paging request at 0000000000003b10
  IP: [<ffffffffa0053d7e>] sas_ata_end_eh+0x12/0x5e [libsas]
  ...
  [<ffffffffa004d1af>] sas_unregister_common_dev+0x78/0xc9 [libsas]
  [<ffffffffa004d4d4>] sas_unregister_dev+0x4f/0xad [libsas]
  [<ffffffffa004d5b1>] sas_unregister_domain_devices+0x7f/0xbf [libsas]
  [<ffffffffa004c487>] sas_deform_port+0x61/0x1b8 [libsas]
  [<ffffffffa004bed0>] sas_phye_loss_of_signal+0x29/0x2b [libsas]

...and kills the awkward "sata domain_device briefly existing in the
domain without an ata_port" state.

Reported-by: default avatarMichal Kosciowski <michal.kosciowski@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Acked-by: default avatarJeff Garzik <jgarzik@redhat.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 0f3fce5c
Loading
Loading
Loading
Loading
+20 −15
Original line number Diff line number Diff line
@@ -3839,18 +3839,25 @@ void ata_sas_port_stop(struct ata_port *ap)
}
EXPORT_SYMBOL_GPL(ata_sas_port_stop);

int ata_sas_async_port_init(struct ata_port *ap)
/**
 * ata_sas_async_probe - simply schedule probing and return
 * @ap: Port to probe
 *
 * For batch scheduling of probe for sas attached ata devices, assumes
 * the port has already been through ata_sas_port_init()
 */
void ata_sas_async_probe(struct ata_port *ap)
{
	int rc = ap->ops->port_start(ap);

	if (!rc) {
		ap->print_id = atomic_inc_return(&ata_print_id);
	__ata_port_probe(ap);
}
EXPORT_SYMBOL_GPL(ata_sas_async_probe);

	return rc;
int ata_sas_sync_probe(struct ata_port *ap)
{
	return ata_port_probe(ap);
}
EXPORT_SYMBOL_GPL(ata_sas_async_port_init);
EXPORT_SYMBOL_GPL(ata_sas_sync_probe);


/**
 *	ata_sas_port_init - Initialize a SATA device
@@ -3867,12 +3874,10 @@ int ata_sas_port_init(struct ata_port *ap)
{
	int rc = ap->ops->port_start(ap);

	if (!rc) {
		ap->print_id = atomic_inc_return(&ata_print_id);
		rc = ata_port_probe(ap);
	}

	if (rc)
		return rc;
	ap->print_id = atomic_inc_return(&ata_print_id);
	return 0;
}
EXPORT_SYMBOL_GPL(ata_sas_port_init);

+5 −1
Original line number Diff line number Diff line
@@ -4549,8 +4549,12 @@ static int ipr_ata_slave_alloc(struct scsi_device *sdev)
	ENTER;
	if (sdev->sdev_target)
		sata_port = sdev->sdev_target->hostdata;
	if (sata_port)
	if (sata_port) {
		rc = ata_sas_port_init(sata_port->ap);
		if (rc == 0)
			rc = ata_sas_sync_probe(sata_port->ap);
	}

	if (rc)
		ipr_slave_destroy(sdev);

+10 −23
Original line number Diff line number Diff line
@@ -546,11 +546,12 @@ static struct ata_port_info sata_port_info = {
	.port_ops = &sas_sata_ops
};

int sas_ata_init_host_and_port(struct domain_device *found_dev)
int sas_ata_init(struct domain_device *found_dev)
{
	struct sas_ha_struct *ha = found_dev->port->ha;
	struct Scsi_Host *shost = ha->core.shost;
	struct ata_port *ap;
	int rc;

	ata_host_init(&found_dev->sata_dev.ata_host,
		      ha->dev,
@@ -567,8 +568,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev)
	ap->private_data = found_dev;
	ap->cbl = ATA_CBL_SATA;
	ap->scsi_host = shost;
	/* publish initialized ata port */
	smp_wmb();
	rc = ata_sas_port_init(ap);
	if (rc) {
		ata_sas_port_destroy(ap);
		return rc;
	}
	found_dev->sata_dev.ap = ap;

	return 0;
@@ -648,18 +652,13 @@ static void sas_get_ata_command_set(struct domain_device *dev)
void sas_probe_sata(struct asd_sas_port *port)
{
	struct domain_device *dev, *n;
	int err;

	mutex_lock(&port->ha->disco_mutex);
	list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
	list_for_each_entry(dev, &port->disco_list, disco_list_node) {
		if (!dev_is_sata(dev))
			continue;

		err = sas_ata_init_host_and_port(dev);
		if (err)
			sas_fail_probe(dev, __func__, err);
		else
			ata_sas_async_port_init(dev->sata_dev.ap);
		ata_sas_async_probe(dev->sata_dev.ap);
	}
	mutex_unlock(&port->ha->disco_mutex);

@@ -718,18 +717,6 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie)
	sas_put_device(dev);
}

static bool sas_ata_dev_eh_valid(struct domain_device *dev)
{
	struct ata_port *ap;

	if (!dev_is_sata(dev))
		return false;
	ap = dev->sata_dev.ap;
	/* consume fully initialized ata ports */
	smp_rmb();
	return !!ap;
}

void sas_ata_strategy_handler(struct Scsi_Host *shost)
{
	struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
@@ -753,7 +740,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)

		spin_lock(&port->dev_list_lock);
		list_for_each_entry(dev, &port->dev_list, dev_list_node) {
			if (!sas_ata_dev_eh_valid(dev))
			if (!dev_is_sata(dev))
				continue;
			async_schedule_domain(async_sas_ata_eh, dev, &async);
		}
+10 −3
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ static int sas_get_port_device(struct asd_sas_port *port)
	struct asd_sas_phy *phy;
	struct sas_rphy *rphy;
	struct domain_device *dev;
	int rc = -ENODEV;

	dev = sas_alloc_device();
	if (!dev)
@@ -110,9 +111,16 @@ static int sas_get_port_device(struct asd_sas_port *port)

	sas_init_dev(dev);

	dev->port = port;
	switch (dev->dev_type) {
	case SAS_END_DEV:
	case SATA_DEV:
		rc = sas_ata_init(dev);
		if (rc) {
			rphy = NULL;
			break;
		}
		/* fall through */
	case SAS_END_DEV:
		rphy = sas_end_device_alloc(port->port);
		break;
	case EDGE_DEV:
@@ -131,7 +139,7 @@ static int sas_get_port_device(struct asd_sas_port *port)

	if (!rphy) {
		sas_put_device(dev);
		return -ENODEV;
		return rc;
	}

	rphy->identify.phy_identifier = phy->phy->identify.phy_identifier;
@@ -139,7 +147,6 @@ static int sas_get_port_device(struct asd_sas_port *port)
	sas_fill_in_rphy(dev, rphy);
	sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);
	port->port_dev = dev;
	dev->port = port;
	dev->linkrate = port->linkrate;
	dev->min_linkrate = port->linkrate;
	dev->max_linkrate = port->linkrate;
+5 −3
Original line number Diff line number Diff line
@@ -790,12 +790,14 @@ static struct domain_device *sas_ex_discover_end_dev(
		if (res)
			goto out_free;

		sas_init_dev(child);
		res = sas_ata_init(child);
		if (res)
			goto out_free;
		rphy = sas_end_device_alloc(phy->port);
		if (unlikely(!rphy))
		if (!rphy)
			goto out_free;

		sas_init_dev(child);

		child->rphy = rphy;
		get_device(&rphy->dev);

Loading