Linux kernel sysfs device_attribute节点的创建和读写分析

备注:内核版本:5.4

一、重要结构体

device.h (msm-5.4\include\linux)

struct attribute {
    const char      *name;
    umode_t         mode;
...
};
struct device_attribute {  // 设备属性文件
    struct attribute    attr;
    ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
    ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
};
 
struct attribute_group {
    const char      *name;
    umode_t         (*is_visible)(struct kobject *, struct attribute *, int);
    umode_t         (*is_bin_visible)(struct kobject *, struct bin_attribute *, int);
    struct attribute    **attrs;
    struct bin_attribute    **bin_attrs;
};
 
struct driver_attribute {  // 驱动属性文件
    struct attribute attr;
    ssize_t (*show)(struct device_driver *driver, char *buf);
    ssize_t (*store)(struct device_driver *driver, const char *buf,
             size_t count);
};
1. 驱动开发中我们通常使用
static DEVICE_ATTR_RW(xxx);
static DEVICE_ATTR_RO(xxx);
static DEVICE_ATTR_WO(xxx);
 
//如drm_sysfs.c (msm-5.4/drivers/gpu/drm)
static DEVICE_ATTR_RW(status);
static DEVICE_ATTR_RO(enabled);
static DEVICE_ATTR_RO(dpms);
static DEVICE_ATTR_RO(modes);
 
static struct attribute *connector_dev_attrs[] = {
    &dev_attr_status.attr,
    &dev_attr_enabled.attr,
    &dev_attr_dpms.attr,
    &dev_attr_modes.attr,
    NULL
};
 
2. 宏展开
#define DEVICE_ATTR_RW(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR_RO(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#define DEVICE_ATTR_WO(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
 //sysfs.h (msm-5.4\include\linux)
 #define __ATTR_RO(_name) {                     \
    .attr   = { .name = __stringify(_name), .mode = 0444 },     \
    .show   = _name##_show,                     \
}
#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)
#define __ATTR(_name, _mode, _show, _store) {               \
    .attr = {.name = __stringify(_name),                \
         .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },     \
    .show   = _show,                        \
    .store  = _store,                       \
}
 
static device_attribute dev_attr_status = {
    .attr   = {
        .name = "status",
        .mode = 0644
        },
    .show   = status_show,
    .store  = status_store,
}

二、kernel API

struct device *device_create_with_groups(struct class *class,
                     struct device *parent, dev_t devt,
                     void *drvdata,
                     const struct attribute_group **groups,
                     const char *fmt, ...)  //函数创建设备和一堆属性文件
int sysfs_create_group(struct kobject *kobj,
                     const struct attribute_group *grp)//函数创建一堆属性文件
 
extern int device_create_file(struct device *device,
                  const struct device_attribute *entry);//函数创建单个文件
extern void device_remove_file(struct device *dev,
                   const struct device_attribute *attr);//函数删除单个文件
 
struct kernfs_node *sysfs_get_dirent(struct kernfs_node *parent,
                           const char *name) // 通过sysfs属性文件名字获取struct kernfs_node
void sysfs_notify_dirent(struct kernfs_node *kn) //用上面函数获取到的struct kernfs_node去唤醒在读写属性文件(sysfs节点)时因调用select()或poll()而阻塞的用户进程
void sysfs_notify(struct kobject *kobj, const char *dir,
                const char *attr) //用于用来唤醒在读写属性文件(sysfs节点)时因调用select()或poll()而阻塞的用户进程

注意:sysfs 属性文件poll()或者select()的时候 fd.events 只能包含 POLLPRI | POLLERR,不然会阻塞不住

详细见:linux kernel poll调用过程分析

API使用举例

  1. kernel 举例

sde_crtc.c (msm-5.4/techpack/display/msm/sde)

1、通过 int device_create_with_groups(struct kobject *kobj, const struct attribute_group *grp)创建一堆属性文件
static DEVICE_ATTR_RO(vsync_event);
static DEVICE_ATTR_RO(measured_fps);
static DEVICE_ATTR_RW(fps_periodicity_ms);
 
static struct attribute *sde_crtc_dev_attrs[] = {
    &dev_attr_vsync_event.attr,
    &dev_attr_measured_fps.attr,
    &dev_attr_fps_periodicity_ms.attr,
    NULL
};
static const struct attribute_group sde_crtc_attr_group = {
    .attrs = sde_crtc_dev_attrs,
};
 
