Commit 7170be5f authored by Neil Horman's avatar Neil Horman Committed by Linus Torvalds

[PATCH] convert /proc/devices to use seq_file interface

A Christoph suggested that the /proc/devices file be converted to use the
seq_file interface.  This patch does that.

I've obxerved one or two installation that had sufficiently large sans that
they overran the 4k limit on /proc/devices.
Signed-off-by: default avatarNeil Horman <nhorman@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent faf3a989
...@@ -38,34 +38,100 @@ static inline int major_to_index(int major) ...@@ -38,34 +38,100 @@ static inline int major_to_index(int major)
return major % MAX_PROBE_HASH; return major % MAX_PROBE_HASH;
} }
#ifdef CONFIG_PROC_FS struct blkdev_info {
/* get block device names in somewhat random order */ int index;
int get_blkdev_list(char *p, int used) struct blk_major_name *bd;
};
/*
* 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_sem held
*/
void *get_next_blkdev(void *dev)
{ {
struct blk_major_name *n; struct blkdev_info *info;
int i, len;
len = snprintf(p, (PAGE_SIZE-used), "\nBlock devices:\n"); 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;
}
down(&block_subsys_sem); while (info->index < ARRAY_SIZE(major_names)) {
for (i = 0; i < ARRAY_SIZE(major_names); i++) { if (info->bd)
for (n = major_names[i]; n; n = n->next) { info->bd = info->bd->next;
if (info->bd)
goto out;
/* /*
* If the curent string plus the 5 extra characters * No devices on this chain, move to the next
* in the line would run us off the page, then we're done
*/ */
if ((len + used + strlen(n->name) + 5) >= PAGE_SIZE) info->index++;
goto page_full; info->bd = (info->index < ARRAY_SIZE(major_names)) ?
len += sprintf(p+len, "%3d %s\n", major_names[info->index] : NULL;
n->major, n->name); if (info->bd)
} goto out;
} }
page_full:
out:
return info;
}
void *acquire_blkdev_list(void)
{
down(&block_subsys_sem);
return get_next_blkdev(NULL);
}
void release_blkdev_list(void *dev)
{
up(&block_subsys_sem); up(&block_subsys_sem);
kfree(dev);
}
/*
* Count the number of records in the blkdev_list.
* must be called with the block_subsys_sem 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++;
}
return len; return count;
} }
#endif
/*
* extract the major and name values from a blkdev_info struct
* passed in as a void to *dev. Must be called with
* block_subsys_sem 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;
}
int register_blkdev(unsigned int major, const char *name) int register_blkdev(unsigned int major, const char *name)
{ {
......
...@@ -35,7 +35,7 @@ static struct char_device_struct { ...@@ -35,7 +35,7 @@ static struct char_device_struct {
unsigned int major; unsigned int major;
unsigned int baseminor; unsigned int baseminor;
int minorct; int minorct;
const char *name; char name[64];
struct file_operations *fops; struct file_operations *fops;
struct cdev *cdev; /* will die */ struct cdev *cdev; /* will die */
} *chrdevs[MAX_PROBE_HASH]; } *chrdevs[MAX_PROBE_HASH];
...@@ -46,34 +46,84 @@ static inline int major_to_index(int major) ...@@ -46,34 +46,84 @@ static inline int major_to_index(int major)
return major % MAX_PROBE_HASH; return major % MAX_PROBE_HASH;
} }
/* get char device names in somewhat random order */ struct chrdev_info {
int get_chrdev_list(char *page) int index;
{
struct char_device_struct *cd; struct char_device_struct *cd;
int i, len; };
len = sprintf(page, "Character devices:\n"); void *get_next_chrdev(void *dev)
{
struct chrdev_info *info;
down(&chrdevs_lock); if (dev == NULL) {
for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { info = kmalloc(sizeof(*info), GFP_KERNEL);
for (cd = chrdevs[i]; cd; cd = cd->next) { 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;
/* /*
* if the current name, plus the 5 extra characters * No devices on this chain, move to the next
* in the device line for this entry
* would run us off the page, we're done
*/ */
if ((len+strlen(cd->name) + 5) >= PAGE_SIZE) info->index++;
goto page_full; info->cd = (info->index < ARRAY_SIZE(chrdevs)) ?
chrdevs[info->index] : NULL;
if (info->cd)
goto out;
}
out:
return info;
}
len += sprintf(page+len, "%3d %s\n", void *acquire_chrdev_list(void)
cd->major, cd->name); {
} down(&chrdevs_lock);
} return get_next_chrdev(NULL);
page_full: }
void release_chrdev_list(void *dev)
{
up(&chrdevs_lock); up(&chrdevs_lock);
kfree(dev);
}
int count_chrdev_list(void)
{
struct char_device_struct *cd;
int i, count;
count = 0;
return len; for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) {
for (cd = chrdevs[i]; cd; cd = cd->next)
count++;
}
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;
} }
/* /*
...@@ -121,7 +171,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, ...@@ -121,7 +171,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor,
cd->major = major; cd->major = major;
cd->baseminor = baseminor; cd->baseminor = baseminor;
cd->minorct = minorct; cd->minorct = minorct;
cd->name = name; strncpy(cd->name,name, 64);
i = major_to_index(major); i = major_to_index(major);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/time.h> #include <linux/time.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/fs.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/mman.h> #include <linux/mman.h>
...@@ -62,7 +63,6 @@ ...@@ -62,7 +63,6 @@
*/ */
extern int get_hardware_list(char *); extern int get_hardware_list(char *);
extern int get_stram_list(char *); extern int get_stram_list(char *);
extern int get_chrdev_list(char *);
extern int get_filesystem_list(char *); extern int get_filesystem_list(char *);
extern int get_exec_domain_list(char *); extern int get_exec_domain_list(char *);
extern int get_dma_list(char *); extern int get_dma_list(char *);
...@@ -248,6 +248,154 @@ static int cpuinfo_open(struct inode *inode, struct file *file) ...@@ -248,6 +248,154 @@ static int cpuinfo_open(struct inode *inode, struct file *file)
{ {
return seq_open(file, &cpuinfo_op); 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)++;
break;
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,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static struct file_operations proc_cpuinfo_operations = { static struct file_operations proc_cpuinfo_operations = {
.open = cpuinfo_open, .open = cpuinfo_open,
.read = seq_read, .read = seq_read,
...@@ -450,14 +598,6 @@ static struct file_operations proc_stat_operations = { ...@@ -450,14 +598,6 @@ static struct file_operations proc_stat_operations = {
.release = single_release, .release = single_release,
}; };
static int devices_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = get_chrdev_list(page);
len += get_blkdev_list(page+len, len);
return proc_calc_metrics(page, start, off, count, eof, len);
}
/* /*
* /proc/interrupts * /proc/interrupts
*/ */
...@@ -582,7 +722,6 @@ void __init proc_misc_init(void) ...@@ -582,7 +722,6 @@ void __init proc_misc_init(void)
#ifdef CONFIG_STRAM_PROC #ifdef CONFIG_STRAM_PROC
{"stram", stram_read_proc}, {"stram", stram_read_proc},
#endif #endif
{"devices", devices_read_proc},
{"filesystems", filesystems_read_proc}, {"filesystems", filesystems_read_proc},
{"cmdline", cmdline_read_proc}, {"cmdline", cmdline_read_proc},
{"locks", locks_read_proc}, {"locks", locks_read_proc},
...@@ -598,6 +737,7 @@ void __init proc_misc_init(void) ...@@ -598,6 +737,7 @@ void __init proc_misc_init(void)
entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
if (entry) if (entry)
entry->proc_fops = &proc_kmsg_operations; entry->proc_fops = &proc_kmsg_operations;
create_seq_entry("devices", 0, &proc_devinfo_operations);
create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations); create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
create_seq_entry("partitions", 0, &proc_partitions_operations); create_seq_entry("partitions", 0, &proc_partitions_operations);
create_seq_entry("stat", 0, &proc_stat_operations); create_seq_entry("stat", 0, &proc_stat_operations);
......
...@@ -1383,6 +1383,12 @@ extern int register_chrdev(unsigned int, const char *, ...@@ -1383,6 +1383,12 @@ extern int register_chrdev(unsigned int, const char *,
extern int unregister_chrdev(unsigned int, const char *); extern int unregister_chrdev(unsigned int, const char *);
extern void unregister_chrdev_region(dev_t, unsigned); extern void unregister_chrdev_region(dev_t, unsigned);
extern int chrdev_open(struct inode *, struct file *); 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 *);
/* fs/block_dev.c */ /* fs/block_dev.c */
#define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
...@@ -1391,6 +1397,11 @@ extern const char *bdevname(struct block_device *bdev, char *buffer); ...@@ -1391,6 +1397,11 @@ extern const char *bdevname(struct block_device *bdev, char *buffer);
extern struct block_device *lookup_bdev(const char *); extern struct block_device *lookup_bdev(const char *);
extern struct block_device *open_bdev_excl(const char *, int, void *); extern struct block_device *open_bdev_excl(const char *, int, void *);
extern void close_bdev_excl(struct block_device *); 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 init_special_inode(struct inode *, umode_t, dev_t); extern void init_special_inode(struct inode *, umode_t, dev_t);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment