+/**
+ * ata_scsi_offline_dev - offline attached SCSI device
+ * @dev: ATA device to offline attached SCSI device for
+ *
+ * This function is called from ata_eh_hotplug() and responsible
+ * for taking the SCSI device attached to @dev offline. This
+ * function is called with host_set lock which protects dev->sdev
+ * against clearing.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if attached SCSI device exists, 0 otherwise.
+ */
+int ata_scsi_offline_dev(struct ata_device *dev)
+{
+ if (dev->sdev) {
+ scsi_device_set_state(dev->sdev, SDEV_OFFLINE);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * ata_scsi_remove_dev - remove attached SCSI device
+ * @dev: ATA device to remove attached SCSI device for
+ *
+ * This function is called from ata_eh_scsi_hotplug() and
+ * responsible for removing the SCSI device attached to @dev.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+static void ata_scsi_remove_dev(struct ata_device *dev)
+{
+ struct ata_port *ap = dev->ap;
+ struct scsi_device *sdev;
+ unsigned long flags;
+
+ /* Alas, we need to grab scan_mutex to ensure SCSI device
+ * state doesn't change underneath us and thus
+ * scsi_device_get() always succeeds. The mutex locking can
+ * be removed if there is __scsi_device_get() interface which
+ * increments reference counts regardless of device state.
+ */
+ mutex_lock(&ap->host->scan_mutex);
+ spin_lock_irqsave(ap->lock, flags);
+
+ /* clearing dev->sdev is protected by host_set lock */
+ sdev = dev->sdev;
+ dev->sdev = NULL;
+
+ if (sdev) {
+ /* If user initiated unplug races with us, sdev can go
+ * away underneath us after the host_set lock and
+ * scan_mutex are released. Hold onto it.
+ */
+ if (scsi_device_get(sdev) == 0) {
+ /* The following ensures the attached sdev is
+ * offline on return from ata_scsi_offline_dev()
+ * regardless it wins or loses the race
+ * against this function.
+ */
+ scsi_device_set_state(sdev, SDEV_OFFLINE);
+ } else {
+ WARN_ON(1);
+ sdev = NULL;
+ }
+ }
+
+ spin_unlock_irqrestore(ap->lock, flags);
+ mutex_unlock(&ap->host->scan_mutex);
+
+ if (sdev) {
+ ata_dev_printk(dev, KERN_INFO, "detaching (SCSI %s)\n",
+ sdev->sdev_gendev.bus_id);
+
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ }
+}
+
+/**
+ * ata_scsi_hotplug - SCSI part of hotplug
+ * @data: Pointer to ATA port to perform SCSI hotplug on
+ *
+ * Perform SCSI part of hotplug. It's executed from a separate
+ * workqueue after EH completes. This is necessary because SCSI
+ * hot plugging requires working EH and hot unplugging is
+ * synchronized with hot plugging with a mutex.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_scsi_hotplug(void *data)
+{
+ struct ata_port *ap = data;
+ int i;
+
+ if (ap->flags & ATA_FLAG_UNLOADING) {
+ DPRINTK("ENTER/EXIT - unloading\n");
+ return;
+ }
+
+ DPRINTK("ENTER\n");
+
+ /* unplug detached devices */
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ struct ata_device *dev = &ap->device[i];
+ 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);
+ }
+
+ /* scan for new ones */
+ ata_scsi_scan_host(ap);
+
+ /* If we scanned while EH was in progress, scan would have
+ * failed silently. Requeue if there are enabled but
+ * unattached devices.
+ */
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ struct ata_device *dev = &ap->device[i];
+ if (ata_dev_enabled(dev) && !dev->sdev) {
+ queue_delayed_work(ata_aux_wq, &ap->hotplug_task, HZ);
+ break;
+ }
+ }
+
+ 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->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->lock, flags);
+
+ return rc;
+}
+
+/**
+ * ata_scsi_dev_rescan - initiate scsi_rescan_device()
+ * @data: Pointer to ATA port to perform scsi_rescan_device()
+ *
+ * After ATA pass thru (SAT) commands are executed successfully,
+ * libata need to propagate the changes to SCSI layer. This
+ * function must be executed from ata_aux_wq such that sdev
+ * attach/detach don't race with rescan.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_scsi_dev_rescan(void *data)
+{
+ struct ata_port *ap = data;
+ struct ata_device *dev;
+ unsigned int i;
+
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ dev = &ap->device[i];
+
+ if (ata_dev_enabled(dev) && dev->sdev)
+ scsi_rescan_device(&(dev->sdev->sdev_gendev));
+ }
+}