]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] cdev-cidr, part 1
authorAlexander Viro <viro@www.linux.org.uk>
Sun, 25 May 2003 04:42:20 +0000 (21:42 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Sun, 25 May 2003 04:42:20 +0000 (21:42 -0700)
New object: struct cdev.  It contains a kobject, a pointer to
file_operations and a pointer to owner module.  These guys have a search
structure of the same sort as gendisks and chrdev_open() picks
file_operations from them.

Intended use: embed such animal in driver-owned structure (e.g.
tty_driver) and register it as associated with given range of device
numbers.  Generic code will do lookup for such object and use it for the
rest.

The behaviour of register_chrdev() is _not_ changed - it allocates
struct cdev and registers it; any old driver will work as if nothing had
changed.

On that stage we only use it during chrdev_open() to find
file_operations.  Later it will be cached in inode->i_cdev (and index in
range - in inode->i_cindex) so that ->open() could get whatever objects
it wants directly without any special-cased lookups, etc.

drivers/char/tty_io.c
fs/char_dev.c
include/linux/cdev.h [new file with mode: 0644]
include/linux/fs.h
include/linux/tty_driver.h

index ec5bd80e34ff9eda1c7138f781c2a7c974c5e32f..6d698dee57d8513fccab8349fa85b813b4ef4208 100644 (file)
@@ -2242,6 +2242,7 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index)
 EXPORT_SYMBOL(tty_register_device);
 EXPORT_SYMBOL(tty_unregister_device);
 
+static struct kobject tty_kobj = {.name = "tty"};
 /*
  * Called by a tty driver to register itself.
  */
@@ -2250,13 +2251,14 @@ int tty_register_driver(struct tty_driver *driver)
        int error;
         int i;
        dev_t dev;
+       char *s;
 
        if (driver->flags & TTY_DRIVER_INSTALLED)
                return 0;
 
        if (!driver->major) {
                error = alloc_chrdev_region(&dev, driver->num,
-                                               (char*)driver->name, &tty_fops);
+                                               (char*)driver->name);
                if (!error) {
                        driver->major = MAJOR(dev);
                        driver->minor_start = MINOR(dev);
@@ -2264,11 +2266,24 @@ int tty_register_driver(struct tty_driver *driver)
        } else {
                dev = MKDEV(driver->major, driver->minor_start);
                error = register_chrdev_region(dev, driver->num,
-                                               (char*)driver->name, &tty_fops);
+                                               (char*)driver->name);
        }
        if (error < 0)
                return error;
 
+       driver->cdev.kobj.parent = &tty_kobj;
+       strcpy(driver->cdev.kobj.name, driver->name);
+       for (s = strchr(driver->cdev.kobj.name, '/'); s; s = strchr(s, '/'))
+               *s = '!';
+       cdev_init(&driver->cdev, &tty_fops);
+       driver->cdev.owner = driver->owner;
+       error = cdev_add(&driver->cdev, dev, driver->num);
+       if (error) {
+               cdev_put(&driver->cdev);
+               unregister_chrdev_region(dev, driver->num);
+               return error;
+       }
+
        if (!driver->put_char)
                driver->put_char = tty_default_put_char;
        
@@ -2279,7 +2294,7 @@ int tty_register_driver(struct tty_driver *driver)
                    tty_register_device(driver, i, NULL);
        }
        proc_tty_register_driver(driver);
-       return error;
+       return 0;
 }
 
 /*
@@ -2293,6 +2308,7 @@ int tty_unregister_driver(struct tty_driver *driver)
        if (*driver->refcount)
                return -EBUSY;
 
+       cdev_unmap(MKDEV(driver->major, driver->minor_start), driver->num);
        unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
                                driver->num);
 
@@ -2318,6 +2334,7 @@ int tty_unregister_driver(struct tty_driver *driver)
                        tty_unregister_device(driver, i);
        }
        proc_tty_unregister_driver(driver);
+       cdev_del(&driver->cdev);
        return 0;
 }
 
@@ -2364,6 +2381,14 @@ static int __init tty_class_init(void)
 }
 
 postcore_initcall(tty_class_init);
+static struct cdev tty_cdev, console_cdev;
+#ifdef CONFIG_UNIX98_PTYS
+static struct cdev ptmx_cdev;
+#endif
+#ifdef CONFIG_VT
+static struct cdev vc0_cdev;
+#endif
 
 /*
  * Ok, now we can initialize the rest of the tty devices and can count
@@ -2371,29 +2396,40 @@ postcore_initcall(tty_class_init);
  */
 void __init tty_init(void)
 {
-       if (register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1,
-                                  "/dev/tty", &tty_fops) < 0)
+       strcpy(tty_cdev.kobj.name, "dev.tty");
+       cdev_init(&tty_cdev, &tty_fops);
+       if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
+           register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
                panic("Couldn't register /dev/tty driver\n");
        devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 0), S_IFCHR|S_IRUGO|S_IWUGO, "tty");
        tty_add_class_device ("tty", MKDEV(TTYAUX_MAJOR, 0), NULL);
 
-       if (register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1,
-                                  "/dev/console", &tty_fops) < 0)
+       strcpy(console_cdev.kobj.name, "dev.console");
+       cdev_init(&console_cdev, &tty_fops);
+       if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
+           register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
                panic("Couldn't register /dev/console driver\n");
        devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 1), S_IFCHR|S_IRUSR|S_IWUSR, "console");
        tty_add_class_device ("console", MKDEV(TTYAUX_MAJOR, 1), NULL);
 
+       tty_kobj.kset = tty_cdev.kobj.kset;
+       kobject_register(&tty_kobj);
+
 #ifdef CONFIG_UNIX98_PTYS
-       if (register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1,
-                                  "/dev/ptmx", &tty_fops) < 0)
+       strcpy(ptmx_cdev.kobj.name, "dev.ptmx");
+       cdev_init(&ptmx_cdev, &tty_fops);
+       if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
+           register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
                panic("Couldn't register /dev/ptmx driver\n");
        devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 2), S_IFCHR|S_IRUGO|S_IWUGO, "ptmx");
        tty_add_class_device ("ptmx", MKDEV(TTYAUX_MAJOR, 2), NULL);
 #endif
        
 #ifdef CONFIG_VT
-       if (register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1,
-                                  "/dev/vc/0", &tty_fops) < 0)
+       strcpy(vc0_cdev.kobj.name, "dev.vc0");
+       cdev_init(&vc0_cdev, &tty_fops);
+       if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
+           register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
                panic("Couldn't register /dev/tty0 driver\n");
        devfs_mk_cdev(MKDEV(TTY_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vc/0");
        tty_add_class_device ("tty0", MKDEV(TTY_MAJOR, 0), NULL);
index f20b95221a025188273a6b9a14280999131ed00e..430e1e28b4528c6a4b205b1d573d60910c2c551e 100644 (file)
 #include <linux/smp_lock.h>
 #include <linux/devfs_fs_kernel.h>
 
+#include <linux/kobject.h>
+#include <linux/kobj_map.h>
+#include <linux/cdev.h>
+
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
 
+static struct kobj_map *cdev_map;
+
 #define MAX_PROBE_HASH 255     /* random */
 
 static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
@@ -32,6 +38,7 @@ static struct char_device_struct {
        int minorct;
        const char *name;
        struct file_operations *fops;
+       struct cdev *cdev;              /* will die */
 } *chrdevs[MAX_PROBE_HASH];
 
 /* index in the above */
@@ -59,56 +66,24 @@ int get_chrdev_list(char *page)
        return len;
 }
 
-/*
- * Return the function table of a device, if present.
- * Increment the reference count of module in question.
- */
-static struct file_operations *
-lookup_chrfops(unsigned int major, unsigned int minor)
-{
-       struct char_device_struct *cd;
-       struct file_operations *ret = NULL;
-       int i;
-
-       i = major_to_index(major);
-
-       read_lock(&chrdevs_lock);
-       for (cd = chrdevs[i]; cd; cd = cd->next) {
-               if (major == cd->major &&
-                   minor - cd->baseminor < cd->minorct) {
-                       ret = fops_get(cd->fops);
-                       break;
-               }
-       }
-       read_unlock(&chrdevs_lock);
-
-       return ret;
-}
-
 /*
  * Return the function table of a device, if present.
  * Load the driver if needed.
  * Increment the reference count of module in question.
  */
-static struct file_operations *
-get_chrfops(unsigned int major, unsigned int minor)
+static struct file_operations *get_chrfops(dev_t dev)
 {
        struct file_operations *ret = NULL;
-
-       if (!major)
-               return NULL;
-
-       ret = lookup_chrfops(major, minor);
-
-#ifdef CONFIG_KMOD
-       if (!ret) {
-               request_module("char-major-%d", major);
-
-               read_lock(&chrdevs_lock);
-               ret = lookup_chrfops(major, minor);
-               read_unlock(&chrdevs_lock);
+       int index;
+       struct kobject *kobj = kobj_lookup(cdev_map, dev, &index);
+
+       if (kobj) {
+               struct cdev *p = container_of(kobj, struct cdev, kobj);
+               struct module *owner = p->owner;
+               ret = fops_get(p->ops);
+               cdev_put(p);
+               module_put(owner);
        }
-#endif
        return ret;
 }
 
@@ -125,8 +100,7 @@ get_chrfops(unsigned int major, unsigned int minor)
  */
 static struct char_device_struct *
 __register_chrdev_region(unsigned int major, unsigned int baseminor,
-                          int minorct, const char *name,
-                          struct file_operations *fops)
+                          int minorct, const char *name)
 {
        struct char_device_struct *cd, **cp;
        int ret = 0;
@@ -157,7 +131,6 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor,
        cd->baseminor = baseminor;
        cd->minorct = minorct;
        cd->name = name;
-       cd->fops = fops;
 
        i = major_to_index(major);
 
@@ -200,8 +173,7 @@ __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
        return cd;
 }
 
-int register_chrdev_region(dev_t from, unsigned count, char *name,
-                          struct file_operations *fops)
+int register_chrdev_region(dev_t from, unsigned count, char *name)
 {
        struct char_device_struct *cd;
        dev_t to = from + count;
@@ -212,7 +184,7 @@ int register_chrdev_region(dev_t from, unsigned count, char *name,
                if (next > to)
                        next = to;
                cd = __register_chrdev_region(MAJOR(n), MINOR(n),
-                              next - n, name, fops);
+                              next - n, name);
                if (IS_ERR(cd))
                        goto fail;
        }
@@ -226,11 +198,10 @@ fail:
        return PTR_ERR(cd);
 }
 
-int alloc_chrdev_region(dev_t *dev, unsigned count, char *name,
-                          struct file_operations *fops)
+int alloc_chrdev_region(dev_t *dev, unsigned count, char *name)
 {
        struct char_device_struct *cd;
-       cd = __register_chrdev_region(0, 0, count, name, fops);
+       cd = __register_chrdev_region(0, 0, count, name);
        if (IS_ERR(cd))
                return PTR_ERR(cd);
        *dev = MKDEV(cd->major, cd->baseminor);
@@ -241,11 +212,36 @@ int register_chrdev(unsigned int major, const char *name,
                    struct file_operations *fops)
 {
        struct char_device_struct *cd;
+       struct cdev *cdev;
+       char *s;
+       int err = -ENOMEM;
 
-       cd = __register_chrdev_region(major, 0, 256, name, fops);
+       cd = __register_chrdev_region(major, 0, 256, name);
        if (IS_ERR(cd))
                return PTR_ERR(cd);
-       return cd->major;
+       
+       cdev = cdev_alloc();
+       if (!cdev)
+               goto out2;
+
+       cdev->owner = fops->owner;
+       cdev->ops = fops;
+       strcpy(cdev->kobj.name, name);
+       for (s = strchr(cdev->kobj.name, '/'); s; s = strchr(s, '/'))
+               *s = '!';
+               
+       err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
+       if (err)
+               goto out;
+
+       cd->cdev = cdev;
+
+       return major ? 0 : cd->major;
+out:
+       cdev_put(cdev);
+out2:
+       __unregister_chrdev_region(cd->major, 0, 256);
+       return err;
 }
 
 void unregister_chrdev_region(dev_t from, unsigned count)
@@ -263,7 +259,12 @@ void unregister_chrdev_region(dev_t from, unsigned count)
 
 int unregister_chrdev(unsigned int major, const char *name)
 {
-       kfree(__unregister_chrdev_region(major, 0, 256));
+       struct char_device_struct *cd;
+       cdev_unmap(MKDEV(major, 0), 256);
+       cd = __unregister_chrdev_region(major, 0, 256);
+       if (cd && cd->cdev)
+               cdev_del(cd->cdev);
+       kfree(cd);
        return 0;
 }
 
@@ -274,7 +275,7 @@ int chrdev_open(struct inode * inode, struct file * filp)
 {
        int ret = -ENODEV;
 
-       filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev));
+       filp->f_op = get_chrfops(kdev_t_to_nr(inode->i_rdev));
        if (filp->f_op) {
                ret = 0;
                if (filp->f_op->open != NULL) {
@@ -315,3 +316,100 @@ const char *cdevname(kdev_t dev)
 
        return buffer;
 }
+
+static struct kobject *exact_match(dev_t dev, int *part, void *data)
+{
+       struct cdev *p = data;
+       return &p->kobj;
+}
+
+static int exact_lock(dev_t dev, void *data)
+{
+       struct cdev *p = data;
+       return cdev_get(p) ? 0 : -1;
+}
+
+int cdev_add(struct cdev *p, dev_t dev, unsigned count)
+{
+       int err = kobject_add(&p->kobj);
+       if (err)
+               return err;
+       return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
+}
+
+void cdev_unmap(dev_t dev, unsigned count)
+{
+       kobj_unmap(cdev_map, dev, count);
+}
+
+void cdev_del(struct cdev *p)
+{
+       kobject_del(&p->kobj);
+       cdev_put(p);
+}
+
+struct kobject *cdev_get(struct cdev *p)
+{
+       struct module *owner = p->owner;
+       struct kobject *kobj;
+
+       if (owner && !try_module_get(owner))
+               return NULL;
+       kobj = kobject_get(&p->kobj);
+       if (!kobj)
+               module_put(owner);
+       return kobj;
+}
+
+static decl_subsys(cdev, NULL, NULL);
+
+static void cdev_dynamic_release(struct kobject *kobj)
+{
+       struct cdev *p = container_of(kobj, struct cdev, kobj);
+       kfree(p);
+}
+
+static struct kobj_type ktype_cdev_dynamic = {
+       .release        = cdev_dynamic_release,
+};
+
+static struct kset kset_dynamic = {
+       .subsys = &cdev_subsys,
+       .kobj = {.name = "major",},
+       .ktype = &ktype_cdev_dynamic,
+};
+
+struct cdev *cdev_alloc(void)
+{
+       struct cdev *p = kmalloc(sizeof(struct cdev), GFP_KERNEL);
+       if (p) {
+               memset(p, 0, sizeof(struct cdev));
+               p->kobj.kset = &kset_dynamic;
+               kobject_init(&p->kobj);
+       }
+       return p;
+}
+
+void cdev_init(struct cdev *cdev, struct file_operations *fops)
+{
+       kobj_set_kset_s(cdev, cdev_subsys);
+       kobject_init(&cdev->kobj);
+       cdev->ops = fops;
+}
+
+static struct kobject *base_probe(dev_t dev, int *part, void *data)
+{
+       char name[30];
+       sprintf(name, "char-major-%d", MAJOR(dev));
+       request_module(name);
+       return NULL;
+}
+
+static int __init chrdev_init(void)
+{
+       subsystem_register(&cdev_subsys);
+       kset_register(&kset_dynamic);
+       cdev_map = kobj_map_init(base_probe, &cdev_subsys);
+       return 0;
+}
+subsys_initcall(chrdev_init);
diff --git a/include/linux/cdev.h b/include/linux/cdev.h
new file mode 100644 (file)
index 0000000..faa67d2
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _LINUX_CDEV_H
+#define _LINUX_CDEV_H
+#ifdef __KERNEL__
+
+struct cdev {
+       struct kobject kobj;
+       struct module *owner;
+       struct file_operations *ops;
+};
+
+void cdev_init(struct cdev *, struct file_operations *);
+
+struct cdev *cdev_alloc(void);
+
+static inline void cdev_put(struct cdev *p)
+{
+       if (p)
+               kobject_put(&p->kobj);
+}
+
+struct kobject *cdev_get(struct cdev *);
+
+int cdev_add(struct cdev *, dev_t, unsigned);
+
+void cdev_del(struct cdev *);
+
+void cdev_unmap(dev_t, unsigned);
+
+#endif
+#endif
index 1b7fac010f1fdf040f25a04c8e55919e42586646..6b32c6ab8727b333aac55509b65e13b0715ed398 100644 (file)
@@ -1056,10 +1056,8 @@ extern void bd_release(struct block_device *);
 extern void blk_run_queues(void);
 
 /* fs/char_dev.c */
-extern int alloc_chrdev_region(dev_t *, unsigned, char *,
-                               struct file_operations *);
-extern int register_chrdev_region(dev_t, unsigned, char *,
-                               struct file_operations *);
+extern int alloc_chrdev_region(dev_t *, unsigned, char *);
+extern int register_chrdev_region(dev_t, unsigned, char *);
 extern int register_chrdev(unsigned int, const char *,
                           struct file_operations *);
 extern int unregister_chrdev(unsigned int, const char *);
index d8d9fc435a3fead98f14817b87f294f926642339..ef234da28a8cb543827da978edeb39e308ebca63 100644 (file)
 
 #include <linux/fs.h>
 #include <linux/list.h>
+#include <linux/cdev.h>
 
 struct tty_driver {
        int     magic;          /* magic number for this structure */
+       struct cdev cdev;
        struct module   *owner;
        const char      *driver_name;
        const char      *name;