Messages in this thread Patch in this message | | | Date | Thu, 21 Nov 2002 17:03:44 -0600 (CST) | From | Patrick Mochel <> | Subject | Re: [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");
| |