static int __init init_sd(void)
{
int majors = 0, i;
/*
Linux中所有的主设备号也是被各种各样的设备所瓜分.其中,8,65-71,136-143这么个16个号码就被scsi disk所霸占了.
sd_major()函数的返回值就是这16个数字.每个主设备号可以带256个次设备号.
*/
for (i = 0; i < SD_MAJORS; i++)
// 注册块设备驱动程序。为块设备预订一个主设备号。
if (register_blkdev(sd_major(i), "sd") == 0)
majors++;
return scsi_register_driver(&sd_template.gendrv);
}
init_sd主要内容如下:
(1)给sd驱动分配8,65-71,136-143这么个16个主设备号,当sd驱动探测到设备时就会从这16个主设备号里分配。
(2)调用scsi_register_driver注册sd驱动。
scsi_register_driver(&sd_template.gendrv);
// scsi_bus_type目录在/sys/bus/scsi
drv->bus = &scsi_bus_type;
return driver_register(drv);
INIT_LIST_HEAD(&drv->devices);
init_MUTEX_LOCKED(&drv->unload_sem);
return bus_add_driver(drv);
// scsi_bus_type
struct bus_type * bus = get_bus(drv->bus);
// sd
kobject_set_name(&drv->kobj, "%s", drv->name);
// 所以驱动应该注册在scsi_bus_type的驱动目录下
drv->kobj.kset = &bus->drivers;
kobject_register(&drv->kobj)
driver_attach(drv);
// device_add 里会把设备加入到总线链表里
list_for_each(entry, &bus->devices.list)
struct device * dev = container_of(entry, struct device, bus_list);
if (!dev->driver)
driver_probe_device(drv, dev);
dev->driver = drv;
if (drv->probe)
// 对于sd驱动就是 sd_probe
int error = drv->probe(dev);
if (error)
dev->driver = NULL;
return error;
device_bind_driver(dev);
list_add_tail(&dev->driver_list, &dev->driver->devices);
/*
/sys/bus/scsi/drivers/sd目录下创建0:0:0:0链接文件
链接到/sys/devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0
*/
sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj));
/*
/sys/devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0目录下创建driver链接文件
链接到/sys/bus/scsi/drivers/sd
*/
sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver");
module_add_driver(drv->owner, drv);
driver_add_attrs(bus, drv);
(1)将sd驱动注册到/sys/bus/scsi目录下。
(2)遍历总线的设备链表,用sd驱动的probe探测设备,如果探测到设备,就调用device_bind_driver把设备和驱动绑定起来。
sd驱动的probe函数就是sd_probe
static int sd_probe(struct device *dev)
{
struct scsi_device *sdp = to_scsi_device(dev);
struct scsi_disk *sdkp;
struct gendisk *gd;
/* 如果SCSI设备不是磁盘或者磁光盘,则退出 */
if ((sdp->type != TYPE_DISK) && (sdp->type != TYPE_MOD))
goto out;
/* 分配一个scsi_disk描述符 */
sdkp = kmalloc(sizeof(*sdkp), GFP_KERNEL);
memset (sdkp, 0, sizeof(*sdkp));
kref_init(&sdkp->kref);
/* 分配通用磁盘描述符,SCSI磁盘最多16个分区 */
gd = alloc_disk(16);
/* 分配磁盘名,如sda-sdz,sdaa-sdzz */
if (!idr_pre_get(&sd_index_idr, GFP_KERNEL))
goto out_put;
spin_lock(&sd_index_lock);
error = idr_get_new(&sd_index_idr, NULL, &index);
spin_unlock(&sd_index_lock);
/* 设备数量太多,退出 */
if (index >= SD_MAX_DISKS)
error = -EBUSY;
if (error)
goto out_put;
sdkp->device = sdp;
sdkp->driver = &sd_template;
sdkp->disk = gd;
sdkp->index = index;
sdkp->openers = 0;
/* 设置超时时间 */
if (!sdp->timeout) {
if (sdp->type == TYPE_DISK)
sdp->timeout = SD_TIMEOUT;
else/* 磁光盘超时时间应当长一点 */
sdp->timeout = SD_MOD_TIMEOUT;
}
/* 计算设备的主次设备号,特殊的计算方法是为了与2.4兼容 */
gd->major = sd_major((index & 0xf0) >> 4);
gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
gd->minors = 16;
/* 设置磁盘回调 */
gd->fops = &sd_fops;
if (index < 26) {
sprintf(gd->disk_name, "sd%c", 'a' + index % 26);
} else if (index < (26 + 1) * 26) {
sprintf(gd->disk_name, "sd%c%c",
'a' + index / 26 - 1,'a' + index % 26);
} else {
const unsigned int m1 = (index / 26 - 1) / 26 - 1;
const unsigned int m2 = (index / 26 - 1) % 26;
const unsigned int m3 = index % 26;
sprintf(gd->disk_name, "sd%c%c%c",
'a' + m1, 'a' + m2, 'a' + m3);
}
strcpy(gd->devfs_name, sdp->devfs_name);
/* 将驱动、设备、通用磁盘数据结构关联起来 */
gd->private_data = &sdkp->driver;
/* 发送SCSI命令获取设备信息并设置描述符字段 */
sd_revalidate_disk(gd);
gd->driverfs_dev = &sdp->sdev_gendev;
gd->flags = GENHD_FL_DRIVERFS;
if (sdp->removable)
gd->flags |= GENHD_FL_REMOVABLE;
gd->queue = sdkp->device->request_queue;
dev_set_drvdata(dev, sdkp);
/* 将该设备添加到通用块IO系统中 */
add_disk(gd);
(1)分配scsi_disk和gendisk描述符。
(2)从idr_tree中查找一个空闲节点,并且获取该空闲节点的id号,并根据该id号生成盘符和设备的主次设备号。
(3)设备gendisk的请求队列为主机适配器驱动里为scsi_device分配的队列。
(4)调用add_disk将gendisk添加到通用块IO系统中。
void add_disk(struct gendisk *disk)
{
/**
* 设置GENHD_FL_UP标志。
*/
disk->flags |= GENHD_FL_UP;
/**
* 建立设备驱动程序和设备的主设备号之间的连接。
*/
blk_register_region(MKDEV(disk->major, disk->first_minor),
disk->minors, NULL, exact_match, exact_lock, disk);
/**
* 注册设备驱动程序模型的gendisk的kobject结构,它作为设备驱动程序的一个新设备。
* 并扫描磁盘中的分区表,对每个分区,初始化其hd_struct描述符。同时注册设备驱动程序模型中的分区。
*/
register_disk(disk);
/**
* 注册请求队列描述符中内嵌的kobject结构。
*/
blk_register_queue(disk);
}
比较重要的是register_disk函数
void register_disk(struct gendisk *disk)
{
/* 设置磁盘名称 */
strlcpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN);
/* ewww... some of these buggers have / in name... */
s = strchr(disk->kobj.name, '/');
if (s)
*s = '!';
/* 将磁盘设备添加到sys文件系统中 */
if ((err = kobject_add(&disk->kobj)))
return;
disk_sysfs_symlinks(disk);
struct device *target = get_device(disk->driverfs_dev);
/*
/sys/block/sda目录下创建“device”链接文件,链接到
/sys/devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0
*/
sysfs_create_link(&disk->kobj,&target->kobj,"device");
/*
/sys/devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0目录下创建“block”链接文件,
链接到/sys/block/sda
*/
sysfs_create_link(&target->kobj,&disk->kobj,"block");
/* No minors to use for partitions */
if (disk->minors == 1) {/* 没有逻辑分区,将设备添加到dev中 */
if (disk->devfs_name[0] != '\0')
devfs_add_disk(disk);
return;
}
/* always add handle for the whole disk */
/* 处理磁盘分区 */
devfs_add_partitioned(disk);
/* No such device (e.g., media were just removed) */
/* 设备已经被移除,退出 */
if (!get_capacity(disk))
return;
/* 获取设备的引用计数 */
bdev = bdget_disk(disk, 0);
if (!bdev)
return;
/* 读取磁盘分区标志 */
bdev->bd_invalidated = 1;
/* 扫描磁盘分区,建立磁盘与分区的关系,并将分区添加到系统中 */
if (blkdev_get(bdev, FMODE_READ, 0) < 0)
return;
blkdev_put(bdev);
}
(1)调用kobject_add将通用磁盘设备gendisk加入到/sys/block中。
(2)调用disk_sysfs_symlinks将磁盘设备目录/sys/block/sda和pci设备目录/sys/devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0关联起来。
(3)调用blkdev_get扫描磁盘分区,建立磁盘与分区的关系,并将分区添加到系统中。
我们看一下bdget_disk如何根据gendisk获得block_device的
static inline struct block_device *bdget_disk(struct gendisk *disk, int index)
return bdget(MKDEV(disk->major, disk->first_minor) + index);
/* 在dev文件系统中找到设备的inode */
inode = iget5_locked(bd_mnt->mnt_sb, hash(dev), bdev_test, bdev_set, &dev);
struct hlist_head *head = inode_hashtable + hash(sb, hashval);
inode = ifind(sb, head, test, data);
inode = find_inode(sb, head, test, data);
if (inode)
__iget(inode);
if (inode)
return inode;
// 如果没找到就分配一个inode
return get_new_inode(sb, head, test, set, data);
// alloc_inode 不仅仅分配了inode,还分配了device
struct inode *inode = alloc_inode(sb);
// 会调用bdev_alloc_inode
if (sb->s_op->alloc_inode)
inode = sb->s_op->alloc_inode(sb);
struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep, SLAB_KERNEL);
return &ei->vfs_inode;
else
inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);
old = find_inode(sb, head, test, data);
if (!old)
set(inode, data)
BDEV_I(inode)->bdev.bd_dev = *(dev_t *)data;
inodes_stat.nr_inodes++;
list_add(&inode->i_list, &inode_in_use);
list_add(&inode->i_sb_list, &sb->s_inodes);
hlist_add_head(&inode->i_hash, head);
inode->i_state = I_LOCK|I_NEW;
return inode;
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);
inode->i_data.backing_dev_info = &default_backing_dev_info;
spin_lock(&bdev_lock);
list_add(&bdev->bd_list, &all_bdevs);
spin_unlock(&bdev_lock);
unlock_new_inode(inode);
}
return bdev;
(1)调用iget5_locked获得块设备的inode,如果没有就会创建,对于bdev会创建bdev_inode,这个结构体含有inode和block_device两个字段。
(2)初始化inode和block_device的各个字段。
下面看一下blkdev_get函数
blkdev_get(bdev, FMODE_READ, 0)
struct file fake_file = {};
struct dentry fake_dentry = {};
fake_file.f_mode = mode;
fake_file.f_flags = flags;
fake_file.f_dentry = &fake_dentry;
fake_dentry.d_inode = bdev->bd_inode;
return do_open(bdev, &fake_file);
disk = get_gendisk(bdev->bd_dev, &part);
struct kobject *kobj = kobj_lookup(bdev_map, dev, part);
return kobj ? to_disk(kobj) : NULL;
if (!bdev->bd_openers)
/**
* 第一次访问,以前没有打开过
* 就初始化它的bd_disk
*/
bdev->bd_disk = disk;
bdev->bd_contains = bdev;
if (!part) {/* 是一个整盘,而不是分区 */
struct backing_dev_info *bdi;
if (disk->fops->open) {
/**
* 该盘定义了打开方法。就执行它
* 该方法是由块设备驱动程序定义的定制函数。
*/
ret = disk->fops->open(bdev->bd_inode, file);
sd_open(struct inode *inode, struct file *filp)
sdkp = scsi_disk_get(disk)
sdkp = scsi_disk(disk);
return sdkp;
sdev = sdkp->device;
if (ret)
goto out_first;
}
if (!bdev->bd_openers) {
bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
bdi = blk_get_backing_dev_info(bdev);
if (bdi == NULL)
bdi = &default_backing_dev_info;
bdev->bd_inode->i_data.backing_dev_info = bdi;
}
if (bdev->bd_invalidated)
rescan_partitions(disk, bdev);
bdev->bd_invalidated = 0;
/* 删除内存中的分区信息 */
for (p = 1; p < disk->minors; p++)
delete_partition(disk, p);
state = check_partition(disk, bdev)
state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
disk_name(hd, 0, state->name);
/* 将check_partition检测到的分区添加到系统中 */
for (p = 1; p < state->limit; p++) {
sector_t size = state->parts[p].size;
sector_t from = state->parts[p].from;
if (!size)/* 分区长度为0,忽略 */
continue;
/* 将分区添加到系统中 */
add_partition(disk, p, from, size);
struct hd_struct *p = kmalloc(sizeof(*p), GFP_KERNEL);
p->start_sect = start;
p->nr_sects = len;
p->partno = part;
if (isdigit(disk->kobj.name[strlen(disk->kobj.name)-1]))
snprintf(p->kobj.name,KOBJ_NAME_LEN,"%sp%d",disk->kobj.name,part);
else
snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part); // 比如sda1
p->kobj.parent = &disk->kobj;
p->kobj.ktype = &ktype_part;
kobject_init(&p->kobj);
// 注册 /dev/block/sda/sda1
kobject_add(&p->kobj);
// 向udev发生事件
if (!disk->part_uevent_suppress)
kobject_uevent(&p->kobj, KOBJ_ADD);
sysfs_create_link(&p->kobj, &block_subsys.kset.kobj, "subsystem");
partition_sysfs_add_subdir(p);
disk->part[part-1] = p;