设为首页 收藏本站
查看: 726|回复: 0

[经验分享] Linux scsi disk driver

[复制链接]

尚未签到

发表于 2018-5-23 09:02:45 | 显示全部楼层 |阅读模式
  sd.c

static void sd_config_discard(struct scsi_disk *, unsigned int);
static void sd_config_write_same(struct scsi_disk *);
static int  sd_revalidate_disk(struct gendisk *);
static void sd_unlock_native_capacity(struct gendisk *disk);
static int  sd_probe(struct device *);
static int  sd_remove(struct device *);
static void sd_shutdown(struct device *);
static int sd_suspend(struct device *);
static int sd_resume(struct device *);
static void sd_rescan(struct device *);
static int sd_init_command(struct scsi_cmnd *SCpnt);
static void sd_uninit_command(struct scsi_cmnd *SCpnt);
static int sd_done(struct scsi_cmnd *);
static int sd_eh_action(struct scsi_cmnd *, int);
static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer);
static void scsi_disk_release(struct device *cdev);
static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);
static void sd_print_result(struct scsi_disk *, int);module_init(init_sd);
/**
*init_sd - entry point for this driver (both when built in or when
*a module).
*
*Note: this function registers this driver with the scsi mid-level.
**/
static int __init init_sd(void)
{
int majors = 0, i, err;
SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
for (i = 0; i < SD_MAJORS; i++)
        注册驱动
if (register_blkdev(sd_major(i), "sd") == 0)
majors++;
if (!majors)
return -ENODEV;
err = class_register(&sd_disk_class);
if (err)
goto err_out;
sd_cdb_cache = kmem_cache_create("sd_ext_cdb", SD_EXT_CDB_SIZE,
0, 0, NULL);
if (!sd_cdb_cache) {
printk(KERN_ERR "sd: can't init extended cdb cache\n");
goto err_out_class;
}
sd_cdb_pool = mempool_create_slab_pool(SD_MEMPOOL_SIZE, sd_cdb_cache);
if (!sd_cdb_pool) {
printk(KERN_ERR "sd: can't init extended cdb pool\n");
goto err_out_cache;
}
        在scsi总线上注册驱动
err = scsi_register_driver(&sd_template.gendrv);
if (err)
goto err_out_driver;
return 0;
err_out_driver:
mempool_destroy(sd_cdb_pool);
err_out_cache:
kmem_cache_destroy(sd_cdb_cache);
err_out_class:
class_unregister(&sd_disk_class);
err_out:
for (i = 0; i < SD_MAJORS; i++)
unregister_blkdev(sd_major(i), "sd");
return err;
}int scsi_register_driver(struct device_driver *drv)
{
drv->bus = &scsi_bus_type;
return driver_register(drv);
}  sd_probe->async_schedule_domain(sd_probe_async, sdkp, &scsi_sd_probe_domain);
  sd_probe_async->add_disk
  sd_revalidate_disk
  

  可以从gendisk获取scsi_disk和scsi_device
  struct gendisk *disk
  struct scsi_disk *sdkp = scsi_disk(disk);
  struct scsi_device *sdp = sdkp->device;
  

  一些接口函数:
  static struct scsi_disk *scsi_disk_get(struct gendisk *disk);
  static struct scsi_disk *scsi_disk_get_from_dev(struct device *dev);
  

  

  

  

  scsi_scan.c
scsi_rescan_device
scsi_sysfs.c
static ssize_t
store_rescan_field (struct device *dev, struct device_attribute *attr,
    const char *buf, size_t count)
{
scsi_rescan_device(dev);
return count;
}  scsi.c
  subsys_initcall(init_scsi);
