lkml.org 
[lkml]   [2002]   [Nov]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: [BUG] sysfs on 2.5.48 unable to remove files while in use

    > Very cool. Bitkeeper is one of those things I never bothered
    > with yet (mainly because I feel some comfortable with CVS.)
    > It looks like it might be worth going through ramp-up time
    > on bk to keep up with changes to the kernel.

    There are also nightly snapshots, though I don't recall the URL off the
    top of my head..

    > > It seems that having a pure sysfs implementation would be superior,
    > > instead of having to use a character device to write to. After looking
    > > into this, I realize that a couple of pieces of infrastructure are needed,
    > > so I'm working on that, and will post a modified version of your module
    > > once I'm done..
    >
    > I look forward to seeing it.

    Ok, there are basically two parts to it: the required modifications to
    sysfs and your updated module.

    The appended patch adds the support to sysfs be defining struct
    subsys_attribute for declaring attributes of subsystems themselves, as
    well as the needed helpers for creation/teardown and read/write.

    I've attached a replacement for noisy.c that creates a sysfs file named
    'ctl' that handles addition and deletion of nprobes, similar to the char
    device you had created. From the top of the file:

    /*
    * Noisy Interface for Linux
    *
    * This driver allows arbitrary printk's to be inserted into
    * executing kernel code by using the new kprobes interface.
    * A message is attached to an address, and when that address
    * is reached, the message is printed.
    *
    * This uses a sysfs control file to manage a list of probes.
    * The sysfs directory is at
    *
    * /sys/noisy/
    *
    * and the control is named 'ctl'.
    *
    * A Noisy Probe can be added by echoing into the file, like:
    *
    * $ echo "add <address> <message>" > /sys/noisy/ctl
    *
    * where <address> is the address to break on, and <message>
    * is the message to print when the address is reached.
    *
    * Probes can be removed by doing:
    *
    * $ echo "del <address>" > /sys/noisy/ctl
    *
    * where <address> is the address of the probe.
    *
    * The probes themselves get a directory under /sys/noisy/, and
    * the name of the directory is the address of the probe. Each
    * probe directory contains one file ('message') that contains
    * the message to be printed. (More may be added later).
    */

    I've tried to comment the changes and the necessary steps in making this
    work.

    [ Note: While I'm generally happy with the way things work, I realize that
    it still requires a decent amount of overhead in using the sysfs interface
    (see the file). I'll be looking into shrinking this...]

    Everything seems to work..

    > It looks like the patch is against the bk tree, and does not apply cleanly
    > to
    > strait 2.5.48. I don't know how much has changed to sysfs/inode.c, but
    > I can see where the last hunk is looking too far up, so I'll try it anyway.

    Ah yes, I forgot there was a patch applied to sysfs since 2.5.48. I've
    included everything since 2.5.48 in the one I've appended.


    -pat

    ===== fs/sysfs/inode.c 1.60 vs 1.67 =====
    --- 1.60/fs/sysfs/inode.c Sat Nov 16 15:01:34 2002
    +++ 1.67/fs/sysfs/inode.c Thu Nov 21 17:01:53 2002
    @@ -23,6 +23,8 @@
    * Please see Documentation/filesystems/sysfs.txt for more information.
    */

    +#undef DEBUG
    +
    #include <linux/list.h>
    #include <linux/init.h>
    #include <linux/pagemap.h>
    @@ -87,16 +89,17 @@
    return inode;
    }

    -static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
    +static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
    {
    struct inode *inode;
    int error = 0;

    if (!dentry->d_inode) {
    inode = sysfs_get_inode(dir->i_sb, mode, dev);
    - if (inode)
    + if (inode) {
    d_instantiate(dentry, inode);
    - else
    + dget(dentry);
    + } else
    error = -ENOSPC;
    } else
    error = -EEXIST;
    @@ -142,17 +145,43 @@
    return error;
    }

    -static int sysfs_unlink(struct inode *dir, struct dentry *dentry)
    +#define to_subsys(k) container_of(k,struct subsystem,kobj)
    +#define to_sattr(a) container_of(a,struct subsys_attribute,attr)
    +
    +/**
    + * Subsystem file operations.
    + * These operations allow subsystems to have files that can be
    + * read/written.
    + */
    +ssize_t subsys_attr_show(struct kobject * kobj, struct attribute * attr,
    + char * page, size_t count, loff_t off)
    {
    - struct inode *inode = dentry->d_inode;
    - down(&inode->i_sem);
    - dentry->d_inode->i_nlink--;
    - up(&inode->i_sem);
    - d_invalidate(dentry);
    - dput(dentry);
    - return 0;
    + struct subsystem * s = to_subsys(kobj);
    + struct subsys_attribute * sattr = to_sattr(attr);
    + ssize_t ret = 0;
    +
    + if (sattr->show)
    + ret = sattr->show(s,page,count,off);
    + return ret;
    }

    +ssize_t subsys_attr_store(struct kobject * kobj, struct attribute * attr,
    + const char * page, size_t count, loff_t off)
    +{
    + struct subsystem * s = to_subsys(kobj);
    + struct subsys_attribute * sattr = to_sattr(attr);
    + ssize_t ret = 0;
    +
    + if (sattr->store)
    + ret = sattr->store(s,page,count,off);
    + return ret;
    +}
    +
    +static struct sysfs_ops subsys_sysfs_ops = {
    + .show = subsys_attr_show,
    + .store = subsys_attr_store,
    +};
    +
    /**
    * sysfs_read_file - read an attribute.
    * @file: file pointer.
    @@ -173,17 +202,11 @@
    sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
    {
    struct attribute * attr = file->f_dentry->d_fsdata;
    - struct sysfs_ops * ops = NULL;
    - struct kobject * kobj;
    + struct sysfs_ops * ops = file->private_data;
    + struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
    unsigned char *page;
    ssize_t retval = 0;

    - kobj = file->f_dentry->d_parent->d_fsdata;
    - if (kobj && kobj->subsys)
    - ops = kobj->subsys->sysfs_ops;
    - if (!ops || !ops->show)
    - return 0;
    -
    if (count > PAGE_SIZE)
    count = PAGE_SIZE;

    @@ -234,16 +257,11 @@
    sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
    {
    struct attribute * attr = file->f_dentry->d_fsdata;
    - struct sysfs_ops * ops = NULL;
    - struct kobject * kobj;
    + struct sysfs_ops * ops = file->private_data;
    + struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
    ssize_t retval = 0;
    char * page;

    - kobj = file->f_dentry->d_parent->d_fsdata;
    - if (kobj && kobj->subsys)
    - ops = kobj->subsys->sysfs_ops;
    - if (!ops || !ops->store)
    - return 0;

    page = (char *)__get_free_page(GFP_KERNEL);
    if (!page)
    @@ -275,21 +293,77 @@
    return retval;
    }

    -static int sysfs_open_file(struct inode * inode, struct file * filp)
    +static int check_perm(struct inode * inode, struct file * file)
    {
    - struct kobject * kobj;
    + struct kobject * kobj = kobject_get(file->f_dentry->d_parent->d_fsdata);
    + struct attribute * attr = file->f_dentry->d_fsdata;
    + struct sysfs_ops * ops = NULL;
    int error = 0;

    - kobj = filp->f_dentry->d_parent->d_fsdata;
    - if ((kobj = kobject_get(kobj))) {
    - struct attribute * attr = filp->f_dentry->d_fsdata;
    - if (!attr)
    - error = -EINVAL;
    - } else
    - error = -EINVAL;
    + if (!kobj || !attr)
    + goto Einval;
    +
    + /* if the kobject has no subsystem, then it is a subsystem itself,
    + * so give it the subsys_sysfs_ops.
    + */
    + if (kobj->subsys)
    + ops = kobj->subsys->sysfs_ops;
    + else
    + ops = &subsys_sysfs_ops;
    +
    + /* No sysfs operations, either from having no subsystem,
    + * or the subsystem have no operations.
    + */
    + if (!ops)
    + goto Eaccess;
    +
    + /* File needs write support.
    + * The inode's perms must say it's ok,
    + * and we must have a store method.
    + */
    + if (file->f_mode & FMODE_WRITE) {
    +
    + if (!(inode->i_mode & S_IWUGO))
    + goto Eperm;
    + if (!ops->store)
    + goto Eaccess;
    +
    + }
    +
    + /* File needs read support.
    + * The inode's perms must say it's ok, and we there
    + * must be a show method for it.
    + */
    + if (file->f_mode & FMODE_READ) {
    + if (!(inode->i_mode & S_IRUGO))
    + goto Eperm;
    + if (!ops->show)
    + goto Eaccess;
    + }
    +
    + /* No error? Great, store the ops in file->private_data
    + * for easy access in the read/write functions.
    + */
    + file->private_data = ops;
    + goto Done;
    +
    + Einval:
    + error = -EINVAL;
    + goto Done;
    + Eaccess:
    + error = -EACCES;
    + goto Done;
    + Eperm:
    + error = -EPERM;
    + Done:
    return error;
    }

    +static int sysfs_open_file(struct inode * inode, struct file * filp)
    +{
    + return check_perm(inode,filp);
    +}
    +
    static int sysfs_release(struct inode * inode, struct file * filp)
    {
    struct kobject * kobj = filp->f_dentry->d_parent->d_fsdata;
    @@ -541,7 +615,8 @@
    /* make sure dentry is really there */
    if (victim->d_inode &&
    (victim->d_parent->d_inode == dir->d_inode)) {
    - sysfs_unlink(dir->d_inode,victim);
    + simple_unlink(dir->d_inode,victim);
    + d_delete(victim);
    }
    }
    up(&dir->d_inode->i_sem);
    @@ -599,19 +674,16 @@
    list_for_each_safe(node,next,&dentry->d_subdirs) {
    struct dentry * d = list_entry(node,struct dentry,d_child);
    /* make sure dentry is still there */
    - if (d->d_inode)
    - sysfs_unlink(dentry->d_inode,d);
    + if (d->d_inode) {
    + simple_unlink(dentry->d_inode,d);
    + d_delete(dentry);
    + }
    }

    - d_invalidate(dentry);
    - if (simple_empty(dentry)) {
    - dentry->d_inode->i_nlink -= 2;
    - dentry->d_inode->i_flags |= S_DEAD;
    - parent->d_inode->i_nlink--;
    - }
    up(&dentry->d_inode->i_sem);
    - dput(dentry);
    -
    + d_invalidate(dentry);
    + simple_rmdir(parent->d_inode,dentry);
    + d_delete(dentry);
    up(&parent->d_inode->i_sem);
    dput(parent);
    }
    @@ -622,4 +694,3 @@
    EXPORT_SYMBOL(sysfs_remove_link);
    EXPORT_SYMBOL(sysfs_create_dir);
    EXPORT_SYMBOL(sysfs_remove_dir);
    -MODULE_LICENSE("GPL");/*
    * Noisy Interface for Linux
    *
    * This driver allows arbitrary printk's to be inserted into
    * executing kernel code by using the new kprobes interface.
    * A message is attached to an address, and when that address
    * is reached, the message is printed.
    *
    * This uses a sysfs control file to manage a list of probes.
    * The sysfs directory is at
    *
    * /sys/noisy/
    *
    * and the control is named 'ctl'.
    *
    * A Noisy Probe can be added by echoing into the file, like:
    *
    * $ echo "add <address> <message>" > /sys/noisy/ctl
    *
    * where <address> is the address to break on, and <message>
    * is the message to print when the address is reached.
    *
    * Probes can be removed by doing:
    *
    * $ echo "del <address>" > /sys/noisy/ctl
    *
    * where <address> is the address of the probe.
    *
    * The probes themselves get a directory under /sys/noisy/, and
    * the name of the directory is the address of the probe. Each
    * probe directory contains one file ('message') that contains
    * the message to be printed. (More may be added later).
    *
    * Copyright (C) 2002 Rusty Lynch <rusty@linux.intel.com>
    */

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/kprobes.h>
    #include <linux/slab.h>
    #include <linux/kobject.h>

    /* exported by arch/YOURARCH/kernel/traps.c */
    extern int valid_kernel_address(unsigned long);

    #define MAX_MSG_SIZE 128

    #define to_nprobe(entry) container_of(entry,struct nprobe,kobj.entry);

    static DECLARE_MUTEX(noisy_sem);
    static struct subsystem noisy_subsys;

    /*
    * struct nrpobe: data structure for managing list of probe points
    */
    struct nprobe {
    struct kprobe probe;
    char message[MAX_MSG_SIZE + 1];
    struct kobject kobj;
    };

    /*
    * Probe handlers.
    * Only one is used (pre) to print the message out.
    */
    static void noisy_pre_handler(struct kprobe *p, struct pt_regs *r)
    {
    struct nprobe *c = container_of(p, struct nprobe, probe);
    printk(KERN_CRIT "%s: %s\n", __FUNCTION__, c->message);
    }

    static void noisy_post_handler(struct kprobe *p, struct pt_regs *r,
    unsigned long flags)
    { }

    static int noisy_fault_handler(struct kprobe *p, struct pt_regs *r, int trapnr)
    {
    /* Let the kernel handle this fault */
    return 0;
    }


    /*
    * struct noisy_attribute - used for defining probe attributes, with a
    * typesafe show method.
    */
    struct noisy_attribute {
    struct attribute attr;
    ssize_t (*show)(struct nprobe *,char *,size_t,loff_t);
    };


    /**
    * noisy_attr_show - forward sysfs read call to proper handler.
    * @kobj: kobject of probe being acessed.
    * @attr: generic attribute portion of struct noisy_attribute.
    * @page: buffer to write into.
    * @count: number of bytes requested.
    * @off: offset into buffer.
    *
    * This is called from sysfs and is necessary to convert the generic
    * kobject into the right type, and to convert the attribute into the
    * right attribute type.
    */

    static ssize_t noisy_attr_show(struct kobject * kobj, struct attribute * attr,
    char * page, size_t count, loff_t off)
    {
    struct nprobe * n = container_of(kobj,struct nprobe,kobj);
    struct noisy_attribute * noisy_attr = container_of(attr,struct noisy_attribute,attr);
    return noisy_attr->show ? noisy_attr->show(n,page,count,off) : 0;
    }

    /*
    * noisy_sysfs_ops - sysfs operations for struct nprobes.
    */
    static struct sysfs_ops noisy_sysfs_ops = {
    .show = noisy_attr_show,
    };


    /* Default Attribute - the message to print out. */
    static ssize_t noisy_message_read(struct nprobe * n, char * page, size_t count, loff_t off)
    {
    return off ? 0: snprintf(page,MAX_MSG_SIZE,"%s\n",n->message);
    }

    static struct noisy_attribute attr_message = {
    .attr = { .name = "message", .mode = S_IRUGO },
    .show = noisy_message_read,
    };

    /* Declare array of default attributes to be added when an nprobe is added */
    static struct attribute * default_attrs[] = {
    &attr_message.attr,
    NULL,
    };


    /* Declare noisy_subsys for addition to sysfs */
    static struct subsystem noisy_subsys = {
    .kobj = { .name = "noisy" },
    .default_attrs = default_attrs,
    .sysfs_ops = &noisy_sysfs_ops,
    };


    /*
    * noisy ctl attribute.
    * This is declared as an attribute of the subsystem, and added in
    * noisy_init().
    *
    * Reading this attribute dumps all the registered noisy probes.
    * Writing to it either adds or deletes a noisy probe, as described at
    * the beginning of the file.
    */
    static ssize_t ctl_show(struct subsystem * s, char * page, size_t count, loff_t off)
    {
    char * str = page;
    int ret = 0;

    down(&noisy_sem);
    if (!off) {
    struct list_head * entry, * next;
    list_for_each_safe(entry,next,&noisy_subsys.list) {
    struct nprobe * n = to_nprobe(entry);
    if ((ret + MAX_MSG_SIZE) > PAGE_SIZE)
    break;
    str += snprintf(str,PAGE_SIZE - ret,"%p: %s\n",
    n->probe.addr,n->message);
    ret = str - page;
    }
    }
    up(&noisy_sem);
    return ret;
    }

    static int add(unsigned long addr, char * message)
    {
    struct nprobe * n;
    int error = 0;

    if (!valid_kernel_address(addr))
    return -EFAULT;

    n = kmalloc(sizeof(struct nprobe),GFP_KERNEL);
    if (!n)
    return -ENOMEM;
    memset(n,0,sizeof(struct nprobe));

    n->probe.addr = (kprobe_opcode_t *)addr;
    strncpy(n->message,message,MAX_MSG_SIZE);
    n->probe.pre_handler = noisy_pre_handler;
    n->probe.post_handler = noisy_post_handler;
    n->probe.fault_handler = noisy_fault_handler;

    /* Doing this manually will be unnecessary soon. */
    kobject_init(&n->kobj);
    n->kobj.subsys = &noisy_subsys;
    snprintf(n->kobj.name, KOBJ_NAME_LEN, "%p", n->probe.addr);

    if ((error = register_kprobe(&n->probe))) {
    printk(KERN_ERR "Unable to register probe at %p\n",
    (n->probe).addr);
    goto Error;
    }
    if ((error = kobject_register(&n->kobj))) {
    unregister_kprobe(&n->probe);
    goto Error;
    }
    goto Done;
    Error:
    kfree(n);
    Done:
    return error;
    }

    static int del(unsigned long addr)
    {
    struct list_head * entry;
    struct nprobe * n;

    list_for_each(entry,&noisy_subsys.list) {
    n = to_nprobe(entry);
    if ((unsigned long)(n->probe.addr) == addr) {
    kobject_unregister(&n->kobj);
    unregister_kprobe(&n->probe);
    return 0;
    }
    }
    return -EFAULT;
    }

    static ssize_t ctl_store(struct subsystem * s, const char * page, size_t count, loff_t off)
    {
    char message[MAX_MSG_SIZE];
    char ctl[16];
    unsigned long addr;
    int num;
    int error;
    int ret = 0;

    down(&noisy_sem);
    if (off)
    goto Done;
    num = sscanf(page,"%15s 0x%lx %128s",ctl,&addr,message);
    if (!num) {
    error = -EINVAL;
    goto Done;
    }
    if (!strcmp(ctl,"add") && num == 3)
    error = add(addr,message);
    else if (!strcmp(ctl,"del") && num == 2)
    error = del(addr);
    else
    error = -EINVAL;
    ret = error ? error : count;
    Done:
    up(&noisy_sem);
    return ret;
    }

    static struct subsys_attribute subsys_attr_ctl = {
    .attr = { .name = "ctl", .mode = 0644 },
    .show = ctl_show,
    .store = ctl_store,
    };


    static int __init noisy_init(void)
    {
    subsystem_register(&noisy_subsys);
    subsys_create_file(&noisy_subsys,&subsys_attr_ctl);
    return 0;
    }

    static void __exit noisy_exit (void)
    {
    subsys_remove_file(&noisy_subsys,&subsys_attr_ctl);
    subsystem_unregister(&noisy_subsys);
    }

    module_init(noisy_init);
    module_exit(noisy_exit);

    MODULE_AUTHOR("Rusty Lynch");
    MODULE_LICENSE("GPL");
    \
     
     \ /
      Last update: 2005-03-22 13:31    [W:0.062 / U:2.504 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site