static const struct attribute_group *sde_crtc_attr_groups[] = {
    &sde_crtc_attr_group,
    NULL,
};
 
int sde_crtc_post_init(struct drm_device *dev, struct drm_crtc *crtc)
{
    struct sde_crtc *sde_crtc;
    int rc = 0;
...
    sde_crtc = to_sde_crtc(crtc);
    sde_crtc->sysfs_dev = device_create_with_groups(
        dev->primary->kdev->class, dev->primary->kdev, 0, crtc,
        sde_crtc_attr_groups, "sde-crtc-%d", crtc->index);  // 创建设备和一堆属性文件,下面分析device_create_with_groups函数创建设备和属性文件的函数调用过程
 
分析函数调用
struct device *device_create_with_groups(struct class *class,
                     struct device *parent, dev_t devt,
                     void *drvdata,
                     const struct attribute_group **groups,
                     const char *fmt, ...)
|-->device_create_groups_vargs(class, parent, devt, drvdata, groups, fmt, vargs);
    |-->dev->groups = groups;
    |-->device_add(dev);
        |-->device_add_attrs(dev);
            |-->device_add_groups(dev, dev->groups);
                |-->sysfs_create_groups(&dev->kobj, groups); 熟悉的api
                    |-->internal_create_groups(kobj, 0, groups);
                        |-->internal_create_group(kobj, update, groups[i]);
                            |-->kernfs_create_dir_ns(kobj->sd, grp->name, S_IRWXU | S_IRUGO | S_IXUGO, uid, gid, kobj, NULL);
                            |-->create_files(kn, kobj, uid, gid, grp, update);
 
如手机sysfs文件系统中呈现:
venus:/sys/class/drm/sde-crtc-0 # ls -l
total 0
lrwxrwxrwx 1 root root    0 2020-10-20 19:06 device -> ../../card0
-rw-r--r-- 1 root root 4096 2020-10-20 19:06 fps_periodicity_ms  ######
-r--r--r-- 1 root root 4096 2020-10-20 19:06 measured_fps       ######
drwxr-xr-x 2 root root    0 2020-10-20 19:06 power
lrwxrwxrwx 1 root root    0 2020-10-20 19:06 subsystem -> ../../../../../../../class/drm
-rw-r--r-- 1 root root 4096 2020-10-20 19:06 uevent
-r--r--r-- 1 root root 4096 2020-10-20 19:06 vsync_event        ######
....
 
2、通过sysfs_get_dirent api 获取属性文件的struct kernfs_node
    sde_crtc->vsync_event_sf = sysfs_get_dirent(
        sde_crtc->sysfs_dev->kobj.sd, "vsync_event");
    sde_crtc->retire_frame_event_sf = sysfs_get_dirent(
        sde_crtc->sysfs_dev->kobj.sd, "retire_frame_event");
3、当属性文件内容变化的时候通知唤醒
static void sde_crtc_vblank_cb(void *data)
{
    struct drm_crtc *crtc = (struct drm_crtc *)data;
    struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
 
    /* keep statistics on vblank callback - with auto reset via debugfs */
    if (ktime_compare(sde_crtc->vblank_cb_time, ktime_set(0, 0)) == 0)
        sde_crtc->vblank_cb_time = ktime_get();
    else
        sde_crtc->vblank_cb_count++;
 
    sde_crtc->vblank_last_cb_time = ktime_get();
    sysfs_notify_dirent(sde_crtc->vsync_event_sf);  #######
 
    drm_crtc_handle_vblank(crtc);
    DRM_DEBUG_VBL("crtc%d\n", crtc->base.id);
    SDE_EVT32_VERBOSE(DRMID(crtc));
}

2 应用举例

上层应用程序通过poll()或者select()sysfs属性文件的简单使用例子

int fd = open("/sys/class/drm/sde-crtc-0/vsync_event", O_RDONLY);
struct pollfd test_poll;
test_poll.fd = fd;
test_poll.events = POLLERR | POLLPRI;
for(;;) {
    ret = poll(&test_poll, 1, -1);
    if (ret <= 0) {
        printf("poll error\n");
        continue;
    } else {
        if (test_poll.revents & POLLPRI) {
            if(read(fd, buf, 64)) {
                ...
            } else {
                ...
            }
        } else {
            ...
        }
    }
}

三、API分析

struct device *device_create_with_groups(struct class *class,
                     struct device *parent, dev_t devt,
                     void *drvdata,
                     const struct attribute_group **groups,
                     const char *fmt, ...)
|-->device_create_groups_vargs(class, parent, devt, drvdata, groups, fmt, vargs);
    |-->dev->groups = groups;
    |-->device_add(dev);
        |-->device_add_attrs(dev);
            |-->device_add_groups(dev, dev->groups);
                |-->sysfs_create_groups(&dev->kobj, groups); 熟悉的api
                    |-->internal_create_groups(kobj, 0, groups);
                        |-->internal_create_group(kobj, update, groups[i]);
                            |-->kernfs_create_dir_ns(kobj->sd, grp->name, S_IRWXU | S_IRUGO | S_IXUGO, uid, gid, kobj, NULL);
                            |-->create_files(kn, kobj, uid, gid, grp, update);
                                |--> sysfs_add_file_mode_ns(parent, *attr, false, mode, uid, gid, NULL);
 
sysfs_create_file(struct kobject *kobj, const struct attribute *attr)
|--> sysfs_create_file_ns(kobj, attr, NULL);
    |--> sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, uid, gid, ns);
