[PATCH] struct char_device

Alexander Viro (viro@math.psu.edu)
Tue, 22 May 2001 10:18:28 -0400 (EDT)


Linus, patch below adds the missing half of kdev_t -
for block devices we already have a unique pointer (struct block_device *,
inode->i_bdev) and that adds a similar animal for character devices.
That is, it adds a new structure (struct char_device) and a cache
indexed by dev_t. init_special_inode() sets ->i_cdev to corresponding
element of cache (creating it if needed).
Result: ->i_cdev is shared by all inodes of given character device
(i.e. if we need per-device objects we can put them there), we can use
stuct char_device * as ID for character devices, we can (in 2.5) get
rid of i_rdev - it's covered by ->i_bdev and ->i_cdev now.
Patch is pretty straightforward - cache handling is lifted from
fs/block_device, the rest is trivial.
Please, consider applying.
Al

diff -urN S5-pre4/fs/Makefile S5-pre4-cdev/fs/Makefile
--- S5-pre4/fs/Makefile Thu May 3 17:13:26 2001
+++ S5-pre4-cdev/fs/Makefile Tue May 22 09:12:11 2001
@@ -11,8 +11,8 @@
mod-subdirs := nls

obj-y := open.o read_write.o devices.o file_table.o buffer.o \
- super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
- ioctl.o readdir.o select.o fifo.o locks.o \
+ super.o block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
+ fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
filesystems.o

diff -urN S5-pre4/fs/block_dev.c S5-pre4-cdev/fs/block_dev.c
--- S5-pre4/fs/block_dev.c Sat May 19 22:46:35 2001
+++ S5-pre4-cdev/fs/block_dev.c Tue May 22 08:34:44 2001
@@ -392,7 +392,7 @@
}
}

-void __init bdev_init(void)
+void __init bdev_cache_init(void)
{
int i;
struct list_head *head = bdev_hashtable;
diff -urN S5-pre4/fs/char_dev.c S5-pre4-cdev/fs/char_dev.c
--- S5-pre4/fs/char_dev.c Wed Dec 31 19:00:00 1969
+++ S5-pre4-cdev/fs/char_dev.c Tue May 22 10:03:10 2001
@@ -0,0 +1,114 @@
+/*
+ * linux/fs/block_dev.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#define HASH_BITS 6
+#define HASH_SIZE (1UL << HASH_BITS)
+#define HASH_MASK (HASH_SIZE-1)
+static struct list_head cdev_hashtable[HASH_SIZE];
+static spinlock_t cdev_lock = SPIN_LOCK_UNLOCKED;
+static kmem_cache_t * cdev_cachep;
+
+#define alloc_cdev() \
+ ((struct char_device *) kmem_cache_alloc(cdev_cachep, SLAB_KERNEL))
+#define destroy_cdev(cdev) kmem_cache_free(cdev_cachep, (cdev))
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct char_device * cdev = (struct char_device *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ {
+ memset(cdev, 0, sizeof(*cdev));
+ sema_init(&cdev->sem, 1);
+ }
+}
+
+void __init cdev_cache_init(void)
+{
+ int i;
+ struct list_head *head = cdev_hashtable;
+
+ i = HASH_SIZE;
+ do {
+ INIT_LIST_HEAD(head);
+ head++;
+ i--;
+ } while (i);
+
+ cdev_cachep = kmem_cache_create("cdev_cache",
+ sizeof(struct char_device),
+ 0, SLAB_HWCACHE_ALIGN, init_once,
+ NULL);
+ if (!cdev_cachep)
+ panic("Cannot create cdev_cache SLAB cache");
+}
+
+/*
+ * Most likely _very_ bad one - but then it's hardly critical for small
+ * /dev and can be fixed when somebody will need really large one.
+ */
+static inline unsigned long hash(dev_t dev)
+{
+ unsigned long tmp = dev;
+ tmp = tmp + (tmp >> HASH_BITS) + (tmp >> HASH_BITS*2);
+ return tmp & HASH_MASK;
+}
+
+static struct char_device *cdfind(dev_t dev, struct list_head *head)
+{
+ struct list_head *p;
+ struct char_device *cdev;
+ for (p=head->next; p!=head; p=p->next) {
+ cdev = list_entry(p, struct char_device, hash);
+ if (cdev->dev != dev)
+ continue;
+ atomic_inc(&cdev->count);
+ return cdev;
+ }
+ return NULL;
+}
+
+struct char_device *cdget(dev_t dev)
+{
+ struct list_head * head = cdev_hashtable + hash(dev);
+ struct char_device *cdev, *new_cdev;
+ spin_lock(&cdev_lock);
+ cdev = cdfind(dev, head);
+ spin_unlock(&cdev_lock);
+ if (cdev)
+ return cdev;
+ new_cdev = alloc_cdev();
+ if (!new_cdev)
+ return NULL;
+ atomic_set(&new_cdev->count,1);
+ new_cdev->dev = dev;
+ spin_lock(&cdev_lock);
+ cdev = cdfind(dev, head);
+ if (!cdev) {
+ list_add(&new_cdev->hash, head);
+ spin_unlock(&cdev_lock);
+ return new_cdev;
+ }
+ spin_unlock(&cdev_lock);
+ destroy_cdev(new_cdev);
+ return cdev;
+}
+
+void cdput(struct char_device *cdev)
+{
+ if (atomic_dec_and_test(&cdev->count)) {
+ spin_lock(&cdev_lock);
+ list_del(&cdev->hash);
+ spin_unlock(&cdev_lock);
+ destroy_cdev(cdev);
+ }
+}
+
diff -urN S5-pre4/fs/dcache.c S5-pre4-cdev/fs/dcache.c
--- S5-pre4/fs/dcache.c Sat Apr 28 02:12:56 2001
+++ S5-pre4-cdev/fs/dcache.c Tue May 22 09:22:43 2001
@@ -1250,6 +1250,9 @@
kmem_cache_t *bh_cachep;
EXPORT_SYMBOL(bh_cachep);

+extern void bdev_cache_init(void);
+extern void cdev_cache_init(void);
+
void __init vfs_caches_init(unsigned long mempages)
{
bh_cachep = kmem_cache_create("buffer_head",
@@ -1279,4 +1282,7 @@
#endif

dcache_init(mempages);
+ inode_init(mempages);
+ bdev_cache_init();
+ cdev_cache_init();
}
diff -urN S5-pre4/fs/devfs/base.c S5-pre4-cdev/fs/devfs/base.c
--- S5-pre4/fs/devfs/base.c Sat May 19 22:46:35 2001
+++ S5-pre4-cdev/fs/devfs/base.c Tue May 22 08:51:03 2001
@@ -2256,6 +2256,7 @@
{
inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
de->u.fcb.u.device.minor);
+ inode->i_cdev = cdget (kdev_t_to_nr(inode->i_rdev));
}
else if ( S_ISBLK (de->inode.mode) )
{
diff -urN S5-pre4/fs/devices.c S5-pre4-cdev/fs/devices.c
--- S5-pre4/fs/devices.c Fri Feb 16 19:00:19 2001
+++ S5-pre4-cdev/fs/devices.c Tue May 22 08:31:04 2001
@@ -203,6 +203,7 @@
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = to_kdev_t(rdev);
+ inode->i_cdev = cdget(rdev);
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = to_kdev_t(rdev);
diff -urN S5-pre4/fs/inode.c S5-pre4-cdev/fs/inode.c
--- S5-pre4/fs/inode.c Sat May 19 22:46:35 2001
+++ S5-pre4-cdev/fs/inode.c Mon May 21 22:49:48 2001
@@ -497,6 +497,10 @@
bdput(inode->i_bdev);
inode->i_bdev = NULL;
}
+ if (inode->i_cdev) {
+ cdput(inode->i_cdev);
+ inode->i_cdev = NULL;
+ }
inode->i_state = I_CLEAR;
}

