DebugFS 的权限管理、并发保护和最佳实践。
4.1 权限管理
文件权限设置
DebugFS 使用标准 Linux 文件权限模型:
c
/* 权限位定义 */
#define S_IRUSR 0400 /* 用户可读 */
#define S_IWUSR 0200 /* 用户可写 */
#define S_IXUSR 0100 /* 用户可执行 */
#define S_IRGRP 0040 /* 组可读 */
#define S_IWGRP 0020 /* 组可写 */
#define S_IXGRP 0010 /* 组可执行 */
#define S_IROTH 0004 /* 其他用户可读 */
#define S_IWOTH 0002 /* 其他用户可写 */
#define S_IXOTH 0001 /* 其他用户可执行 */
/* 常用组合 */
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) /* 0444 所有人可读 */
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) /* 0222 所有人可写 */
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) /* 0111 所有人可执行 */
常见权限模式
c
/* 只读文件(最常用) */
debugfs_create_file("info", 0444, dir, data, &fops);
/* 0444 = r--r--r-- */
/* 只写文件 */
debugfs_create_file("control", 0200, dir, data, &fops);
/* 0200 = -w------- (只有 root 可写)*/
/* 读写文件 */
debugfs_create_file("value", 0644, dir, data, &fops);
/* 0644 = rw-r--r-- (root 可写,其他人只读)*/
/* 仅 root 可访问 */
debugfs_create_file("sensitive", 0600, dir, data, &fops);
/* 0600 = rw------- */
/* 用户读写,组只读 */
debugfs_create_file("config", 0640, dir, data, &fops);
/* 0640 = rw-r----- */
AMDGPU 实际权限使用
c
/* drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c */
/* 只读:查询信息 */
debugfs_create_file("amdgpu_vbios", 0444, root, adev, &fops);
/* 只读但需要触发操作 */
debugfs_create_file("amdgpu_evict_vram", 0400, root, adev, &fops);
/* 只写:配置控制 */
debugfs_create_file("amdgpu_force_sclk", 0200, root, adev, &fops);
/* 读写:寄存器访问 */
debugfs_create_file("amdgpu_regs", S_IFREG | 0400, root, adev, &fops);
/* 可配置的控制文件 */
debugfs_create_file("amdgpu_preempt_ib", 0600, root, adev, &fops);
安全访问控制
1. 默认安全策略
c
/* DebugFS 挂载点默认权限 */
/sys/kernel/debug/ /* drwx------ (0700) 只有 root 可访问 */
含义:
- 默认情况下,普通用户无法访问任何 debugfs 文件
- 即使文件权限是 0444,也需要能访问父目录
2. 运行时权限检查
c
/* 在文件操作中添加权限检查 */
static ssize_t sensitive_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
/* 检查是否有 CAP_SYS_ADMIN 权限 */
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/* 或检查是否是 root 用户 */
if (!uid_eq(current_uid(), GLOBAL_ROOT_UID))
return -EACCES;
/* 执行实际操作 */
/* ... */
return count;
}
4.2 并发保护
互斥锁的使用
基本互斥锁保护
c
struct my_device {
struct mutex lock;
u32 shared_value;
bool busy;
};
static ssize_t value_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct my_device *dev = file->private_data;
u32 val;
int ret;
ret = kstrtou32_from_user(buf, count, 0, &val);
if (ret)
return ret;
/* 保护共享数据 */
mutex_lock(&dev->lock);
dev->shared_value = val;
dev->busy = true;
mutex_unlock(&dev->lock);
return count;
}
static ssize_t value_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct my_device *dev = file->private_data;
char tmp[32];
int len;
u32 val;
/* 保护读取 */
mutex_lock(&dev->lock);
val = dev->shared_value;
mutex_unlock(&dev->lock);
len = snprintf(tmp, sizeof(tmp), "%u\n", val);
return simple_read_from_buffer(buf, count, ppos, tmp, len);
}
文件私有数据锁
c
/* AMDGPU regs2 示例 */
struct amdgpu_debugfs_regs2_data {
struct amdgpu_device *adev;
struct mutex lock; /* 保护配置状态 */
struct {
bool use_grbm;
bool use_srbm;
/* ... 配置字段 ... */
} id;
};
static int amdgpu_debugfs_regs2_open(struct inode *inode, struct file *file)
{
struct amdgpu_debugfs_regs2_data *rd;
rd = kzalloc(sizeof(*rd), GFP_KERNEL);
if (!rd)
return -ENOMEM;
rd->adev = inode->i_private;
mutex_init(&rd->lock); /* 初始化锁 */
file->private_data = rd;
return 0;
}
static int amdgpu_debugfs_regs2_release(struct inode *inode, struct file *file)
{
struct amdgpu_debugfs_regs2_data *rd = file->private_data;
mutex_destroy(&rd->lock); /* 销毁锁 */
kfree(rd);
return 0;
}
static long amdgpu_debugfs_regs2_ioctl(struct file *f, unsigned int cmd,
unsigned long data)
{
struct amdgpu_debugfs_regs2_data *rd = f->private_data;
int r;
mutex_lock(&rd->lock); /* 保护配置修改 */
switch (cmd) {
case AMDGPU_DEBUGFS_REGS2_IOC_SET_STATE:
r = copy_from_user(&rd->id, (void __user *)data, sizeof(rd->id));
break;
default:
r = -EINVAL;
}
mutex_unlock(&rd->lock);
return r;
}
防止竞态条件
设备移除时的保护
c
/* 使用 debugfs_file_get/put 防止设备移除竞态 */
static ssize_t safe_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct dentry *dentry = file->f_path.dentry;
struct my_device *dev;
ssize_t ret;
/* 获取文件引用,防止并发删除 */
if (!debugfs_file_get(dentry))
return -EIO;
dev = file->private_data;
/* 检查设备是否已被移除 */
if (!dev || dev->removed) {
ret = -ENODEV;
goto out;
}
/* 执行实际读取 */
ret = do_read(dev, buf, count, ppos);
out:
debugfs_file_put(dentry);
return ret;
}
seq_file 与锁
c
/* seq_file 中使用锁 */
static int info_show(struct seq_file *m, void *v)
{
struct my_device *dev = m->private;
/* 在整个 show 函数期间持有锁 */
mutex_lock(&dev->lock);
seq_printf(m, "Status: %s\n", dev->status);
seq_printf(m, "Counter: %llu\n", dev->counter);
mutex_unlock(&dev->lock);
return 0;
}
/* 或者使用 RCU 读锁 */
static int list_show(struct seq_file *m, void *v)
{
struct my_device *dev = m->private;
struct item *item;
rcu_read_lock();
list_for_each_entry_rcu(item, &dev->item_list, list) {
seq_printf(m, "%s\n", item->name);
}
rcu_read_unlock();
return 0;
}
最佳实践汇总
1. 权限设置原则
c
/* ✅ 推荐的权限设置 */
/* 纯查询信息 - 只读 */
debugfs_create_file("info", 0444, dir, data, &fops);
/* 触发一次性操作 - 只读但有副作用 */
debugfs_create_file("trigger_test", 0400, dir, data, &fops);
/* 简单配置 - 可读写 */
debugfs_create_file("config", 0644, dir, data, &fops);
/* 危险操作 - 只有 root 可写 */
debugfs_create_file("reset", 0600, dir, data, &fops);
/* ❌ 避免的权限设置 */
/* 不要给普通用户写权限(在 debugfs 中) */
debugfs_create_file("bad", 0666, dir, data, &fops); // ❌
/* 不要使用执行权限 */
debugfs_create_file("bad", 0755, dir, data, &fops); // ❌
2. 并发保护模板
c
struct safe_device {
struct mutex hw_lock; /* 保护硬件访问 */
struct mutex data_lock; /* 保护数据结构 */
void __iomem *regs;
u32 config_value;
bool removed;
};
static ssize_t safe_hw_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct safe_device *dev = file->private_data;
u32 value;
int r;
/* 1. 电源管理 */
r = pm_runtime_get_sync(dev->parent);
if (r < 0) {
pm_runtime_put_autosuspend(dev->parent);
return r;
}
/* 2. 硬件访问锁 */
mutex_lock(&dev->hw_lock);
/* 3. 检查设备状态 */
if (dev->removed) {
r = -ENODEV;
goto unlock;
}
/* 4. 访问硬件 */
value = readl(dev->regs + *ppos);
/* 5. 解锁 */
unlock:
mutex_unlock(&dev->hw_lock);
/* 6. 电源管理清理 */
pm_runtime_mark_last_busy(dev->parent);
pm_runtime_put_autosuspend(dev->parent);
/* 7. 返回数据 */
if (r)
return r;
return simple_read_from_buffer(buf, count, ppos, &value, sizeof(value));
}
3. 错误处理原则
c
/* ✅ 正确的错误处理 */
static int my_init(void)
{
struct dentry *dir;
/* DebugFS 失败不应影响主要功能 */
dir = debugfs_create_dir("mydriver", NULL);
/* 继续创建文件,即使 dir 是 ERR_PTR */
debugfs_create_file("info", 0444, dir, data, &fops);
/* 主要功能初始化 */
return init_hardware();
}
/* ❌ 不推荐:过度检查 */
static int my_init_bad(void)
{
struct dentry *dir;
dir = debugfs_create_dir("mydriver", NULL);
if (IS_ERR(dir)) {
pr_err("Failed to create debugfs\n");
return PTR_ERR(dir); /* 不应因此失败 */
}
/* ... */
}
小结
DebugFS 安全性和并发控制要点:
权限管理
- ✅ 默认使用只读权限(0444)
- ✅ 危险操作限制为 root(0600)
- ✅ 利用 debugfs 挂载点的默认保护
并发保护
- 🔒 使用互斥锁保护共享数据
- 🔐 防止设备移除时的竞态条件
最佳实践
- 📝 DebugFS 失败不应影响主功能
- 🔍 添加设备状态检查
- 🎯 使用合适的锁粒度
- 📊 遵循内核编码规范
至此,第一部分"Linux DebugFS 机制与实现"完成!
📖 下一步: [第二部分 AMD 驱动中的 DebugFS 架构] 编写中...