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

Commit e37c4913 authored by Jeff Mahoney's avatar Jeff Mahoney Committed by James Bottomley
Browse files

[SCSI] iterate over devices individually for /proc/scsi/scsi



On systems with very large numbers (> 1600 or so) of SCSI devices,
cat /proc/scsi/scsi ends up failing with -ENOMEM. This is due to
the show routine simply iterating over all of the devices with
bus_for_each_dev(), and trying to dump all of them into the buffer
at the same time. On my test system (using scsi_debug with 4064 devices),
the output ends up being ~ 632k, far more than kmalloc will typically allow.

This patch defines its own seq_file opreations to iterate over the scsi
devices.The result is that each show() operation only dumps ~ 180 bytes
into the buffer at a time so we don't run out of memory.

If the "Attached devices" header isn't required, we can dump the
sfile->private bit completely.

Signed-off-by: default avatarJeff Mahoney <jeffm@suse.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent 9c324b8b
Loading
Loading
Loading
Loading
+52 −6
Original line number Diff line number Diff line
@@ -386,13 +386,59 @@ static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
 * @s: output goes here
 * @p: not used
 */
static int proc_scsi_show(struct seq_file *s, void *p)
static int always_match(struct device *dev, void *data)
{
	seq_printf(s, "Attached devices:\n");
	bus_for_each_dev(&scsi_bus_type, NULL, s, proc_print_scsidevice);
	return 0;
	return 1;
}

static inline struct device *next_scsi_device(struct device *start)
{
	struct device *next = bus_find_device(&scsi_bus_type, start, NULL,
					      always_match);
	put_device(start);
	return next;
}

static void *scsi_seq_start(struct seq_file *sfile, loff_t *pos)
{
	struct device *dev = NULL;
	loff_t n = *pos;

	while ((dev = next_scsi_device(dev))) {
		if (!n--)
			break;
		sfile->private++;
	}
	return dev;
}

static void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
{
	(*pos)++;
	sfile->private++;
	return next_scsi_device(v);
}

static void scsi_seq_stop(struct seq_file *sfile, void *v)
{
	put_device(v);
}

static int scsi_seq_show(struct seq_file *sfile, void *dev)
{
	if (!sfile->private)
		seq_puts(sfile, "Attached devices:\n");

	return proc_print_scsidevice(dev, sfile);
}

static const struct seq_operations scsi_seq_ops = {
	.start	= scsi_seq_start,
	.next	= scsi_seq_next,
	.stop	= scsi_seq_stop,
	.show	= scsi_seq_show
};

/**
 * proc_scsi_open - glue function
 * @inode: not used
@@ -406,7 +452,7 @@ static int proc_scsi_open(struct inode *inode, struct file *file)
	 * We don't really need this for the write case but it doesn't
	 * harm either.
	 */
	return single_open(file, proc_scsi_show, NULL);
	return seq_open(file, &scsi_seq_ops);
}

static const struct file_operations proc_scsi_operations = {
@@ -415,7 +461,7 @@ static const struct file_operations proc_scsi_operations = {
	.read		= seq_read,
	.write		= proc_scsi_write,
	.llseek		= seq_lseek,
	.release	= single_release,
	.release	= seq_release,
};

/**