创建属性文件的内部最后都是调用sysfs_add_file_mode_ns()函数
 
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
               const struct attribute *attr, bool is_bin,
               umode_t mode, kuid_t uid, kgid_t gid, const void *ns)
{
    struct lock_class_key *key = NULL;
    const struct kernfs_ops *ops;
    struct kernfs_node *kn;
    loff_t size;
 
    if (!is_bin) {
        struct kobject *kobj = parent->priv;
        const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
 
        /* every kobject with an attribute needs a ktype assigned */
        if (WARN(!sysfs_ops, KERN_ERR
             "missing sysfs attribute operations for kobject: %s\n",
             kobject_name(kobj)))
            return -EINVAL;
 
        if (sysfs_ops->show && sysfs_ops->store) {
            if (mode & SYSFS_PREALLOC)
                ops = &sysfs_prealloc_kfops_rw;
            else
                ops = &sysfs_file_kfops_rw; ############## 记住挂上来的结构体 struct kernfs_ops *ops = &sysfs_file_kfops_rw
        } else if (sysfs_ops->show) {
            if (mode & SYSFS_PREALLOC)
                ops = &sysfs_prealloc_kfops_ro;
            else
                ops = &sysfs_file_kfops_ro; ############## 记住挂上来的结构体 struct kernfs_ops *ops = &sysfs_file_kfops_ro
        } else if (sysfs_ops->store) {
            if (mode & SYSFS_PREALLOC)
                ops = &sysfs_prealloc_kfops_wo;
            else
                ops = &sysfs_file_kfops_wo;  ############## 记住挂上来的结构体 struct kernfs_ops *ops = &sysfs_file_kfops_wo
        } else
            ops = &sysfs_file_kfops_empty;
 
        size = PAGE_SIZE;
    } else {
        struct bin_attribute *battr = (void *)attr;
 
        if (battr->mmap)
            ops = &sysfs_bin_kfops_mmap;
        else if (battr->read && battr->write)
            ops = &sysfs_bin_kfops_rw;
        else if (battr->read)
            ops = &sysfs_bin_kfops_ro;
        else if (battr->write)
            ops = &sysfs_bin_kfops_wo;
        else
            ops = &sysfs_file_kfops_empty;
 
        size = battr->size;
    }
 
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    if (!attr->ignore_lockdep)
        key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endif
 
    kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
                  size, ops, (void *)attr, ns, key);
    if (IS_ERR(kn)) {
        if (PTR_ERR(kn) == -EEXIST)
            sysfs_warn_dup(parent, attr->name);
        return PTR_ERR(kn);
    }
    return 0;
}
---------------------
static const struct kernfs_ops sysfs_file_kfops_ro = {
    .seq_show   = sysfs_kf_seq_show,
};
 
static const struct kernfs_ops sysfs_file_kfops_wo = {
    .write      = sysfs_kf_write,
};
 
