Line data Source code
1 : /*
2 : * linux/drivers/scsi/scsi_proc.c
3 : *
4 : * The functions in this file provide an interface between
5 : * the PROC file system and the SCSI device drivers
6 : * It is mainly used for debugging, statistics and to pass
7 : * information directly to the lowlevel driver.
8 : *
9 : * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
10 : * Version: 0.99.8 last change: 95/09/13
11 : *
12 : * generic command parser provided by:
13 : * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de>
14 : *
15 : * generic_proc_info() support of xxxx_info() by:
16 : * Michael A. Griffith <grif@acm.org>
17 : */
18 :
19 : #include <linux/module.h>
20 : #include <linux/init.h>
21 : #include <linux/string.h>
22 : #include <linux/mm.h>
23 : #include <linux/slab.h>
24 : #include <linux/proc_fs.h>
25 : #include <linux/errno.h>
26 : #include <linux/blkdev.h>
27 : #include <linux/seq_file.h>
28 : #include <linux/mutex.h>
29 : #include <asm/uaccess.h>
30 :
31 : #include <scsi/scsi.h>
32 : #include <scsi/scsi_device.h>
33 : #include <scsi/scsi_host.h>
34 : #include <scsi/scsi_transport.h>
35 :
36 : #include "scsi_priv.h"
37 : #include "scsi_logging.h"
38 :
39 :
40 : /* 4K page size, but our output routines, use some slack for overruns */
41 : #define PROC_BLOCK_SIZE (3*1024)
42 :
43 1 : static struct proc_dir_entry *proc_scsi;
44 :
45 : /* Protect sht->present and sht->proc_dir */
46 1 : static DEFINE_MUTEX(global_host_template_mutex);
47 :
48 : /**
49 : * proc_scsi_read - handle read from /proc by calling host's proc_info() command
50 : * @buffer: passed to proc_info
51 : * @start: passed to proc_info
52 : * @offset: passed to proc_info
53 : * @length: passed to proc_info
54 : * @eof: returns whether length read was less than requested
55 : * @data: pointer to a &struct Scsi_Host
56 : */
57 :
58 : static int proc_scsi_read(char *buffer, char **start, off_t offset,
59 : int length, int *eof, void *data)
60 : {
61 0 : struct Scsi_Host *shost = data;
62 0 : int n;
63 :
64 0 : n = shost->hostt->proc_info(shost, buffer, start, offset, length, 0);
65 0 : *eof = (n < length);
66 :
67 0 : return n;
68 : }
69 :
70 : /**
71 : * proc_scsi_write_proc - Handle write to /proc by calling host's proc_info()
72 : * @file: not used
73 : * @buf: source of data to write.
74 : * @count: number of bytes (at most PROC_BLOCK_SIZE) to write.
75 : * @data: pointer to &struct Scsi_Host
76 : */
77 : static int proc_scsi_write_proc(struct file *file, const char __user *buf,
78 : unsigned long count, void *data)
79 : {
80 0 : struct Scsi_Host *shost = data;
81 0 : ssize_t ret = -ENOMEM;
82 0 : char *page;
83 0 : char *start;
84 0 :
85 0 : if (count > PROC_BLOCK_SIZE)
86 0 : return -EOVERFLOW;
87 :
88 0 : page = (char *)__get_free_page(GFP_KERNEL);
89 0 : if (page) {
90 0 : ret = -EFAULT;
91 0 : if (copy_from_user(page, buf, count))
92 0 : goto out;
93 0 : ret = shost->hostt->proc_info(shost, page, &start, 0, count, 1);
94 : }
95 : out:
96 0 : free_page((unsigned long)page);
97 0 : return ret;
98 0 : }
99 :
100 : /**
101 : * scsi_proc_hostdir_add - Create directory in /proc for a scsi host
102 : * @sht: owner of this directory
103 : *
104 : * Sets sht->proc_dir to the new directory.
105 : */
106 :
107 : void scsi_proc_hostdir_add(struct scsi_host_template *sht)
108 : {
109 0 : if (!sht->proc_info)
110 0 : return;
111 :
112 0 : mutex_lock(&global_host_template_mutex);
113 0 : if (!sht->present++) {
114 0 : sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi);
115 0 : if (!sht->proc_dir)
116 0 : printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
117 : __func__, sht->proc_name);
118 : }
119 0 : mutex_unlock(&global_host_template_mutex);
120 0 : }
121 :
122 : /**
123 : * scsi_proc_hostdir_rm - remove directory in /proc for a scsi host
124 : * @sht: owner of directory
125 : */
126 : void scsi_proc_hostdir_rm(struct scsi_host_template *sht)
127 : {
128 3 : if (!sht->proc_info)
129 1 : return;
130 :
131 1 : mutex_lock(&global_host_template_mutex);
132 8 : if (!--sht->present && sht->proc_dir) {
133 1 : remove_proc_entry(sht->proc_name, proc_scsi);
134 1 : sht->proc_dir = NULL;
135 : }
136 1 : mutex_unlock(&global_host_template_mutex);
137 1 : }
138 :
139 :
140 : /**
141 : * scsi_proc_host_add - Add entry for this host to appropriate /proc dir
142 : * @shost: host to add
143 : */
144 : void scsi_proc_host_add(struct Scsi_Host *shost)
145 : {
146 0 : struct scsi_host_template *sht = shost->hostt;
147 0 : struct proc_dir_entry *p;
148 0 : char name[10];
149 :
150 0 : if (!sht->proc_dir)
151 0 : return;
152 :
153 0 : sprintf(name,"%d", shost->host_no);
154 0 : p = create_proc_read_entry(name, S_IFREG | S_IRUGO | S_IWUSR,
155 : sht->proc_dir, proc_scsi_read, shost);
156 0 : if (!p) {
157 0 : printk(KERN_ERR "%s: Failed to register host %d in"
158 : "%s\n", __func__, shost->host_no,
159 : sht->proc_name);
160 0 : return;
161 : }
162 :
163 0 : p->write_proc = proc_scsi_write_proc;
164 0 : }
165 :
166 : /**
167 : * scsi_proc_host_rm - remove this host's entry from /proc
168 : * @shost: which host
169 : */
170 : void scsi_proc_host_rm(struct Scsi_Host *shost)
171 : {
172 0 : char name[10];
173 :
174 0 : if (!shost->hostt->proc_dir)
175 0 : return;
176 :
177 0 : sprintf(name,"%d", shost->host_no);
178 0 : remove_proc_entry(name, shost->hostt->proc_dir);
179 0 : }
180 : /**
181 : * proc_print_scsidevice - return data about this host
182 : * @dev: A scsi device
183 : * @data: &struct seq_file to output to.
184 : *
185 : * Description: prints Host, Channel, Id, Lun, Vendor, Model, Rev, Type,
186 : * and revision.
187 : */
188 : static int proc_print_scsidevice(struct device *dev, void *data)
189 : {
190 0 : struct scsi_device *sdev;
191 0 : struct seq_file *s = data;
192 0 : int i;
193 0 :
194 0 : if (!scsi_is_sdev_device(dev))
195 0 : goto out;
196 :
197 0 : sdev = to_scsi_device(dev);
198 0 : seq_printf(s,
199 : "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ",
200 : sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
201 0 : for (i = 0; i < 8; i++) {
202 0 : if (sdev->vendor[i] >= 0x20)
203 0 : seq_printf(s, "%c", sdev->vendor[i]);
204 : else
205 0 : seq_printf(s, " ");
206 : }
207 :
208 0 : seq_printf(s, " Model: ");
209 0 : for (i = 0; i < 16; i++) {
210 0 : if (sdev->model[i] >= 0x20)
211 0 : seq_printf(s, "%c", sdev->model[i]);
212 : else
213 0 : seq_printf(s, " ");
214 : }
215 :
216 0 : seq_printf(s, " Rev: ");
217 0 : for (i = 0; i < 4; i++) {
218 0 : if (sdev->rev[i] >= 0x20)
219 0 : seq_printf(s, "%c", sdev->rev[i]);
220 : else
221 0 : seq_printf(s, " ");
222 : }
223 :
224 0 : seq_printf(s, "\n");
225 :
226 0 : seq_printf(s, " Type: %s ", scsi_device_type(sdev->type));
227 0 : seq_printf(s, " ANSI SCSI revision: %02x",
228 : sdev->scsi_level - (sdev->scsi_level > 1));
229 0 : if (sdev->scsi_level == 2)
230 0 : seq_printf(s, " CCS\n");
231 : else
232 0 : seq_printf(s, "\n");
233 :
234 : out:
235 0 : return 0;
236 : }
237 :
238 : /**
239 : * scsi_add_single_device - Respond to user request to probe for/add device
240 : * @host: user-supplied decimal integer
241 : * @channel: user-supplied decimal integer
242 : * @id: user-supplied decimal integer
243 : * @lun: user-supplied decimal integer
244 : *
245 : * Description: called by writing "scsi add-single-device" to /proc/scsi/scsi.
246 : *
247 : * does scsi_host_lookup() and either user_scan() if that transport
248 : * type supports it, or else scsi_scan_host_selected()
249 : *
250 : * Note: this seems to be aimed exclusively at SCSI parallel busses.
251 : */
252 :
253 : static int scsi_add_single_device(uint host, uint channel, uint id, uint lun)
254 : {
255 1 : struct Scsi_Host *shost;
256 2 : int error = -ENXIO;
257 :
258 3 : shost = scsi_host_lookup(host);
259 2 : if (!shost)
260 1 : return error;
261 :
262 3 : if (shost->transportt->user_scan)
263 1 : error = shost->transportt->user_scan(shost, channel, id, lun);
264 : else
265 5 : error = scsi_scan_host_selected(shost, channel, id, lun, 1);
266 4 : scsi_host_put(shost);
267 1 : return error;
268 : }
269 :
270 : /**
271 : * scsi_remove_single_device - Respond to user request to remove a device
272 : * @host: user-supplied decimal integer
273 : * @channel: user-supplied decimal integer
274 : * @id: user-supplied decimal integer
275 : * @lun: user-supplied decimal integer
276 : *
277 : * Description: called by writing "scsi remove-single-device" to
278 : * /proc/scsi/scsi. Does a scsi_device_lookup() and scsi_remove_device()
279 : */
280 : static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
281 : {
282 1 : struct scsi_device *sdev;
283 1 : struct Scsi_Host *shost;
284 2 : int error = -ENXIO;
285 :
286 3 : shost = scsi_host_lookup(host);
287 2 : if (!shost)
288 1 : return error;
289 2 : sdev = scsi_device_lookup(shost, channel, id, lun);
290 2 : if (sdev) {
291 2 : scsi_remove_device(sdev);
292 2 : scsi_device_put(sdev);
293 1 : error = 0;
294 : }
295 :
296 4 : scsi_host_put(shost);
297 1 : return error;
298 : }
299 :
300 : /**
301 : * proc_scsi_write - handle writes to /proc/scsi/scsi
302 : * @file: not used
303 : * @buf: buffer to write
304 : * @length: length of buf, at most PAGE_SIZE
305 : * @ppos: not used
306 : *
307 : * Description: this provides a legacy mechanism to add or remove devices by
308 : * Host, Channel, ID, and Lun. To use,
309 : * "echo 'scsi add-single-device 0 1 2 3' > /proc/scsi/scsi" or
310 : * "echo 'scsi remove-single-device 0 1 2 3' > /proc/scsi/scsi" with
311 : * "0 1 2 3" replaced by the Host, Channel, Id, and Lun.
312 : *
313 : * Note: this seems to be aimed at parallel SCSI. Most modern busses (USB,
314 : * SATA, Firewire, Fibre Channel, etc) dynamically assign these values to
315 : * provide a unique identifier and nothing more.
316 : */
317 :
318 :
319 : static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
320 : size_t length, loff_t *ppos)
321 : {
322 1 : int host, channel, id, lun;
323 1 : char *buffer, *p;
324 1 : int err;
325 1 :
326 5 : if (!buf || length > PAGE_SIZE)
327 2 : return -EINVAL;
328 1 :
329 3 : buffer = (char *)__get_free_page(GFP_KERNEL);
330 3 : if (!buffer)
331 2 : return -ENOMEM;
332 1 :
333 2 : err = -EFAULT;
334 6 : if (copy_from_user(buffer, buf, length))
335 2 : goto out;
336 1 :
337 2 : err = -EINVAL;
338 3 : if (length < PAGE_SIZE)
339 2 : buffer[length] = '\0';
340 3 : else if (buffer[PAGE_SIZE-1])
341 1 : goto out;
342 :
343 : /*
344 : * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
345 : * with "0 1 2 3" replaced by your "Host Channel Id Lun".
346 : */
347 3 : if (!strncmp("scsi add-single-device", buffer, 22)) {
348 1 : p = buffer + 23;
349 :
350 2 : host = simple_strtoul(p, &p, 0);
351 2 : channel = simple_strtoul(p + 1, &p, 0);
352 2 : id = simple_strtoul(p + 1, &p, 0);
353 2 : lun = simple_strtoul(p + 1, &p, 0);
354 :
355 3 : err = scsi_add_single_device(host, channel, id, lun);
356 :
357 : /*
358 : * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
359 : * with "0 1 2 3" replaced by your "Host Channel Id Lun".
360 : */
361 3 : } else if (!strncmp("scsi remove-single-device", buffer, 25)) {
362 1 : p = buffer + 26;
363 :
364 2 : host = simple_strtoul(p, &p, 0);
365 2 : channel = simple_strtoul(p + 1, &p, 0);
366 2 : id = simple_strtoul(p + 1, &p, 0);
367 2 : lun = simple_strtoul(p + 1, &p, 0);
368 :
369 3 : err = scsi_remove_single_device(host, channel, id, lun);
370 : }
371 :
372 : /*
373 : * convert success returns so that we return the
374 : * number of bytes consumed.
375 : */
376 6 : if (!err)
377 3 : err = length;
378 :
379 : out:
380 3 : free_page((unsigned long)buffer);
381 6 : return err;
382 : }
383 :
384 : /**
385 : * proc_scsi_show - show contents of /proc/scsi/scsi (attached devices)
386 : * @s: output goes here
387 : * @p: not used
388 : */
389 : static int proc_scsi_show(struct seq_file *s, void *p)
390 : {
391 1 : seq_printf(s, "Attached devices:\n");
392 1 : bus_for_each_dev(&scsi_bus_type, NULL, s, proc_print_scsidevice);
393 1 : return 0;
394 : }
395 :
396 : /**
397 : * proc_scsi_open - glue function
398 : * @inode: not used
399 : * @file: passed to single_open()
400 : *
401 : * Associates proc_scsi_show with this file
402 : */
403 : static int proc_scsi_open(struct inode *inode, struct file *file)
404 : {
405 1 : /*
406 : * We don't really need this for the write case but it doesn't
407 : * harm either.
408 : */
409 2 : return single_open(file, proc_scsi_show, NULL);
410 : }
411 :
412 1 : static const struct file_operations proc_scsi_operations = {
413 : .owner = THIS_MODULE,
414 : .open = proc_scsi_open,
415 : .read = seq_read,
416 : .write = proc_scsi_write,
417 : .llseek = seq_lseek,
418 : .release = single_release,
419 : };
420 :
421 : /**
422 : * scsi_init_procfs - create scsi and scsi/scsi in procfs
423 : */
424 : int __init scsi_init_procfs(void)
425 : {
426 1 : struct proc_dir_entry *pde;
427 :
428 1 : proc_scsi = proc_mkdir("scsi", NULL);
429 2 : if (!proc_scsi)
430 1 : goto err1;
431 :
432 2 : pde = proc_create("scsi/scsi", 0, NULL, &proc_scsi_operations);
433 2 : if (!pde)
434 1 : goto err2;
435 :
436 1 : return 0;
437 1 :
438 : err2:
439 1 : remove_proc_entry("scsi", NULL);
440 : err1:
441 3 : return -ENOMEM;
442 : }
443 :
444 : /**
445 : * scsi_exit_procfs - Remove scsi/scsi and scsi from procfs
446 : */
447 : void scsi_exit_procfs(void)
448 : {
449 4 : remove_proc_entry("scsi/scsi", NULL);
450 4 : remove_proc_entry("scsi", NULL);
451 4 : }
|