From 7170be5f586b59bdcdab082778a5d9203ba7b667 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Sat, 14 Jan 2006 13:20:38 -0800 Subject: [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: Neil Horman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/char_dev.c | 96 +++++++++++++++++++++++-------- fs/proc/proc_misc.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 223 insertions(+), 33 deletions(-) (limited to 'fs') diff --git a/fs/char_dev.c b/fs/char_dev.c index 3b1b1eefdbb0..21195c481637 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -35,7 +35,7 @@ static struct char_device_struct { unsigned int major; unsigned int baseminor; int minorct; - const char *name; + char name[64]; struct file_operations *fops; struct cdev *cdev; /* will die */ } *chrdevs[MAX_PROBE_HASH]; @@ -46,34 +46,84 @@ static inline int major_to_index(int major) return major % MAX_PROBE_HASH; } -/* get char device names in somewhat random order */ -int get_chrdev_list(char *page) -{ +struct chrdev_info { + int index; struct char_device_struct *cd; - int i, len; +}; - len = sprintf(page, "Character devices:\n"); +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) +{ down(&chrdevs_lock); + return get_next_chrdev(NULL); +} + +void release_chrdev_list(void *dev) +{ + up(&chrdevs_lock); + kfree(dev); +} + + +int count_chrdev_list(void) +{ + 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) { - /* - * if the current name, plus the 5 extra characters - * in the device line for this entry - * would run us off the page, we're done - */ - if ((len+strlen(cd->name) + 5) >= PAGE_SIZE) - goto page_full; - - - len += sprintf(page+len, "%3d %s\n", - cd->major, cd->name); - } + for (cd = chrdevs[i]; cd; cd = cd->next) + count++; } -page_full: - up(&chrdevs_lock); - return len; + 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, cd->major = major; cd->baseminor = baseminor; cd->minorct = minorct; - cd->name = name; + strncpy(cd->name,name, 64); i = major_to_index(major); diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 63bf6c00fa0c..8f8014285a34 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,6 @@ */ extern int get_hardware_list(char *); extern int get_stram_list(char *); -extern int get_chrdev_list(char *); extern int get_filesystem_list(char *); extern int get_exec_domain_list(char *); extern int get_dma_list(char *); @@ -248,6 +248,154 @@ 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)++; + 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 = { .open = cpuinfo_open, .read = seq_read, @@ -450,14 +598,6 @@ static struct file_operations proc_stat_operations = { .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 */ @@ -582,7 +722,6 @@ void __init proc_misc_init(void) #ifdef CONFIG_STRAM_PROC {"stram", stram_read_proc}, #endif - {"devices", devices_read_proc}, {"filesystems", filesystems_read_proc}, {"cmdline", cmdline_read_proc}, {"locks", locks_read_proc}, @@ -598,6 +737,7 @@ void __init proc_misc_init(void) entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); if (entry) 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("partitions", 0, &proc_partitions_operations); create_seq_entry("stat", 0, &proc_stat_operations); -- cgit v1.2.2