static const struct kernfs_ops sysfs_file_kfops_rw = {
    .seq_show   = sysfs_kf_seq_show,
    .write      = sysfs_kf_write,
};
---------------------
注意:上面的ops 是struct kernfs_ops *ops,不是struct file_operations,
大家都知道vfs_open、vfs_poll、vfs_read、vfs_write 等调用,最终都是调用到驱动的struct file_operations中挂载的函数
so,下面分析一下sysfs属性文件的struct file_operations 是怎么挂上来的

四、sysfs的mount

  1. init 进程mount sysfs文件系统

miui-r-venus-dev/system/core/init/main.cpp


int main(int argc, char** argv)
|-->FirstStageMain(argc, argv);
    |-->CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
  1. kernel 注册sysfs文件系统

start_kernel(void)      // main.c
|-->vfs_caches_init();
    |--> mnt_init();     // namespace.c
        |-->kernfs_init();
        |-->sysfs_init();    // 我们重点看这个 #########
        |-->init_rootfs();       // 创建rootfs文件系统
        |-->init_mount_tree();    // mount rootfs文件系统
 
int __init sysfs_init(void)
{
    int err;
    sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,NULL);
    sysfs_root_kn = sysfs_root->kn;
    err = register_filesystem(&sysfs_fs_type);
    return 0;
}
 
static struct file_system_type sysfs_fs_type = {
    .name           = "sysfs",
    .init_fs_context    = sysfs_init_fs_context,  ########## 在这个函数中将fc->ops = &sysfs_fs_context_ops;  挂载下面的fs_context_operations操作函数集
    .kill_sb        = sysfs_kill_sb,
    .fs_flags       = FS_USERNS_MOUNT,
};
 
static const struct fs_context_operations sysfs_fs_context_ops = {
    .free       = sysfs_fs_context_free,
    .get_tree   = sysfs_get_tree,  ##########
};
  1. mount sysfs文件系统

当init 进程mount sysfs的时候,驱动层会调用ksys_mount()

mount("sysfs", "/sys", "sysfs", 0, NULL);

// namespace.c (msm-5.4/fs)
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
        char __user *, type, unsigned long, flags, void __user *, data)
{
    return ksys_mount(dev_name, dir_name, type, flags, data);
}
ksys_mount(dev_name, dir_name, type, flags, data);
|-->do_mount(kernel_dev, dir_name, kernel_type, flags, options);
    |-->do_new_mount(&path, type_page, sb_flags, mnt_flags, dev_name, data_page);
        |--> type = get_fs_type(fstype); // 如struct file_system_type sysfs_fs_type
        |--> fc = fs_context_for_mount(type, sb_flags);
            |--> alloc_fs_context(fs_type, NULL, sb_flags, 0, FS_CONTEXT_FOR_MOUNT);
                |--> init_fs_context = fc->fs_type->init_fs_context;
                    |--> ret = init_fs_context(fc);  // .init_fs_context = sysfs_init_fs_context,
                         在这个函数中将fc->ops = &sysfs_fs_context_ops; 
                        |--> fc->ops = &sysfs_fs_context_ops; // 挂载fs_context_operations操作函数集
        |-->err = vfs_get_tree(fc);
            |--> error = fc->ops->get_tree(fc); // .get_tree   = sysfs_get_tree,  ######## 3.1小结 后面重点分析
        |-->err = do_new_mount_fc(fc, path, mnt_flags);
            |--> mnt = vfs_create_mount(fc);
            |--> error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags);
 
3.1小结
static int sysfs_get_tree(struct fs_context *fc)
{
    struct kernfs_fs_context *kfc = fc->fs_private;
    int ret;
 
    ret = kernfs_get_tree(fc);  ######## 3.2小结 下面分析
    if (ret)
        return ret;
 
    if (kfc->new_sb_created)
        fc->root->d_sb->s_iflags |= SB_I_USERNS_VISIBLE;
    return 0;
}
 
3.2小结
kernfs_get_tree(struct fs_context *fc)
|--> kernfs_fill_super(sb, kfc);
    |--> inode = kernfs_get_inode(sb, info->root->kn);
        |--> kernfs_init_inode(kn, inode);
 
