mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
[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>
This commit is contained in:
parent
a2c348fe01
commit
68eef3b479
4 changed files with 79 additions and 323 deletions
103
block/genhd.c
103
block/genhd.c
|
@ -17,8 +17,6 @@
|
|||
#include <linux/buffer_head.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define MAX_PROBE_HASH 255 /* random */
|
||||
|
||||
static struct subsystem block_subsys;
|
||||
|
||||
static DEFINE_MUTEX(block_subsys_lock);
|
||||
|
@ -31,108 +29,29 @@ static struct blk_major_name {
|
|||
struct blk_major_name *next;
|
||||
int major;
|
||||
char name[16];
|
||||
} *major_names[MAX_PROBE_HASH];
|
||||
} *major_names[BLKDEV_MAJOR_HASH_SIZE];
|
||||
|
||||
/* index in the above - for now: assume no multimajor ranges */
|
||||
static inline int major_to_index(int major)
|
||||
{
|
||||
return major % MAX_PROBE_HASH;
|
||||
return major % BLKDEV_MAJOR_HASH_SIZE;
|
||||
}
|
||||
|
||||
struct blkdev_info {
|
||||
int index;
|
||||
struct blk_major_name *bd;
|
||||
};
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
/*
|
||||
* iterate over a list of blkdev_info structures. allows
|
||||
* the major_names array to be iterated over from outside this file
|
||||
* must be called with the block_subsys_lock held
|
||||
*/
|
||||
void *get_next_blkdev(void *dev)
|
||||
void blkdev_show(struct seq_file *f, off_t offset)
|
||||
{
|
||||
struct blkdev_info *info;
|
||||
struct blk_major_name *dp;
|
||||
|
||||
if (dev == NULL) {
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto out;
|
||||
info->index=0;
|
||||
info->bd = major_names[info->index];
|
||||
if (info->bd)
|
||||
goto out;
|
||||
} else {
|
||||
info = dev;
|
||||
}
|
||||
|
||||
while (info->index < ARRAY_SIZE(major_names)) {
|
||||
if (info->bd)
|
||||
info->bd = info->bd->next;
|
||||
if (info->bd)
|
||||
goto out;
|
||||
/*
|
||||
* No devices on this chain, move to the next
|
||||
*/
|
||||
info->index++;
|
||||
info->bd = (info->index < ARRAY_SIZE(major_names)) ?
|
||||
major_names[info->index] : NULL;
|
||||
if (info->bd)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return info;
|
||||
}
|
||||
|
||||
void *acquire_blkdev_list(void)
|
||||
{
|
||||
mutex_lock(&block_subsys_lock);
|
||||
return get_next_blkdev(NULL);
|
||||
}
|
||||
|
||||
void release_blkdev_list(void *dev)
|
||||
{
|
||||
mutex_unlock(&block_subsys_lock);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Count the number of records in the blkdev_list.
|
||||
* must be called with the block_subsys_lock held
|
||||
*/
|
||||
int count_blkdev_list(void)
|
||||
{
|
||||
struct blk_major_name *n;
|
||||
int i, count;
|
||||
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(major_names); i++) {
|
||||
for (n = major_names[i]; n; n = n->next)
|
||||
count++;
|
||||
if (offset < BLKDEV_MAJOR_HASH_SIZE) {
|
||||
mutex_lock(&block_subsys_lock);
|
||||
for (dp = major_names[offset]; dp; dp = dp->next)
|
||||
seq_printf(f, "%3d %s\n", dp->major, dp->name);
|
||||
mutex_unlock(&block_subsys_lock);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* extract the major and name values from a blkdev_info struct
|
||||
* passed in as a void to *dev. Must be called with
|
||||
* block_subsys_lock held
|
||||
*/
|
||||
int get_blkdev_info(void *dev, int *major, char **name)
|
||||
{
|
||||
struct blkdev_info *info = dev;
|
||||
|
||||
if (info->bd == NULL)
|
||||
return 1;
|
||||
|
||||
*major = info->bd->major;
|
||||
*name = info->bd->name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
int register_blkdev(unsigned int major, const char *name)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/devfs_fs_kernel.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/kobj_map.h>
|
||||
|
@ -27,8 +28,6 @@
|
|||
|
||||
static struct kobj_map *cdev_map;
|
||||
|
||||
#define MAX_PROBE_HASH 255 /* random */
|
||||
|
||||
static DEFINE_MUTEX(chrdevs_lock);
|
||||
|
||||
static struct char_device_struct {
|
||||
|
@ -39,93 +38,29 @@ static struct char_device_struct {
|
|||
char name[64];
|
||||
struct file_operations *fops;
|
||||
struct cdev *cdev; /* will die */
|
||||
} *chrdevs[MAX_PROBE_HASH];
|
||||
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
|
||||
|
||||
/* index in the above */
|
||||
static inline int major_to_index(int major)
|
||||
{
|
||||
return major % MAX_PROBE_HASH;
|
||||
return major % CHRDEV_MAJOR_HASH_SIZE;
|
||||
}
|
||||
|
||||
struct chrdev_info {
|
||||
int index;
|
||||
struct char_device_struct *cd;
|
||||
};
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
void *get_next_chrdev(void *dev)
|
||||
{
|
||||
struct chrdev_info *info;
|
||||
|
||||
if (dev == NULL) {
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto out;
|
||||
info->index=0;
|
||||
info->cd = chrdevs[info->index];
|
||||
if (info->cd)
|
||||
goto out;
|
||||
} else {
|
||||
info = dev;
|
||||
}
|
||||
|
||||
while (info->index < ARRAY_SIZE(chrdevs)) {
|
||||
if (info->cd)
|
||||
info->cd = info->cd->next;
|
||||
if (info->cd)
|
||||
goto out;
|
||||
/*
|
||||
* No devices on this chain, move to the next
|
||||
*/
|
||||
info->index++;
|
||||
info->cd = (info->index < ARRAY_SIZE(chrdevs)) ?
|
||||
chrdevs[info->index] : NULL;
|
||||
if (info->cd)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return info;
|
||||
}
|
||||
|
||||
void *acquire_chrdev_list(void)
|
||||
{
|
||||
mutex_lock(&chrdevs_lock);
|
||||
return get_next_chrdev(NULL);
|
||||
}
|
||||
|
||||
void release_chrdev_list(void *dev)
|
||||
{
|
||||
mutex_unlock(&chrdevs_lock);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
|
||||
int count_chrdev_list(void)
|
||||
void chrdev_show(struct seq_file *f, off_t offset)
|
||||
{
|
||||
struct char_device_struct *cd;
|
||||
int i, count;
|
||||
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) {
|
||||
for (cd = chrdevs[i]; cd; cd = cd->next)
|
||||
count++;
|
||||
if (offset < CHRDEV_MAJOR_HASH_SIZE) {
|
||||
mutex_lock(&chrdevs_lock);
|
||||
for (cd = chrdevs[offset]; cd; cd = cd->next)
|
||||
seq_printf(f, "%3d %s\n", cd->major, cd->name);
|
||||
mutex_unlock(&chrdevs_lock);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int get_chrdev_info(void *dev, int *major, char **name)
|
||||
{
|
||||
struct chrdev_info *info = dev;
|
||||
|
||||
if (info->cd == NULL)
|
||||
return 1;
|
||||
|
||||
*major = info->cd->major;
|
||||
*name = info->cd->name;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
/*
|
||||
* Register a single major with a specified minor range.
|
||||
|
|
|
@ -249,155 +249,64 @@ static int cpuinfo_open(struct inode *inode, struct file *file)
|
|||
return seq_open(file, &cpuinfo_op);
|
||||
}
|
||||
|
||||
enum devinfo_states {
|
||||
CHR_HDR,
|
||||
CHR_LIST,
|
||||
BLK_HDR,
|
||||
BLK_LIST,
|
||||
DEVINFO_DONE
|
||||
};
|
||||
|
||||
struct devinfo_state {
|
||||
void *chrdev;
|
||||
void *blkdev;
|
||||
unsigned int num_records;
|
||||
unsigned int cur_record;
|
||||
enum devinfo_states state;
|
||||
};
|
||||
|
||||
static void *devinfo_start(struct seq_file *f, loff_t *pos)
|
||||
{
|
||||
struct devinfo_state *info = f->private;
|
||||
|
||||
if (*pos) {
|
||||
if ((info) && (*pos <= info->num_records))
|
||||
return info;
|
||||
return NULL;
|
||||
}
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
f->private = info;
|
||||
info->chrdev = acquire_chrdev_list();
|
||||
info->blkdev = acquire_blkdev_list();
|
||||
info->state = CHR_HDR;
|
||||
info->num_records = count_chrdev_list();
|
||||
info->num_records += count_blkdev_list();
|
||||
info->num_records += 2; /* Character and Block headers */
|
||||
*pos = 1;
|
||||
info->cur_record = *pos;
|
||||
return info;
|
||||
}
|
||||
|
||||
static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos)
|
||||
{
|
||||
int idummy;
|
||||
char *ndummy;
|
||||
struct devinfo_state *info = f->private;
|
||||
|
||||
switch (info->state) {
|
||||
case CHR_HDR:
|
||||
info->state = CHR_LIST;
|
||||
(*pos)++;
|
||||
/*fallthrough*/
|
||||
case CHR_LIST:
|
||||
if (get_chrdev_info(info->chrdev,&idummy,&ndummy)) {
|
||||
/*
|
||||
* The character dev list is complete
|
||||
*/
|
||||
info->state = BLK_HDR;
|
||||
} else {
|
||||
info->chrdev = get_next_chrdev(info->chrdev);
|
||||
}
|
||||
(*pos)++;
|
||||
break;
|
||||
case BLK_HDR:
|
||||
info->state = BLK_LIST;
|
||||
(*pos)++;
|
||||
/*fallthrough*/
|
||||
case BLK_LIST:
|
||||
if (get_blkdev_info(info->blkdev,&idummy,&ndummy)) {
|
||||
/*
|
||||
* The block dev list is complete
|
||||
*/
|
||||
info->state = DEVINFO_DONE;
|
||||
} else {
|
||||
info->blkdev = get_next_blkdev(info->blkdev);
|
||||
}
|
||||
(*pos)++;
|
||||
break;
|
||||
case DEVINFO_DONE:
|
||||
(*pos)++;
|
||||
info->cur_record = *pos;
|
||||
info = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (info)
|
||||
info->cur_record = *pos;
|
||||
return info;
|
||||
}
|
||||
|
||||
static void devinfo_stop(struct seq_file *f, void *v)
|
||||
{
|
||||
struct devinfo_state *info = f->private;
|
||||
|
||||
if (info) {
|
||||
release_chrdev_list(info->chrdev);
|
||||
release_blkdev_list(info->blkdev);
|
||||
f->private = NULL;
|
||||
kfree(info);
|
||||
}
|
||||
}
|
||||
|
||||
static int devinfo_show(struct seq_file *f, void *arg)
|
||||
{
|
||||
int major;
|
||||
char *name;
|
||||
struct devinfo_state *info = f->private;
|
||||
|
||||
switch(info->state) {
|
||||
case CHR_HDR:
|
||||
seq_printf(f,"Character devices:\n");
|
||||
/* fallthrough */
|
||||
case CHR_LIST:
|
||||
if (!get_chrdev_info(info->chrdev,&major,&name))
|
||||
seq_printf(f,"%3d %s\n",major,name);
|
||||
break;
|
||||
case BLK_HDR:
|
||||
seq_printf(f,"\nBlock devices:\n");
|
||||
/* fallthrough */
|
||||
case BLK_LIST:
|
||||
if (!get_blkdev_info(info->blkdev,&major,&name))
|
||||
seq_printf(f,"%3d %s\n",major,name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations devinfo_op = {
|
||||
.start = devinfo_start,
|
||||
.next = devinfo_next,
|
||||
.stop = devinfo_stop,
|
||||
.show = devinfo_show,
|
||||
};
|
||||
|
||||
static int devinfo_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &devinfo_op);
|
||||
}
|
||||
|
||||
static struct file_operations proc_devinfo_operations = {
|
||||
.open = devinfo_open,
|
||||
static struct file_operations proc_cpuinfo_operations = {
|
||||
.open = cpuinfo_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static struct file_operations proc_cpuinfo_operations = {
|
||||
.open = cpuinfo_open,
|
||||
static int devinfo_show(struct seq_file *f, void *v)
|
||||
{
|
||||
int i = *(loff_t *) v;
|
||||
|
||||
if (i < CHRDEV_MAJOR_HASH_SIZE) {
|
||||
if (i == 0)
|
||||
seq_printf(f, "Character devices:\n");
|
||||
chrdev_show(f, i);
|
||||
} else {
|
||||
i -= CHRDEV_MAJOR_HASH_SIZE;
|
||||
if (i == 0)
|
||||
seq_printf(f, "\nBlock devices:\n");
|
||||
blkdev_show(f, i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *devinfo_start(struct seq_file *f, loff_t *pos)
|
||||
{
|
||||
if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
|
||||
return pos;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void devinfo_stop(struct seq_file *f, void *v)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
static struct seq_operations devinfo_ops = {
|
||||
.start = devinfo_start,
|
||||
.next = devinfo_next,
|
||||
.stop = devinfo_stop,
|
||||
.show = devinfo_show
|
||||
};
|
||||
|
||||
static int devinfo_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return seq_open(filp, &devinfo_ops);
|
||||
}
|
||||
|
||||
static struct file_operations proc_devinfo_operations = {
|
||||
.open = devinfo_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
|
|
|
@ -1413,6 +1413,7 @@ extern void bd_release_from_disk(struct block_device *, struct gendisk *);
|
|||
#endif
|
||||
|
||||
/* fs/char_dev.c */
|
||||
#define CHRDEV_MAJOR_HASH_SIZE 255
|
||||
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
|
||||
extern int register_chrdev_region(dev_t, unsigned, const char *);
|
||||
extern int register_chrdev(unsigned int, const char *,
|
||||
|
@ -1420,25 +1421,17 @@ extern int register_chrdev(unsigned int, const char *,
|
|||
extern int unregister_chrdev(unsigned int, const char *);
|
||||
extern void unregister_chrdev_region(dev_t, unsigned);
|
||||
extern int chrdev_open(struct inode *, struct file *);
|
||||
extern int get_chrdev_list(char *);
|
||||
extern void *acquire_chrdev_list(void);
|
||||
extern int count_chrdev_list(void);
|
||||
extern void *get_next_chrdev(void *);
|
||||
extern int get_chrdev_info(void *, int *, char **);
|
||||
extern void release_chrdev_list(void *);
|
||||
extern void chrdev_show(struct seq_file *,off_t);
|
||||
|
||||
/* fs/block_dev.c */
|
||||
#define BLKDEV_MAJOR_HASH_SIZE 255
|
||||
#define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
|
||||
extern const char *__bdevname(dev_t, char *buffer);
|
||||
extern const char *bdevname(struct block_device *bdev, char *buffer);
|
||||
extern struct block_device *lookup_bdev(const char *);
|
||||
extern struct block_device *open_bdev_excl(const char *, int, void *);
|
||||
extern void close_bdev_excl(struct block_device *);
|
||||
extern void *acquire_blkdev_list(void);
|
||||
extern int count_blkdev_list(void);
|
||||
extern void *get_next_blkdev(void *);
|
||||
extern int get_blkdev_info(void *, int *, char **);
|
||||
extern void release_blkdev_list(void *);
|
||||
extern void blkdev_show(struct seq_file *,off_t);
|
||||
|
||||
extern void init_special_inode(struct inode *, umode_t, dev_t);
|
||||
|
||||
|
|
Loading…
Reference in a new issue