1.5.3 关联block_device结构
接下来是register_disk函数,来自fs/partitions/check.c:
473 /* Not exported, helper to add_disk(). */ 474 void register_disk(struct gendisk *disk) 475 { 476 struct block_device *bdev; 477 char *s; 478 int i; 479 struct hd_struct *p; 480 int err; 481 482 strlcpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN); 483 /* ewww... some of these buggers have / in name... */ 484 s = strchr(disk->kobj.name, '/'); 485 if (s) 486 *s = '!'; 487 if ((err = kobject_add(&disk->kobj))) 488 return; 489 err = disk_sysfs_symlinks(disk); 490 if (err) { 491 kobject_del(&disk->kobj); 492 return; 493 } 494 disk_sysfs_add_subdirs(disk); 495 496 /* No minors to use for partitions */ 497 if (disk->minors == 1) 498 goto exit; 499 500 /* No such device (e.g., media were just removed) */ 501 if (!get_capacity(disk)) 502 goto exit; 503 504 bdev = bdget_disk(disk, 0); 505 if (!bdev) 506 goto exit; 507 508 /* scan partition table, but suppress uevents */ 509 bdev->bd_invalidated = 1; 510 disk->part_uevent_suppress = 1; 511 err = blkdev_get(bdev, FMODE_READ, 0); 512 disk->part_uevent_suppress = 0; 513 if (err < 0) 514 goto exit; 515 blkdev_put(bdev); 516 517 exit: 518 /* announce disk after possible partitions are already created */ 519 kobject_uevent(&disk->kobj, KOBJ_ADD); 520 521 /* announce possible partitions */ 522 for (i = 1; i < disk->minors; i++) { 523 p = disk->part[i-1]; 524 if (!p || !p->nr_sects) 525 continue; 526 kobject_uevent(&p->kobj, KOBJ_ADD); 527 } 528 } |
首先487行这个kobject_add的作用是很直观的,在Sysfs中为这块磁盘建一个子目录,例如我们为的硬盘建立一个块设备驱动,则会在/sys/block/目录中看到一个sdf,要是把这个调用kobject_add函数这行注释掉,肯定就看不到这个sdf目录。这里有两个问题:
第一,为什么kobject_add这么一调用,生成的这个子目录的名字就叫做“sdf”,而不叫做别的呢?其实在sd_probe中做过这么一件事情,通过精心计算得到disk_name的,而这个disk_name正是struct gendisk的一个成员,这里我们看到482行我们把disk_name给了kobj.name,这就是为什么我们调用kobject_add添加一个kobject的时候,它的名字就是我们当时的disk_name。
第二,为什么生成的这个子目录是在/sys/block目录下面,而不是在别的位置呢?还记得在alloc_disk_node中我们申请struct gendisk的情景么?kobj_set_kset_s(disk,block_subsys)做的就是让disk对应的kobject从属于block_subsys对应的kobject下面。这就是为什么我们现在添加这个kobject的时候,它很自然的就会在/sys/block子目录下面建立文件。
继续走,disk_sysfs_symlinks来自fs/partitions/check.c,这个函数虽然不短,但是比较浅显易懂。
static int disk_sysfs_symlinks(struct gendisk *disk){ struct device *target = get_device(disk->driverfs_dev); int err; char *disk_name = NULL;
if (target) { disk_name = make_block_name(disk); if (!disk_name) { err = -ENOMEM; goto err_out; }
err = sysfs_create_link(&disk->kobj, &target->kobj, "device"); if (err) goto err_out_disk_name;
err = sysfs_create_link(&target->kobj, &disk->kobj, disk_name); if (err) goto err_out_dev_link; }
err = sysfs_create_link(&disk->kobj, &block_subsys.kobj, "subsystem"); if (err) goto err_out_disk_name_lnk;
kfree(disk_name);
return 0;
err_out_disk_name_lnk: if (target) { sysfs_remove_link(&target->kobj, disk_name); err_out_dev_link: sysfs_remove_link(&disk->kobj, "device"); err_out_disk_name: kfree(disk_name); err_out: put_device(target); } return err; } |
我们用实际效果来解读这个函数。首先我们看正常工作的U盘会在/sys/block/sdf下面有哪些内容:
[root@localhost ~]# ls /sys/block/sdf/
capability dev device holders queue range removable size slaves stat subsystem uevent
第一个sysfs_create_link创建的就是这里这个device这个软链接文件。我们来看它链接到哪里去了:
[root@localhost ~]# ls -l /sys/block/sdf/device
lrwxrwxrwx 1 root root 0 Dec 13 07:09
/sys/block/sdf/device
-> ../../devices/pci0000:00/0000:00:1d.7/usb4/4-4/4-4:1.0/host24/target24:0:0/24:0:0:0
第二个sysfs_create_link则从那边又建立一个反链接,又给链接回来了:
[root@localhost~]# ls -l ……
lrwxrwxrwx 1 root root 0 Dec 13 21:16
/sys/devices/pci0000:00/0000:00:1d.7/usb4/4-4/4-4:1.0/host24/target24:0:0/24:0:0:0/block:sdf -> ../../../../../../../../../block/sdf
于是这就等于你中有我我中有你,你那边有一个文件链接到了我这边,我这边有一个文件链接到了你那边。第三个sysfs_create_link,生成的是/sys/block/sdf/subsystem这个软链接文件。
[root@localhost ~]# ls -l /sys/block/sdf/subsystem
lrwxrwxrwx 1 root root 0 Dec 13 07:09 /sys/block/sdf/subsystem -> ../../block
三个链接文件建立好之后,disk_sysfs_symlinks也就结束了它的使命。接下来一个函数是disk_sysfs_add_subdirs。同样来自fs/partitions/check.c:
static inline void disk_sysfs_add_subdirs(struct gendisk *disk){ struct kobject *k;
k = kobject_get(&disk->kobj); disk->holder_dir = kobject_add_dir(k, "holders"); disk->slave_dir = kobject_add_dir(k, "slaves"); kobject_put(k); } |
这个函数的意图太明显了,无非就是建立holders和slaves两个子目录。
504行接着调用一个内联函数,bdget_disk,《Thinking in C++》告诉我们内联函数最好定义在头文件中,所以这个函数来自include/linux/genhd.h:
static inline struct block_device *bdget_disk(struct gendisk *disk, int index){ return bdget(MKDEV(disk->major, disk->first_minor) + index); } |
bdget来自fs/block_dev.c:
struct block_device *bdget(dev_t dev){ struct block_device *bdev; struct inode *inode;
inode = iget5_locked(bd_mnt->mnt_sb, hash(dev), bdev_test, bdev_set, &dev);
if (!inode) return NULL;
bdev = &BDEV_I(inode)->bdev;
if (inode->i_state & I_NEW) { bdev->bd_contains = NULL; bdev->bd_inode = inode; bdev->bd_block_size = (1 << inode->i_blkbits); bdev->bd_part_count = 0; bdev->bd_invalidated = 0; inode->i_mode = S_IFBLK; inode->i_rdev = dev; inode->i_bdev = bdev; inode->i_data.a_ops = &def_blk_aops; mapping_set_gfp_mask(&inode->i_data, GFP_USER);
|