static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
{
    kernfs_get(kn);
    inode->i_private = kn;
    inode->i_mapping->a_ops = &kernfs_aops;
    inode->i_op = &kernfs_iops;
    inode->i_generation = kn->id.generation;
 
    set_default_inode_attr(inode, kn->mode);
    kernfs_refresh_inode(kn, inode);
 
    /* initialize inode according to type */
    switch (kernfs_type(kn)) {
    case KERNFS_DIR:
        inode->i_op = &kernfs_dir_iops;
        inode->i_fop = &kernfs_dir_fops;
        if (kn->flags & KERNFS_EMPTY_DIR)
            make_empty_dir_inode(inode);
        break;
    case KERNFS_FILE:
        inode->i_size = kn->attr.size;
        inode->i_fop = &kernfs_file_fops;  ########### 终于找到sysfs 文件的struct file_operations操作函数集
        break;
    case KERNFS_LINK:
        inode->i_op = &kernfs_symlink_iops;
        break;
    default:
        BUG();
    }
 
    unlock_new_inode(inode);
}
 
const struct file_operations kernfs_file_fops = {
    .read       = kernfs_fop_read,  #######
    .write      = kernfs_fop_write, #######
    .llseek     = generic_file_llseek,
    .mmap       = kernfs_fop_mmap,
    .open       = kernfs_fop_open,  #######
    .release    = kernfs_fop_release,
    .poll       = kernfs_fop_poll,   #######
    .fsync      = noop_fsync,
};

五、应用程序read sysfs属性文件是如何调用到属性文件的show函数的

因为应用程序调用sysfs属性文件的read函数,所以肯定要调用到struct file_operations kernfs_file_fops中的.read = kernfs_fop_read,

  1. 先看看kernfs_fop_read函数

static ssize_t kernfs_fop_read(struct file *file, char __user *user_buf,
                   size_t count, loff_t *ppos)
{
    struct kernfs_open_file *of = kernfs_of(file);
 
    if (of->kn->flags & KERNFS_HAS_SEQ_SHOW)
        return seq_read(file, user_buf, count, ppos);  1.1 上面分析没有注意KERNFS_HAS_SEQ_SHOW,所有seq_read和kernfs_file_direct_read 分别在下面分析
    else
        return kernfs_file_direct_read(of, user_buf, count, ppos); 1.2 下面分析
}
 
