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

Commit 41bda9c9 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik
Browse files

libata-link: update hotplug to handle PMP links



Update hotplug to handle PMP links.  When PMP is attached, the PMP
number corresponds to C of SCSI H:C:I:L.  While at it, change argument
to ata_find_dev() to @devno from @id to avoid confusion with SCSI
device ID.

Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 9b1e2658
Loading
Loading
Loading
Loading
+5 −2
Original line number Original line Diff line number Diff line
@@ -6604,6 +6604,7 @@ int ata_host_activate(struct ata_host *host, int irq,
void ata_port_detach(struct ata_port *ap)
void ata_port_detach(struct ata_port *ap)
{
{
	unsigned long flags;
	unsigned long flags;
	struct ata_link *link;
	struct ata_device *dev;
	struct ata_device *dev;


	if (!ap->ops->error_handler)
	if (!ap->ops->error_handler)
@@ -6621,8 +6622,10 @@ void ata_port_detach(struct ata_port *ap)
	 */
	 */
	spin_lock_irqsave(ap->lock, flags);
	spin_lock_irqsave(ap->lock, flags);


	ata_link_for_each_dev(dev, &ap->link)
	ata_port_for_each_link(link, ap) {
		ata_link_for_each_dev(dev, link)
			ata_dev_disable(dev);
			ata_dev_disable(dev);
	}


	spin_unlock_irqrestore(ap->lock, flags);
	spin_unlock_irqrestore(ap->lock, flags);


+109 −50
Original line number Original line Diff line number Diff line
@@ -2423,21 +2423,36 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
	return 0;
	return 0;
}
}


static struct ata_device * ata_find_dev(struct ata_port *ap, int id)
static struct ata_device * ata_find_dev(struct ata_port *ap, int devno)
{
{
	if (likely(id < ata_link_max_devices(&ap->link)))
	if (ap->nr_pmp_links == 0) {
		return &ap->link.device[id];
		if (likely(devno < ata_link_max_devices(&ap->link)))
			return &ap->link.device[devno];
	} else {
		if (likely(devno < ap->nr_pmp_links))
			return &ap->pmp_link[devno].device[0];
	}

	return NULL;
	return NULL;
}
}


static struct ata_device * __ata_scsi_find_dev(struct ata_port *ap,
static struct ata_device * __ata_scsi_find_dev(struct ata_port *ap,
					const struct scsi_device *scsidev)
					const struct scsi_device *scsidev)
{
{
	int devno;

	/* skip commands not addressed to targets we simulate */
	/* skip commands not addressed to targets we simulate */
	if (ap->nr_pmp_links == 0) {
		if (unlikely(scsidev->channel || scsidev->lun))
		if (unlikely(scsidev->channel || scsidev->lun))
			return NULL;
			return NULL;
		devno = scsidev->id;
	} else {
		if (unlikely(scsidev->id || scsidev->lun))
			return NULL;
		devno = scsidev->channel;
	}


	return ata_find_dev(ap, scsidev->id);
	return ata_find_dev(ap, devno);
}
}


/**
/**
@@ -2951,34 +2966,47 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
{
{
	int tries = 5;
	int tries = 5;
	struct ata_device *last_failed_dev = NULL;
	struct ata_device *last_failed_dev = NULL;
	struct ata_link *link;
	struct ata_device *dev;
	struct ata_device *dev;


	if (ap->flags & ATA_FLAG_DISABLED)
	if (ap->flags & ATA_FLAG_DISABLED)
		return;
		return;


 repeat:
 repeat:
	ata_link_for_each_dev(dev, &ap->link) {
	ata_port_for_each_link(link, ap) {
		ata_link_for_each_dev(dev, link) {
			struct scsi_device *sdev;
			struct scsi_device *sdev;
			int channel = 0, id = 0;


			if (!ata_dev_enabled(dev) || dev->sdev)
			if (!ata_dev_enabled(dev) || dev->sdev)
				continue;
				continue;


		sdev = __scsi_add_device(ap->scsi_host, 0, dev->devno, 0, NULL);
			if (ata_is_host_link(link))
				id = dev->devno;
			else
				channel = link->pmp;

			sdev = __scsi_add_device(ap->scsi_host, channel, id, 0,
						 NULL);
			if (!IS_ERR(sdev)) {
			if (!IS_ERR(sdev)) {
				dev->sdev = sdev;
				dev->sdev = sdev;
				scsi_device_put(sdev);
				scsi_device_put(sdev);
			}
			}
		}
		}
	}


	/* If we scanned while EH was in progress or allocation
	/* If we scanned while EH was in progress or allocation
	 * failure occurred, scan would have failed silently.  Check
	 * failure occurred, scan would have failed silently.  Check
	 * whether all devices are attached.
	 * whether all devices are attached.
	 */
	 */
	ata_link_for_each_dev(dev, &ap->link) {
	ata_port_for_each_link(link, ap) {
		ata_link_for_each_dev(dev, link) {
			if (ata_dev_enabled(dev) && !dev->sdev)
			if (ata_dev_enabled(dev) && !dev->sdev)
			break;
				goto exit_loop;
		}
		}
	if (!dev)
	}
 exit_loop:
	if (!link)
		return;
		return;


	/* we're missing some SCSI devices */
	/* we're missing some SCSI devices */
@@ -3092,6 +3120,25 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
	}
	}
}
}


