lkml.org 
[lkml]   [2011]   [May]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[v2 089/115] RFC: sysctl: change type of ctl_procfs_refs to u8
    Date
    255 files per registered header should be enough for everyone.
    If not, either:
    - register another header (and another, and another) each with max 255 files
    - change the type of ctl_procfs_refs to something bigger (e.g. u16)

    This patch makes two assumptions:

    - there will be at max a single inode created for each sysctl
    file. That means that the ctl_table_header will be (at max)
    incremented once for each of it's files. For directories the counter
    will be incremented only once (when creating an inode for the
    directory itself).

    - there are no sysctl tables in the kernel with more than 255 entries.

    Signed-off-by: Lucian Adrian Grijincu <lucian.grijincu@gmail.com>
    ---
    fs/proc/proc_sysctl.c | 12 +++++++++---
    include/linux/sysctl.h | 11 +++++++----
    kernel/sysctl.c | 10 +++++++++-
    kernel/sysctl_check.c | 12 ++++++++++++
    4 files changed, 37 insertions(+), 8 deletions(-)

    diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
    index b3e2453..9580794 100644
    --- a/fs/proc/proc_sysctl.c
    +++ b/fs/proc/proc_sysctl.c
    @@ -20,13 +20,15 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
    struct inode *inode;
    struct proc_inode *ei;

    + if (sysctl_proc_inode_get(head))
    + goto err_get;
    +
    inode = new_inode(sb);
    if (!inode)
    - goto out;
    + goto err_new_inode;

    inode->i_ino = get_next_ino();

    - sysctl_proc_inode_get(head);
    ei = PROC_I(inode);
    ei->sysctl = head;
    ei->sysctl_entry = table;
    @@ -44,8 +46,12 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
    inode->i_op = &proc_sys_dir_operations;
    inode->i_fop = &proc_sys_dir_file_operations;
    }
    -out:
    return inode;
    +
    +err_new_inode:
    + sysctl_proc_inode_put(head);
    +err_get:
    + return NULL;
    }

    static struct ctl_table *find_in_table(struct ctl_table *p, struct qstr *name)
    diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
    index 036d1aa..d5d9b66f 100644
    --- a/include/linux/sysctl.h
    +++ b/include/linux/sysctl.h
    @@ -947,7 +947,7 @@ extern void sysctl_init_group(struct ctl_table_group *group,

    /* get/put a reference to this header that
    * will be/was embedded in a procfs proc_inode */
    -extern void sysctl_proc_inode_get(struct ctl_table_header *);
    +extern int sysctl_proc_inode_get(struct ctl_table_header *);
    extern void sysctl_proc_inode_put(struct ctl_table_header *);

    extern int sysctl_is_seen(struct ctl_table_header *);
    @@ -1059,13 +1059,16 @@ struct ctl_table_header {
    /* references to this header from contexts that
    * can access fields of this header */
    int ctl_use_refs;
    - /* references to this header from procfs inodes.
    - * procfs embeds a pointer to the header in proc_inode */
    - int ctl_procfs_refs;
    /* counts references to this header from other
    * headers (through ->parent) plus the reference
    * returned by __register_sysctl_paths */
    int ctl_header_refs;
    + /* references to this header from procfs inodes.
    + * procfs embeds a pointer to the header in proc_inode.
    + * If there's at max one inode created per file then
    + * the max value of this is the number of files in the
    + * ctl_table array, or 1 for directories. */
    + u8 ctl_procfs_refs;
    };
    struct rcu_head rcu;
    };
    diff --git a/kernel/sysctl.c b/kernel/sysctl.c
    index 26c2bc6..3e30e78 100644
    --- a/kernel/sysctl.c
    +++ b/kernel/sysctl.c
    @@ -1546,11 +1546,19 @@ static void start_unregistering(struct ctl_table_header *p)
    }
    }

    -void sysctl_proc_inode_get(struct ctl_table_header *head)
    +int sysctl_proc_inode_get(struct ctl_table_header *head)
    {
    + int err = 0;
    spin_lock(&sysctl_lock);
    head->ctl_procfs_refs++;
    + if (unlikely(head->ctl_procfs_refs == 0)) {
    + /* restore old value */
    + head->ctl_procfs_refs--;
    + err = 1;
    + WARN(head->ctl_procfs_refs == 0, "sysctl: ctl_procfs_refs overflow");
    + }
    spin_unlock(&sysctl_lock);
    + return err;
    }

    static void free_head(struct rcu_head *rcu)
    diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c
    index b9573e0..205f721 100644
    --- a/kernel/sysctl_check.c
    +++ b/kernel/sysctl_check.c
    @@ -29,6 +29,8 @@ int sysctl_check_table(const struct ctl_path *path,
    struct ctl_table *table)
    {
    struct ctl_table *t;
    + unsigned int max_bits, max_files;
    + unsigned int nr_files = 0;
    int error = 0;

    if (nr_dirs > CTL_MAXNAME - 1) {
    @@ -37,6 +39,7 @@ int sysctl_check_table(const struct ctl_path *path,
    }

    for(t = table; t->procname; t++) {
    + nr_files ++;
    if ((t->proc_handler == proc_dostring) ||
    (t->proc_handler == proc_dointvec) ||
    (t->proc_handler == proc_dointvec_minmax) ||
    @@ -58,6 +61,15 @@ int sysctl_check_table(const struct ctl_path *path,
    FAIL("bogus .mode");
    }

    + /* make sure we can increment the header's ctl_procfs_refs
    + * counter for each file in the table. If this fails we either
    + * need to change the type of the ctl_procfs_refs variable, or
    + * register more tables in the same directory. */
    + max_bits = 8 * sizeof(((struct ctl_table_header *) 0)->ctl_procfs_refs);
    + max_files = 1 << max_bits;
    + if (nr_files >= max_files)
    + FAIL("too many files in registered table");
    +
    if (error)
    dump_stack();

    --
    1.7.5.134.g1c08b


    \
     
     \ /
      Last update: 2011-05-09 00:45    [W:0.025 / U:0.116 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site