1.1分析seq_read
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
|-->m->buf = seq_buf_alloc(m->size = PAGE_SIZE); / 这个地方需要很注意,sysfs属性文件拷贝数据到用户空间,一次只能拷贝一个PAGE_SIZE
|-->p = m->op->start(m, &m->index); // op是 struct seq_operations *op; 这个是怎么挂上来的呢??????????  猜测是open函数大家sysfs 属性文件节点的时候
|-->err = m->op->show(m, p); // 需要找到这个show函数是哪个????
还记得上面4、sysfs的mount中分析的结果吗:
const struct file_operations kernfs_file_fops = {
    .read       = kernfs_fop_read,
    .write      = kernfs_fop_write,
    .llseek     = generic_file_llseek,
    .mmap       = kernfs_fop_mmap,
    .open       = kernfs_fop_open,
    .release    = kernfs_fop_release,
    .poll       = kernfs_fop_poll,
    .fsync      = noop_fsync,
};
sysfs 的struct file_operations是 kernfs_file_fops,所有open的时候会调用.open = kernfs_fop_open,看一下kernfs_fop_open函数
static int kernfs_fop_open(struct inode *inode, struct file *file)
{
    if (ops->seq_show) ### 这个有定义吗??ops是 struct kernfs_ops *ops;
        error = seq_open(file, &kernfs_seq_ops);
还记得上面3、API分析中sysfs_add_file_mode_ns函数挂载上来的const struct kernfs_ops *ops吗?
截取关键部分:
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
               const struct attribute *attr, bool is_bin,
               umode_t mode, kuid_t uid, kgid_t gid, const void *ns)
{
...
    if (sysfs_ops->show && sysfs_ops->store) {
        ops = &sysfs_file_kfops_rw;
    } else if (sysfs_ops->show) {
        ops = &sysfs_file_kfops_ro;
    } else if (sysfs_ops->store) {
        ops = &sysfs_file_kfops_wo;
------------
static const struct kernfs_ops sysfs_file_kfops_rw = {
    .seq_show   = sysfs_kf_seq_show,
    .write      = sysfs_kf_write,
};
static const struct kernfs_ops sysfs_file_kfops_ro = {
    .seq_show   = sysfs_kf_seq_show,
};
-------------
也就是sysfs文件只要有read权限就会有seq_show,所有会调用到seq_open(file, &kernfs_seq_ops),将kernfs_seq_ops挂载上来
static const struct seq_operations kernfs_seq_ops = {
    .start = kernfs_seq_start,
    .next = kernfs_seq_next,
    .stop = kernfs_seq_stop,
    .show = kernfs_seq_show,
};
所以,上面:
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
|-->p = m->op->start(m, &m->index); // .start = kernfs_seq_start,
|-->err = m->op->show(m, p);   // .show = kernfs_seq_show,
 
static int kernfs_seq_show(struct seq_file *sf, void *v)
{
    struct kernfs_open_file *of = sf->private;
 
    of->event = atomic_read(&of->kn->attr.open->event);
 
    return of->kn->attr.ops->seq_show(sf, v); // sysfs_kf_seq_show
}
 
static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
{
...
    const struct sysfs_ops *ops = sysfs_file_ops(of->kn); // 这个函数在1.1小结已经分析
    if (ops->show) {
        count = ops->show(kobj, of->kn->priv, buf);看下面2、下面会重点分析sysfs_ops是如何挂上的
...
}
 
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
{
    struct kobject *kobj = kn->parent->priv;
 
    if (kn->flags & KERNFS_LOCKDEP)
        lockdep_assert_held(kn);
    return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;  //获取sysfs_ops, 大多数的设备都是dev_sysfs_ops, 在device_initialize中设定
}
#######################################
1.2 分析kernfs_file_direct_read
static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
                       char __user *user_buf, size_t count,
                       loff_t *ppos)
{
    ssize_t len = min_t(size_t, count, PAGE_SIZE);
        buf = kmalloc(len, GFP_KERNEL);  // 这个地方需要很注意,sysfs属性文件拷贝数据到用户空间,一次只能拷贝一个PAGE_SIZE
    ops = kernfs_ops(of->kn);
    if (ops->read)
        len = ops->read(of, buf, len, *ppos); //上面3、API分析 中的分析sysfs_add_file_mode_ns 现在派上用场
----------------
        if (sysfs_ops->show && sysfs_ops->store) {
            if (mode & SYSFS_PREALLOC)
                ops = &sysfs_prealloc_kfops_rw;
            else
                ops = &sysfs_file_kfops_rw;
----------------
220static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
221 .read       = sysfs_kf_read,
222 .write      = sysfs_kf_write,
223 .prealloc   = true,
224};
 
static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
                 size_t count, loff_t pos)
{
    const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
    struct kobject *kobj = of->kn->parent->priv;
    ssize_t len;
 
    /*
     * If buf != of->prealloc_buf, we don't know how
     * large it is, so cannot safely pass it to ->show
     */
    if (WARN_ON_ONCE(buf != of->prealloc_buf))
        return 0;
    len = ops->show(kobj, of->kn->priv, buf);
    if (len < 0)
        return len;
    if (pos) {
        if (len <= pos)
            return 0;
        len -= pos;
        memmove(buf, buf + pos, len);
    }
    return min_t(ssize_t, count, len);
}
  1. 下面分析sysfs_ops是如何挂上的

int device_register(struct device *dev)
{
    device_initialize(dev); // 调用 kobject_init(&dev->kobj, &device_ktype);
    return device_add(dev);
}
void device_initialize(struct device *dev)
{
    dev->kobj.kset = devices_kset;
    kobject_init(&dev->kobj, &device_ktype);
...
}
static struct kobj_type device_ktype = {
    .release    = device_release,
    .sysfs_ops  = &dev_sysfs_ops,  ########## 属性文件的sysfs_ops 操作函数集就是这样挂上来的,  2.1小结, 继续分析
    .namespace  = device_namespace,
    .get_ownership  = device_get_ownership,
};
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
    kobject_init_internal(kobj);
    kobj->ktype = ktype;  ####### kobj->ktype = &device_ktype  记住这个
    return;
..
}
2.1小结
static const struct sysfs_ops dev_sysfs_ops = {
    .show   = dev_attr_show,
    .store  = dev_attr_store,
};
 
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
                 char *buf)
{
    struct device_attribute *dev_attr = to_dev_attr(attr);
    struct device *dev = kobj_to_dev(kobj);
    ssize_t ret = -EIO;
 
    if (dev_attr->show)
        ret = dev_attr->show(dev, dev_attr, buf);  ##### 总于,调用到了我们static DEVICE_ATTR_RW(status)定义的show函数,store函数类似,就不分析了
 
    return ret;
}

