第4章:DebugFS 安全性和并发控制

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 架构] 编写中...


相关推荐
CS Beginner2 小时前
【Linux】快速配置wifi和SSH服务
linux·运维·ssh
become__better2 小时前
判断ceph osd 节点磁盘异常
linux·运维·ceph
JavaWizard-M2 小时前
centos7官方下载链接
linux·centos
我在人间贩卖青春2 小时前
Unix和Linux简史及标准化
linux·unix
特级业务专家3 小时前
这下发布不需要Jenkins了
linux·git·docker
小鹏linux3 小时前
【linux】进程与服务管理命令 - at
linux·运维·服务器
博语小屋3 小时前
TCP:协议、序列化与反序列化、JSON 数据和jsoncpp
linux·网络·网络协议·tcp/ip·json
硬核子牙3 小时前
手写生产级eBPF内存检测工具
linux
.生产的驴3 小时前
DockerCompoe 部署注册中心Nacos 一键部署 单机+Mysql8
java·linux·运维·spring boot·缓存·docker·doc