Linux3.10.0块IO子系统流程(7)-- 请求处理完成
/**
* scsi_done - Enqueue the finished SCSI command into the done queue.
* @cmd: The SCSI Command for which a low-level device driver (LLDD) gives
* ownership back to SCSI Core -- i.e. the LLDD has finished with it.
*
* Description: This function is the mid-level's (SCSI Core) interrupt routine,
* which regains ownership of the SCSI command (de facto) from a LLDD, and
* enqueues the command to the done queue for further processing.
*
* This is the producer of the done queue who enqueues at the tail.
*
* This function is interrupt context safe.
*/
static void scsi_done(struct scsi_cmnd *cmd)
{
trace_scsi_dispatch_cmd_done(cmd);
blk_complete_request(cmd->request);
} /**
* blk_complete_request - end I/O on a request
* @req: the request being processed
*
* Description:
* Ends all I/O on a request. It does not handle partial completions,
* unless the driver actually implements this in its completion callback
* through requeueing. The actual completion happens out-of-order,
* through a softirq handler. The user must have registered a completion
* callback through blk_queue_softirq_done().
* 如果用户在编译内核时指定了FAIL_IO_TIMEOUT选项,则提供在请求处理完成时注入错误的能力。
* Linux内核包含了大量的代码来“注入”错误,其思想是模拟故障,让我们检查程序对故障的处理是否完善。
* 请求完成逻辑调用blk_mark_rq_complete函数以原子的方式设置块设备驱动层请求的REQ_ATOM_COMPLETE标志位,这是为了防止错误恢复定时器同时来试图“抢夺”这个块设备驱动层请求
**/
void blk_complete_request(struct request *req)
{
if (unlikely(blk_should_fake_timeout(req->q)))
return;
if (!blk_mark_rq_complete(req))
__blk_complete_request(req);
}
void __blk_complete_request(struct request *req)
{
int ccpu, cpu;
struct request_queue *q = req->q;
unsigned long flags;
bool shared = false; BUG_ON(!q->softirq_done_fn); local_irq_save(flags);
cpu = smp_processor_id(); /*
* Select completion CPU
*/
if (req->cpu != -) {
ccpu = req->cpu;
if (!test_bit(QUEUE_FLAG_SAME_FORCE, &q->queue_flags))
shared = cpus_share_cache(cpu, ccpu);
} else
ccpu = cpu; /*
* If current CPU and requested CPU share a cache, run the softirq on
* the current CPU. One might concern this is just like
* QUEUE_FLAG_SAME_FORCE, but actually not. blk_complete_request() is
* running in interrupt handler, and currently I/O controller doesn't
* support multiple interrupts, so current CPU is unique actually. This
* avoids IPI sending from current CPU to the first CPU of a group.
*/
if (ccpu == cpu || shared) {
struct list_head *list;
do_local:
list = &__get_cpu_var(blk_cpu_done);
list_add_tail(&req->csd.list, list); /*
* if the list only contains our just added request,
* signal a raise of the softirq. If there are already
* entries there, someone already raised the irq but it
* hasn't run yet.
*/
if (list->next == &req->csd.list)
raise_softirq_irqoff(BLOCK_SOFTIRQ); // 触发软中断,这个中断绑定blk_done_softirq
} else if (raise_blk_irq(ccpu, req))
goto do_local; local_irq_restore(flags);
}
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 ;
}
blk_softirq_init
软中断处理函数如下,这个函数首先将CPU已完成请求链表中的所有项转移到一个局部链表,这样做的目的是为了在这进行处理的时候,尽可能少地打扰CPU的完成请求链表,也就是不妨碍新的完成请求加入到这个链表。然后循环处理局部链表的每个项,将它从链表中删除,然后调用请求队列的软中断完成回调函数来处理。
/*
* 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);
}
}
软中断完成回调函数是依赖请求队列的,对于SCSI设备,这个回调函数被设定为scsi_softirq_done,具体设定的时机是在为SCSI设备分配请求队列时,参见scsi_alloc_queue
static void scsi_softirq_done(struct request *rq)
{
struct scsi_cmnd *cmd = rq->special;
unsigned long wait_for = (cmd->allowed + ) * rq->timeout;
int disposition; INIT_LIST_HEAD(&cmd->eh_entry); /* 首先修改所属SCSI设备的统计计数器,包括递增已完成命令计数器iodone_cnt和返回错误结果时递增已出错命令计数器ioerr_cnt */
atomic_inc(&cmd->device->iodone_cnt);
if (cmd->result)
atomic_inc(&cmd->device->ioerr_cnt); /*
* scsi_decide_disposition确定如何处理这条命令
* SUCCESS:调用scsi_finish_command结束,后续继续分析
* NEEDS_RETRY:
* ADD_TO_MLQUEUE:后面两种情况都将命令重新排入请求队列,前者立即重试,后者经过一定延时后重试
* 其他返回值调用scsi_eh_scmd_add进入错误恢复。如果进入错误恢复流程,返回1,这种情况下无需再处理这条命令,如果返回0则只能调用scsi_finish_command结束
*/
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, ))
scsi_finish_command(cmd);
}
}
scsi_finish_command
/**
* scsi_finish_command - cleanup and pass command back to upper layer
* @cmd: the command
*
* Description: 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_target *starget = scsi_target(sdev);
struct Scsi_Host *shost = sdev->host;
struct scsi_driver *drv;
unsigned int good_bytes; scsi_device_unbusy(sdev); /*
* 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 = ;
starget->target_blocked = ;
sdev->device_blocked = ; /*
* 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 << ); SCSI_LOG_MLCOMPLETE(, sdev_printk(KERN_INFO, sdev,
"Notifying upper driver of completion "
"(result %x)\n", cmd->result)); /*
* 要进行完成处理,首先必须知道SCSI已经成功完成的字节数,scsi_bufflen函数从SCSI数据缓冲区得到这个数据
* 如果请求不是来自SCSI公共服务层,那么它一定来自上层,也就表明处理这个请求的设备必定被绑定到了高层驱动,
* 如果定义了done回调,则调用它,对于SCSI磁盘高层驱动,对应实现为sd_done函数,这个函数返回调整后的已完成字节数
* 有了已完成字节数,就可以调用scsi_io_completion
*/
good_bytes = scsi_bufflen(cmd);
if (cmd->request->cmd_type != REQ_TYPE_BLOCK_PC) {
int old_good_bytes = good_bytes;
drv = scsi_cmd_to_driver(cmd);
if (drv->done)
good_bytes = drv->done(cmd);
/*
* USB may not give sense identifying bad sector and
* simply return a residue instead, so subtract off the
* residue if drv->done() error processing indicates no
* change to the completion length.
*/
if (good_bytes == old_good_bytes)
good_bytes -= scsi_get_resid(cmd);
}
scsi_io_completion(cmd, good_bytes);
}
scsi_io_completion……
Linux3.10.0块IO子系统流程(7)-- 请求处理完成的更多相关文章
- Linux3.10.0块IO子系统流程(0)-- 块IO子系统概述
前言:这个系列主要是记录自己学习Linux块IO子系统的过程,其中代码分析皆基于Linux3.10.0版本,如有描述错误或不妥之处,敬请指出! 参考书籍:存储技术原理分析--基于Linux 2.6内核 ...
- Linux3.10.0块IO子系统流程(3)-- SCSI策略例程
很长时间以来,Linux块设备使用了一种称为“蓄流/泄流”(plugging/unplugging)的技术来改进吞吐率.简单而言,这种工作方式类似浴盆排水系统的塞子.当IO被提交时,它被储存在一个队列 ...
- Linux3.10.0块IO子系统流程(2)-- 构造、排序、合并请求
Linux块设备可以分为三类.分别针对顺序访问物理设备.随机访问物理设备和逻辑设备(即“栈式设备”) 类型 make_request_fn request_fn 备注 SCSI 设备等 从bio构 ...
- Linux3.10.0块IO子系统流程(6)-- 派发SCSI命令到低层驱动
在SCSI策略例程中最后调用scsi_dispatch_cmd将SCSI命令描述符派发给低层驱动进行处理 /** * scsi_dispatch_command - Dispatch a comman ...
- Linux3.10.0块IO子系统流程(5)-- 为SCSI命令准备聚散列表
SCSI数据缓冲区组织成聚散列表的形式.Linux内核中表示聚散列表的基本数据结构是scatterlist,虽然名字中有list,但它只对应一个内存缓冲区,聚散列表就是多个scatterlist的组合 ...
- Linux3.10.0块IO子系统流程(1)-- 上层提交请求
Linux通用块层提供给上层的接口函数是submit_bio.上层在构造好bio之后,调用submit_bio提交给通用块层处理. submit_bio函数如下: void submit_bi ...
- Linux3.10.0块IO子系统流程(4)-- 为请求构造SCSI命令
首先来看scsi_prep_fn int scsi_prep_fn(struct request_queue *q, struct request *req) { struct scsi_device ...
- DPA 9.1.85 升级到DPA 10.0.352流程
SolarWinds DPA的升级其实是一件非常简单的事情,这里介绍一下从DPA 9.1.95升级到 DPA 10.0.352版本的流程.为什么要升级呢? DPA给用户发的邮件已经写的非常清楚了(如下 ...
- 【转】linux IO子系统和文件系统读写流程
原文地址:linux IO子系统和文件系统读写流程 我们含有分析的,是基于2.6.32及其后的内核. 我们在linux上总是要保存数据,数据要么保存在文件系统里(如ext3),要么就保存在裸设备里.我 ...
随机推荐
- 面向对象编程之OC
面向对象概述 面向对象是一种符合人类思想习惯的编程思想.现实生活中存在各种形态不同的事物,这些事物之间存在着各种各样的联系,在程序中使用对象来映射现实中的事物,使用对象的关系来描述事物之间的联系,这种 ...
- CentOS 7 yum安装zabbix 设置中文界面
1. 配置安装前环境 2. 安装zabbix 3. 设置中文环境 准备搭建环境 : 系统:CentOS7.5 首先关闭SElinux 和防火墙 安装MariaDB数据库 [root@DaMoWa ...
- eclipse添加mybatis插件
1,在 eclipse中选择 Help->eclipse marketplace 2, 搜索安装
- Kubernetes有状态应用管理——PetSet
目录贴:Kubernetes学习系列 1.介绍 在Kubernetes中,大多数的Pod管理都是基于无状态.一次性的理念.例如Replication Controller,它只是简单的保证可提供服务的 ...
- Rest和Restful & http
Rest :Representational State Transfer 表述性状态转移 Restful: Rest+ful形容词,遵循Rest原则的应用程序或设计 Rest原则: 1. 网络上的所 ...
- rman实验——测试备份压缩
oracle rman自带的备份压缩机制,可以有效的压缩备份的大小,降低磁盘的占用率.但是也会因为压缩而消耗更多的系统性能,和增加备份时间.现在就通过实验来看压缩和不压缩的区别. 进行不压缩全备 RM ...
- loj#2483. 「CEOI2017」Building Bridges 斜率优化 cdq分治
loj#2483. 「CEOI2017」Building Bridges 链接 https://loj.ac/problem/2483 思路 \[f[i]=f[j]+(h[i]-h[j])^2+(su ...
- parquet 简介
原文 Parquet 列式存储格式 面向分析型业务的列式存储格式 由 Twitter 和 Cloudera 合作开发,2015 年 5 月从 Apache 的孵化器里毕业成为 Apache 顶级项目 ...
- CSS【06】:CSS显示模式
div和Span标签 作用:一般用于配合css完成网页的基本布局 <style> .header{ width: 980px; height: 100px; background: red ...
- 牛客-数据库SQL实战
查找最晚入职员工的所有信息 CREATE TABLE `employees` ( `emp_no` ) NOT NULL, `birth_date` date NOT NULL, `first_nam ...