六、验证上面的分析

最简单的方法,在属性文件的show和store函数中打印dump_stack()

如drm_sysfs.c (msm-5.4/drivers/gpu/drm)

static DEVICE_ATTR_RW(status);

static ssize_t status_store(struct device *device,struct device_attribute *attr,const char *buf, size_t count)

static ssize_t status_show(struct device *device, struct device_attribute *attr, char *buf)

分别打印dump_stack()看一下函数调用栈

cat /sys/class/drm/card0-DSI-1/status

[ 89.689473] Call trace:
[ 89.689494] dump_backtrace.cfi_jt+0x0/0x4
[ 89.689508] show_stack+0x18/0x24
[ 89.689522] dump_stack+0xe0/0x160
[ 89.689538] status_show+0x1c/0x70
[ 89.689561] dev_attr_show+0x48/0xa8
[ 89.689577] sysfs_kf_seq_show+0xb8/0x14c
[ 89.689585] kernfs_seq_show+0x48/0x84
[ 89.689607] seq_read+0x1c8/0x5b8
[ 89.689614] kernfs_fop_read+0x64/0x1cc
[ 89.689630] __vfs_read+0x60/0x204
[ 89.689636] vfs_read+0xbc/0x15c
[ 89.689643] ksys_read+0x78/0xe4
[ 89.689649] __arm64_sys_read+0x1c/0x28
[ 89.689661] el0_svc_common+0xb8/0x1a8
[ 89.689666] el0_svc_handler+0x74/0x98
[ 89.689674] el0_svc+0x8/0xc

echo on > /sys/class/drm/card0-DSI-1/status

[ 121.242235] Call trace:
[ 121.242263] dump_backtrace.cfi_jt+0x0/0x4
[ 121.242283] show_stack+0x18/0x24
[ 121.242302] dump_stack+0xe0/0x160
[ 121.242324] status_store+0x2c/0x180
[ 121.242344] dev_attr_store+0x34/0x80
[ 121.242363] sysfs_kf_write+0x60/0xb8
[ 121.242374] kernfs_fop_write+0x124/0x1b8
[ 121.242397] __vfs_write+0x60/0x20c
[ 121.242409] vfs_write+0xe4/0x1a8
[ 121.242422] ksys_write+0x78/0xe4
[ 121.242434] __arm64_sys_write+0x1c/0x28
[ 121.242449] el0_svc_common+0xb8/0x1a8
[ 121.242460] el0_svc_handler+0x74/0x98
[ 121.242478] el0_svc+0x8/0xc

dump_stack验证结果符合代码分析

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
`device_create_file` 和 `sysfs_create_file` 函数都可以用于在 sysfs 文件系统创建文件,但它们的使用场景略有不同。 `sysfs_create_file` 函数是一个通用的函数,它可以在 sysfs 文件系统的任意位置创建文件。该函数的原型如下: ```c int sysfs_create_file(struct kobject *kobj, const struct attribute *attr); ``` 其,`kobj` 参数是指向 `struct kobject` 结构体的指针,该结构体表示要在其下面创建文件的 sysfs 对象。`attr` 参数是指向 `struct attribute` 结构体的指针,该结构体描述了要创建的文件的属性。`sysfs_create_file` 函数会在 sysfs 文件系统创建一个与 `attr` 描述的属性相关联的文件,并将其与 `kobj` 参数指向的 sysfs 对象关联起来。 相比之下,`device_create_file` 函数更加专业化。它是针对 Linux 设备驱动程序的一种特殊机制,用于在与设备相关联的 sysfs 对象下创建文件。该函数的原型如下: ```c int device_create_file(struct device *dev, const struct attribute *attr); ``` 其,`dev` 参数是指向 `struct device` 结构体的指针,该结构体表示与设备相关联的设备对象。`attr` 参数是指向 `struct attribute` 结构体的指针,该结构体描述了要创建的文件的属性。`device_create_file` 函数会在 sysfs 文件系统创建一个与 `attr` 描述的属性相关联的文件,并将其与 `dev` 参数指向的设备对象关联起来。 因此,如果你需要在 sysfs 文件系统的任意位置创建文件,可以使用 `sysfs_create_file` 函数。而如果你需要在 Linux 设备驱动程序为设备创建文件,应该使用 `device_create_file` 函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liuzl_2010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值