@@ -750,6 +754,7 @@
memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
inode->i_pipe = NULL;
inode->i_bdev = NULL;
+ inode->i_cdev = NULL;
inode->i_data.a_ops = &empty_aops;
inode->i_data.host = inode;
inode->i_data.gfp_mask = GFP_HIGHUSER;
diff -urN S5-pre4/include/linux/fs.h S5-pre4-cdev/include/linux/fs.h
--- S5-pre4/include/linux/fs.h Sat May 19 22:46:36 2001
+++ S5-pre4-cdev/include/linux/fs.h Tue May 22 09:14:25 2001
@@ -384,6 +384,14 @@
int gfp_mask; /* how to allocate the pages */
};

+struct char_device {
+ struct list_head hash;
+ atomic_t count;
+ dev_t dev;
+ atomic_t openers;
+ struct semaphore sem;
+};
+
struct block_device {
struct list_head bd_hash;
atomic_t bd_count;
@@ -426,8 +434,10 @@
struct address_space *i_mapping;
struct address_space i_data;
struct dquot *i_dquot[MAXQUOTAS];
+ /* These three should probably be a union */
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
+ struct char_device *i_cdev;

unsigned long i_dnotify_mask; /* Directory notify events */
struct dnotify_struct *i_dnotify; /* for directory notifications */
@@ -982,6 +992,8 @@
extern int unregister_blkdev(unsigned int, const char *);
extern struct block_device *bdget(dev_t);
extern void bdput(struct block_device *);
+extern struct char_device *cdget(dev_t);
+extern void cdput(struct char_device *);
extern int blkdev_open(struct inode *, struct file *);
extern struct file_operations def_blk_fops;
extern struct file_operations def_fifo_fops;
diff -urN S5-pre4/init/main.c S5-pre4-cdev/init/main.c
--- S5-pre4/init/main.c Sat May 19 22:46:36 2001
+++ S5-pre4-cdev/init/main.c Tue May 22 08:34:09 2001
@@ -93,7 +93,6 @@
extern void ppc_init(void);
extern void sysctl_init(void);
extern void signals_init(void);
-extern void bdev_init(void);
extern int init_pcmcia_ds(void);
extern void net_notifier_init(void);

@@ -569,8 +568,6 @@
ccwcache_init();
#endif
signals_init();
- bdev_init();
- inode_init(mempages);
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
diff -urN S5-pre4/kernel/ksyms.c S5-pre4-cdev/kernel/ksyms.c
--- S5-pre4/kernel/ksyms.c Sat May 19 22:46:37 2001
+++ S5-pre4-cdev/kernel/ksyms.c Tue May 22 09:06:47 2001
@@ -186,6 +186,8 @@
EXPORT_SYMBOL(notify_change);
EXPORT_SYMBOL(set_blocksize);
EXPORT_SYMBOL(getblk);
+EXPORT_SYMBOL(cdget);
+EXPORT_SYMBOL(cdput);
EXPORT_SYMBOL(bdget);
EXPORT_SYMBOL(bdput);
EXPORT_SYMBOL(bread);

-
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/