为啥内核支持一次调用注册不同主号的设备

Posted

技术标签:

【中文标题】为啥内核支持一次调用注册不同主号的设备【英文标题】:Why kernel supports registering device of different major numbers in one call为什么内核支持一次调用注册不同主号的设备 【发布时间】:2018-12-08 10:08:13 【问题描述】:

块设备驱动程序可以调用blk_register_region() 来声称对更大范围的设备号负责。例如,在ramdisk驱动的初始化中,它调用blk_register_region来声明所有可能的ram disk。

static int __init brd_init(void)

    // ...

    blk_register_region(MKDEV(RAMDISK_MAJOR, 0), 1UL << MINORBITS,
                  THIS_MODULE, brd_probe, NULL, NULL);

    return 0;

像 ramdisk 和 floppy 这样的驱动程序使用它们自己的主设备号(RAMDISK_MAJOR / FLOPPY_MAJOR 在major.h 中定义)注册。但我读了代码 的blk_register_region,发现这个函数支持一次调用注册不同主号的设备。

函数 blk_register_region 简单地包装了 kobj_map。 kobj_map 支持在一次调用中声明多达 255 个主要设备。这种需求从何而来?

void blk_register_region(dev_t devt, unsigned long range, struct module *module,
             struct kobject *(*probe)(dev_t, int *, void *),
             int (*lock)(dev_t, void *), void *data)

    kobj_map(bdev_map, devt, range, module, probe, lock, data);


int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
         struct module *module, kobj_probe_t *probe,
         int (*lock)(dev_t, void *), void *data)

    // calculate the range of major numbers
    unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
    unsigned index = MAJOR(dev);
    unsigned i;
    struct probe *p;

    if (n > 255)
        n = 255;

    p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);

    if (p == NULL)
        return -ENOMEM;
    // ....

非常感谢。

【问题讨论】:

【参考方案1】:

But I read the code of blk_register_region and found this function supports registering device of different major numbers in one call

实际情况并非如此。如果您仔细查看代码,它们都会得到相同的主编号(请参阅下面代码中带有我注释的行)。

for (i = 0; i < n; i++, p++) 
        p->owner = module;
        p->get = probe;
        p->lock = lock;
        p->dev = dev;      /* See the assigned device number */
        p->range = range;
        p->data = data;

关于支持行为的要求,查看thisLWN文章驱动移植:gendisk接口注册块设备号范围部分。

直接引用文章,

对 add_disk() 的调用会隐式分配一组次要编号 (在给定的主编号下)从 first_minor 到 first_minor+minors-1。如果您的驱动程序必须只响应操作 对于初始化时存在的磁盘,无需担心 进一步关于号码分配。即使是传统的呼吁 register_blkdev() 是可选的,可能很快就会被删除。一些司机, 但是,需要能够对更大范围的 初始化时的设备号。

如果是这种情况,答案是调用 blk_register_region()

这里有几件事需要注意(同样来自文章),

range 是要分配的次要号码的数量。 当调用 blk_register_region() 时,它只是简单地记下所需区域并返回。请注意,在特定区域内可以有多个注册!在查找时,最“具体”的注册(范围最小的注册)获胜。

【讨论】:

谢谢。 unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1; if (n &gt; 255) n = 255; 这几行暗示 var n(主编号的计数)可能大于 255。所以如果 blk_register_region 只处理相同的主编号,那么这些行有什么需要?

以上是关于为啥内核支持一次调用注册不同主号的设备的主要内容,如果未能解决你的问题,请参考以下文章

16 内核中注册设备

js面向对象为啥要init进行初始化?

Linux驱动开发基础

一个主设备号是如何支持多个次设备?

内核实现信号捕捉原理

█c# 为啥有的dll不能直接引用,必须regsvr32 注册后 才能引用?