unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
- current_sdev->sdev_target->starget_sdev_user = NULL;
+ scsi_target(current_sdev)->starget_sdev_user = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
/*
blk_run_queue(current_sdev->request_queue);
spin_lock_irqsave(shost->host_lock, flags);
- if (current_sdev->sdev_target->starget_sdev_user)
+ if (scsi_target(current_sdev)->starget_sdev_user)
goto out;
list_for_each_entry_safe(sdev, tmp, ¤t_sdev->same_target_siblings,
same_target_siblings) {
}
if (blk_pc_request(req)) { /* SG_IO ioctl from block level */
- req->errors = (driver_byte(result) & DRIVER_SENSE) ?
- (CHECK_CONDITION << 1) : (result & 0xff);
+ req->errors = result;
if (result) {
clear_errors = 0;
if (cmd->sense_buffer[0] & 0x70) {
} else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
if(unlikely(specials_only)) {
- if(specials_only == SDEV_QUIESCE)
+ if(specials_only == SDEV_QUIESCE ||
+ specials_only == SDEV_BLOCK)
return BLKPREP_DEFER;
printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to device being removed\n",
if (!scsi_host_queue_ready(q, shost, sdev))
goto not_ready;
if (sdev->single_lun) {
- if (sdev->sdev_target->starget_sdev_user &&
- sdev->sdev_target->starget_sdev_user != sdev)
+ if (scsi_target(sdev)->starget_sdev_user &&
+ scsi_target(sdev)->starget_sdev_user != sdev)
goto not_ready;
- sdev->sdev_target->starget_sdev_user = sdev;
+ scsi_target(sdev)->starget_sdev_user = sdev;
}
shost->host_busy++;
u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost)
{
struct device *host_dev;
+ u64 bounce_limit = 0xffffffff;
if (shost->unchecked_isa_dma)
return BLK_BOUNCE_ISA;
-
- host_dev = scsi_get_device(shost);
- if (PCI_DMA_BUS_IS_PHYS && host_dev && host_dev->dma_mask)
- return *host_dev->dma_mask;
-
/*
* Platforms with virtual-DMA translation
* hardware have no practical limit.
*/
- return BLK_BOUNCE_ANY;
+ if (!PCI_DMA_BUS_IS_PHYS)
+ return BLK_BOUNCE_ANY;
+
+ host_dev = scsi_get_device(shost);
+ if (host_dev && host_dev->dma_mask)
+ bounce_limit = *host_dev->dma_mask;
+
+ return bounce_limit;
}
struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
if (!sreq)
return -ENOMEM;
- sreq->sr_data_direction = DMA_NONE;
- scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries);
+ sreq->sr_data_direction = DMA_NONE;
+ scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries);
if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
- (sreq->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION &&
+ ((sreq->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION ||
+ (sreq->sr_sense_buffer[2] & 0x0f) == NOT_READY) &&
sdev->removable) {
sdev->changed = 1;
sreq->sr_result = 0;
case SDEV_CREATED:
case SDEV_OFFLINE:
case SDEV_QUIESCE:
+ case SDEV_BLOCK:
break;
default:
goto illegal;
case SDEV_CREATED:
case SDEV_RUNNING:
case SDEV_QUIESCE:
+ case SDEV_BLOCK:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
+ case SDEV_BLOCK:
+ switch (oldstate) {
+ case SDEV_CREATED:
+ case SDEV_RUNNING:
break;
default:
goto illegal;
case SDEV_CREATED:
case SDEV_RUNNING:
case SDEV_OFFLINE:
+ case SDEV_BLOCK:
break;
default:
goto illegal;
}
EXPORT_SYMBOL(scsi_device_resume);
+static int
+device_quiesce_fn(struct device *dev, void *data)
+{
+ scsi_device_quiesce(to_scsi_device(dev));
+ return 0;
+}
+
+void
+scsi_target_quiesce(struct scsi_target *starget)
+{
+ device_for_each_child(&starget->dev, NULL, device_quiesce_fn);
+}
+EXPORT_SYMBOL(scsi_target_quiesce);
+
+static int
+device_resume_fn(struct device *dev, void *data)
+{
+ scsi_device_resume(to_scsi_device(dev));
+ return 0;
+}
+
+void
+scsi_target_resume(struct scsi_target *starget)
+{
+ device_for_each_child(&starget->dev, NULL, device_resume_fn);
+}
+EXPORT_SYMBOL(scsi_target_resume);
+
+/**
+ * scsi_internal_device_block - internal function to put a device
+ * temporarily into the SDEV_BLOCK state
+ * @sdev: device to block
+ *
+ * Block request made by scsi lld's to temporarily stop all
+ * scsi commands on the specified device. Called from interrupt
+ * or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_BLOCK state
+ * (which must be a legal transition). When the device is in this
+ * state, all commands are deferred until the scsi lld reenables
+ * the device with scsi_device_unblock or device_block_tmo fires.
+ * This routine assumes the host_lock is held on entry.
+ *
+ * As the LLDD/Transport that is calling this function doesn't
+ * actually know what the device state is, the function may be
+ * called at an inappropriate time. Therefore, before requesting
+ * the state change, the function validates that the transition is
+ * valid.
+ **/
+int
+scsi_internal_device_block(struct scsi_device *sdev)
+{
+ request_queue_t *q = sdev->request_queue;
+ unsigned long flags;
+ int err = 0;
+
+ if ((sdev->sdev_state != SDEV_CREATED) &&
+ (sdev->sdev_state != SDEV_RUNNING))
+ return 0;
+
+ err = scsi_device_set_state(sdev, SDEV_BLOCK);
+ if (err)
+ return err;
+
+ /*
+ * The device has transitioned to SDEV_BLOCK. Stop the
+ * block layer from calling the midlayer with this device's
+ * request queue.
+ */
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_internal_device_block);
+
+/**
+ * scsi_internal_device_unblock - resume a device after a block request
+ * @sdev: device to resume
+ *
+ * Called by scsi lld's or the midlayer to restart the device queue
+ * for the previously suspended scsi device. Called from interrupt or
+ * normal process context.
+ *
+ * Returns zero if successful or error if not.
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_RUNNING state
+ * (which must be a legal transition) allowing the midlayer to
+ * goose the queue for this device. This routine assumes the
+ * host_lock is held upon entry.
+ *
+ * As the LLDD/Transport that is calling this function doesn't
+ * actually know what the device state is, the function may be
+ * called at an inappropriate time. Therefore, before requesting
+ * the state change, the function validates that the transition is
+ * valid.
+ **/
+int
+scsi_internal_device_unblock(struct scsi_device *sdev)
+{
+ request_queue_t *q = sdev->request_queue;
+ int err;
+ unsigned long flags;
+
+ if (sdev->sdev_state != SDEV_BLOCK)
+ return 0;
+
+ /*
+ * Try to transition the scsi device to SDEV_RUNNING
+ * and goose the device queue if successful.
+ */
+ err = scsi_device_set_state(sdev, SDEV_RUNNING);
+ if (err)
+ return err;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_internal_device_unblock);