summaryrefslogtreecommitdiffstats
path: root/fs/devices.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-07-27 23:20:03 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-07-27 23:20:03 +0000
commit89eba5eb77bbf92ffed6686c951cc35f4027e71f (patch)
treeb56887b1753ca2573002bc7f60e5f3e47c33b116 /fs/devices.c
parentf7ff3f5a67747c7714c3db772d05965a0c033705 (diff)
Merge with Linux 2.4.0-test5-pre5.
Diffstat (limited to 'fs/devices.c')
-rw-r--r--fs/devices.c89
1 files changed, 46 insertions, 43 deletions
diff --git a/fs/devices.c b/fs/devices.c
index 3023747da..dd7db7730 100644
--- a/fs/devices.c
+++ b/fs/devices.c
@@ -35,6 +35,7 @@ struct device_struct {
struct file_operations * fops;
};
+static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
static struct device_struct chrdevs[MAX_CHRDEV] = {
{ NULL, NULL },
};
@@ -47,11 +48,13 @@ int get_device_list(char * page)
int len;
len = sprintf(page, "Character devices:\n");
+ read_lock(&chrdevs_lock);
for (i = 0; i < MAX_CHRDEV ; i++) {
if (chrdevs[i].fops) {
len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name);
}
}
+ read_unlock(&chrdevs_lock);
len += get_blkdev_list(page+len);
return len;
}
@@ -59,68 +62,66 @@ int get_device_list(char * page)
/*
Return the function table of a device.
Load the driver if needed.
+ Increment the reference count of module in question.
*/
-static struct file_operations * get_fops(
- unsigned int major,
- unsigned int minor,
- unsigned int maxdev,
- const char *mangle, /* String to use to build the module name */
- struct device_struct tb[])
+static struct file_operations * get_chrfops(unsigned int major, unsigned int minor)
{
struct file_operations *ret = NULL;
- if (major < maxdev){
+ if (!major || major >= MAX_CHRDEV)
+ return NULL;
+
+ read_lock(&chrdevs_lock);
+ ret = fops_get(chrdevs[major].fops);
+ read_unlock(&chrdevs_lock);
#ifdef CONFIG_KMOD
- /*
- * I do get request for device 0. I have no idea why. It happen
- * at shutdown time for one. Without the following test, the
- * kernel will happily trigger a request_module() which will
- * trigger kmod and modprobe for nothing (since there
- * is no device with major number == 0. And furthermore
- * it locks the reboot process :-(
- *
- * Jacques Gelinas (jacques@solucorp.qc.ca)
- *
- * A. Haritsis <ah@doc.ic.ac.uk>: fix for serial module
- * though we need the minor here to check if serial dev,
- * we pass only the normal major char dev to kmod
- * as there is no other loadable dev on these majors
- */
- if ((isa_tty_dev(major) && need_serial(major,minor)) ||
- (major != 0 && !tb[major].fops)) {
- char name[20];
- sprintf(name, mangle, major);
- request_module(name);
+ 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;
}
-#endif
- ret = tb[major].fops;
+ unlock_kernel();
}
- return ret;
-}
+ if (!ret) {
+ 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);
-struct file_operations * get_chrfops(unsigned int major, unsigned int minor)
-{
- return get_fops (major,minor,MAX_CHRDEV,"char-major-%d",chrdevs);
+#endif
+ return ret;
}
int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
{
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);
return major;
}
}
+ write_unlock(&chrdevs_lock);
return -EBUSY;
}
if (major >= MAX_CHRDEV)
return -EINVAL;
- if (chrdevs[major].fops && chrdevs[major].fops != fops)
+ write_lock(&chrdevs_lock);
+ if (chrdevs[major].fops && chrdevs[major].fops != fops) {
+ write_unlock(&chrdevs_lock);
return -EBUSY;
+ }
chrdevs[major].name = name;
chrdevs[major].fops = fops;
+ write_unlock(&chrdevs_lock);
return 0;
}
@@ -128,12 +129,14 @@ int unregister_chrdev(unsigned int major, const char * name)
{
if (major >= MAX_CHRDEV)
return -EINVAL;
- if (!chrdevs[major].fops)
- return -EINVAL;
- if (strcmp(chrdevs[major].name, name))
+ write_lock(&chrdevs_lock);
+ if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) {
+ write_unlock(&chrdevs_lock);
return -EINVAL;
+ }
chrdevs[major].name = NULL;
chrdevs[major].fops = NULL;
+ write_unlock(&chrdevs_lock);
return 0;
}
@@ -144,15 +147,15 @@ int chrdev_open(struct inode * inode, struct file * filp)
{
int ret = -ENODEV;
- lock_kernel();
- filp->f_op = fops_get(get_chrfops(MAJOR(inode->i_rdev),
- MINOR(inode->i_rdev)));
+ filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if (filp->f_op) {
ret = 0;
- if (filp->f_op->open != NULL)
+ if (filp->f_op->open != NULL) {
+ lock_kernel();
ret = filp->f_op->open(inode,filp);
+ unlock_kernel();
+ }
}
- unlock_kernel();
return ret;
}