分类: LINUX
2009-08-03 21:10:25
发送命令:
int scsi_dispatch_cmd(struct scsi_cmnd *cmd)里面rtn = host->hostt->queuecommand(cmd, scsi_done);会调用命令队列函数,最终执行scsi_done函数,scsi_done函数调用__scsi_done:
void __scsi_done(struct scsi_cmnd *cmd)
{
struct request *rq = cmd->request;
/*
* Set the serial numbers back to zero
*/
cmd->serial_number = 0;
atomic_inc(&cmd->device->iodone_cnt);
if (cmd->result)
atomic_inc(&cmd->device->ioerr_cnt);
BUG_ON(!rq);
/*
* The uptodate/nbytes values don't matter, as we allow partial
* completes and thus will check this in the softirq callback
*/
rq->completion_data = cmd;
blk_complete_request(rq);
}
void blk_complete_request(struct request *req)
{
struct list_head *cpu_list;
unsigned long flags;
BUG_ON(!req->q->softirq_done_fn);
local_irq_save(flags);
cpu_list = &__get_cpu_var(blk_cpu_done);
list_add_tail(&req->donelist, cpu_list);
raise_softirq_irqoff(BLOCK_SOFTIRQ);//激活软中断执行
local_irq_restore(flags);
}
软中断的注册在ll_rw_blk.c的blk_dev_init函数里open_softirq(BLOCK_SOFTIRQ, blk_done_softirq, NULL);
所以激活软中断后会执行到blk_done_softirq函数里
static void blk_done_softirq(struct softirq_action *h)
{
struct list_head *cpu_list, local_list;
local_irq_disable();
cpu_list = &__get_cpu_var(blk_cpu_done);
list_replace_init(cpu_list, &local_list);
local_irq_enable();
while (!list_empty(&local_list)) {
struct request *rq = list_entry(local_list.next, struct request, donelist);
list_del_init(&rq->donelist);
rq->q->softirq_done_fn(rq);//执行软中断函数,下半部,调用scsi_softirq_done 函数
}
}
static void scsi_softirq_done(struct request *rq)
{
struct scsi_cmnd *cmd = rq->completion_data;
unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command;
int disposition;
INIT_LIST_HEAD(&cmd->eh_entry);
disposition = scsi_decide_disposition(cmd);
if (disposition != SUCCESS &&
time_before(cmd->jiffies_at_alloc + wait_for, jiffies)) {
sdev_printk(KERN_ERR, cmd->device,
"timing out command, waited %lus\n",
wait_for/HZ);
disposition = SUCCESS;
}
scsi_log_completion(cmd, disposition);
switch (disposition) {
case SUCCESS:
scsi_finish_command(cmd);
break;
case NEEDS_RETRY:
scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY);
break;
case ADD_TO_MLQUEUE:
scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
break;
default:
if (!scsi_eh_scmd_add(cmd, 0))
scsi_finish_command(cmd);
}
}
scsi_decide_disposition函数里面会决定命令完成的状态,基本是host byte的处理,一般都会返回SUCCESS;
/*
* Function: scsi_finish_command
*
* Purpose: Pass command off to upper layer for finishing of I/O
* request, waking processes that are waiting on results,
* etc.
*/
void scsi_finish_command(struct scsi_cmnd *cmd)
{
struct scsi_device *sdev = cmd->device;
struct Scsi_Host *shost = sdev->host;
struct scsi_driver *drv;
unsigned int good_bytes;
scsi_device_unbusy(sdev);//释放scsi_device设备
/*
* Clear the flags which say that the device/host is no longer
* capable of accepting new commands. These are set in scsi_queue.c
* for both the queue full condition on a device, and for a
* host full condition on the host.
*
* XXX(hch): What about locking?
*/
shost->host_blocked = 0;
sdev->device_blocked = 0;
/*
* If we have valid sense information, then some kind of recovery
* must have taken place. Make a note of this.
*/
if (SCSI_SENSE_VALID(cmd))
cmd->result |= (DRIVER_SENSE << 24);
SCSI_LOG_MLCOMPLETE(4, sdev_printk(KERN_INFO, sdev,
"Notifying upper driver of completion "
"(result %x)\n", cmd->result));
good_bytes = cmd->request_bufflen;
if (cmd->request->cmd_type != REQ_TYPE_BLOCK_PC) {
//这里对于scsi命令是不会执行到这里的,针对scsi命令,cmd_type都是REQ_TYPE_BLOCK_PC这个类型
drv = scsi_cmd_to_driver(cmd);
if (drv->done)
good_bytes = drv->done(cmd);
}
scsi_io_completion(cmd, good_bytes);
}
void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
{
int result = cmd->result;
int this_count = cmd->request_bufflen;
struct request_queue *q = cmd->device->request_queue;
struct request *req = cmd->request;
int clear_errors = 1;
struct scsi_sense_hdr sshdr;
int sense_valid = 0;
int sense_deferred = 0;
scsi_release_buffers(cmd);
if (result) {
sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
if (sense_valid)
sense_deferred = scsi_sense_is_deferred(&sshdr);
}
if (blk_pc_request(req)) { /* SG_IO ioctl from block level */
req->errors = result;
if (result) {
clear_errors = 0;
if (sense_valid && req->sense) {
/*
* SG_IO wants current and deferred errors
*/
int len = 8 + cmd->sense_buffer[7];
if (len > SCSI_SENSE_BUFFERSIZE)
len = SCSI_SENSE_BUFFERSIZE;
memcpy(req->sense, cmd->sense_buffer, len);
req->sense_len = len;
}
}
req->data_len = cmd->resid;
}
/*
* Next deal with any sectors which we were able to correctly
* handle.
*/
SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, "
"%d bytes done.\n",
req->nr_sectors, good_bytes));
SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n", cmd->use_sg));
//printk("%ld sectors total %d bytes done.\n", req->nr_sectors, good_bytes);
//printk("use_sg is %d\n", cmd->use_sg);
if (clear_errors)
req->errors = 0;
/* A number of bytes were successfully read. If there
* are leftovers and there is some kind of error
* (result != 0), retry the rest.
*/
//这里调用scsi_end_request,如果result = 0的话,会从这里直接返回,表示//命令执行完成,继续下一个命令的执行
//否则不会返回
if (scsi_end_request(cmd, 1, good_bytes, result == 0) == NULL)
return;
/* good_bytes = 0, or (inclusive) there were leftovers and
* result = 0, so scsi_end_request couldn't retry.
*/
if (sense_valid && !sense_deferred) {
switch (sshdr.sense_key) {
case UNIT_ATTENTION:
if (cmd->device->removable) {
/* Detected disc change. Set a bit
* and quietly refuse further access.
*/
cmd->device->changed = 1;
scsi_end_request(cmd, 0, this_count, 1);
return;
} else {
/* Must have been a power glitch, or a
* bus reset. Could not have been a
* media change, so we just retry the
* request and see what happens.
*/
scsi_requeue_command(q, cmd);
return;
}
break;
case ILLEGAL_REQUEST:
/* If we had an ILLEGAL REQUEST returned, then
* we may have performed an unsupported
* command. The only thing this should be
* would be a ten byte read where only a six
* byte read was supported. Also, on a system
* where READ CAPACITY failed, we may have
* read past the end of the disk.
*/
if ((cmd->device->use_10_for_rw &&
sshdr.asc == 0x20 && sshdr.ascq == 0x00) &&
(cmd->cmnd[0] == READ_10 ||
cmd->cmnd[0] == WRITE_10)) {
cmd->device->use_10_for_rw = 0;
/* This will cause a retry with a
* 6-byte command.
*/
scsi_requeue_command(q, cmd);
return;
} else {
scsi_end_request(cmd, 0, this_count, 1);
return;
}
break;
case NOT_READY:
/* If the device is in the process of becoming
* ready, or has a temporary blockage, retry.
*/
if (sshdr.asc == 0x04) {
switch (sshdr.ascq) {
case 0x01: /* becoming ready */
case 0x04: /* format in progress */
case 0x05: /* rebuild in progress */
case 0x06: /* recalculation in progress */
case 0x07: /* operation in progress */
case 0x08: /* Long write in progress */
case 0x09: /* self test in progress */
scsi_requeue_command(q, cmd);
return;
default:
break;
}
}
if (!(req->cmd_flags & REQ_QUIET))
scsi_cmd_print_sense_hdr(cmd,
"Device not ready",
&sshdr);
scsi_end_request(cmd, 0, this_count, 1);
return;
case VOLUME_OVERFLOW:
if (!(req->cmd_flags & REQ_QUIET)) {
scmd_printk(KERN_INFO, cmd,
"Volume overflow, CDB: ");
__scsi_print_command(cmd->cmnd);
scsi_print_sense("", cmd);
}
/* See SSC3rXX or current. */
scsi_end_request(cmd, 0, this_count, 1);
return;
default:
break;
}
}
if (host_byte(result) == DID_RESET) {
/* Third party bus reset or reset for error recovery
* reasons. Just retry the request and see what
* happens.
*/
scsi_requeue_command(q, cmd);
return;
}
if (result) {
if (!(req->cmd_flags & REQ_QUIET)) {
scsi_print_result(cmd);
if (driver_byte(result) & DRIVER_SENSE)
scsi_print_sense("", cmd);
}
}
scsi_end_request(cmd, 0, this_count, !result);
}
static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate,
int bytes, int requeue)
{
struct request_queue *q = cmd->device->request_queue;
struct request *req = cmd->request;
unsigned long flags;
/*
* If there are blocks left over at the end, set up the command
* to queue the remainder of them.
*/
//命令执行完成,如果命令需要重新执行,重新加入命令队列,返回cmd
if (end_that_request_chunk(req, uptodate, bytes)) {
int leftover = (req->hard_nr_sectors << 9);
if (blk_pc_request(req))
leftover = req->data_len;
/* kill remainder if no retrys */
if (!uptodate && blk_noretry_request(req))
end_that_request_chunk(req, 0, leftover);
else {
if (requeue) {
/*
* Bleah. Leftovers again. Stick the
* leftovers in the front of the
* queue, and goose the queue again.
*/
scsi_requeue_command(q, cmd);
cmd = NULL;
}
return cmd; //命令需要重新执行,或者执行遇到错误
}
}
add_disk_randomness(req->rq_disk);
spin_lock_irqsave(q->queue_lock, flags);
if (blk_rq_tagged(req))
blk_queue_end_tag(q, req);
end_that_request_last(req, uptodate);
spin_unlock_irqrestore(q->queue_lock, flags);
/*
* This will goose the queue request function at the end, so we don't
* need to worry about launching another command.
*/
scsi_next_command(cmd);
return NULL;
}
void scsi_next_command(struct scsi_cmnd *cmd)
{
struct scsi_device *sdev = cmd->device;
struct request_queue *q = sdev->request_queue;
/* need to hold a reference on the device before we let go of the cmd */
get_device(&sdev->sdev_gendev);
scsi_put_command(cmd);//释放一个scsi命令
scsi_run_queue(q);//选择一个合适的请求队列,建立命令
/* ok to remove device now */
put_device(&sdev->sdev_gendev);
}
static void scsi_run_queue(struct request_queue *q)
void blk_run_queue(struct request_queue *q)
{
unsigned long flags;
spin_lock_irqsave(q->queue_lock, flags);
blk_remove_plug(q);
/*
* Only recurse once to avoid overrunning the stack, let the unplug
* handling reinvoke the handler shortly if we already got there.
*/
if (!elv_queue_empty(q)) {
if (!test_and_set_bit(QUEUE_FLAG_REENTER, &q->queue_flags)) {
q->request_fn(q);
clear_bit(QUEUE_FLAG_REENTER, &q->queue_flags);
} else {
blk_plug_device(q);
kblockd_schedule_work(&q->unplug_work);//调度执行unplug_work,
来自INIT_WORK(&q->unplug_work, blk_unplug_work);
void blk_queue_make_request(struct request_queue * q, make_request_fn * mfn)
只有当unplug_work任务调度执行到了,才能有以后命令的执行过程;如果这里定时函数blk_unplug_timeout被执行,还会去调度执行unplug_work任务,直到unplug_work任务被调度执行到
}
}
spin_unlock_irqrestore(q->queue_lock, flags);
}
static void blk_unplug_timeout(unsigned long data)
{
struct request_queue *q = (struct request_queue *)data;
blk_add_trace_pdu_int(q, BLK_TA_UNPLUG_TIMER, NULL,
q->rq.count[READ] + q->rq.count[WRITE]);
kblockd_schedule_work(&q->unplug_work);
}