]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
[PATCH] libata-hp: implement warmplug
authorTejun Heo <htejun@gmail.com>
Wed, 31 May 2006 09:28:07 +0000 (18:28 +0900)
committerTejun Heo <htejun@gmail.com>
Wed, 31 May 2006 09:28:07 +0000 (18:28 +0900)
Implement warmplug.  User-initiated unplug can be detected by
hostt->slave_destroy() and plug by transportt->user_scan().  This
patch only implements the two callbacks.  The next function will hook
them.

Signed-off-by: Tejun Heo <htejun@gmail.com>
drivers/scsi/libata-core.c
drivers/scsi/libata-scsi.c
include/linux/libata.h

index 8df8ecc51a7883cde3dd360d3d835be92aa0c7e6..c61cfc742388b29637fd006729d84ac527353a43 100644 (file)
@@ -5942,6 +5942,7 @@ EXPORT_SYMBOL_GPL(ata_port_queue_task);
 EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
 EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
 EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
+EXPORT_SYMBOL_GPL(ata_scsi_slave_destroy);
 EXPORT_SYMBOL_GPL(ata_scsi_change_queue_depth);
 EXPORT_SYMBOL_GPL(ata_scsi_release);
 EXPORT_SYMBOL_GPL(ata_host_intr);
index 12563998d97c493c535605b574b4ec682f696696..7c1ac58c430add349e76b82f011d057639f76159 100644 (file)
@@ -57,6 +57,8 @@ static struct ata_device * __ata_scsi_find_dev(struct ata_port *ap,
                                        const struct scsi_device *scsidev);
 static struct ata_device * ata_scsi_find_dev(struct ata_port *ap,
                                            const struct scsi_device *scsidev);
+static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
+                             unsigned int id, unsigned int lun);
 
 
 #define RW_RECOVERY_MPAGE 0x1
@@ -726,6 +728,40 @@ int ata_scsi_slave_config(struct scsi_device *sdev)
        return 0;       /* scsi layer doesn't check return value, sigh */
 }
 
+/**
+ *     ata_scsi_slave_destroy - SCSI device is about to be destroyed
+ *     @sdev: SCSI device to be destroyed
+ *
+ *     @sdev is about to be destroyed for hot/warm unplugging.  If
+ *     this unplugging was initiated by libata as indicated by NULL
+ *     dev->sdev, this function doesn't have to do anything.
+ *     Otherwise, SCSI layer initiated warm-unplug is in progress.
+ *     Clear dev->sdev, schedule the device for ATA detach and invoke
+ *     EH.
+ *
+ *     LOCKING:
+ *     Defined by SCSI layer.  We don't really care.
+ */
+void ata_scsi_slave_destroy(struct scsi_device *sdev)
+{
+       struct ata_port *ap = ata_shost_to_port(sdev->host);
+       unsigned long flags;
+       struct ata_device *dev;
+
+       if (!ap->ops->error_handler)
+               return;
+
+       spin_lock_irqsave(&ap->host_set->lock, flags);
+       dev = __ata_scsi_find_dev(ap, sdev);
+       if (dev && dev->sdev) {
+               /* SCSI device already in CANCEL state, no need to offline it */
+               dev->sdev = NULL;
+               dev->flags |= ATA_DFLAG_DETACH;
+               ata_port_schedule_eh(ap);
+       }
+       spin_unlock_irqrestore(&ap->host_set->lock, flags);
+}
+
 /**
  *     ata_scsi_change_queue_depth - SCSI callback for queue depth config
  *     @sdev: SCSI device to configure queue depth for
@@ -2902,3 +2938,56 @@ void ata_scsi_hotplug(void *data)
 
        DPRINTK("EXIT\n");
 }
+
+/**
+ *     ata_scsi_user_scan - indication for user-initiated bus scan
+ *     @shost: SCSI host to scan
+ *     @channel: Channel to scan
+ *     @id: ID to scan
+ *     @lun: LUN to scan
+ *
+ *     This function is called when user explicitly requests bus
+ *     scan.  Set probe pending flag and invoke EH.
+ *
+ *     LOCKING:
+ *     SCSI layer (we don't care)
+ *
+ *     RETURNS:
+ *     Zero.
+ */
+static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
+                             unsigned int id, unsigned int lun)
+{
+       struct ata_port *ap = ata_shost_to_port(shost);
+       unsigned long flags;
+       int rc = 0;
+
+       if (!ap->ops->error_handler)
+               return -EOPNOTSUPP;
+
+       if ((channel != SCAN_WILD_CARD && channel != 0) ||
+           (lun != SCAN_WILD_CARD && lun != 0))
+               return -EINVAL;
+
+       spin_lock_irqsave(&ap->host_set->lock, flags);
+
+       if (id == SCAN_WILD_CARD) {
+               ap->eh_info.probe_mask |= (1 << ATA_MAX_DEVICES) - 1;
+               ap->eh_info.action |= ATA_EH_SOFTRESET;
+       } else {
+               struct ata_device *dev = ata_find_dev(ap, id);
+
+               if (dev) {
+                       ap->eh_info.probe_mask |= 1 << dev->devno;
+                       ap->eh_info.action |= ATA_EH_SOFTRESET;
+               } else
+                       rc = -EINVAL;
+       }
+
+       if (rc == 0)
+               ata_port_schedule_eh(ap);
+
+       spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+       return rc;
+}
index 407115624d9f508c6d99eb7559621facc69354b4..74786c33c52659b4c28f7f9475dcaf2547417e0a 100644 (file)
@@ -734,6 +734,7 @@ extern int ata_std_bios_param(struct scsi_device *sdev,
                              struct block_device *bdev,
                              sector_t capacity, int geom[]);
 extern int ata_scsi_slave_config(struct scsi_device *sdev);
+extern void ata_scsi_slave_destroy(struct scsi_device *sdev);
 extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
                                       int queue_depth);
 extern struct ata_device *ata_dev_pair(struct ata_device *adev);