Chinaunix首页 | 论坛 | 博客
  • 博客访问: 805911
  • 博文数量: 127
  • 博客积分: 2669
  • 博客等级: 少校
  • 技术积分: 1680
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-23 11:39
文章分类

全部博文(127)

文章存档

2014年(5)

2013年(19)

2012年(25)

2011年(9)

2010年(25)

2009年(44)

分类: LINUX

2013-08-04 20:07:31

此函数通过调用其他的函数,完成了scsi_debug模块主要的工作:接收和处理SCSI命令。对于真实的物理设备,在此接收到命令之后会发送给具体的物理设备,例如QLogic系列HBA卡驱动函数qla2xxx_queuecommand所示,完成了对于FC连接的检查之后进行数据发送。

       rval = fc_remote_port_chkready(rport);

       if (rval) {

              cmd->result = rval;

              ql_dbg(ql_dbg_io, vha, 0x3003,

                  "fc_remote_port_chkready failed for cmd=%p, rval=0x%x.\n",

                  cmd, rval);

              goto qc24_fail_command;

       }

       if (atomic_read(&fcport->state) != FCS_ONLINE) {

              if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||

                     atomic_read(&base_vha->loop_state) == LOOP_DEAD) {

                     ql_dbg(ql_dbg_io, vha, 0x3005,

                         "Returning DNC, fcport_state=%d loop_state=%d.\n",

                         atomic_read(&fcport->state),

                         atomic_read(&base_vha->loop_state));

                     cmd->result = DID_NO_CONNECT << 16;

                     goto qc24_fail_command;

              }

              goto qc24_target_busy;

       }

 

       sp = qla2x00_get_new_sp(base_vha, fcport, cmd);

       if (!sp)

              goto qc24_host_busy;

 

       rval = ha->isp_ops->start_scsi(sp);

       if (rval != QLA_SUCCESS) {

              ql_dbg(ql_dbg_io, vha, 0x3013,

                  "Start scsi failed rval=%d for cmd=%p.\n", rval, cmd);

              goto qc24_host_busy_free_sp;

       }

对于scsi_debug模块本身来说,没有真实的物理设备可以发送,因此在此模拟处理请求并返回结果就可以了,关于如何处理SCSI命令及返回值将在3.3中详述。

0xfae69100 : scsi_debug_queuecommand+0x0/0x0 [scsi_debug]

 0xc06f311c : scsi_dispatch_cmd+0xdc/0x300 [kernel]

 0xc06f9da6 : scsi_request_fn+0x396/0x690 [kernel]

 0xc05fc407 : __blk_run_queue+0x27/0x30 [kernel]

 0xc0613a84 : cfq_insert_request+0x2a4/0x640 [kernel]

 0xc05f6b4a : elv_insert+0xaa/0x1b0 [kernel]

 0xc05f6c99 : __elv_add_request+0x49/0xa0 [kernel]

 0xc05ff681 : __make_request+0xe1/0x570 [kernel]

 0xc05fe1f6 : generic_make_request+0x3a6/0x610 [kernel]

 0xc05fe4d5 : submit_bio+0x75/0x110 [kernel]

 0xc056f483 : submit_bh+0xf3/0x140 [kernel]

 0xc057115b : __block_write_full_page+0x20b/0x3b0 [kernel]

 0xc0571c69 : block_write_full_page_endio+0xa9/0xe0 [kernel]

 0xc0571cb2 : block_write_full_page+0x12/0x20 [kernel]

 0xc0575f1f : blkdev_writepage+0xf/0x20 [kernel]

 0xc050601b : __writepage+0xb/0x30 [kernel]

 0xc0506ded : write_cache_pages+0x13d/0x370 [kernel]

 0xc050703f : generic_writepages+0x1f/0x30 [kernel]

 0xc0507067 : do_writepages+0x17/0x30 [kernel]

 0xc0568d8c : writeback_single_inode+0xbc/0x240 [kernel]

 0xc056913b : writeback_sb_inodes+0xab/0x160 [kernel]

以上为scsi_debug模块在writeback写入数据情况下的调用栈,从中可以看出依次涉及到了linux kernelvfs层,block层,scsi mid layer(scsi_dispatch_cmd)

使用rmmod scsi_debug移除模块时,调用栈如下,从中可以看出remove device时需要调用注册总线的remove函数,同时涉及到了scsi upper layer(sd_remove)mid layer(scsi_dispatch_cmd)

0xfae69100 : scsi_debug_queuecommand+0x0/0x0 [scsi_debug]

 0xc06f311c : scsi_dispatch_cmd+0xdc/0x300 [kernel]

 0xc06f9da6 : scsi_request_fn+0x396/0x690 [kernel]

 0xc05fc4a9 : __generic_unplug_device+0x29/0x30 [kernel]

 0xc0602dd8 : blk_execute_rq_nowait+0x68/0xd0 [kernel]

 0xc0602ebd : blk_execute_rq+0x7d/0xe0 [kernel]

 0xc06fb370 : scsi_execute+0xb0/0x130 [kernel]

 0xc06fb59b : scsi_execute_req+0x8b/0x140 [kernel]

 0xf9b8a60a : sd_sync_cache+0xba/0x100 [sd_mod]

 0xf9b8a7fa : sd_shutdown+0x6a/0x130 [sd_mod]

 0xf9b8aa1a : sd_remove+0x5a/0xa0 [sd_mod]

 0xc06e36e2 : __device_release_driver+0x52/0xb0 [kernel]

 0xc06e3800 : device_release_driver+0x20/0x40 [kernel]

 0xc06e297c : bus_remove_device+0x7c/0xd0 [kernel]

 0xc06e0a61 : device_del+0xf1/0x180 [kernel]

 0xc06ff2f9 : __scsi_remove_device+0x99/0xa0 [kernel]

 0xc06fbeff : scsi_forget_host+0x4f/0x60 [kernel]

 0xc06f4a43 : scsi_remove_host+0x53/0xe0 [kernel]

 0xfae64f84 : sdebug_driver_remove+0x24/0x80 [scsi_debug]

 0xc06e36e2 : __device_release_driver+0x52/0xb0 [kernel]

 0xc06e3800 : device_release_driver+0x20/0x40 [kernel]

  • scsi_debug_abortscsi_debug_bus_resetscsi_debug_device_resetscsi_debug_host_reset

