]>
Commit | Line | Data |
---|---|---|
6a930a95 BS |
1 | From: Jeff Mahoney <jeffm@suse.com> |
2 | Subject: [PATCH] scsi: iterate over devices individually for /proc/scsi/scsi | |
3 | References: 263731 | |
4 | Patch-mainline: Probably never, hch wants to kill /proc/scsi/scsi anyway. | |
5 | ||
6 | On systems with very large numbers (> 1600 or so) of SCSI devices, | |
7 | cat /proc/scsi/scsi ends up failing with -ENOMEM. This is due to | |
8 | the show routine simply iterating over all of the devices with | |
9 | bus_for_each_dev(), and trying to dump all of them into the buffer | |
10 | at the same time. On my test system (using scsi_debug with 4064 devices), | |
11 | the output ends up being ~ 632k, far more than kmalloc will typically allow. | |
12 | ||
13 | This patch defines its own seq_file opreations to iterate over the scsi | |
14 | devices.The result is that each show() operation only dumps ~ 180 bytes | |
15 | into the buffer at a time so we don't run out of memory. | |
16 | ||
17 | If the "Attached devices" header isn't required, we can dump the | |
18 | sfile->private bit completely. | |
19 | ||
20 | Signed-off-by: Jeff Mahoney <jeffm@suse.com> | |
21 | ||
22 | --- | |
23 | ||
24 | drivers/scsi/scsi_proc.c | 58 ++++++++++++++++++++++++++++++++++++++++++----- | |
25 | 1 file changed, 52 insertions(+), 6 deletions(-) | |
26 | ||
27 | --- a/drivers/scsi/scsi_proc.c | |
28 | +++ b/drivers/scsi/scsi_proc.c | |
29 | @@ -389,13 +389,59 @@ static ssize_t proc_scsi_write(struct fi | |
30 | * @s: output goes here | |
31 | * @p: not used | |
32 | */ | |
33 | -static int proc_scsi_show(struct seq_file *s, void *p) | |
34 | +static int always_match(struct device *dev, void *data) | |
35 | { | |
36 | - seq_printf(s, "Attached devices:\n"); | |
37 | - bus_for_each_dev(&scsi_bus_type, NULL, s, proc_print_scsidevice); | |
38 | - return 0; | |
39 | + return 1; | |
40 | } | |
41 | ||
42 | +static inline struct device *next_scsi_device(struct device *start) | |
43 | +{ | |
44 | + struct device *next = bus_find_device(&scsi_bus_type, start, NULL, | |
45 | + always_match); | |
46 | + put_device(start); | |
47 | + return next; | |
48 | +} | |
49 | + | |
50 | +static void *scsi_seq_start(struct seq_file *sfile, loff_t *pos) | |
51 | +{ | |
52 | + struct device *dev = NULL; | |
53 | + loff_t n = *pos; | |
54 | + | |
55 | + while ((dev = next_scsi_device(dev))) { | |
56 | + if (!n--) | |
57 | + break; | |
58 | + sfile->private++; | |
59 | + } | |
60 | + return dev; | |
61 | +} | |
62 | + | |
63 | +static void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos) | |
64 | +{ | |
65 | + (*pos)++; | |
66 | + sfile->private++; | |
67 | + return next_scsi_device(v); | |
68 | +} | |
69 | + | |
70 | +static void scsi_seq_stop(struct seq_file *sfile, void *v) | |
71 | +{ | |
72 | + put_device(v); | |
73 | +} | |
74 | + | |
75 | +static int scsi_seq_show(struct seq_file *sfile, void *dev) | |
76 | +{ | |
77 | + if (!sfile->private) | |
78 | + seq_puts(sfile, "Attached devices:\n"); | |
79 | + | |
80 | + return proc_print_scsidevice(dev, sfile); | |
81 | +} | |
82 | + | |
83 | +static struct seq_operations scsi_seq_ops = { | |
84 | + .start = scsi_seq_start, | |
85 | + .next = scsi_seq_next, | |
86 | + .stop = scsi_seq_stop, | |
87 | + .show = scsi_seq_show | |
88 | +}; | |
89 | + | |
90 | /** | |
91 | * proc_scsi_open - glue function | |
92 | * @inode: not used | |
93 | @@ -409,7 +455,7 @@ static int proc_scsi_open(struct inode * | |
94 | * We don't really need this for the write case but it doesn't | |
95 | * harm either. | |
96 | */ | |
97 | - return single_open(file, proc_scsi_show, NULL); | |
98 | + return seq_open(file, &scsi_seq_ops); | |
99 | } | |
100 | ||
101 | static const struct file_operations proc_scsi_operations = { | |
102 | @@ -418,7 +464,7 @@ static const struct file_operations proc | |
103 | .read = seq_read, | |
104 | .write = proc_scsi_write, | |
105 | .llseek = seq_lseek, | |
106 | - .release = single_release, | |
107 | + .release = seq_release, | |
108 | }; | |
109 | ||
110 | /** |