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_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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
/* skip commands not addressed to targets we simulate */
|
int devno;
|
||||||
if (unlikely(scsidev->channel || scsidev->lun))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return ata_find_dev(ap, scsidev->id);
|
/* skip commands not addressed to targets we simulate */
|
||||||
|
if (ap->nr_pmp_links == 0) {
|
||||||
|
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, devno);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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)
|
||||||
* 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
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)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue