[toc]

drivers/base/map.c
kobj_map: 一个通用的设备号到内核对象的映射引擎
本代码片段是Linux内核中一个相对底层但非常关键的通用映射子系统——kobj_map。其核心功能是提供一个可扩展的、基于回调的哈希表,用于将一个设备号(dev_t)高效地映射到一个内核对象(kobject)。这个机制是cdev(字符设备)、bdev(块设备)等许多与设备号相关的子系统的基础引擎。它通过一种链式哈希表和回调函数的设计,实现了设备注册、注销和查找的核心逻辑。
实现原理分析
如代码开头的注释所言,这是一个历史悠久的设计,虽然性能可能不是最优,但其设计思想非常精巧。
核心数据结构 (kobj_map):
probes[255]: 这是一个哈希表。它的大小是固定的255个桶(bucket)。
- 哈希函数:
MAJOR(dev) % 255。它使用设备号的主设备号(Major number)对255取模,来决定一个设备应该被放入哪个桶中。
struct probe: 这是哈希表中的节点。它不是直接存储kobject,而是存储一个**“探测器”**。这个探测器包含了:
dev 和 range: 定义了这个探测器所覆盖的设备号范围。
get (kobj_probe_t *): 一个回调函数指针。当查找到这个探测器时,系统会调用这个函数来动态地获取最终的kobject。
lock: 另一个回调函数,用于在使用get回调之前锁定底层对象。
data: 一个私有数据指针,会传递给get和lock回调。
lock: 一个互斥锁,用于保护整个哈希表的并发访问。
映射/注册 (kobj_map):
- 职责: 将一个新的设备号范围及其对应的探测器(回调函数等)添加到哈希表中。
- 实现:
- 它首先为要覆盖的所有主设备号(最多255个)分配一组
probe结构体。
- 然后,它锁定互斥锁。
- 对于每一个受影响的主设备号,它计算出哈希桶的索引(
index % 255)。
- 它将新的
probe节点以头插法的方式插入到对应哈希桶的链表中。链表是根据range大小排序的,范围小的排在前面,这是一种优化,使得查找时能更快地找到最精确匹配的范围。
查找 (kobj_lookup):
- 职责: 这是
kobj_map的核心服务。给定一个设备号dev,找到对应的kobject。
- 实现:
- 计算哈希桶索引
MAJOR(dev) % 255,并遍历该桶的probe链表。
- 对于链表中的每个
probe节点,检查dev是否落在[p->dev, p->dev + p->range - 1]的范围内。
- 最佳匹配: 它会寻找覆盖范围最小(
p->range - 1最小)的那个probe节点,这被称为“最佳匹配”(best match)。
- 找到最佳匹配后,它会:
- 增加
probe所属模块的引用计数(try_module_get)。
- 调用
probe节点中的lock回调函数(如cdev的exact_lock,它会增加cdev的引用计数)。
- 调用
probe节点中的get回调函数(如cdev的exact_match),这个回调真正返回kobject指针。
- 递减模块引用计数。
goto retry: 这是一个处理竞态条件的机制。如果在解锁后、调用probe回调期间,底层对象发生了变化(例如,probe返回NULL),它会重新加锁并进行重试。
代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
struct kobj_map {
struct probe { struct probe *next; dev_t dev; unsigned long range; struct module *owner; kobj_probe_t *get; int (*lock)(dev_t, void *); void *data; } *probes[255]; struct mutex *lock; };
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) {
mutex_lock(domain->lock); for (i = 0, p -= n; i < n; i++, p++, index++) { struct probe **s = &domain->probes[index % 255]; while (*s && (*s)->range < range) s = &(*s)->next; p->next = *s; *s = p; } mutex_unlock(domain->lock); return 0; }
void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range) {
mutex_lock(domain->lock); for (i = 0; i < n; i++, index++) { struct probe **s; for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) { struct probe *p = *s; if (p->dev == dev && p->range == range) { *s = p->next; if (!found) found = p; break; } } } mutex_unlock(domain->lock); kfree(found); }
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index) { retry: mutex_lock(domain->lock); for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) { if (p->dev > dev || p->dev + p->range - 1 < dev) continue; if (p->range - 1 >= best) break; if (!try_module_get(p->owner)) continue; if (p->lock && p->lock(dev, data) < 0) { module_put(owner); continue; } mutex_unlock(domain->lock); kobj = probe(dev, index, data); module_put(owner); if (kobj) return kobj; goto retry; } mutex_unlock(domain->lock); return NULL; }
struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock) {
base->dev = 1; base->range = ~0; base->get = base_probe; for (i = 0; i < 255; i++) p->probes[i] = base; p->lock = lock; return p; }
|