forked from mirrors/linux
		
	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: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
		
							parent
							
								
									9b1e2658fa
								
							
						
					
					
						commit
						41bda9c980
					
				
					 2 changed files with 115 additions and 53 deletions
				
			
		| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue