device_add_attrs

/**
 * 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 设备模型"规范与实现分离"的架构智慧。

你可能感兴趣的:(windows,服务器,linux)