Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 68eef3b4 authored by Joe Korty's avatar Joe Korty Committed by Linus Torvalds
Browse files

[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: default avatarJoe Korty <joe.korty@ccur.com>
Cc: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a2c348fe
Loading
Loading
Loading
Loading
+11 −92
Original line number Diff line number Diff line
@@ -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;

        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;
        }
	struct blk_major_name *dp;

out:
        return info;
}

void *acquire_blkdev_list(void)
{
	if (offset < BLKDEV_MAJOR_HASH_SIZE) {
		mutex_lock(&block_subsys_lock);
        return get_next_blkdev(NULL);
}

void release_blkdev_list(void *dev)
{
		for (dp = major_names[offset]; dp; dp = dp->next)
			seq_printf(f, "%3d %s\n", dp->major, dp->name);
		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++;
	}

	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)
{
+11 −76
Original line number Diff line number Diff line
@@ -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)
void chrdev_show(struct seq_file *f, off_t offset)
{
	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;
}
	struct char_device_struct *cd;

void *acquire_chrdev_list(void)
{
	if (offset < CHRDEV_MAJOR_HASH_SIZE) {
		mutex_lock(&chrdevs_lock);
	return get_next_chrdev(NULL);
}

void release_chrdev_list(void *dev)
{
		for (cd = chrdevs[offset]; cd; cd = cd->next)
			seq_printf(f, "%3d %s\n", cd->major, cd->name);
		mutex_unlock(&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)
			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;
}
#endif /* CONFIG_PROC_FS */

/*
 * Register a single major with a specified minor range.
+36 −127
Original line number Diff line number Diff line
@@ -249,144 +249,60 @@ 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
static struct file_operations proc_cpuinfo_operations = {
	.open		= cpuinfo_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

struct devinfo_state {
	void *chrdev;
	void *blkdev;
	unsigned int num_records;
	unsigned int cur_record;
	enum devinfo_states state;
};
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)
{
	struct devinfo_state *info = f->private;

	if (*pos) {
		if ((info) && (*pos <= info->num_records))
			return info;
	if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
		return pos;
	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;
	if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
		return NULL;
	return pos;
}

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;
	/* Nothing to do */
}

static  struct seq_operations devinfo_op = {
static struct seq_operations devinfo_ops = {
	.start = devinfo_start,
	.next  = devinfo_next,
	.stop  = devinfo_stop,
	.show   = devinfo_show,
	.show  = devinfo_show
};

static int devinfo_open(struct inode *inode, struct file *file)
static int devinfo_open(struct inode *inode, struct file *filp)
{
	return seq_open(file, &devinfo_op);
	return seq_open(filp, &devinfo_ops);
}

static struct file_operations proc_devinfo_operations = {
@@ -396,13 +312,6 @@ static struct file_operations proc_devinfo_operations = {
	.release	= seq_release,
};

static struct file_operations proc_cpuinfo_operations = {
	.open		= cpuinfo_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

extern struct seq_operations vmstat_op;
static int vmstat_open(struct inode *inode, struct file *file)
{
+4 −11
Original line number Diff line number Diff line
@@ -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);