Re: RFC/Patch - Implode devfs

Adam J. Richter (adam@yggdrasil.com)
Tue, 31 Dec 2002 23:08:14 -0800


--G4iJoqBmSsgzjUCe
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

I have made a new version of my mini-devfs (attached below).
I have also made a first version of my devfs_helper program to handle
the functionality of devfsd on systems that use my mini-devfs.
devfs_helper is avaiable from the following location:

ftp://ftp.yggdrasil.com/pub/dist/device_control/devfs_helper/devfs_helper-0.1.tar.gz

This version of devfs_helper only supports "EXECUTE" and
"MODLOAD" actions, which may be the only events that are really
necessary. In the future, I envision eliminating the "MODLOAD"
action, since it is equivalent to
"EXECUTE modprobe -C /etc/modprobe.devfs $devname".

devfs_helper is currently 211 lines of C code. In comparison,
devfsd-1.3.25/*.[ch] is 3143 lines.

Also, here is version 2 of my mini-devfs. I am embarassed
to say that I omitted Richard Gooch's copyright notice on his code that
I copoied into mini-devfs/numspace.c. This patch corrects that and
fixes a variety of other little problems. It also changes the directory
of this facility to fs/mini-devfs/. One change that is necessary for
operation with devfs_helper is that the event names are now in all
capitals ("REGISTER" and "LOOKUP") to match the format of /etc/devfsd.conf.

mini-devfs has grown by 21 lines, mostly due to my restoring
Richard's copyright notice. Here is an updated size comparison in case
anyone is interested:

devfs 3614 lines 25,863 bytes
mini-devfs 650 lines 5,562 bytes
Reduction 5.6x 4.6x

-- 
Adam J. Richter     __     ______________   575 Oroville Road
adam@yggdrasil.com     \ /                  Milpitas, California 95035
+1 408 309-6081         | g g d r a s i l   United States of America
                         "Free Software For The Rest Of Us."

--G4iJoqBmSsgzjUCe Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="minidevfs2.diff"

--- linux-2.5.53/include/linux/devfs_fs_kernel.h 2002-12-23 21:21:01.000000000 -0800 +++ linux/include/linux/devfs_fs_kernel.h 2002-12-31 18:45:59.000000000 -0800 @@ -24,7 +24,11 @@ #define DEVFS_SPECIAL_CHR 0 #define DEVFS_SPECIAL_BLK 1 +#ifdef CONFIG_DEVFS_SMALL +typedef struct dentry * devfs_handle_t; +#else typedef struct devfs_entry * devfs_handle_t; +#endif #ifdef CONFIG_DEVFS_FS --- linux-2.5.53/fs/namei.c 2002-12-23 21:19:59.000000000 -0800 +++ linux/fs/namei.c 2002-12-31 19:07:06.000000000 -0800 @@ -1377,9 +1377,9 @@ } /* SMP-safe */ -static struct dentry *lookup_create(struct nameidata *nd, int is_dir) +struct dentry *lookup_create(struct nameidata *nd, int is_dir) { struct dentry *dentry; --- linux-2.5.53/fs/Kconfig 2002-12-23 21:20:32.000000000 -0800 +++ linux/fs/Kconfig 2002-12-31 18:45:59.000000000 -0800 @@ -847,7 +847,21 @@ If unsure, say N. +config DEVFS_SMALL + bool "Smaller /dev file system (EXPERIMENTAL)" + depends on EXPERIMENTAL && DEVFS_FS + ---help--- + New smaller devfs without automatic partition rereading + (which impeded user level partition handling) and currently + without some features used by arch/ia64/sn. + + If unsure, say N. + +config DEVFS_BIG + bool + depends on DEVFS_FS && !DEVFS_SMALL + config DEVFS_MOUNT bool "Automatically mount at boot" depends on DEVFS_FS --- linux-2.5.53/fs/Makefile 2002-12-23 21:20:32.000000000 -0800 +++ linux/fs/Makefile 2002-12-31 18:45:59.000000000 -0800 @@ -65,7 +65,8 @@ obj-$(CONFIG_VFAT_FS) += vfat/ obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_ISO9660_FS) += isofs/ -obj-$(CONFIG_DEVFS_FS) += devfs/ +obj-$(CONFIG_DEVFS_BIG) += devfs/ +obj-$(CONFIG_DEVFS_SMALL) += mini-devfs/ obj-$(CONFIG_HFS_FS) += hfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ --- linux-2.5.53/fs/mini-devfs/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux/fs/mini-devfs/Makefile 2002-12-31 18:45:59.000000000 -0800 @@ -0,0 +1,9 @@ +# +# Makefile for the linux ramfs routines. +# + +export-objs := inode.o numspace.o + +obj-$(CONFIG_RAMFS) += ramfs.o + +ramfs-objs := inode.o numspace.o --- linux-2.5.53/fs/mini-devfs/inode.c 1969-12-31 16:00:00.000000000 -0800 +++ linux/fs/mini-devfs/inode.c 2002-12-31 22:28:02.000000000 -0800 @@ -0,0 +1,550 @@ +/* + * Stripped down Device File System (devfs2) + * Adapted from ramfs by Adam J. Richter. Ramfs was written by + * Linus Torvalds. + * + * Copyright (C) 2000 Linus Torvalds. + * 2000 Transmeta Corp. + * 2002 Yggdrasil Computing, Inc. + * + * Usage limits added to ramfs by David Gibson, Linuxcare Australia. + * This file is released under the GPL. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/fs.h> +#include <linux/pagemap.h> +#include <linux/highmem.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/smp_lock.h> +#include <linux/backing-dev.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/namei.h> +#include <linux/mount.h> + +#include <asm/uaccess.h> +#include <asm/string.h> + +/* For now, must be the same as devfs magic to appease glibc. See + __posix_openpt in glibc-2.3/sysdeps/unix/sysv/linux/getpt.c + TODO(?): Adopt a new magic number and adjust glibc? */ +#define DEVFS2_MAGIC DEVFS_SUPER_MAGIC + + +/* TODO: Move this to some .h file or, more likely, use a slightly + different interface from lookup_create. */ +extern struct dentry *lookup_create(struct nameidata *nd, int is_dir); + +static struct super_operations devfs2_ops; +static struct address_space_operations devfs2_aops; +static struct file_operations devfs2_file_operations; +static struct inode_operations devfs2_dir_inode_operations; +struct vfsmount *devfs2_vfsmount; + +static char devfs_helper[150] = "/sbin/devfs_helper"; +module_param_string(devfs_helper, devfs_helper, sizeof(devfs_helper), 0644); + +static struct backing_dev_info devfs2_backing_dev_info = { + .ra_pages = 0, /* No readahead */ + .memory_backed = 1, /* Does not contribute to dirty memory */ +}; + +static void devfs_event(const char *event, struct dentry *dentry) +{ + char path[64]; + + if (devfs_generate_path(dentry, path, sizeof(path)) == 0) { + const char *argv[] = { devfs_helper, event, path, NULL }; + static char *envp[] = {"PATH=/bin:/sbin:/usr/bin:/usr/sbin", + "HOME=/", NULL }; + + /* FIXME: Change the call_usermodehelper prototype so + that argv and envp are type const so we won't have + to cast the type of argv. */ + call_usermodehelper(devfs_helper, (char**) argv, envp); + } else + BUG(); +} + +static struct inode *devfs2_get_inode(struct super_block *sb, int mode, dev_t dev) +{ + struct inode * inode = new_inode(sb); + + if (!inode) + return NULL; + + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_rdev = NODEV; + inode->i_mapping->a_ops = &devfs2_aops; + inode->i_mapping->backing_dev_info = &devfs2_backing_dev_info; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + + switch (mode & S_IFMT) { + default: + init_special_inode(inode, mode, dev); + break; + case S_IFREG: + inode->i_fop = &devfs2_file_operations; + break; + case S_IFDIR: + inode->i_op = &devfs2_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; + break; + case S_IFLNK: + inode->i_op = &page_symlink_inode_operations; + break; + } + + return inode; +} + +static struct dentry * devfs2_lookup(struct inode *inode, struct dentry *dentry) +{ + // AJR commented out temporarily to address bug. -- try not AJR + devfs_event("LOOKUP", dentry); + return simple_lookup(inode, dentry); +} + + +/* + * File creation. Allocate an inode, and we're done.. + */ +/* SMP-safe */ +static int +devfs2_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) +{ + struct inode * inode = devfs2_get_inode(dir->i_sb, mode, dev); + int error = -ENOSPC; + + if (inode) { + d_instantiate(dentry, inode); + dget(dentry); /* Extra count - pin the dentry in core */ + error = 0; + devfs_event("REGISTER", dentry); + } + + return error; +} + +static int devfs2_mkdir(struct inode * dir, struct dentry * dentry, int mode) +{ + int retval = devfs2_mknod(dir, dentry, mode | S_IFDIR, 0); + if (!retval) + dir->i_nlink++; + return retval; +} + +static int devfs2_create(struct inode *dir, struct dentry *dentry, int mode) +{ + return devfs2_mknod(dir, dentry, mode | S_IFREG, 0); +} + + +static int devfs2_symlink(struct inode * dir, struct dentry *dentry, const char * symname) +{ + struct inode *inode; + int error = -ENOSPC; + + inode = devfs2_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0); + if (inode) { + int l = strlen(symname)+1; + error = page_symlink(inode, symname, l); + if (!error) { + d_instantiate(dentry, inode); + dget(dentry); + } else + iput(inode); + } + return error; +} + +static struct address_space_operations devfs2_aops = { + .readpage = simple_readpage, + .prepare_write = simple_prepare_write, + .commit_write = simple_commit_write +}; + +static struct file_operations devfs2_file_operations = { + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .fsync = simple_sync_file, + .sendfile = generic_file_sendfile, +}; + +static struct inode_operations devfs2_dir_inode_operations = { + .create = devfs2_create, + .lookup = devfs2_lookup, + .link = simple_link, + .unlink = simple_unlink, + .symlink = devfs2_symlink, + .mkdir = devfs2_mkdir, + .rmdir = simple_rmdir, + .mknod = devfs2_mknod, + .rename = simple_rename, +}; + +static struct super_operations devfs2_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, +}; + +static int devfs2_fill_super(struct super_block * sb, void * data, int silent) +{ + struct inode *inode; + struct dentry *root; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = DEVFS2_MAGIC; + sb->s_op = &devfs2_ops; + inode = devfs2_get_inode(sb, S_IFDIR | 0755, 0); + if (!inode) + return -ENOMEM; + + root = d_alloc_root(inode); + if (!root) { + iput(inode); + return -ENOMEM; + } + sb->s_root = root; + return 0; +} + +static struct super_block *devfs2_get_sb(struct file_system_type *fs_type, + int flags, char *dev_name, void *data) +{ + return get_sb_single(fs_type, flags, data, devfs2_fill_super); +} + +static struct file_system_type devfs2_fs_type = { + .name = "devfs", + .get_sb = devfs2_get_sb, + .kill_sb = kill_litter_super, +}; + +static int __init init_devfs2_fs(void) +{ + int err; + + if (devfs2_vfsmount != NULL) + return 0; + + err = register_filesystem(&devfs2_fs_type); + if (err) + return err; + + devfs2_vfsmount = kern_mount(&devfs2_fs_type); + + if (IS_ERR(devfs2_vfsmount)) { + unregister_filesystem(&devfs2_fs_type); + return PTR_ERR(devfs2_vfsmount); + } + else + return 0; +} + +static void __exit exit_devfs2_fs(void) +{ + unregister_filesystem(&devfs2_fs_type); +} + +module_init(init_devfs2_fs) +module_exit(exit_devfs2_fs) + +MODULE_LICENSE("GPL"); + +/* Called and returns with dcache_lock held. */ +static int walk_parents_mkdir(const char **path, struct nameidata *nd, + int is_dir) +{ + char *slash; + char buf[strlen(*path)+1]; + int err; + + while ((slash = strchr(*path, '/')) != NULL) { + int len = slash - *path; + memcpy(buf, *path, len); + buf[len] = '\0'; + + spin_lock(&dcache_lock); + err = link_path_walk(buf, nd); /* releases dcache_lock */ + + BUG_ON(err); /* AJR */ + if (err) + return err; + + nd->dentry = lookup_create(nd, is_dir); + + if (IS_ERR(nd->dentry)) { + BUG(); + return PTR_ERR(nd->dentry); + } + + if (!nd->dentry->d_inode) + err = vfs_mkdir(nd->dentry->d_parent->d_inode, + nd->dentry, 0644); + + up(&nd->dentry->d_parent->d_inode->i_sem); + + if (err) { + BUG_ON(err); /* AJR */ + if (err) + return err; + } + + *path += len + 1; + } + return 0; +} + +/* On success, returns with (*parent_inode)->i_sem taken. */ +static int devfs_decode(devfs_handle_t dir, const char *name, int is_dir, + struct inode **parent_inode, struct dentry **dentry) +{ + struct nameidata nd; + int err; + + if (dir == NULL) { + if (devfs2_vfsmount == NULL && init_devfs2_fs() != 0) + BUG(); + + dir = devfs2_vfsmount->mnt_sb->s_root; + } + + memset(&nd, 0, sizeof(nd)); + nd.flags = LOOKUP_PARENT; + /* nd.mnt = current->fs->rootmnt; FIXME? */ + nd.mnt = devfs2_vfsmount; + nd.dentry = dir; + + err = walk_parents_mkdir(&name, &nd, is_dir); + if (err) + return err; + + spin_lock(&dcache_lock); + err = link_path_walk(name, &nd); + if (err) { + printk ("AJR devfs_decode: link_path_walk(name %s, &nd) got error %d.\n", name, err); + BUG(); + return err; + } + + *dentry = lookup_create(&nd, is_dir); + + BUG_ON(IS_ERR(*dentry)); /* AJR */ + if (IS_ERR(*dentry)) + return PTR_ERR(*dentry); + else { + *parent_inode = (*dentry)->d_parent->d_inode; + return 0; + } +} + + +devfs_handle_t devfs_register (devfs_handle_t dir, + const char *name, + unsigned int flags, + unsigned int major, + unsigned int minor, + umode_t mode, + void *ops, void *info) +{ + struct inode *parent_inode; + struct dentry *dentry; + int err = devfs_decode(dir, name, 0, &parent_inode, &dentry); + + if (!err) { + err = vfs_mknod(parent_inode, dentry, mode, + MKDEV(major, minor)); + up(&parent_inode->i_sem); + if (err) { + printk ("AJR devfs_register(dir %p, name %s, flags 0x%x,\n\tmajor %d, minor %d, mode 0%o, ops %p, info %p)\n\tfailed, err %d.\n", + dir, name, flags, major, minor, mode, ops, info, err); + BUG_ON(err != -EEXIST); /* AJR */ + dput(dentry); + } + } + + return err ? NULL : dentry; + +} +EXPORT_SYMBOL(devfs_register); + +void devfs_unregister (devfs_handle_t de) +{ + if (S_ISDIR(de->d_inode->i_mode)) + vfs_rmdir(de->d_parent->d_inode, de); + else + vfs_unlink(de->d_parent->d_inode, de); +} +EXPORT_SYMBOL(devfs_unregister); + +void devfs_put (devfs_handle_t de) +{ + dput(de); +} +EXPORT_SYMBOL(devfs_put); + +int devfs_mk_symlink (devfs_handle_t dir, const char *name, + unsigned int flags, const char *link, + devfs_handle_t *handle, void *info) +{ + struct inode *parent_inode; + struct dentry *dentry; + int err = devfs_decode(dir, name, 0, &parent_inode, &dentry); + + if (!err) { + err = vfs_symlink(parent_inode, dentry, link); + up(&parent_inode->i_sem); + } + + dput(dentry); + return err; +} +EXPORT_SYMBOL(devfs_mk_symlink); + +devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, void *info) +{ + struct inode *parent_inode; + struct dentry *dentry; + int err = devfs_decode(dir, name, 1, &parent_inode, &dentry); + + if (!err) { + err = vfs_mkdir(parent_inode, dentry, 0644); + up(&parent_inode->i_sem); + if (err) { + BUG_ON (err != -EEXIST); /* AJR */ + dput(dentry); + } + } + else + BUG(); /* AJR */ + return err ? NULL : dentry; +} +EXPORT_SYMBOL(devfs_mk_dir); + +/* From fs/devfs/base.c: */ +void devfs_remove(const char *fmt, ...) +{ + char buf[64]; + va_list args; + int len; + struct nameidata nd; + int err; + + va_start(args, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, args); + if (len >= sizeof(buf)) { + BUG(); + return; + } + + buf[sizeof(buf)-1] = '\0'; + + memset(&nd, 0, sizeof(nd)); + nd.mnt = devfs2_vfsmount; + nd.dentry = devfs2_vfsmount->mnt_sb->s_root; + + spin_lock(&dcache_lock); + err = link_path_walk(buf, &nd); + if (!err) { + devfs_unregister(nd.dentry); + devfs_put(nd.dentry); + } + else + printk ("AJR devfs_remove: link_path_walk(buf %s, len %d) " + "returned err %d.\n", buf, len, err); + +} + +EXPORT_SYMBOL(devfs_remove); + +int devfs_only(void) +{ + return 0; +} + +void __init mount_devfs_fs (void) +{ +#ifdef CONFIG_DEVFS_MOUNT + int err; + + err = do_mount ("none", "/dev", "devfs", 0, ""); + if (err == 0) printk (KERN_INFO "Mounted devfs on /dev\n"); + else printk ("(): unable to mount devfs2, err: %d\n", err); +#endif +} /* End Function mount_devfs_fs */ + + +static int path_len (struct dentry *de, struct dentry *root) +{ + int len = 0; + while (de != root) { + len += de->d_name.len + 1; /* count the '/' */ + de = de->d_parent; + } + return len; /* -1 because we omit the leading '/', + +1 because we include trailing '\0' */ +} + +int devfs_generate_path (devfs_handle_t de, char *path, int buflen) +{ + struct dentry *devfs_root; + int len; + char *path_orig = path; + + if (de == NULL) { + BUG(); + return -EINVAL; + } + + devfs_root = devfs2_vfsmount->mnt_sb->s_root; + + if (de == devfs_root) { + BUG(); + return -EINVAL; + } + + spin_lock(&dcache_lock); + len = path_len(de, devfs_root); + if (len > buflen) { + spin_unlock(&dcache_lock); + BUG(); + return -ENAMETOOLONG; + } + + path += len - 1; + *path = '\0'; + + for (;;) { + path -= de->d_name.len; + memcpy(path, de->d_name.name, de->d_name.len); + de = de->d_parent; + if (de == devfs_root) + break; + *(--path) = '/'; + } + + spin_unlock(&dcache_lock); + + BUG_ON(path != path_orig); + + return 0; +} + +int devfs_set_file_size (devfs_handle_t de, unsigned long size) +{ + /* STUB */ + BUG(); + return -1; +} --- linux-2.5.53/fs/mini-devfs/numspace.c 1969-12-31 16:00:00.000000000 -0800 +++ linux/fs/mini-devfs/numspace.c 2002-12-31 22:39:35.000000000 -0800 @@ -0,0 +1,100 @@ +/* numspace operations copied from devfs/util.c, which have the following + copyright notice: + + Copyright (C) 1998-2002 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. +*/ + +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/devfs_fs_kernel.h> + +/** + * devfs_alloc_unique_number - Allocate a unique (positive) number. + * @space: The number space to allocate from. + * + * Returns the allocated unique number, else a negative error code. + * This routine is thread safe and may block. + */ + +int devfs_alloc_unique_number (struct unique_numspace *space) +{ + int number; + unsigned int length; + + /* Get around stupid lack of semaphore initialiser */ + spin_lock (&space->init_lock); + if (!space->sem_initialised) + { + sema_init (&space->semaphore, 1); + space->sem_initialised = 1; + } + spin_unlock (&space->init_lock); + down (&space->semaphore); + if (space->num_free < 1) + { + void *bits; + + if (space->length < 16) length = 16; + else length = space->length << 1; + if ( ( bits = vmalloc (length) ) == NULL ) + { + up (&space->semaphore); + return -ENOMEM; + } + if (space->bits != NULL) + { + memcpy (bits, space->bits, space->length); + vfree (space->bits); + } + space->num_free = (length - space->length) << 3; + space->bits = bits; + memset (bits + space->length, 0, length - space->length); + space->length = length; + } + number = find_first_zero_bit (space->bits, space->length << 3); + --space->num_free; + __set_bit (number, space->bits); + up (&space->semaphore); + return number; +} /* End Function devfs_alloc_unique_number */ +EXPORT_SYMBOL(devfs_alloc_unique_number); + + +/** + * devfs_dealloc_unique_number - Deallocate a unique (positive) number. + * @space: The number space to deallocate from. + * @number: The number to deallocate. + * + * This routine is thread safe and may block. + */ + +void devfs_dealloc_unique_number (struct unique_numspace *space, int number) +{ + int was_set; + + if (number < 0) return; + down (&space->semaphore); + was_set = __test_and_clear_bit (number, space->bits); + if (was_set) ++space->num_free; + up (&space->semaphore); + if (!was_set) printk ("(): number %d was already free\n", number); +} /* End Function devfs_dealloc_unique_number */ +EXPORT_SYMBOL(devfs_dealloc_unique_number);

--G4iJoqBmSsgzjUCe-- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/