#include <linux/spinlock.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_device.h>
-#include <scsi/scsi_request.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport.h>
#include <linux/libata.h>
#define SECTOR_SIZE 512
typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, const u8 *scsicmd);
-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 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
#define RW_RECOVERY_MPAGE_LEN 12
struct scsi_transport_template ata_scsi_transport_template = {
.eh_strategy_handler = ata_scsi_error,
.eh_timed_out = ata_scsi_timed_out,
+ .user_scan = ata_scsi_user_scan,
};
int ata_scsi_device_resume(struct scsi_device *sdev)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
- struct ata_device *dev = &ap->device[sdev->id];
+ struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
return ata_device_resume(dev);
}
int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
- struct ata_device *dev = &ap->device[sdev->id];
+ struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
return ata_device_suspend(dev, state);
}
int ata_scsi_slave_config(struct scsi_device *sdev)
{
+ struct ata_port *ap = ata_shost_to_port(sdev->host);
+ struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
+
ata_scsi_sdev_config(sdev);
blk_queue_max_phys_segments(sdev->request_queue, LIBATA_MAX_PRD);
- if (sdev->id < ATA_MAX_DEVICES) {
- struct ata_port *ap;
- struct ata_device *dev;
-
- ap = ata_shost_to_port(sdev->host);
- dev = &ap->device[sdev->id];
-
+ if (dev)
ata_scsi_dev_config(sdev, dev);
- }
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->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->lock, flags);
+}
+
/**
* ata_scsi_change_queue_depth - SCSI callback for queue depth config
* @sdev: SCSI device to configure queue depth for
u8 *cdb = cmd->cmnd;
int need_sense = (qc->err_mask != 0);
+ /* We snoop the SET_FEATURES - Write Cache ON/OFF command, and
+ * schedule EH_REVALIDATE operation to update the IDENTIFY DEVICE
+ * cache
+ */
+ if (!need_sense && (qc->tf.command == ATA_CMD_SET_FEATURES) &&
+ ((qc->tf.feature == SETFEATURES_WC_ON) ||
+ (qc->tf.feature == SETFEATURES_WC_OFF))) {
+ qc->ap->eh_info.action |= ATA_EH_REVALIDATE;
+ ata_port_schedule_eh(qc->ap);
+ }
+
/* For ATA pass thru (SAT) commands, generate a sense block if
* user mandated it or if there's an error. Note that if we
* generate because the user forced us to, a check condition
return 0;
dpofua = 0;
- if (ata_dev_supports_fua(args->id) && dev->flags & ATA_DFLAG_LBA48 &&
+ if (ata_dev_supports_fua(args->id) && (dev->flags & ATA_DFLAG_LBA48) &&
(!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count))
dpofua = 1 << 4;
qc->tf.feature |= ATAPI_DMADIR;
}
- qc->nbytes = cmd->bufflen;
+ qc->nbytes = cmd->request_bufflen;
return 0;
}
+static struct ata_device * ata_find_dev(struct ata_port *ap, int id)
+{
+ if (likely(id < ATA_MAX_DEVICES))
+ return &ap->device[id];
+ return NULL;
+}
+
+static struct ata_device * __ata_scsi_find_dev(struct ata_port *ap,
+ const struct scsi_device *scsidev)
+{
+ /* skip commands not addressed to targets we simulate */
+ if (unlikely(scsidev->channel || scsidev->lun))
+ return NULL;
+
+ return ata_find_dev(ap, scsidev->id);
+}
+
+/**
+ * ata_scsi_dev_enabled - determine if device is enabled
+ * @dev: ATA device
+ *
+ * Determine if commands should be sent to the specified device.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 0 if commands are not allowed / 1 if commands are allowed
+ */
+
+static int ata_scsi_dev_enabled(struct ata_device *dev)
+{
+ if (unlikely(!ata_dev_enabled(dev)))
+ return 0;
+
+ if (!atapi_enabled || (dev->ap->flags & ATA_FLAG_NO_ATAPI)) {
+ if (unlikely(dev->class == ATA_DEV_ATAPI)) {
+ ata_dev_printk(dev, KERN_WARNING,
+ "WARNING: ATAPI is %s, device ignored.\n",
+ atapi_enabled ? "not supported with this driver" : "disabled");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
/**
* ata_scsi_find_dev - lookup ata_device from scsi_cmnd
* @ap: ATA port to which the device is attached
* RETURNS:
* Associated ATA device, or %NULL if not found.
*/
-
static struct ata_device *
ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev)
{
- struct ata_device *dev;
-
- /* skip commands not addressed to targets we simulate */
- if (likely(scsidev->id < ATA_MAX_DEVICES))
- dev = &ap->device[scsidev->id];
- else
- return NULL;
-
- if (unlikely((scsidev->channel != 0) ||
- (scsidev->lun != 0)))
- return NULL;
+ struct ata_device *dev = __ata_scsi_find_dev(ap, scsidev);
- if (unlikely(!ata_dev_enabled(dev)))
+ if (unlikely(!dev || !ata_scsi_dev_enabled(dev)))
return NULL;
- if (!atapi_enabled || (ap->flags & ATA_FLAG_NO_ATAPI)) {
- if (unlikely(dev->class == ATA_DEV_ATAPI)) {
- ata_dev_printk(dev, KERN_WARNING,
- "WARNING: ATAPI is %s, device ignored.\n",
- atapi_enabled ? "not supported with this driver" : "disabled");
- return NULL;
- }
- }
-
return dev;
}
{
struct ata_taskfile *tf = &(qc->tf);
struct scsi_cmnd *cmd = qc->scsicmd;
+ struct ata_device *dev = qc->dev;
if ((tf->protocol = ata_scsi_map_proto(scsicmd[1])) == ATA_PROT_UNKNOWN)
goto invalid_fld;
+ /* We may not issue DMA commands if no DMA mode is set */
+ if (tf->protocol == ATA_PROT_DMA && dev->dma_mode == 0)
+ goto invalid_fld;
+
if (scsicmd[1] & 0xe0)
/* PIO multi not supported yet */
goto invalid_fld;
* TODO: find out if we need to do more here to
* cover scatter/gather case.
*/
- qc->nsect = cmd->bufflen / ATA_SECT_SIZE;
+ qc->nsect = cmd->request_bufflen / ATA_SECT_SIZE;
/* request result TF */
qc->flags |= ATA_QCFLAG_RESULT_TF;
ap = ata_shost_to_port(shost);
spin_unlock(shost->host_lock);
- spin_lock(&ap->host_set->lock);
+ spin_lock(ap->lock);
ata_scsi_dump_cdb(ap, cmd);
done(cmd);
}
- spin_unlock(&ap->host_set->lock);
+ spin_unlock(ap->lock);
spin_lock(shost->host_lock);
return rc;
}
void ata_scsi_scan_host(struct ata_port *ap)
{
- struct ata_device *dev;
unsigned int i;
if (ap->flags & ATA_FLAG_DISABLED)
return;
for (i = 0; i < ATA_MAX_DEVICES; i++) {
- dev = &ap->device[i];
+ struct ata_device *dev = &ap->device[i];
+ struct scsi_device *sdev;
+
+ if (!ata_dev_enabled(dev) || dev->sdev)
+ continue;
- if (ata_dev_enabled(dev))
- scsi_scan_target(&ap->host->shost_gendev, 0, i, 0, 0);
+ sdev = __scsi_add_device(ap->host, 0, i, 0, NULL);
+ if (!IS_ERR(sdev)) {
+ dev->sdev = sdev;
+ scsi_device_put(sdev);
+ }
}
}
/**
- * ata_schedule_scsi_eh - schedule EH for SCSI host
- * @shost: SCSI host to invoke error handling on.
+ * ata_scsi_offline_dev - offline attached SCSI device
+ * @dev: ATA device to offline attached SCSI device for
*
- * Schedule SCSI EH without scmd. This is a hack.
+ * 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)
- **/
-void ata_schedule_scsi_eh(struct Scsi_Host *shost)
+ *
+ * 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(shost->host_lock, flags);
+ spin_lock_irqsave(ap->lock, flags);
- if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||
- scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) {
- shost->host_eh_scheduled++;
- scsi_eh_wakeup(shost);
+ 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;
}
- spin_unlock_irqrestore(shost->host_lock, flags);
+ 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));
+ }
}