patch-2.4.0-test5 linux/fs/devices.c

Next file: linux/fs/dquot.c
Previous file: linux/fs/devfs/base.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test4/linux/fs/devices.c linux/fs/devices.c
@@ -35,6 +35,7 @@
 	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 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 @@
 /*
 	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 @@
 {
 	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 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;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)