static void ata_scsi_handle_link_detach(struct ata_link *link)
{
	struct ata_port *ap = link->ap;
	struct ata_device *dev;

	ata_link_for_each_dev(dev, link) {
		unsigned long flags;

		if (!(dev->flags & ATA_DFLAG_DETACHED))
			continue;

		spin_lock_irqsave(ap->lock, flags);
		dev->flags &= ~ATA_DFLAG_DETACHED;
		spin_unlock_irqrestore(ap->lock, flags);

		ata_scsi_remove_dev(dev);
	}
}

/**
/**
 *	ata_scsi_hotplug - SCSI part of hotplug
 *	ata_scsi_hotplug - SCSI part of hotplug
 *	@work: Pointer to ATA port to perform SCSI hotplug on
 *	@work: Pointer to ATA port to perform SCSI hotplug on
@@ -3108,7 +3155,7 @@ void ata_scsi_hotplug(struct work_struct *work)
{
{
	struct ata_port *ap =
	struct ata_port *ap =
		container_of(work, struct ata_port, hotplug_task.work);
		container_of(work, struct ata_port, hotplug_task.work);
	struct ata_device *dev;
	int i;


	if (ap->pflags & ATA_PFLAG_UNLOADING) {
	if (ap->pflags & ATA_PFLAG_UNLOADING) {
		DPRINTK("ENTER/EXIT - unloading\n");
		DPRINTK("ENTER/EXIT - unloading\n");
@@ -3117,19 +3164,14 @@ void ata_scsi_hotplug(struct work_struct *work)


	DPRINTK("ENTER\n");
	DPRINTK("ENTER\n");


	/* unplug detached devices */
	/* Unplug detached devices.  We cannot use link iterator here
	ata_link_for_each_dev(dev, &ap->link) {
	 * because PMP links have to be scanned even if PMP is
		unsigned long flags;
	 * currently not attached.  Iterate manually.

	 */
		if (!(dev->flags & ATA_DFLAG_DETACHED))
	ata_scsi_handle_link_detach(&ap->link);
			continue;
	if (ap->pmp_link)

		for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
		spin_lock_irqsave(ap->lock, flags);
			ata_scsi_handle_link_detach(&ap->pmp_link[i]);
		dev->flags &= ~ATA_DFLAG_DETACHED;
		spin_unlock_irqrestore(ap->lock, flags);

		ata_scsi_remove_dev(dev);
	}


	/* scan for new ones */
	/* scan for new ones */
	ata_scsi_scan_host(ap, 0);
	ata_scsi_scan_host(ap, 0);