static int __init init_scsi(void)
{
int error;
error = scsi_init_queue();
if (error)
return error;
error = scsi_init_procfs();
if (error)
goto cleanup_queue;
error = scsi_init_devinfo();
if (error)
goto cleanup_procfs;
error = scsi_init_hosts();
if (error)
goto cleanup_devlist;
error = scsi_init_sysctl();
if (error)
goto cleanup_hosts;
error = scsi_sysfs_register();
if (error)
goto cleanup_sysctl;
scsi_netlink_init();
printk(KERN_NOTICE "SCSI subsystem initialized\n");
return 0;
cleanup_sysctl:
scsi_exit_sysctl();
cleanup_hosts:
scsi_exit_hosts();
cleanup_devlist:
scsi_exit_devinfo();
cleanup_procfs:
scsi_exit_procfs();
cleanup_queue:
scsi_exit_queue();
printk(KERN_ERR "SCSI subsystem failed to initialize, error = %d\n",
       -error);
return error;
}  接口函数:
  int scsi_device_get(struct scsi_device *sdev)
  

  scsi_sysfs.c
  scsi_scan->scsi_scan_host_selected->scsi_scan_channel->__scsi_scan_target->scsi_probe_and_add_lun->scsi_probe_lun->scsi_execute_req->scsi_execute_req_flags->scsi_execute->blk_execute_rq->blk_execute_rq_nowait->
  __elv_add_request(q, rq, where);
  __blk_run_queue(q);->
  

  /**
  * __blk_run_queue_uncond - run a queue whether or not it has been stopped
  * @q:The queue to run
  *
  * Description:
  *    Invoke request handling on a queue if there are any pending requests.
  *    May be used to restart request handling after a request has completed.
  *    This variant runs the queue whether or not the queue has been
  *    stopped. Must be called with the queue lock held and interrupts
  *    disabled. See also @blk_run_queue.
  */
  inline void __blk_run_queue_uncond(struct request_queue *q)
  {
  if (unlikely(blk_queue_dead(q)))
  return;
  

  /*
   * Some request_fn implementations, e.g. scsi_request_fn(), unlock
   * the queue lock internally. As a result multiple threads may be
   * running such a request function concurrently. Keep track of the
   * number of active request_fn invocations such that blk_drain_queue()
   * can wait until all these request_fn calls have finished.
   */
  q->request_fn_active++;
  q->request_fn(q);
  q->request_fn_active--;
  }
  

  scsi_request_fn->scsi_dispatch_cmd->【host->hostt->queuecommand(host, cmd)】->scsi_done(cmd)->blk_complete_request->__blk_complete_request->raise_softirq_irqoff(BLOCK_SOFTIRQ);激活软中断执行->blk_done_softirq->调用软中断的下半部:request_queue->softirq_done_fn()也就是scsi_softirq_done->scsi_finish_command->scsi_io_completion->scsi_end_request->scsi_next_command->从队列取一个request;
  blk_requeue_request(q, req);调用调度器,重新选取request
  scsi_run_queue(q); ->blk_run_queue->__blk_run_queue->__blk_run_queue_uncond->q->request_fn(q);也就是scsi_request_fn;
  这样构成了一个循环
  

  

  

static __init int blk_softirq_init(void)
{
int i;
for_each_possible_cpu(i)
INIT_LIST_HEAD(&per_cpu(blk_cpu_done, i));
open_softirq(BLOCK_SOFTIRQ, blk_done_softirq);
register_hotcpu_notifier(&blk_cpu_notifier);
return 0;
}/*
* Softirq action handler - move entries to local list and loop over them
* while passing them to the queue registered handler.
*/
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;
rq = list_entry(local_list.next, struct request, csd.list);
list_del_init(&rq->csd.list);
rq->q->softirq_done_fn(rq);
}
}  