这是LLDD中定义的错误处理逻辑,如果不定义,则SCSI midlayer会提供自己的定义,这些函数将在kernel提供的scsi_ehX错误处理线程中执行,该线程定义和创建代码在drivers/scsi/host.c中:

struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)

{

shost->ehandler = kthread_run(scsi_error_handler, shost,

                     "scsi_eh_%d", shost->host_no);

       if (IS_ERR(shost->ehandler)) {

              printk(KERN_WARNING "scsi%d: error handler thread failed to spawn, error = %ld\n",

                     shost->host_no, PTR_ERR(shost->ehandler));

              goto fail_kfree;

       }

scsi_debug在此提供这些函数主要是为增加全局的错误计数器,以scsi_debug_bus_reset为例:

       ++num_bus_resets;

       if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {

              sdbg_host = *(struct sdebug_host_info **)shost_priv(hp);

              if (sdbg_host) {

                     list_for_each_entry(dev_info,

                                            &sdbg_host->dev_info_list,

                                            dev_list)

                            dev_info->reset = 1;

              }

       }

  • scsi_debug_biosparam

返回对于指定磁盘的bios参数,通常是由driver自己生成返回,实现如下:

       buf = scsi_bios_ptable(bdev);

       if (buf) {

              res = scsi_partsize(buf, capacity,

                                &info[2], &info[0], &info[1]);

              kfree(buf);

              if (! res)

                     return res;

       }

       info[0] = sdebug_heads;

       info[1] = sdebug_sectors_per;

       info[2] = sdebug_cylinders_per;

       return 0;

scsi_bios_ptable定义在drivers/scsi/scsicam.c中,功能是从指定的device的第一个sector上读取分区表内容,随后交给scsi_partsize完成分区表的创建,按照(柱面,磁头,扇区)的格式存储在info[2]info[0]info[1]中,若不能正常获取分区表信息,则赋予全局默认值。

除了sdebug_driver_template提供的与scsi mid layer的结合之外,scsi_debug也提供了一个虚拟的总线以绑定虚拟出来的scsi设备,如下:

static struct bus_type pseudo_lld_bus = {

       .name = "pseudo",

       .match = pseudo_lld_bus_match,

       .probe = sdebug_driver_probe,

       .remove = sdebug_driver_remove,

};

其中pseudo_lld_bus_match恒返回1sdebug_driver_probe中在总线pseudo_lld_bus上注册了scsi设备sdebug_driver_templatesdebug_driver_remove则从总线上移除了该设备。do_create_driverfs_filesdo_remove_driverfs_files负责对于/sys/bus/pseudo/drivers/scsi_debug下文件的创建和删除,与sdebug_xx_storesdebug_xx_show组合起来完成了将scsi_debug模块信息导出和动态修改的功能。linuxscsi模块提供了超时机制,定义在schedule_resp函数中,如下:

static int schedule_resp(struct scsi_cmnd * cmnd,

                      struct sdebug_dev_info * devip,

                      done_funct_t done, int scsi_result, int delta_jiff)

{

       if (delta_jiff <= 0) {

              if (cmnd)

                     cmnd->result = scsi_result;

              if (done)

                     done(cmnd);

              return 0;

       } else {

              unsigned long iflags;

              int k;

              struct sdebug_queued_cmd * sqcp = NULL;

              spin_lock_irqsave(&queued_arr_lock, iflags);

              for (k = 0; k < scsi_debug_max_queue; ++k) {

                     sqcp = &queued_arr[k];

                     if (! sqcp->in_use)

                            break;

              }

              if (k >= scsi_debug_max_queue) {

                     spin_unlock_irqrestore(&queued_arr_lock, iflags);

                     printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");

                     return 1;  /* report busy to mid level */

              }

              sqcp->in_use = 1;

              sqcp->a_cmnd = cmnd;

              sqcp->scsi_result = scsi_result;

              sqcp->done_funct = done;

              sqcp->cmnd_timer.function = timer_intr_handler;

              sqcp->cmnd_timer.data = k;

              sqcp->cmnd_timer.expires = jiffies + delta_jiff;

              add_timer(&sqcp->cmnd_timer);

              spin_unlock_irqrestore(&queued_arr_lock, iflags);

              if (cmnd)

                     cmnd->result = 0;

              return 0;

       }

在此设置了sdebug_queued_cmd中封装的scsi command及定时器timer,同时设置了回调函数done_funct,如果delta_jiff小于零则直接执行传入的回调以通知mid layer

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