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

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
...@@ -6604,6 +6604,7 @@ int ata_host_activate(struct ata_host *host, int irq, ...@@ -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) ...@@ -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_dev_disable(dev); ata_link_for_each_dev(dev, link)
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) ...@@ -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 (unlikely(scsidev->channel || scsidev->lun)) if (ap->nr_pmp_links == 0) {
return NULL; if (unlikely(scsidev->channel || scsidev->lun))
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,22 +2966,32 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync) ...@@ -2951,22 +2966,32 @@ 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) {
struct scsi_device *sdev; ata_link_for_each_dev(dev, link) {
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))
if (!IS_ERR(sdev)) { id = dev->devno;
dev->sdev = sdev; else
scsi_device_put(sdev); channel = link->pmp;
sdev = __scsi_add_device(ap->scsi_host, channel, id, 0,
NULL);
if (!IS_ERR(sdev)) {
dev->sdev = sdev;
scsi_device_put(sdev);
}
} }
} }
...@@ -2974,11 +2999,14 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync) ...@@ -2974,11 +2999,14 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
* 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) {
if (ata_dev_enabled(dev) && !dev->sdev) ata_link_for_each_dev(dev, link) {
break; if (ata_dev_enabled(dev) && !dev->sdev)
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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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;
ehi->action |= ATA_EH_SOFTRESET;
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;
}
} 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,23 +3266,26 @@ void ata_scsi_dev_rescan(struct work_struct *work) ...@@ -3210,23 +3266,26 @@ 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) {
struct scsi_device *sdev = dev->sdev; ata_link_for_each_dev(dev, link) {
struct scsi_device *sdev = dev->sdev;
if (!ata_dev_enabled(dev) || !sdev) if (!ata_dev_enabled(dev) || !sdev)
continue; continue;
if (scsi_device_get(sdev)) if (scsi_device_get(sdev))
continue; continue;
spin_unlock_irqrestore(ap->lock, flags); spin_unlock_irqrestore(ap->lock, flags);
scsi_rescan_device(&(sdev->sdev_gendev)); scsi_rescan_device(&(sdev->sdev_gendev));
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment