lkml.org 
[lkml]   [2003]   [Mar]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH] alternative dev patch
Hi,

This patch makes use of the character device hash and avoids introducing
unnecessary interfaces. I also moved the tty hack where it belongs.
As an example I converted the misc device to demonstrate how drivers can
make use of it.
Probably the most interesting part is how the open path stays short and
fast.

bye, Roman

Index: drivers/char/misc.c
===================================================================
RCS file: /usr/src/cvsroot/linux-2.5/drivers/char/misc.c,v
retrieving revision 1.1.1.7
diff -u -p -r1.1.1.7 misc.c
--- drivers/char/misc.c 15 Feb 2003 01:28:54 -0000 1.1.1.7
+++ drivers/char/misc.c 20 Mar 2003 00:38:23 -0000
@@ -60,11 +60,8 @@ static DECLARE_MUTEX(misc_sem);
* Assigned numbers, used for dynamic minors
*/
#define DYNAMIC_MINORS 64 /* like dynamic majors */
-static unsigned char misc_minors[DYNAMIC_MINORS / 8];

-#ifdef CONFIG_SGI_NEWPORT_GFX
extern void gfx_register(void);
-#endif
extern void streamable_init(void);
extern int rtc_DP8570A_init(void);
extern int rtc_MK48T08_init(void);
@@ -100,45 +97,21 @@ static int misc_read_proc(char *buf, cha

static int misc_open(struct inode * inode, struct file * file)
{
- int minor = minor(inode->i_rdev);
- struct miscdevice *c;
+ struct char_device *cdev = inode->i_cdev;
+ char modname[32];
int err = -ENODEV;
- struct file_operations *old_fops, *new_fops = NULL;
-
- down(&misc_sem);
-
- c = misc_list.next;
+ struct file_operations *fops;

- while ((c != &misc_list) && (c->minor != minor))
- c = c->next;
- if (c != &misc_list)
- new_fops = fops_get(c->fops);
- if (!new_fops) {
- char modname[20];
- up(&misc_sem);
- sprintf(modname, "char-major-%d-%d", MISC_MAJOR, minor);
- request_module(modname);
- down(&misc_sem);
- c = misc_list.next;
- while ((c != &misc_list) && (c->minor != minor))
- c = c->next;
- if (c == &misc_list || (new_fops = fops_get(c->fops)) == NULL)
- goto fail;
+ sprintf(modname, "char-major-%d-%d", MISC_MAJOR, MINOR(cdev->dev));
+ request_module(modname);
+ fops = fops_get(cdev->fops);
+ if (fops) {
+ err = 0;
+ fops_put(file->f_op);
+ file->f_op = fops;
+ if (fops->open)
+ err = fops->open(inode, file);
}
-
- err = 0;
- old_fops = file->f_op;
- file->f_op = new_fops;
- if (file->f_op->open) {
- err=file->f_op->open(inode,file);
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- }
- fops_put(old_fops);
-fail:
- up(&misc_sem);
return err;
}

@@ -167,34 +140,35 @@ static struct file_operations misc_fops
int misc_register(struct miscdevice * misc)
{
static devfs_handle_t devfs_handle, dir;
- struct miscdevice *c;
-
- if (misc->next || misc->prev)
- return -EBUSY;
- down(&misc_sem);
- c = misc_list.next;
-
- while ((c != &misc_list) && (c->minor != misc->minor))
- c = c->next;
- if (c != &misc_list) {
- up(&misc_sem);
- return -EBUSY;
- }
+ struct char_device *cdev;

if (misc->minor == MISC_DYNAMIC_MINOR) {
- int i = DYNAMIC_MINORS;
- while (--i >= 0)
- if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
- break;
- if (i<0)
- {
- up(&misc_sem);
- return -EBUSY;
+ int i;
+
+ for (i = DYNAMIC_MINORS; i > 0; i--) {
+ cdev = cdget(MKDEV(MISC_MAJOR, i));
+ down(&cdev->sem);
+ if (!cdev->fops)
+ goto found;
+ up(&cdev->sem);
+ cdput(cdev);
}
- misc->minor = i;
- }
- if (misc->minor < DYNAMIC_MINORS)
- misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
+ } else {
+ cdev = cdget(MKDEV(MISC_MAJOR, misc->minor));
+ down(&cdev->sem);
+ if (!cdev->fops)
+ goto found;
+ up(&cdev->sem);
+ }
+ return -EBUSY;
+
+found:
+ cdev->fops = misc->fops;
+ cdev->name = misc->name;
+ up(&cdev->sem);
+
+ down(&misc_sem);
+
if (!devfs_handle)
devfs_handle = devfs_mk_dir (NULL, "misc", NULL);
dir = strchr (misc->name, '/') ? NULL : devfs_handle;
@@ -204,14 +178,10 @@ int misc_register(struct miscdevice * mi
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
misc->fops, NULL);

- /*
- * Add it to the front, so that later devices can "override"
- * earlier defaults
- */
misc->prev = &misc_list;
misc->next = misc_list.next;
- misc->prev->next = misc;
- misc->next->prev = misc;
+ misc_list.next->prev = misc;
+ misc_list.next = misc;
up(&misc_sem);
return 0;
}
@@ -228,18 +198,22 @@ int misc_register(struct miscdevice * mi

int misc_deregister(struct miscdevice * misc)
{
- int i = misc->minor;
- if (!misc->next || !misc->prev)
+ struct char_device *cdev;
+
+ cdev = cdget(MKDEV(MISC_MAJOR, misc->minor));
+ down(&cdev->sem);
+ if (cdev->fops != misc->fops) {
+ up(&cdev->sem);
return -EINVAL;
+ }
+ cdev->fops = NULL;
+ cdev->name = NULL;
+ up(&cdev->sem);
+
down(&misc_sem);
misc->prev->next = misc->next;
misc->next->prev = misc->prev;
- misc->next = NULL;
- misc->prev = NULL;
- devfs_unregister (misc->devfs_handle);
- if (i < DYNAMIC_MINORS && i>0) {
- misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
- }
+ devfs_unregister(misc->devfs_handle);
up(&misc_sem);
return 0;
}
Index: drivers/char/tty_io.c
===================================================================
RCS file: /usr/src/cvsroot/linux-2.5/drivers/char/tty_io.c,v
retrieving revision 1.1.1.32
diff -u -p -r1.1.1.32 tty_io.c
--- drivers/char/tty_io.c 17 Mar 2003 22:56:57 -0000 1.1.1.32
+++ drivers/char/tty_io.c 19 Mar 2003 23:20:07 -0000
@@ -274,7 +274,7 @@ static int tty_set_ldisc(struct tty_stru
/* Eduardo Blanco <ejbs@cs.cs.com.uy> */
/* Cyrus Durgin <cider@speakeasy.org> */
if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) {
- char modname [20];
+ char modname [32];
sprintf(modname, "tty-ldisc-%d", ldisc);
request_module (modname);
}
@@ -327,11 +327,15 @@ static int tty_set_ldisc(struct tty_stru
/*
* This routine returns a tty driver structure, given a device number
*/
-struct tty_driver *get_tty_driver(kdev_t device)
+static struct tty_driver *get_tty_driver(kdev_t device)
{
int major, minor;
struct tty_driver *p;
-
+#ifdef CONFIG_KMOD
+ char name[20] = "";
+again:
+#endif
+
minor = minor(device);
major = major(device);

@@ -344,6 +348,13 @@ struct tty_driver *get_tty_driver(kdev_t
continue;
return p;
}
+#ifdef CONFIG_KMOD
+ if (!name[0]) {
+ sprintf(name, "char-major-%d", MAJOR(major));
+ request_module(name);
+ goto again;
+ }
+#endif
return NULL;
}

@@ -2108,22 +2119,33 @@ EXPORT_SYMBOL(tty_unregister_device);
*/
int tty_register_driver(struct tty_driver *driver)
{
- int error;
+ int error = 0;
int i;

if (driver->flags & TTY_DRIVER_INSTALLED)
return 0;

+ down_tty_sem(0);
+ if (driver->major) {
+ struct char_device *cdev = cdget(MKDEV(driver->major, 0));
+ struct file_operations *fops = cdev->fops;
+ cdput(cdev);
+ if (fops == &tty_fops)
+ goto skip;
+ }
error = register_chrdev(driver->major, driver->name, &tty_fops);
- if (error < 0)
+ if (error < 0) {
+ up_tty_sem(0);
return error;
- else if(driver->major == 0)
+ } else if (driver->major == 0)
driver->major = error;
+skip:

if (!driver->put_char)
driver->put_char = tty_default_put_char;

list_add(&driver->tty_drivers, &tty_drivers);
+ up_tty_sem(0);

if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) {
for(i = 0; i < driver->num; i++)
@@ -2147,6 +2169,7 @@ int tty_unregister_driver(struct tty_dri
if (*driver->refcount)
return -EBUSY;

+ down_tty_sem(0);
list_for_each_entry(p, &tty_drivers, tty_drivers) {
if (p == driver)
found++;
@@ -2154,17 +2177,22 @@ int tty_unregister_driver(struct tty_dri
othername = p->name;
}

- if (!found)
- return -ENOENT;
-
- if (othername == NULL) {
- retval = unregister_chrdev(driver->major, driver->name);
- if (retval)
- return retval;
+ if (found) {
+ if (othername) {
+ struct char_device *cdev = cdget(MKDEV(driver->major, 0));
+ cdev->name = othername;
+ cdput(cdev);
+ retval = 0;
+ } else
+ retval = unregister_chrdev(driver->major, driver->name);
} else
- register_chrdev(driver->major, othername, &tty_fops);
+ retval = -ENOENT;

- list_del(&driver->tty_drivers);
+ if (!retval)
+ list_del(&driver->tty_drivers);
+ up_tty_sem(0);
+ if (retval)
+ return retval;

/*
* Free the termios and termios_locked structures because
Index: fs/char_dev.c
===================================================================
RCS file: /usr/src/cvsroot/linux-2.5/fs/char_dev.c,v
retrieving revision 1.1.1.7
diff -u -p -r1.1.1.7 char_dev.c
--- fs/char_dev.c 17 Mar 2003 22:53:20 -0000 1.1.1.7
+++ fs/char_dev.c 19 Mar 2003 23:20:07 -0000
@@ -15,17 +15,8 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
-#include <linux/devfs_fs_kernel.h>

-#ifdef CONFIG_KMOD
#include <linux/kmod.h>
-#include <linux/tty.h>
-
-/* serial module kmod load support */
-struct tty_driver *get_tty_driver(kdev_t device);
-#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR)
-#define need_serial(ma,mi) (get_tty_driver(mk_kdev(ma,mi)) == NULL)
-#endif

#define HASH_BITS 6
#define HASH_SIZE (1UL << HASH_BITS)
@@ -43,8 +34,10 @@ static void init_once(void *foo, kmem_ca
struct char_device *cdev = (struct char_device *) foo;

if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
- SLAB_CTOR_CONSTRUCTOR)
+ SLAB_CTOR_CONSTRUCTOR) {
memset(cdev, 0, sizeof(*cdev));
+ sema_init(&cdev->sem, 1);
+ }
}

void __init cdev_cache_init(void)
@@ -127,27 +120,22 @@ void cdput(struct char_device *cdev)
}
}

-struct device_struct {
- const char * name;
- struct file_operations * fops;
-};
-
-static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
-static struct device_struct chrdevs[MAX_CHRDEV];

int get_chrdev_list(char *page)
{
- int i;
- int len;
+ struct char_device *cdev;
+ dev_t dev;
+ int len, i;

len = sprintf(page, "Character devices:\n");
- read_lock(&chrdevs_lock);
+ spin_lock(&cdev_lock);
for (i = 0; i < MAX_CHRDEV ; i++) {
- if (chrdevs[i].fops) {
- len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name);
- }
+ dev = MKDEV(i, 0);
+ cdev = cdfind(dev, cdev_hashtable + hash(dev));
+ if (cdev && cdev->name)
+ len += sprintf(page+len, "%3d %s\n", i, cdev->name);
}
- read_unlock(&chrdevs_lock);
+ spin_unlock(&cdev_lock);
return len;
}

@@ -156,80 +144,92 @@ int get_chrdev_list(char *page)
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(struct char_device *cdev)
{
- struct file_operations *ret = NULL;
-
- if (!major || major >= MAX_CHRDEV)
- return NULL;
+ struct file_operations *fops;

- read_lock(&chrdevs_lock);
- ret = fops_get(chrdevs[major].fops);
- read_unlock(&chrdevs_lock);
+ down(&cdev->sem);
+ fops = fops_get(cdev->fops);
+ up(&cdev->sem);
+ if (fops || !MINOR(cdev->dev))
+ return fops;
+
+ cdev = cdget(MKDEV(MAJOR(cdev->dev), 0));
+ down(&cdev->sem);
+ fops = fops_get(cdev->fops);
+ up(&cdev->sem);
#ifdef CONFIG_KMOD
- if (ret && isa_tty_dev(major)) {
- lock_kernel();
- if (need_serial(major,minor)) {
- /* Force request_module anyway, but what for? */
- fops_put(ret);
- ret = NULL;
- }
- unlock_kernel();
- }
- if (!ret) {
+ if (!fops) {
char name[20];
- sprintf(name, "char-major-%d", major);
- request_module(name);

- read_lock(&chrdevs_lock);
- ret = fops_get(chrdevs[major].fops);
- read_unlock(&chrdevs_lock);
+ sprintf(name, "char-major-%d", MAJOR(cdev->dev));
+ request_module(name);
+ down(&cdev->sem);
+ fops = fops_get(cdev->fops);
+ up(&cdev->sem);
}
#endif
- return ret;
+ cdput(cdev);
+ return fops;
}
-
+
int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
{
+ struct char_device *cdev;
+
if (major == 0) {
- write_lock(&chrdevs_lock);
for (major = MAX_CHRDEV-1; major > 0; major--) {
- if (chrdevs[major].fops == NULL) {
- chrdevs[major].name = name;
- chrdevs[major].fops = fops;
- write_unlock(&chrdevs_lock);
+ cdev = cdget(MKDEV(major, 0));
+ down(&cdev->sem);
+ if (!cdev->fops) {
+ cdev->fops = fops;
+ cdev->name = name;
+ up(&cdev->sem);
return major;
}
+ up(&cdev->sem);
+ cdput(cdev);
}
- write_unlock(&chrdevs_lock);
return -EBUSY;
}
+
if (major >= MAX_CHRDEV)
return -EINVAL;
- write_lock(&chrdevs_lock);
- if (chrdevs[major].fops && chrdevs[major].fops != fops) {
- write_unlock(&chrdevs_lock);
+
+ cdev = cdget(MKDEV(major, 0));
+ down(&cdev->sem);
+ if (cdev->fops) {
+ up(&cdev->sem);
+ cdput(cdev);
return -EBUSY;
}
- chrdevs[major].name = name;
- chrdevs[major].fops = fops;
- write_unlock(&chrdevs_lock);
+ cdev->fops = fops;
+ cdev->name = name;
+ up(&cdev->sem);
return 0;
}

int unregister_chrdev(unsigned int major, const char * name)
{
+ struct char_device *cdev;
+ int res;
+
if (major >= MAX_CHRDEV)
return -EINVAL;
- write_lock(&chrdevs_lock);
- if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) {
- write_unlock(&chrdevs_lock);
- return -EINVAL;
+
+ cdev = cdget(MKDEV(major, 0));
+ down(&cdev->sem);
+ res = -EINVAL;
+ if (cdev->fops && !strcmp(cdev->name, name)) {
+ cdev->fops = NULL;
+ cdev->name = NULL;
+ cdput(cdev);
+ res = 0;
}
- chrdevs[major].name = NULL;
- chrdevs[major].fops = NULL;
- write_unlock(&chrdevs_lock);
- return 0;
+ up(&cdev->sem);
+
+ cdput(cdev);
+ return res;
}

/*
@@ -237,14 +237,16 @@ int unregister_chrdev(unsigned int major
*/
int chrdev_open(struct inode * inode, struct file * filp)
{
+ struct file_operations *fops;
int ret = -ENODEV;

- filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev));
- if (filp->f_op) {
+ fops = get_chrfops(inode->i_cdev);
+ if (fops) {
ret = 0;
- if (filp->f_op->open != NULL) {
+ filp->f_op = fops;
+ if (fops->open) {
lock_kernel();
- ret = filp->f_op->open(inode,filp);
+ ret = fops->open(inode,filp);
unlock_kernel();
}
}
@@ -262,11 +264,13 @@ struct file_operations def_chr_fops = {

const char * cdevname(kdev_t dev)
{
- static char buffer[32];
- const char * name = chrdevs[major(dev)].name;
+ static char buffer[64];
+ struct char_device *cdev;
+
+ cdev = cdget(kdev_t_to_nr(dev));
+ down(&cdev->sem);
+ sprintf(buffer, "%s(%d,%d)", cdev->name ? cdev->name : "unknown-char", major(dev), minor(dev));
+ up(&cdev->sem);

- if (!name)
- name = "unknown-char";
- sprintf(buffer, "%s(%d,%d)", name, major(dev), minor(dev));
return buffer;
}
Index: include/linux/fs.h
===================================================================
RCS file: /usr/src/cvsroot/linux-2.5/include/linux/fs.h,v
retrieving revision 1.1.1.53
diff -u -p -r1.1.1.53 fs.h
--- include/linux/fs.h 17 Mar 2003 22:54:06 -0000 1.1.1.53
+++ include/linux/fs.h 19 Mar 2003 23:20:07 -0000
@@ -331,8 +331,11 @@ struct address_space {

struct char_device {
struct list_head hash;
+ struct file_operations *fops;
+ const char * name;
atomic_t count;
dev_t dev;
+ struct semaphore sem;
};

struct block_device {
-
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/

\
 
 \ /
  Last update: 2005-03-22 13:34    [W:0.059 / U:0.044 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site