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