lkml.org 
[lkml]   [2009]   [Oct]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH 2/3] Sysfs: Allow directories to be populated dynamically

    From: Matthew Wilcox <willy@linux.intel.com>
    Date: Mon, 19 Oct 2009 01:02:38 -0400
    Subject: [PATCH 2/3] Sysfs: Allow directories to be populated dynamically

    Use function pointers to populate and depopulate sysfs directories with
    dynamically files. Include one user, the attribute groups.
    ---
    fs/sysfs/dir.c | 49 ++++++++++++++++-------
    fs/sysfs/group.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
    fs/sysfs/sysfs.h | 5 ++
    3 files changed, 150 insertions(+), 19 deletions(-)

    diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
    index 5fad489..e32a11d 100644
    --- a/fs/sysfs/dir.c
    +++ b/fs/sysfs/dir.c
    @@ -294,9 +294,13 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
    goto repeat;
    }

    -static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
    +static void sysfs_d_iput(struct dentry *dentry, struct inode *inode)
    {
    - struct sysfs_dirent * sd = dentry->d_fsdata;
    + struct sysfs_dirent *sd = dentry->d_fsdata;
    +
    + if ((sysfs_type(sd) == SYSFS_DIR) && sd->s_dir.depopulate)
    + sd->s_dir.depopulate(dentry, sd);
    + sd->s_flags &= ~SYSFS_FLAG_POPULATED;

    sysfs_put(sd);
    iput(inode);
    @@ -574,6 +578,22 @@ repeat:
    iput(inode);
    }

    +void sysfs_kill_removed_dirents(struct sysfs_addrm_cxt *acxt)
    +{
    + /* kill removed sysfs_dirents */
    + while (acxt->removed) {
    + struct sysfs_dirent *sd = acxt->removed;
    +
    + acxt->removed = sd->s_sibling;
    + sd->s_sibling = NULL;
    +
    + sysfs_drop_dentry(sd);
    + sysfs_deactivate(sd);
    + unmap_bin_file(sd);
    + sysfs_put(sd);
    + }
    +}
    +
    /**
    * sysfs_addrm_finish - finish up sysfs_dirent add/remove
    * @acxt: addrm context to finish up
    @@ -600,18 +620,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
    iput(inode);
    }

    - /* kill removed sysfs_dirents */
    - while (acxt->removed) {
    - struct sysfs_dirent *sd = acxt->removed;
    -
    - acxt->removed = sd->s_sibling;
    - sd->s_sibling = NULL;
    -
    - sysfs_drop_dentry(sd);
    - sysfs_deactivate(sd);
    - unmap_bin_file(sd);
    - sysfs_put(sd);
    - }
    + sysfs_kill_removed_dirents(acxt);
    }

    /**
    @@ -731,6 +740,17 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,

    mutex_lock(&sysfs_mutex);

    + if (!(parent_sd->s_flags & SYSFS_FLAG_POPULATED)) {
    + if (parent_sd->s_dir.populate) {
    + int err = parent_sd->s_dir.populate(dentry->d_parent,
    + parent_sd);
    + if (err) {
    + ret = ERR_PTR(err);
    + goto out_unlock;
    + }
    + }
    + parent_sd->s_flags |= SYSFS_FLAG_POPULATED;
    + }
    sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);

    /* no such entry */
    @@ -1012,7 +1032,6 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
    return 0;
    }

    -
    const struct file_operations sysfs_dir_operations = {
    .read = generic_read_dir,
    .readdir = sysfs_readdir,
    diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
    index 0c4d342..37ac584 100644
    --- a/fs/sysfs/group.c
    +++ b/fs/sysfs/group.c
    @@ -16,6 +16,102 @@
    #include "sysfs.h"


    +/*
    + * i_mutex is not held, but this inode is on its way out of the system, so
    + * nobody gets to mess with it. We can't take the sysfs_mutex here as it
    + * leads to a deadlock scenario where it's held in a path that can run
    + * reclaim, and this function can be called from reclaim context. I guess
    + * prayer is the only solution here (other than splitting sysfs_mutex)
    + */
    +void sysfs_depopulate_group(struct dentry *dentry,
    + const struct attribute_group *grp)
    +
    +{
    + struct sysfs_dirent *dir_sd = dentry->d_fsdata;
    + struct sysfs_addrm_cxt acxt;
    + struct attribute **attrp;
    +
    + memset(&acxt, 0, sizeof(acxt));
    + acxt.parent_sd = dir_sd;
    + acxt.parent_inode = dentry->d_inode;
    +
    + for (attrp = grp->attrs; *attrp; attrp++) {
    + struct attribute *attr = *attrp;
    + struct sysfs_dirent *sd;
    +
    + sd = sysfs_find_dirent(dir_sd, attr->name);
    + if (sd)
    + sysfs_remove_one(&acxt, sd);
    + }
    +
    + sysfs_kill_removed_dirents(&acxt);
    +}
    +EXPORT_SYMBOL(sysfs_depopulate_group);
    +
    +/*
    + * inode->i_mutex is held by the VFS, and sysfs_mutex is held by
    + * sysfs_lookup, so there's no need to call sysfs_addrm_start/finish here.
    + * Nor is there a need to mess around with reference counts.
    + */
    +int sysfs_populate_group(struct dentry *dentry,
    + const struct attribute_group *grp)
    +
    +{
    + struct sysfs_dirent *dir_sd = dentry->d_fsdata;
    + struct kobject *kobj = dir_sd->s_dir.kobj;
    + struct sysfs_addrm_cxt acxt;
    + struct attribute **attrp;
    + int i = 0, error = 0;
    +
    + memset(&acxt, 0, sizeof(acxt));
    + acxt.parent_sd = dir_sd;
    + acxt.parent_inode = dentry->d_inode;
    +
    + for (attrp = grp->attrs; *attrp; attrp++) {
    + struct attribute *attr = *attrp;
    + mode_t mode = attr->mode;
    + struct sysfs_dirent *sd;
    +
    + if (grp->is_visible) {
    + mode_t vis = grp->is_visible(kobj, attr, i++);
    + if (!vis)
    + continue;
    + mode |= vis;
    + }
    +
    + sd = sysfs_new_dirent(attr->name, mode, SYSFS_KOBJ_ATTR);
    + if (!sd) {
    + error = -ENOMEM;
    + break;
    + }
    + sd->s_attr.attr = (void *)attr;
    +
    + error = sysfs_add_one(&acxt, sd);
    + if (error) {
    + sysfs_put(sd);
    + break;
    + }
    + }
    + if (error)
    + sysfs_depopulate_group(dentry, grp);
    + return error;
    +}
    +EXPORT_SYMBOL(sysfs_populate_group);
    +
    +static
    +int group_populate(struct dentry *dentry, struct sysfs_dirent *sd)
    +{
    + struct attribute_group *grp = sd->s_dir.data;
    + return sysfs_populate_group(dentry, grp);
    +}
    +
    +static
    +void group_depopulate(struct dentry *dentry, struct sysfs_dirent *sd)
    +{
    + struct attribute_group *grp = sd->s_dir.data;
    + sysfs_depopulate_group(dentry, grp);
    +}
    +
    static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
    const struct attribute_group *grp)
    {
    @@ -32,6 +128,10 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
    struct attribute *const* attr;
    int error = 0, i;

    + /* Directory isn't currently instantiated; nothing to do */
    + if (!(dir_sd->s_flags & SYSFS_FLAG_POPULATED))
    + return 0;
    +
    for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
    mode_t mode = 0;

    @@ -55,7 +155,6 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
    return error;
    }

    -
    static int internal_create_group(struct kobject *kobj, int update,
    const struct attribute_group *grp)
    {
    @@ -82,10 +181,18 @@ static int internal_create_group(struct kobject *kobj, int update,
    } else {
    sd = sysfs_get(kobj->sd);
    }
    - error = create_files(sd, kobj, grp, update);
    - if (error) {
    - if (grp->name)
    +
    + error = 0;
    + if (update) {
    + error = create_files(sd, kobj, grp, update);
    + if (error && grp->name)
    sysfs_remove_subdir(sd);
    + } else if (!sd->s_dir.populate) {
    + sd->s_dir.populate = group_populate;
    + sd->s_dir.depopulate = group_depopulate;
    + sd->s_dir.data = (void *)grp;
    + } else if (sd->s_dir.populate != group_populate) {
    + error = -EEXIST;
    }
    sysfs_put(sd);
    return error;
    diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
    index af4c4e7..13843fa 100644
    --- a/fs/sysfs/sysfs.h
    +++ b/fs/sysfs/sysfs.h
    @@ -17,6 +17,9 @@ struct sysfs_elem_dir {
    struct kobject *kobj;
    /* children list starts here and goes through sd->s_sibling */
    struct sysfs_dirent *children;
    + int (*populate)(struct dentry *, struct sysfs_dirent *);
    + void (*depopulate)(struct dentry *, struct sysfs_dirent *);
    + void *data;
    };

    struct sysfs_elem_symlink {
    @@ -77,6 +80,7 @@ struct sysfs_dirent {
    #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK)

    #define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK
    +#define SYSFS_FLAG_POPULATED 0x0100
    #define SYSFS_FLAG_REMOVED 0x0200

    static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
    @@ -120,6 +124,7 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
    int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
    void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
    void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt);
    +void sysfs_kill_removed_dirents(struct sysfs_addrm_cxt *acxt);

    struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
    const unsigned char *name);
    --
    1.6.3.3
    --
    Matthew Wilcox Intel Open Source Technology Centre
    "Bill, look, we understand that you're interested in selling us this
    operating system, but compare it to ours. We can't possibly take such
    a retrograde step."


    \
     
     \ /
      Last update: 2009-10-20 07:53    [W:0.033 / U:30.364 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site