Chinaunix首页 | 论坛 | 博客
  • 博客访问: 227682
  • 博文数量: 42
  • 博客积分: 2618
  • 博客等级: 少校
  • 技术积分: 385
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-26 10:04
文章分类

全部博文(42)

文章存档

2013年(2)

2012年(2)

2011年(3)

2010年(17)

2009年(18)

我的朋友

分类: 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.cblk_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);

}

阅读(4181) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~