diff options
author | Jeff Mahoney <jeffm@suse.com> | 2011-04-28 00:22:20 +0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2011-05-01 22:04:02 +0400 |
commit | e37c4913c555bd149eba3af953fb5d3a1686dfbd (patch) | |
tree | ba9fbba3ba75f4a1153825c7833f977f8c6d50c6 | |
parent | 9c324b8ba8e3ee8772a0c716d557e1582699d481 (diff) | |
download | linux-e37c4913c555bd149eba3af953fb5d3a1686dfbd.tar.xz |
[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: Jeff Mahoney <jeffm@suse.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
-rw-r--r-- | drivers/scsi/scsi_proc.c | 58 |
1 files changed, 52 insertions, 6 deletions
diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index c99da926fdac..f46855cd853d 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -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, }; /** |