## 16.4. 一些其他的细节
本节涵盖块层的几个其他的方面, 对于高级读者可能有兴趣. 对于编写一个正确的驱动下面的内容都不需要, 但是它们在某些情况下可能是有用的.
### 16.4.1. 命令预准备
块层为驱动提供一个进制来检查和预处理请求, 在它们被从 elv_next_request 返回前. 这个机制允许驱动提前设立真正的驱动器命令, 决定是否这个请求可被完全处理, 或者进行其他的维护工作.
如果你想使用这个特性, 创建一个命令准备函数, 它要适应这个原型:
~~~
typedef int (prep_rq_fn) (request_queue_t *queue, struct request *req);
~~~
请求结构包含一个成员 cmd, 它是一个 BLK_MAX_CDB 字节的数组; 这个数组可被这个准备函数用来存储实际的硬件命令(或者任何其他的有用信息). 这个函数应当返回一个下列的值:
BLKPREP_OK
命令准备正常进行, 并且这个请求可被传递给你的驱动的请求函数.
BLKPREP_KILL
这个请求不能完成; 它带有一个错误码而失败.
BLKPREP_DEFER
这个请求这次无法完成. 它位于队列的前面, 但是不能传递给请求函数.
准备函数被 elv_next_request 在请求返回到你的驱动之前立刻调用. 如果这个函数返回 BLKPREP_DEFER, 从 elv_next_request 返回给你的驱动的返回值是 NULL. 这个操作描述可能是有用的, 如果, 例如你的设备已达到它能够等候的请求的最大数目.
为使块层调用你的准备函数, 传递它到:
~~~
void blk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func);
~~~
缺省地, 请求队列没有准备函数.
### 16.4.2. 被标识的命令排队
可同时有多个请求被激活的硬件, 常常支持某种被标识的命令排队(TCQ). TCQ 简单地说是关联一个整数 "tag" 到每个请求的技术, 注意当驱动器完成每个请求时, 他可告知驱动是哪一个. 在以前的内核版本, 实现 TCQ 的块驱动不得不自己做所有的工作; 在2.6, 一个 TCQ 支持框架已经被添加到块层, 以给所有的驱动来使用.
如果你的驱动器进行标记命令排队, 你应当在初始化时通知内核这个事实, 使用:
~~~
int blk_queue_init_tags(request_queue_t *queue, int depth, struct blk_queue_tag *tags);
~~~
这里, queue 是你的请求队列, 而 depth 是你的设备能够在任何时间拥有的等待的标记请求的数目. tags 是一个可选的指针指向一个 struct blk_queue_tag 结构数组; 必须有 depth 个. 正常地, tags 可用 NULL, 并且 blk_queue_init_tags 分配这个 数组. 如果, 但是, 你需要和多个设备分享通用的 tags, 你可传递这个标记数组指针(存储在 queue_tags 成员)从另一个请求队列. 你应当从不真正自己分配这个标记数组; 块层需要初始化这个数组并且不输出这个初始化函数给模块.
因为 blk_queue_init_tags 分配内存, 它可能失败. 在那个情况下它返回一个负的错误码给调用者.
如果你的设备可处理的标记的数目改变了, 你可通知内核, 使用:
~~~
int blk_queue_resize_tags(request_queue_t *queue, int new_depth);
~~~
这个队列锁必须在这个调用期间被持有. 这个调用可能失败, 返回一个负错误码.
一个标记和一个请求结构的关联被 blk_queue_start_tag 来完成, 它必须在成员队列锁被持有时调用:
~~~
int blk_queue_start_tag(request_queue_t *queue, struct request *req);
~~~
如果一个 tag 可用, 这个函数分配它给这个请求, 存储这个标识号在 req->tag, 并且返回 0. 它还从队列中解除这个请求, 并且连接它到它自己的标识跟踪结构, 因此你的驱动应当小心不从队列中解除这个请求, 如果在使用标识. 如果没有标识可用, blk_queue_start_tag 将这个请求留在队列并且返回一个非零值.
当一个给定的请求的所有的传送都已完成, 你的驱动应当返回标识, 使用:
~~~
void blk_queue_end_tag(request_queue_t *queue, struct request *req);
~~~
再一次, 你必须持有队列锁, 在调用这个函数之前. 这个调用应当在 end_that_request_first 返回 0 之后进行(意味着这个请求完成), 但要在调用 end_that_request_last 之前. 记住这个请求已经从队列中解除, 因此它对于你的驱动在此点这样做可能是一个错误.
如果你需要找到关联到一个给定标识上的请求(当驱动器报告完成, 例如), 使用 blk_queue_find_tag:
~~~
struct request *blk_queue_find_tag(request_queue_t *qeue, int tag);
~~~
返回值是关联的请求结构, 除非有些事情已经真的出错了.
如果事情真地出错了, 你的请求可能发现它自己不得不复位或者对其中一个它的设备进行一些其他的大动作. 在这种情况下, 任何等待中的标识命令将不会完成. 块层提供一个函数可用帮助在这种情况下恢复:
~~~
void blk_queue_invalidate_tags(request_queue_t *queue);
~~~
这个函数返回所有的等待的标识给这个池, 并且将关联的请求放回请求队列. 你调用这个函数时必须持有队列锁.
- Linux设备驱动第三版
- 第 1 章 设备驱动简介
- 1.1. 驱动程序的角色
- 1.2. 划分内核
- 1.3. 设备和模块的分类
- 1.4. 安全问题
- 1.5. 版本编号
- 1.6. 版权条款
- 1.7. 加入内核开发社团
- 1.8. 本书的内容
- 第 2 章 建立和运行模块
- 2.1. 设置你的测试系统
- 2.2. Hello World 模块
- 2.3. 内核模块相比于应用程序
- 2.4. 编译和加载
- 2.5. 内核符号表
- 2.6. 预备知识
- 2.7. 初始化和关停
- 2.8. 模块参数
- 2.9. 在用户空间做
- 2.10. 快速参考
- 第 3 章 字符驱动
- 3.1. scull 的设计
- 3.2. 主次编号
- 3.3. 一些重要数据结构
- 3.4. 字符设备注册
- 3.5. open 和 release
- 3.6. scull 的内存使用
- 3.7. 读和写
- 3.8. 使用新设备
- 3.9. 快速参考
- 第 4 章 调试技术
- 4.1. 内核中的调试支持
- 4.2. 用打印调试
- 4.3. 用查询来调试
- 4.4. 使用观察来调试
- 4.5. 调试系统故障
- 4.6. 调试器和相关工具
- 第 5 章 并发和竞争情况
- 5.1. scull 中的缺陷
- 5.2. 并发和它的管理
- 5.3. 旗标和互斥体
- 5.4. Completions 机制
- 5.5. 自旋锁
- 5.6. 锁陷阱
- 5.7. 加锁的各种选择
- 5.8. 快速参考
- 第 6 章 高级字符驱动操作
- 6.1. ioctl 接口
- 6.2. 阻塞 I/O
- 6.3. poll 和 select
- 6.4. 异步通知
- 6.5. 移位一个设备
- 6.6. 在一个设备文件上的存取控制
- 6.7. 快速参考
- 第 7 章 时间, 延时, 和延后工作
- 7.1. 测量时间流失
- 7.2. 获知当前时间
- 7.3. 延后执行
- 7.4. 内核定时器
- 7.5. Tasklets 机制
- 7.6. 工作队列
- 7.7. 快速参考
- 第 8 章 分配内存
- 8.1. kmalloc 的真实故事
- 8.2. 后备缓存
- 8.3. get_free_page 和其友
- 8.4. 每-CPU 的变量
- 8.5. 获得大量缓冲
- 8.6. 快速参考
- 第 9 章 与硬件通讯
- 9.1. I/O 端口和 I/O 内存
- 9.2. 使用 I/O 端口
- 9.3. 一个 I/O 端口例子
- 9.4. 使用 I/O 内存
- 9.5. 快速参考
- 第 10 章 中断处理
- 10.1. 准备并口
- 10.2. 安装一个中断处理
- 10.3. 前和后半部
- 10.4. 中断共享
- 10.5. 中断驱动 I/O
- 10.6. 快速参考
- 第 11 章 内核中的数据类型
- 11.1. 标准 C 类型的使用
- 11.2. 安排一个明确大小给数据项
- 11.3. 接口特定的类型
- 11.4. 其他移植性问题
- 11.5. 链表
- 11.6. 快速参考
- 第 12 章 PCI 驱动
- 12.1. PCI 接口
- 12.2. 回顾: ISA
- 12.3. PC/104 和 PC/104+
- 12.4. 其他的 PC 总线
- 12.5. SBus
- 12.6. NuBus 总线
- 12.7. 外部总线
- 12.8. 快速参考
- 第 13 章 USB 驱动
- 13.1. USB 设备基础知识
- 13.2. USB 和 sysfs
- 13.3. USB 的 Urbs
- 13.4. 编写一个 USB 驱动
- 13.5. 无 urb 的 USB 传送
- 13.6. 快速参考
- 第 14 章 Linux 设备模型
- 14.1. Kobjects, Ksets 和 Subsystems
- 14.2. 低级 sysfs 操作
- 14.3. 热插拔事件产生
- 14.4. 总线, 设备, 和驱动
- 14.5. 类
- 14.6. 集成起来
- 14.7. 热插拔
- 14.8. 处理固件
- 14.9. 快速参考
- 第 15 章 内存映射和 DMA
- 15.1. Linux 中的内存管理
- 15.2. mmap 设备操作
- 15.3. 进行直接 I/O
- 15.4. 直接内存存取
- 15.5. 快速参考
- 第 16 章 块驱动
- 16.1. 注册
- 16.2. 块设备操作
- 16.3. 请求处理
- 16.4. 一些其他的细节
- 16.5. 快速参考
- 第 17 章 网络驱动
- 17.1. snull 是如何设计的
- 17.2. 连接到内核
- 17.3. net_device 结构的详情
- 17.4. 打开与关闭
- 17.5. 报文传送
- 17.6. 报文接收
- 17.7. 中断处理
- 17.8. 接收中断缓解
- 17.9. 连接状态的改变
- 17.10. Socket 缓存
- 17.11. MAC 地址解析
- 17.12. 定制 ioctl 命令
- 17.13. 统计信息
- 17.14. 多播
- 17.15. 几个其他细节
- 17.16. 快速参考
- 第 18 章 TTY 驱动
- 18.1. 一个小 TTY 驱动
- 18.2. tty_driver 函数指针
- 18.3. TTY 线路设置
- 18.4. ioctls 函数
- 18.5. TTY 设备的 proc 和 sysfs 处理
- 18.6. tty_driver 结构的细节
- 18.7. tty_operaions 结构的细节
- 18.8. tty_struct 结构的细节
- 18.9. 快速参考