static void scsi_softirq_done(struct request *rq)
{
struct scsi_cmnd *cmd = rq->special;
unsigned long wait_for = (cmd->allowed + 1) * rq->timeout;
int disposition;
INIT_LIST_HEAD(&cmd->eh_entry);
atomic_inc(&cmd->device->iodone_cnt);
if (cmd->result)
atomic_inc(&cmd->device->ioerr_cnt);
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_dispatch_command - Dispatch a command to the low-level driver.
* @cmd: command block we are dispatching.
*
* Return: nonzero return request was rejected and device's queue needs to be
* plugged.
*/
int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
{
struct Scsi_Host *host = cmd->device->host;
int rtn = 0;
atomic_inc(&cmd->device->iorequest_cnt);
/* check if the device is still usable */
if (unlikely(cmd->device->sdev_state == SDEV_DEL)) {
/* in SDEV_DEL we error all commands. DID_NO_CONNECT
* returns an immediate error upwards, and signals
* that the device is no longer present */
cmd->result = DID_NO_CONNECT << 16;
scsi_done(cmd);
/* return 0 (because the command has been processed) */
goto out;
}
/* Check to see if the scsi lld made this device blocked. */
if (unlikely(scsi_device_blocked(cmd->device))) {
/*
* in blocked state, the command is just put back on
* the device queue.  The suspend state has already
* blocked the queue so future requests should not
* occur until the device transitions out of the
* suspend state.
*/
scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked \n"));
/*
* NOTE: rtn is still zero here because we don't need the
* queue to be plugged on return (it's already stopped)
*/
goto out;
}
/*
* If SCSI-2 or lower, store the LUN value in cmnd.
*/
if (cmd->device->scsi_level <= SCSI_2 &&
    cmd->device->scsi_level != SCSI_UNKNOWN) {
cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) |
       (cmd->device->lun << 5 & 0xe0);
}
scsi_log_send(cmd);
/*
* Before we queue this command, check if the command
* length exceeds what the host adapter can handle.
*/
if (cmd->cmd_len > cmd->device->host->max_cmd_len) {
SCSI_LOG_MLQUEUE(3,
printk("queuecommand : command too long. "
       "cdb_size=%d host->max_cmd_len=%d\n",
       cmd->cmd_len, cmd->device->host->max_cmd_len));
cmd->result = (DID_ABORT << 16);
scsi_done(cmd);
goto out;
}
if (unlikely(host->shost_state == SHOST_DEL)) {
cmd->result = (DID_NO_CONNECT << 16);
scsi_done(cmd);
} else {
trace_scsi_dispatch_cmd_start(cmd);
cmd->scsi_done = scsi_done;
rtn = host->hostt->queuecommand(host, cmd);
。。。。。。。。。。。。在这里将调用host发送cmd,如FC、IB
}
if (rtn) {
trace_scsi_dispatch_cmd_error(cmd, rtn);
if (rtn != SCSI_MLQUEUE_DEVICE_BUSY &&
    rtn != SCSI_MLQUEUE_TARGET_BUSY)
rtn = SCSI_MLQUEUE_HOST_BUSY;
scsi_queue_insert(cmd, rtn);
SCSI_LOG_MLQUEUE(3,
    printk("queuecommand : request rejected\n"));
}
out:
SCSI_LOG_MLQUEUE(3, printk("leaving scsi_dispatch_cmnd()\n"));
return rtn;
}/*
* Create the actual show/store functions and data structures.
*/
static ssize_t
store_scan(struct device *dev, struct device_attribute *attr,
   const char *buf, size_t count)
{
struct Scsi_Host *shost = class_to_shost(dev);
int res;
res = scsi_scan(shost, buf);
if (res == 0)
res = count;
return res;
};
static int scsi_scan(struct Scsi_Host *shost, const char *str)
{
char s1[15], s2[15], s3[15], junk;
unsigned int channel, id, lun;
int res;
res = sscanf(str, "%10s %10s %10s %c", s1, s2, s3, &junk);
if (res != 3)
return -EINVAL;
if (check_set(&channel, s1))
return -EINVAL;
if (check_set(&id, s2))
return -EINVAL;
if (check_set(&lun, s3))
return -EINVAL;
if (shost->transportt->user_scan)
res = shost->transportt->user_scan(shost, channel, id, lun);
else
res = scsi_scan_host_selected(shost, channel, id, lun, 1);
return res;
}  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-480094-1-1.html 上篇帖子: 硬盘安装Kali Linux 下篇帖子: Linux block
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表