|
|
device_for_each_child(dev, NULL, target_unblock); | device_for_each_child(dev, NULL, target_unblock); |
} | } |
EXPORT_SYMBOL_GPL(scsi_target_unblock); | EXPORT_SYMBOL_GPL(scsi_target_unblock); |
|
|
|
/* |
|
* As per scsi_wait_req_end_io(), which was removed in 2.6.15 |
|
*/ |
|
static void scsi_protect_wait_req_end_io(struct request *req, int error) |
|
{ |
|
BUG_ON(!req->waiting); |
|
|
|
complete(req->waiting); |
|
} |
|
|
|
/* |
|
* As per scsi_wait_done(), except calls scsi_device_block |
|
* to block the queue at command completion. Only called by |
|
* scsi_protect_wait(). |
|
* todo: |
|
* - we block the queue regardless of success and rely on the |
|
* scsi_protect_queue function to unblock if the command |
|
* failed... should we also inspect here? |
|
*/ |
|
static void scsi_protect_wait_done(struct scsi_cmnd *cmd) |
|
{ |
|
struct request *req = cmd->request; |
|
struct request_queue *q = cmd->device->request_queue; |
|
struct scsi_device *sdev = cmd->device; |
|
unsigned long flags; |
|
|
|
req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */ |
|
|
|
spin_lock_irqsave(q->queue_lock, flags); |
|
if (blk_rq_tagged(req)) |
|
blk_queue_end_tag(q, req); |
|
spin_unlock_irqrestore(q->queue_lock, flags); |
|
|
|
scsi_internal_device_block(sdev); |
|
|
|
if (req->waiting) |
|
complete(req->waiting); |
|
} |
|
|
|
/* |
|
* As per scsi_wait_req(), except sets the completion function |
|
* as scsi_protect_wait_done(). |
|
*/ |
|
void scsi_protect_wait_req(struct scsi_request *sreq, const void *cmnd, void *buffer, |
|
unsigned bufflen, int timeout, int retries) |
|
{ |
|
DECLARE_COMPLETION(wait); |
|
|
|
sreq->sr_request->waiting = &wait; |
|
sreq->sr_request->rq_status = RQ_SCSI_BUSY; |
|
sreq->sr_request->end_io = scsi_protect_wait_req_end_io; |
|
scsi_do_req(sreq, cmnd, buffer, bufflen, scsi_protect_wait_done, |
|
timeout, retries); |
|
wait_for_completion(&wait); |
|
sreq->sr_request->waiting = NULL; |
|
if (sreq->sr_request->rq_status != RQ_SCSI_DONE) |
|
sreq->sr_result |= (DRIVER_ERROR << 24); |
|
|
|
__scsi_release_request(sreq); |
|
} |
|
|
|
/* |
|
* scsi_unprotect_queue() |
|
* - release the queue that was previously blocked |
|
*/ |
|
int scsi_unprotect_queue(request_queue_t *q){ |
|
|
|
struct scsi_device *sdev = q->queuedata; |
|
int rc = 0, pending = 0; |
|
u8 scsi_cmd[MAX_COMMAND_SIZE]; |
|
struct scsi_sense_hdr sshdr; |
|
|
|
if (sdev->sdev_state != SDEV_BLOCK) |
|
return -ENXIO; |
|
|
|
/* Are there any pending jobs on the queue? */ |
|
pending = ((q->rq.count[READ] > 0) || (q->rq.count[WRITE] > 0)) ? 1 : 0; |
|
|
|
rc = scsi_internal_device_unblock(sdev); |
|
if (rc) |
|
return rc; |
|
|
|
if (!pending) { |
|
printk(KERN_DEBUG "scsi_unprotect_queue(): No pending I/O, re-enabling power management..\n"); |
|
|
|
memset(scsi_cmd, 0, sizeof(scsi_cmd)); |
|
scsi_cmd[0] = ATA_16; |
|
scsi_cmd[1] = (3 << 1); /* Non-data */ |
|
/* scsi_cmd[2] is already 0 -- no off.line, cc, or data xfer */ |
|
scsi_cmd[14] = 0xe5; /* CHECK_POWER_MODE1 */ |
|
|
|
/* Good values for timeout and retries? Values below |
|
from scsi_ioctl_send_command() for default case... */ |
|
if (scsi_execute_req(sdev, scsi_cmd, DMA_NONE, NULL, 0, &sshdr, |
|
(10*HZ), 5)) |
|
rc = -EIO; |
|
} |
|
return rc; |
|
} |
|
EXPORT_SYMBOL_GPL(scsi_unprotect_queue); |
|
|
|
/* |
|
* scsi_protect_queue() |
|
* - build and issue the park/standby command.. |
|
* - queue is blocked during command completion handler |
|
*/ |
|
int scsi_protect_queue(request_queue_t *q, int unload) |
|
{ |
|
struct scsi_device *sdev = q->queuedata; |
|
int rc = 0; |
|
u8 scsi_cmd[MAX_COMMAND_SIZE]; |
|
u8 args[7]; |
|
struct scsi_request *sreq; |
|
unsigned char *sb, *desc; |
|
|
|
if (sdev->sdev_state != SDEV_RUNNING) |
|
return -ENXIO; |
|
|
|
memset(args, 0, sizeof(args)); |
|
|
|
if (unload) { |
|
args[0] = 0xe1; |
|
args[1] = 0x44; |
|
args[3] = 0x4c; |
|
args[4] = 0x4e; |
|
args[5] = 0x55; |
|
} else |
|
args[0] = 0xe0; |
|
|
|
memset(scsi_cmd, 0, sizeof(scsi_cmd)); |
|
scsi_cmd[0] = ATA_16; |
|
scsi_cmd[1] = (3 << 1); /* Non-data */ |
|
scsi_cmd[2] = 0x20; /* no off.line, or data xfer, request cc */ |
|
scsi_cmd[4] = args[1]; |
|
scsi_cmd[6] = args[2]; |
|
scsi_cmd[8] = args[3]; |
|
scsi_cmd[10] = args[4]; |
|
scsi_cmd[12] = args[5]; |
|
scsi_cmd[14] = args[0]; |
|
|
|
sreq = scsi_allocate_request(sdev, GFP_KERNEL); |
|
if (!sreq) { |
|
rc = -EINTR; |
|
goto error; |
|
} |
|
|
|
sreq->sr_data_direction = DMA_NONE; |
|
|
|
scsi_protect_wait_req(sreq, scsi_cmd, NULL, 0, (10*HZ), 5); |
|
|
|
if (!sreq->sr_result == ((DRIVER_SENSE << 24) + SAM_STAT_CHECK_CONDITION)) { |
|
printk(KERN_DEBUG "scsi_protect_queue(): head NOT parked!..\n"); |
|
scsi_unprotect_queue(q); /* just in case we still managed to block */ |
|
rc = -EIO; |
|
goto error; |
|
} |
|
|
|
sb = sreq->sr_sense_buffer; |
|
desc = sb + 8; |
|
|
|
/* Retrieve data from check condition */ |
|
args[1] = desc[3]; |
|
args[2] = desc[5]; |
|
args[3] = desc[7]; |
|
args[4] = desc[9]; |
|
args[5] = desc[11]; |
|
args[0] = desc[13]; |
|
|
|
if (unload) { |
|
if (args[3] == 0xc4) |
|
printk(KERN_DEBUG "scsi_protect_queue(): head parked..\n"); |
|
else { |
|
/* error parking the head */ |
|
printk(KERN_DEBUG "scsi_protect_queue(): head NOT parked!..\n"); |
|
rc = -EIO; |
|
scsi_unprotect_queue(q); |
|
} |
|
} else |
|
printk(KERN_DEBUG "scsi_protect_queue(): head park not requested, used standby!..\n"); |
|
|
|
error: |
|
scsi_release_request(sreq); |
|
return rc; |
|
} |
|
EXPORT_SYMBOL_GPL(scsi_protect_queue); |
| |
/** | /** |
* scsi_kmap_atomic_sg - find and atomically map an sg-elemnt | * scsi_kmap_atomic_sg - find and atomically map an sg-elemnt |