aboutsummaryrefslogtreecommitdiffstats
path: root/fs/char_dev.c
diff options
context:
space:
mode:
authorJoe Korty <joe.korty@ccur.com>2006-03-31 05:30:32 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-31 15:18:53 -0500
commit68eef3b4791572ecb70249c7fb145bb3742dd899 (patch)
tree1f61fce839cec8d672ae06a423d46f0a6fcd924d /fs/char_dev.c
parenta2c348fe0117adced11e374329a5ea3f7c43cb41 (diff)
[PATCH] Simplify proc/devices and fix early termination regression
Make baby-simple the code for /proc/devices. Based on the proven design for /proc/interrupts. This also fixes the early-termination regression 2.6.16 introduced, as demonstrated by: # dd if=/proc/devices bs=1 Character devices: 1 mem 27+0 records in 27+0 records out This should also work (but is untested) when /proc/devices >4096 bytes, which I believe is what the original 2.6.16 rewrite fixed. [akpm@osdl.org: cleanups, simplifications] Signed-off-by: Joe Korty <joe.korty@ccur.com> Cc: Neil Horman <nhorman@tuxdriver.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/char_dev.c')
-rw-r--r--fs/char_dev.c87
1 files changed, 11 insertions, 76 deletions
diff --git a/fs/char_dev.c b/fs/char_dev.c
index 4e1b849f912f..f3418f7a6e9d 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -15,6 +15,7 @@
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/smp_lock.h> 16#include <linux/smp_lock.h>
17#include <linux/devfs_fs_kernel.h> 17#include <linux/devfs_fs_kernel.h>
18#include <linux/seq_file.h>
18 19
19#include <linux/kobject.h> 20#include <linux/kobject.h>
20#include <linux/kobj_map.h> 21#include <linux/kobj_map.h>
@@ -27,8 +28,6 @@
27 28
28static struct kobj_map *cdev_map; 29static struct kobj_map *cdev_map;
29 30
30#define MAX_PROBE_HASH 255 /* random */
31
32static DEFINE_MUTEX(chrdevs_lock); 31static DEFINE_MUTEX(chrdevs_lock);
33 32
34static struct char_device_struct { 33static struct char_device_struct {
@@ -39,93 +38,29 @@ static struct char_device_struct {
39 char name[64]; 38 char name[64];
40 struct file_operations *fops; 39 struct file_operations *fops;
41 struct cdev *cdev; /* will die */ 40 struct cdev *cdev; /* will die */
42} *chrdevs[MAX_PROBE_HASH]; 41} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
43 42
44/* index in the above */ 43/* index in the above */
45static inline int major_to_index(int major) 44static inline int major_to_index(int major)
46{ 45{
47 return major % MAX_PROBE_HASH; 46 return major % CHRDEV_MAJOR_HASH_SIZE;
48}
49
50struct chrdev_info {
51 int index;
52 struct char_device_struct *cd;
53};
54
55void *get_next_chrdev(void *dev)
56{
57 struct chrdev_info *info;
58
59 if (dev == NULL) {
60 info = kmalloc(sizeof(*info), GFP_KERNEL);
61 if (!info)
62 goto out;
63 info->index=0;
64 info->cd = chrdevs[info->index];
65 if (info->cd)
66 goto out;
67 } else {
68 info = dev;
69 }
70
71 while (info->index < ARRAY_SIZE(chrdevs)) {
72 if (info->cd)
73 info->cd = info->cd->next;
74 if (info->cd)
75 goto out;
76 /*
77 * No devices on this chain, move to the next
78 */
79 info->index++;
80 info->cd = (info->index < ARRAY_SIZE(chrdevs)) ?
81 chrdevs[info->index] : NULL;
82 if (info->cd)
83 goto out;
84 }
85
86out:
87 return info;
88}
89
90void *acquire_chrdev_list(void)
91{
92 mutex_lock(&chrdevs_lock);
93 return get_next_chrdev(NULL);
94}
95
96void release_chrdev_list(void *dev)
97{
98 mutex_unlock(&chrdevs_lock);
99 kfree(dev);
100} 47}
101 48
49#ifdef CONFIG_PROC_FS
102 50
103int count_chrdev_list(void) 51void chrdev_show(struct seq_file *f, off_t offset)
104{ 52{
105 struct char_device_struct *cd; 53 struct char_device_struct *cd;
106 int i, count;
107
108 count = 0;
109 54
110 for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { 55 if (offset < CHRDEV_MAJOR_HASH_SIZE) {
111 for (cd = chrdevs[i]; cd; cd = cd->next) 56 mutex_lock(&chrdevs_lock);
112 count++; 57 for (cd = chrdevs[offset]; cd; cd = cd->next)
58 seq_printf(f, "%3d %s\n", cd->major, cd->name);
59 mutex_unlock(&chrdevs_lock);
113 } 60 }
114
115 return count;
116} 61}
117 62
118int get_chrdev_info(void *dev, int *major, char **name) 63#endif /* CONFIG_PROC_FS */
119{
120 struct chrdev_info *info = dev;
121
122 if (info->cd == NULL)
123 return 1;
124
125 *major = info->cd->major;
126 *name = info->cd->name;
127 return 0;
128}
129 64
130/* 65/*
131 * Register a single major with a specified minor range. 66 * Register a single major with a specified minor range.