Date: Wed, 13 Oct 1999 04:33:13 -0400
From: Jeff Garzik <jgarzik@pobox.com>
To: Linus Torvalds <torvalds@transmeta.com>
Subject: [patch] dynamic char and block devices
This is a multi-part message in MIME format.
--------------6490499963B66DF0BB388384
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Linus and other penguins--
The attached patch against 2.3.21 updates fs/devices.c to be dynamic,
instead of using two static arrays indexed by major number.
This shrinks the code, and gets rid of something indexed by major/minor
-- helpful on the road to a better kdev_t.
Tests ok on local system after a few days. HFS was only tested to
compile, not actually run-time tested, due to its experimental (a.k.a.
probably broken) nature.
The only other comment is that STATIC_ENT_POOL_MAX could probably be
made smaller, as it is only needed during boot time.
Full changelog below.
Regards,
Jeff
fs/devices.c:
* replace chrdevs[], blkdevs[] static arrays with sorted linked list of
devices. This saves some space by eliminating two static, pre-init'd
arrays and some duplicate code.
* const-ify [kbc]devname return values. All callers checked.
include/linux/fs.h:
* update [kbc]devname prototypes
include/linux/kdev_t.h:
* update kdevname prototype
include/linux/hfs_sysdep.h:
* update for new kdevname return type
--
Custom driver development | Never worry about theory as long
Open source programming | as the machinery does what it's
| supposed to do. -- R. A. Heinlein
--------------6490499963B66DF0BB388384
Content-Type: text/plain; charset=us-ascii;
name="dynamic-devices.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="dynamic-devices.patch"
Index: fs/devices.c
===================================================================
RCS file: /g/cvslan/linux_2_3/fs/devices.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 devices.c
--- fs/devices.c 1999/10/07 07:42:42 1.1.1.1
+++ fs/devices.c 1999/10/13 08:25:26
@@ -7,6 +7,10 @@
*
* Added kerneld support: Jacques Gelinas and Bjorn Ekwall
* (changed to kmod)
+ *
+ * Ditched static array indexed by major number, and updated
+ * to use a linked list: Jeff Garzik, 13 Oct 1999
+ *
*/
#include <linux/config.h>
@@ -15,49 +19,83 @@
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/stat.h>
+#include <linux/slab.h>
#include <linux/fcntl.h>
+#include <linux/init.h>
#include <linux/errno.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(MKDEV(ma,mi)) == NULL)
-#endif
+#endif /* CONFIG_KMOD */
+typedef enum {
+ CHRDEV,
+ BLKDEV
+} devtype_t;
+
+struct device_struct;
struct device_struct {
+ devtype_t devtype;
+ int major;
const char * name;
struct file_operations * fops;
+ struct device_struct *next;
};
-static struct device_struct chrdevs[MAX_CHRDEV] = {
- { NULL, NULL },
-};
+/* static array used during initialization, when kmalloc is absent */
+#define STATIC_ENT_POOL_MAX 512
+static struct device_struct __initdata static_ent_pool[STATIC_ENT_POOL_MAX];
+static unsigned int __initdata static_ent_pool_len = 0;
-static struct device_struct blkdevs[MAX_BLKDEV] = {
- { NULL, NULL },
-};
+/* linked list of registered char and block devices */
+static struct device_struct *devlist = NULL;
+
+/* is it safe to use kmalloc? */
+static int use_kmalloc = 0;
+
+static struct device_struct *ent_from_major(devtype_t devtype, int major)
+{
+ struct device_struct *tmp;
+
+ for (tmp = devlist; tmp; tmp = tmp->next)
+ if (tmp->devtype == devtype && tmp->major == major)
+ return tmp;
+
+ return NULL;
+}
+
+extern inline struct device_struct * chrdev_from_major (int major) {
+ return ent_from_major (CHRDEV, major);
+}
+
+extern inline struct device_struct * blkdev_from_major (int major) {
+ return ent_from_major (BLKDEV, major);
+}
+/* generate /proc/devices output */
int get_device_list(char * page)
{
- int i;
int len;
-
+ struct device_struct *tmp;
+
len = sprintf(page, "Character devices:\n");
- for (i = 0; i < MAX_CHRDEV ; i++) {
- if (chrdevs[i].fops) {
- len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name);
- }
- }
+
+ for (tmp = devlist; tmp; tmp = tmp->next)
+ if (tmp->devtype == CHRDEV)
+ len += sprintf(page+len, "%3d %s\n", tmp->major, tmp->name);
+
len += sprintf(page+len, "\nBlock devices:\n");
- for (i = 0; i < MAX_BLKDEV ; i++) {
- if (blkdevs[i].fops) {
- len += sprintf(page+len, "%3d %s\n", i, blkdevs[i].name);
- }
- }
+
+ for (tmp = devlist; tmp; tmp = tmp->next)
+ if (tmp->devtype == BLKDEV)
+ len += sprintf(page+len, "%3d %s\n", tmp->major, tmp->name);
+
return len;
}
@@ -66,41 +104,44 @@
Load the driver if needed.
*/
static struct file_operations * get_fops(
+ devtype_t devtype,
unsigned int major,
unsigned int minor,
unsigned int maxdev,
- const char *mangle, /* String to use to build the module name */
- struct device_struct tb[])
+ const char *mangle) /* String to use to build the module name */
{
- struct file_operations *ret = NULL;
+ struct device_struct *ent = ent_from_major (devtype, major);
+
+ /*
+ * 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)
+ */
+ if (major == 0 || major >= maxdev)
+ return NULL;
- if (major < maxdev){
#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);
- }
-#endif
- ret = tb[major].fops;
+ /*
+ * 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)) || !ent) {
+ char name[20];
+ sprintf(name, mangle, major);
+ request_module(name);
+
+ ent = ent_from_major (devtype, major);
}
- return ret;
+#endif
+
+ return ent ? ent->fops : NULL;
}
@@ -110,82 +151,164 @@
*/
struct file_operations * get_blkfops(unsigned int major)
{
- return get_fops (major,0,MAX_BLKDEV,"block-major-%d",blkdevs);
+ return get_fops (BLKDEV, major, 0, MAX_BLKDEV, "block-major-%d");
}
struct file_operations * get_chrfops(unsigned int major, unsigned int minor)
{
- return get_fops (major,minor,MAX_CHRDEV,"char-major-%d",chrdevs);
+ return get_fops (CHRDEV, major, minor, MAX_CHRDEV, "char-major-%d");
}
-int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
+static struct device_struct * __init new_static_device (void)
+{
+ if (static_ent_pool_len < STATIC_ENT_POOL_MAX) {
+ static_ent_pool_len++;
+ return &static_ent_pool[static_ent_pool_len - 1];
+ }
+ return NULL;
+}
+
+static struct device_struct *new_device_struct
+ (devtype_t devtype,
+ unsigned int major,
+ const char * name,
+ struct file_operations *fops)
{
+ struct device_struct *last, *tmp, *ent;
+
+ if (use_kmalloc)
+ ent = kmalloc (sizeof (*ent), GFP_KERNEL);
+ else
+ ent = new_static_device ();
+
+ if (!ent)
+ return NULL;
+
+ ent->devtype = devtype;
+ ent->major = major;
+ ent->name = name;
+ ent->fops = fops;
+ ent->next = NULL;
+
+ if (!devlist) {
+ devlist = ent;
+ return ent;
+ }
+
+ tmp = devlist;
+ last = NULL;
+ while (tmp && tmp->major < major) {
+ last = tmp;
+ tmp = tmp->next;
+ }
+
+ if (!last) {
+ ent->next = devlist;
+ devlist = ent;
+ } else {
+ ent->next = last->next;
+ last->next = ent;
+ }
+
+ return ent;
+}
+
+/*
+ * note - fops==NULL must succeed -jgarzik
+ */
+static int register_anydev(unsigned int maxdev,
+ devtype_t devtype,
+ unsigned int major,
+ const char * name,
+ struct file_operations *fops)
+{
+ struct device_struct *ent;
+
+ if (major >= maxdev)
+ return -EINVAL;
+
if (major == 0) {
- for (major = MAX_CHRDEV-1; major > 0; major--) {
- if (chrdevs[major].fops == NULL) {
- chrdevs[major].name = name;
- chrdevs[major].fops = fops;
+ for (major = maxdev-1; major > 0; major--) {
+ ent = ent_from_major(devtype, major);
+ if (!ent) {
+ ent = new_device_struct (devtype, major, name, fops);
+ if (!ent) return -ENOMEM;
return major;
}
+ else if (ent && !ent->fops) {
+ ent->name = name;
+ ent->fops = fops;
+ return major;
+ }
}
return -EBUSY;
}
- if (major >= MAX_CHRDEV)
- return -EINVAL;
- if (chrdevs[major].fops && chrdevs[major].fops != fops)
- return -EBUSY;
- chrdevs[major].name = name;
- chrdevs[major].fops = fops;
+
+ ent = ent_from_major(devtype, major);
+
+ if (!ent) {
+ ent = new_device_struct (devtype, major, name, fops);
+ if (!ent) return -ENOMEM;
+ } else {
+ ent->name = name;
+ ent->fops = fops;
+ }
+
return 0;
}
+
+
+int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
+{
+ return register_anydev (MAX_CHRDEV, CHRDEV, major, name, fops);
+}
+
int register_blkdev(unsigned int major, const char * name, struct file_operations *fops)
{
- if (major == 0) {
- for (major = MAX_BLKDEV-1; major > 0; major--) {
- if (blkdevs[major].fops == NULL) {
- blkdevs[major].name = name;
- blkdevs[major].fops = fops;
- return major;
- }
- }
- return -EBUSY;
+ return register_anydev (MAX_BLKDEV, BLKDEV, major, name, fops);
+}
+
+static int unregister_anydev(devtype_t devtype,
+ unsigned int major,
+ const char * name)
+{
+ struct device_struct *tmp, *last;
+
+ if (!devlist)
+ return -EINVAL;
+
+ tmp = devlist;
+ last = NULL;
+ while (tmp && tmp->major != major) {
+ last = tmp;
+ tmp = tmp->next;
}
- if (major >= MAX_BLKDEV)
+ if (!tmp || strcmp(tmp->name, name))
return -EINVAL;
- if (blkdevs[major].fops && blkdevs[major].fops != fops)
- return -EBUSY;
- blkdevs[major].name = name;
- blkdevs[major].fops = fops;
+
+ if (!last)
+ devlist = devlist->next;
+ else
+ last->next = tmp->next;
+
+ if (use_kmalloc)
+ kfree (tmp);
+
return 0;
}
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))
- return -EINVAL;
- chrdevs[major].name = NULL;
- chrdevs[major].fops = NULL;
- return 0;
+ return unregister_anydev (CHRDEV, major, name);
}
int unregister_blkdev(unsigned int major, const char * name)
{
- if (major >= MAX_BLKDEV)
- return -EINVAL;
- if (!blkdevs[major].fops)
- return -EINVAL;
- if (strcmp(blkdevs[major].name, name))
- return -EINVAL;
- blkdevs[major].name = NULL;
- blkdevs[major].fops = NULL;
- return 0;
+ return unregister_anydev (BLKDEV, major, name);
}
+
/*
* This routine checks whether a removable media has been changed,
* and invalidates all buffer-cache-entries in that case. This
@@ -197,16 +320,17 @@
*/
int check_disk_change(kdev_t dev)
{
- int i;
struct file_operations * fops;
struct super_block * sb;
+ struct device_struct *ent;
- i = MAJOR(dev);
- if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL)
- return 0;
- if (fops->check_media_change == NULL)
+ ent = blkdev_from_major(MAJOR(dev));
+ if (!ent || !ent->fops)
return 0;
- if (!fops->check_media_change(dev))
+
+ fops = ent->fops;
+ if (!fops->check_media_change ||
+ !fops->check_media_change(dev))
return 0;
printk(KERN_DEBUG "VFS: Disk change detected on device %s\n",
@@ -349,34 +473,40 @@
* Print device name (in decimal, hexadecimal or symbolic)
* Note: returns pointer to static data!
*/
-char * kdevname(kdev_t dev)
+const char * kdevname(kdev_t dev)
{
static char buffer[32];
sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev));
return buffer;
}
-char * bdevname(kdev_t dev)
+static const char *anydevname (devtype_t devtype, kdev_t dev)
{
static char buffer[32];
- const char * name = blkdevs[MAJOR(dev)].name;
+ const char * name;
+ struct device_struct *ent = ent_from_major(devtype, MAJOR(dev));
- if (!name)
- name = "unknown-block";
+ if (ent)
+ name = ent->name;
+ else
+ switch (devtype) {
+ case CHRDEV: name = "unknown-char"; break;
+ case BLKDEV: name = "unknown-block"; break;
+ default: name = "unknown"; break;
+ }
sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev));
return buffer;
}
-char * cdevname(kdev_t dev)
+const char * bdevname(kdev_t dev)
{
- static char buffer[32];
- const char * name = chrdevs[MAJOR(dev)].name;
+ return anydevname (BLKDEV, dev);
+}
- if (!name)
- name = "unknown-char";
- sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev));
- return buffer;
+const char * cdevname(kdev_t dev)
+{
+ return anydevname (CHRDEV, dev);
}
void init_special_inode(struct inode *inode, umode_t mode, int rdev)
@@ -396,3 +526,38 @@
else
printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
}
+
+
+/*
+ * copy device list from static, __initdata'd vector into dynamic RAM
+ */
+
+static int __init devices_copy_init_list (void)
+{
+ struct device_struct *ent, *tmp, *last;
+
+ tmp = devlist;
+ last = NULL;
+ while (tmp != NULL) {
+ ent = kmalloc (sizeof (*ent), GFP_KERNEL);
+ if (!ent)
+ panic("kmalloc failed: Cannot init devices subsystem");
+ memcpy (ent, tmp, sizeof (*ent));
+
+ if (last == NULL) {
+ devlist = tmp = ent;
+ } else {
+ last->next = tmp = ent;
+ }
+
+ last = tmp;
+ tmp = tmp->next;
+ }
+
+ use_kmalloc = 1;
+
+ return 0;
+}
+
+/* cheap way to get something done "sometime after kmalloc works" */
+module_init(devices_copy_init_list);
Index: include/linux/fs.h
===================================================================
RCS file: /g/cvslan/linux_2_3/include/linux/fs.h,v
retrieving revision 1.1.1.5
retrieving revision 1.1.1.5.2.1
diff -u -r1.1.1.5 -r1.1.1.5.2.1
--- include/linux/fs.h 1999/10/12 01:24:41 1.1.1.5
+++ include/linux/fs.h 1999/10/12 10:23:54 1.1.1.5.2.1
@@ -741,9 +741,9 @@
extern int chrdev_open(struct inode *, struct file *);
extern struct file_operations def_chr_fops;
extern struct inode_operations chrdev_inode_operations;
-extern char * bdevname(kdev_t);
-extern char * cdevname(kdev_t);
-extern char * kdevname(kdev_t);
+extern const char * bdevname(kdev_t);
+extern const char * cdevname(kdev_t);
+extern const char * kdevname(kdev_t);
extern void init_special_inode(struct inode *, umode_t, int);
extern void init_fifo(struct inode *);
Index: include/linux/hfs_sysdep.h
===================================================================
RCS file: /g/cvslan/linux_2_3/include/linux/hfs_sysdep.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.18.1
diff -u -r1.1.1.1 -r1.1.1.1.18.1
--- include/linux/hfs_sysdep.h 1999/10/07 07:42:52 1.1.1.1
+++ include/linux/hfs_sysdep.h 1999/10/12 10:23:54 1.1.1.1.18.1
@@ -121,7 +121,7 @@
sys_mdb->s_dirt = 1;
}
-extern inline char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
+extern inline const char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
return kdevname(sys_mdb->s_dev);
}
Index: include/linux/kdev_t.h
===================================================================
RCS file: /g/cvslan/linux_2_3/include/linux/kdev_t.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.18.1
diff -u -r1.1.1.1 -r1.1.1.1.18.1
--- include/linux/kdev_t.h 1999/10/07 07:42:51 1.1.1.1
+++ include/linux/kdev_t.h 1999/10/12 10:23:54 1.1.1.1.18.1
@@ -73,7 +73,8 @@
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
#define B_FREE 0xffff /* yuk */
-extern char * kdevname(kdev_t); /* note: returns pointer to static data! */
+/* note: returns pointer to static data! */
+extern const char * kdevname(kdev_t);
/*
As long as device numbers in the outside world have 16 bits only,
--------------6490499963B66DF0BB388384--
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/