@@ -3157,26 +3199,40 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
			      unsigned int id, unsigned int lun)
			      unsigned int id, unsigned int lun)
{
{
	struct ata_port *ap = ata_shost_to_port(shost);
	struct ata_port *ap = ata_shost_to_port(shost);
	struct ata_eh_info *ehi = &ap->link.eh_info;
	unsigned long flags;
	unsigned long flags;
	int rc = 0;
	int devno, rc = 0;


	if (!ap->ops->error_handler)
	if (!ap->ops->error_handler)
		return -EOPNOTSUPP;
		return -EOPNOTSUPP;


	if ((channel != SCAN_WILD_CARD && channel != 0) ||
	if (lun != SCAN_WILD_CARD && lun)
	    (lun != SCAN_WILD_CARD && lun != 0))
		return -EINVAL;
		return -EINVAL;


	if (ap->nr_pmp_links == 0) {
		if (channel != SCAN_WILD_CARD && channel)
			return -EINVAL;
		devno = id;
	} else {
		if (id != SCAN_WILD_CARD && id)
			return -EINVAL;
		devno = channel;
	}

	spin_lock_irqsave(ap->lock, flags);
	spin_lock_irqsave(ap->lock, flags);


	if (id == SCAN_WILD_CARD) {
	if (devno == SCAN_WILD_CARD) {
		ehi->probe_mask |= (1 << ata_link_max_devices(&ap->link)) - 1;
		struct ata_link *link;

		ata_port_for_each_link(link, ap) {
			struct ata_eh_info *ehi = &link->eh_info;
			ehi->probe_mask |= (1 << ata_link_max_devices(link)) - 1;
			ehi->action |= ATA_EH_SOFTRESET;
			ehi->action |= ATA_EH_SOFTRESET;
		}
	} else {
	} else {
		struct ata_device *dev = ata_find_dev(ap, id);
		struct ata_device *dev = ata_find_dev(ap, devno);


		if (dev) {
		if (dev) {
			struct ata_eh_info *ehi = &dev->link->eh_info;
			ehi->probe_mask |= 1 << dev->devno;
			ehi->probe_mask |= 1 << dev->devno;
			ehi->action |= ATA_EH_SOFTRESET;
			ehi->action |= ATA_EH_SOFTRESET;
			ehi->flags |= ATA_EHI_RESUME_LINK;
			ehi->flags |= ATA_EHI_RESUME_LINK;
@@ -3210,12 +3266,14 @@ void ata_scsi_dev_rescan(struct work_struct *work)
{
{
	struct ata_port *ap =
	struct ata_port *ap =
		container_of(work, struct ata_port, scsi_rescan_task);
		container_of(work, struct ata_port, scsi_rescan_task);
	struct ata_link *link;
	struct ata_device *dev;
	struct ata_device *dev;
	unsigned long flags;
	unsigned long flags;


	spin_lock_irqsave(ap->lock, flags);
	spin_lock_irqsave(ap->lock, flags);


	ata_link_for_each_dev(dev, &ap->link) {
	ata_port_for_each_link(link, ap) {
		ata_link_for_each_dev(dev, link) {
			struct scsi_device *sdev = dev->sdev;
			struct scsi_device *sdev = dev->sdev;


			if (!ata_dev_enabled(dev) || !sdev)
			if (!ata_dev_enabled(dev) || !sdev)
@@ -3228,6 +3286,7 @@ void ata_scsi_dev_rescan(struct work_struct *work)
			scsi_device_put(sdev);
			scsi_device_put(sdev);
			spin_lock_irqsave(ap->lock, flags);
			spin_lock_irqsave(ap->lock, flags);
		}
		}
	}


	spin_unlock_irqrestore(ap->lock, flags);
	spin_unlock_irqrestore(ap->lock, flags);
}
}