/**
* bus_add_device - add device to bus
* @dev: device being added
*
* - Add device's bus attributes.
* - Create links to device's bus.
* - Add the device to its bus's list of devices.
*/
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
error = device_add_attrs(bus, dev);
if (error)
goto out_put;
error = device_add_groups(dev, bus->dev_groups);
if (error)
goto out_id;
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev));
if (error)
goto out_groups;
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
if (error)
goto out_subsys;
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
return 0;
out_subsys:
sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_groups:
device_remove_groups(dev, bus->dev_groups);
out_id:
device_remove_attrs(bus, dev);
out_put:
bus_put(dev->bus);
return error;
}
您的观察非常准确!严格来说,`bus_add_device()` 的核心操作是**将设备关联到总线的体系结构中**,而非简单地将总线属性添加到设备。这里存在一个微妙的语义区别,让我们通过以下维度深入解析:
---
### 一、操作的本质:设备与总线的双向绑定
#### 1. **设备视角的关联**
```c
sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem");
```
- 效果:在设备的 sysfs 目录创建 `subsystem` 符号链接指向所属总线
(如 `/sys/devices/pci0000:00/0000:00:1f.2/subsystem → ../../../bus/pci`)
- **意义**:建立设备→总线的导航关系,**不是属性复制**
#### 2. **总线视角的收录**
```c
sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev_name(dev));
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
```
- 效果:
- 在总线的 `devices` 目录下创建设备入口
- 将设备加入总线的设备链表
- **意义**:总线获得设备的管理入口,**不是属性注入**
---
### 二、属性管理的真实过程
#### 1. **总线相关属性的添加**
```c
device_add_attrs(bus, dev);
device_add_groups(dev, bus->dev_groups);
```
- **操作对象**:虽然参数是 `bus`,但实际修改的是 `dev` 的 sysfs 属性
- **属性来源**:
- `bus->dev_attrs`:总线定义的默认属性(如 PCI 设备的 `vendor`/`device`)
- `bus->dev_groups`:总线定义的属性组(如 USB 设备的 `power` 子目录)
#### 2. **关键区别**
| 操作类型 | 示例属性 | 存储位置 | 生命周期 |
|----------------|--------------------------|------------------------|-----------------------|
| 总线定义属性 | `/sys/devices/.../vendor` | 设备的 sysfs 目录 | 随设备删除而移除 |
| 总线本体属性 | `/sys/bus/pci/uevent` | 总线的 sysfs 目录 | 与总线共存亡 |
---
### 三、内核数据结构的体现
```mermaid
classDiagram
class bus_type {
+struct klist klist_devices
+struct device_attrs *dev_attrs
+struct attribute_group **dev_groups
}
class device {
+struct bus_type *bus
+struct kobject kobj
}
bus_type "1" --> "*" device : 管理设备
device --> bus_type : 反向引用
```
- **总线管理设备**:通过 `klist_devices` 链表
- **设备引用总线**:通过 `dev->bus` 指针
- **属性存储**:实际写入设备的 `kobj`,但属性定义来自总线
---
### 四、典型调用栈示例
```plaintext
device_add()
→ bus_add_device() # 当前函数
→ device_add_attrs() # 添加总线相关属性到设备
→ sysfs_create_link() # 建立双向链接
→ device_add_class_symlinks() # 类相关操作(独立于总线)
```
---
### 五、设计哲学解读
1. **关注点分离**:
- 总线定义**公共属性规范**(如所有 PCI 设备都有 `vendor/id`)
- 设备实例化**具体属性存储**(每个设备的 `vendor/id` 值不同)
2. **拓扑优先原则**:
- 先建立设备与总线的关联关系(链表/符号链接)
- 再处理属性等细节,确保基础结构正确
3. **最小化耦合**:
- 设备无需知道总线属性的实现细节
- 总线不直接操作设备内存,通过标准接口管理
---
### 六、实际调试案例
#### 问题场景:
PCI 设备的 `resource` 属性未出现在 sysfs 中
#### 排查步骤:
1. 确认总线属性定义:
```c
// drivers/pci/pci-driver.c
static struct attribute *pci_dev_attrs[] = {
&dev_attr_resource.attr,
NULL
};
```
2. 检查 `bus_add_device` 调用链:
```bash
ftrace跟踪 device_add_attrs() 执行
```
3. 验证结果:
- 属性确实被添加到设备的 sysfs 节点
- 问题实为权限设置导致(`mode` 为 0400)
---
总结来说,`bus_add_device()` 的核心是**建立设备与总线的双向关系**,同时将总线定义的**属性规范实例化到具体设备**。这种设计既保证了总线的统一管理能力,又保持了设备实例的独立性,完美体现了 Linux 设备模型"规范与实现分离"的架构智慧。