linux3.1.5
一, genhd.c: 管理disk, 分区, 和event
1. disk_get_part:返回分区hd_struct指针,由fs调用,block driver调用,initdo_mounts调用
rcu发布(rcu_assign_pointer)与订阅(rcu_dereference):可用于保证因为某种优化引起的指针和指针成员初始化的顺序及读者对该指针及指向成员的访问的顺序。参见下面链接的例子:rcu_dereference要与rcu_read_lock/rcu_read_unlock一起使用。
一个链接详细讨论rcu:
http://labs.chinamobile.com/mblog/225_2830
profiling优化? 在sys/kernel下能看到这个:http://kongjian.baidu.com/jedora/blog/item/e1b7cffc59f7eb86b801a012.html, 简言之:性能分析
struct disk_part_tbl __rcu
struct gendisk:包含major,minors, disk_name, part_tbl(struct disk_part_tbl __rcu *), fops(conststruct block_device_operations *), driverfs_dev(struct device *),queue(struct request_queue)等
struct disk_part_tbl:
包含rcu_head(structrcu_head),part[](很奇怪,gcc可以这样定义指针数组?, iso c99支持, 在gcc的较低版本也不支持)(structhd_struct __rcu)
struct hd_struct :
包含起始扇区, 扇区数,device,kobj,disk stat等
2. disk_part_iter_init,disk_part_iter_next,disk_part_iter_exit用于遍历分区
part_to_dev: 返回hd_struct的device
get_device: 返回kobject树中的kobj对应的device,有时调用这个是为increment reference count fordevice。不许要真的返回值
disk_put_part: 调用put_device,
put_device: decrement reference count
3. disk_map_sector_rcu:由扇区查找对应的分区,ptbl->last_lookup用于加快查找(假定扇区就在最近超找的last_lookup分区),由block_core, driver调用
sector_in_part: 判断扇区是否在该分区
4. seq file 接口:
http://hi.baidu.com/deep_pro/blog/item/5a88f4347726b2305ab5f508.html
简单来讲就是提供了一组序列化接口供procfs使用, 如open, start, next, show, stop,等。好处是可以处理大量数据而且清晰
在block genhd中用于显示major和name
5. register_blkdev/unregister_blkdev://被driver调用
在major_names数组中注册major和name, 如果major参数为0, 就从数组后面开始找空闲成员。每个成员用kmalloc/kfree管理
6. blk_alloc_devt/:blk_free_devt分配dev_t给分区,
dev_t: u long类型
MKDEV: 由major, minor组成dev_t
idr: http://blog.csdn.net/brucexu1978/article/details/7171617参考这个链接http://wenku.baidu.com/view/a554be3d0912a21614792936.html知道rcu的rcu_assign_pointer的作用是给写着指针赋值。
如果分区号小于minors, 就用分区号生成dev_t, 否则从idr生成一个id, 并把分区和id关联。idr_pre_get, idr_get_new用于id管理。 当然在这种情况下major号使用的是BLOCK_EXT_MAJOR(259), 而不是disk的major
blk_free_devt: 其实主要是为了释放idr的
7. blk_register_region/blk_unregister_region,:
调用kobj_map往bdev_map中插入probe等, 该probe由kobj_lookup()调用。而kobj_lookup()也会由get_gendisk调用(在分区号小于minors的时候, 否则idr_find),
8.add_disk:
使用part0分配devt(blk_alloc_devt)
初始化disk的major, minor
register backing_dev_info
调用blk_register_region
调用register_disk
register disk queue到sysfs
增加queue的reference count: Take an extra ref on queue which will be put on disk_release(), so that it sticks around as long as @disk is there.
调用disk_add_events, 增加disk event属性到sysfs, 同时增加event到disk, (kzalloc: 分配并初始化为0)
(disk_del_events: block event. 并从sysfs中删除event属性
disk_release_events: free event从disk)
register_disk:除了增加到kobj, 还进行了分区数, 容量, 等的检查, 并打开block_device进行一些初始化。
增加disk device到device kobj
bdget_disk:由分区号得到block_device, block_device(分区级别的device)
blkdev_get: 打开block_device
blkdev_put: close block_device
9:get_gendisk:由dev_t返回gen_disk和分区信息, 由fs调用
10: bdget_disk: 由分区号和disk得到block_device, 被driver, fs调用
bdget:调用iget5_locked得到dev对应的inode, 如果inode是新的, 则初始化bdev和inode, 并返回bdev
iget5_locked: search for the inode specified by @hashval and @data in the inode cache,
and if present it is return it with an increased reference count. This is
a generalized version of iget_locked() for file systems where the inode
number is not sufficient for unique identification of an inode.
If the inode is not in cache, allocate a new inode and return it locked,
hashed, and with the I_NEW flag set. The file system gets to fill it in
before unlocking it via unlock_new_inode().
11. printk_all_partitions, 打印所有分区信息
disk_seqf_start/disk_seqf_next/disk_seqf_stop/show_partition_start/show_partition/partitions_open/proc_partitions_operations: 用于从procfs遍历disk
12. genhd_device_init:
call class_register to register block_class
call kobj_map_init to init bdev_map(request_module: 会执行modprobe去安装module:http://blog.chinaunix.net/space.php?uid=20564848&do=blog&id=74092)
call blk_dev_init to init kblockd_workqueue, request_cachep(request), and blk_requestq_cachep(request_queue).(request 会被连入request_queue)
(!sysfs_deprecated : top kobj block dir)
kblockd_workqueue:参见http://blog.csdn.net/brucexu1978/article/details/7173100
13,
disk->part0.policy 可以显示disk ro属性
DEVICE_ATTR: 定义device_attribute属性结构体变量。
S_IRUSR
Permits the file's owner to read it.
S_IWUSR
Permits the file's owner to write to it.
S_IRGRP
Permits the file's group to read it.
S_IWGRP
Permits the file's group to write to it.
S_ISREG ( ) 普通文件
S_ISDIR ( ) 目录文件
S_ISCHR ( ) 字符特殊文件
S_ISBLK ( ) 块特殊文件
S_ISFIFO ( ) 管道或F I F O
S_ISLNK ( ) 符号连接( P O S I X . 1或S V R 4无此类型)
S_ISSOC K ( ) 套接字(P O S I X . 1或S V R 4无此类型)
14:
disk_replace_part_tbl: 用新分区(disk_part_tbl)表取代就分区表, 并释放旧分区表
disk_expand_part_tbl: 扩展分区表,旧分区加上新分区生成新分区表, 调用disk_replace_part_tbl用新分区表取代就分区表。由allocate_note和fs调用。
kzalloc_node用于从slab中分配分区表, 调用kmalloc_node with parameter __GFP_ZERO。在非numa体系中, kmalloc_node相当与kmalloc, node并无作用。对于numa,从指定的node中分配。
__builtin_constant_p: gcc常量检测, 判断表达式是否常量, 主要用来优化分支操作: 这个还有一些gcc的其他特性请参见下面的链接(零长度数组, 属性等等):
http://blog.csdn.net/brucexu1978/article/details/7179709。
disk_release:释放disk
15. block_devnode: 返回disk的name 串
gendisk的devnode看来只有pktcddvd blockdriver使用(格式化的disk name的字符串)
disk_type: class_dev_iter(容器)用于parse disk的时候做比较。
16:
diskstats_show:调用seq显示分区信息
part_stat_lock: get rcu lock并返回cpu
part_round_stats: 统计hd_struct的disk_stat
part_stat_unlock:释放rcu
下面的宏值得注意L通过typeof支持泛型:
#define part_stat_read(part, field) \ ({ \ //累加每cpu的该变量field的值
typeof((part)->dkstats->field) res = 0; \
unsigned int _cpu; \
for_each_possible_cpu(_cpu) \
res += per_cpu_ptr((part)->dkstats, _cpu)->field; \
res; \
})
例子: part_stat_read(hd, ios[READ]),
diskstats_op(disk_seqf_start(找到pos参数指定的disk), disk_seqf_next(指向当前iter的下一个, 并将pos++), disk_seqf_stop(释放iter), diskstats_show:显示disk 分区状态)
diskstats_open注册diskstats_op到seq file
proc_diskstats_operations(diskstats_open, seq_read, seq_lseek, seq_release等), proc_partitions_operations由proc_genhd_init调用去生成proc入口。
genhd是一个module: module_init(proc_genhd_init);
17:
blk_lookup_devt: 根据name和分区号得到dev。 init的时候mount调用
18:
alloc_disk: 被block driver调用初始化一个disk。并返回disk
调用alloc_disk_node分配gendisk并初始化部分成员, 尤其是part0, 并初始化其device, class, type等
get_disk: 通过disk得到其device的kobj
put_disk:释放kobj,
两者结合使用来记录disk的引用计数
19:
set_disk_ro: 设置disk的ro flag,
call set_disk_ro_uevent(), 它call kobject_uevent_env(udev方法): 看这个链接
http://blog.csdn.net/brucexu1978/article/details/7188237, 大概是通知用户空间disk ro属性改变
set_device_ro: set device ro policy, 由blkdev, 和block driver调用
bdev_read_only: get ro policy, 由fs, mm等调用
invalidate_partition: sync bdev到disk 分区, 释放inode,bdev等
20.
gcc可以这样定义数组:
static const char *disk_events_strs[] = {
[ilog2(DISK_EVENT_MEDIA_CHANGE)] = "media_change",
ilog2(DISK_EVENT_EJECT_REQUEST)] = "eject_request",
};
disk_block_events/disk_unblock_events: block event执行或者unblock event执行, 允许嵌套, 在block函数中,当block count = 0时, 需要cancel delayed work。在unblock中, 如果block count减到0, 需要重新queue delayed work。
disk_block_events中的: cancel = !ev->block++; ---> cancel = !ev->block(=0, 取反为1, > 0, 取反为0); ev->block = ev->block + 1;
disk_flush_events: 设置clearing mask, 如果block count等于0, 取消delay work, 然后立即queue work without delay。
disk_clear_events: 看来跟flush有点像, 但为何写的那么罗嗦?
可以用container_of()实现一定程度的抽象, 比如to_delayed_work(work)调用container_of(work, struct delayed_work, work), 这里work是delayed_work的成员, 可以看成work是delayed)work的父类。
disk_events_workfn: 报告media change, eject event到用户空间
disk_block_events: 真该打印一下返回的什么值。
disk_events_attrs在disk_add_events的时候在sysfs中创建
param_set_ulong/
param_get_ulong: module设置获取ulong型值
disk_events_set_dfl_poll_msecs: 设值, 并flush events, 怎么就改poll msecs了昵?module_param_cb注册进去的参数disk_events_dfl_poll_msecs(第三个参数, 会转换成kernel_param),
disk_add_events:生成一个events, 生成属性到sysfs,初始化一个event delayed work, 再unblock 这个event, add_disk调用。
disk_del_events: block event, 从event链表取下, 从sysfs删除, del_gendisk调用
disk_release_events: 释放disk event, release disk调用