root/drivers/scsi/scsi_proc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. proc_scsi_host_write
  2. proc_scsi_show
  3. proc_scsi_host_open
  4. scsi_proc_hostdir_add
  5. scsi_proc_hostdir_rm
  6. scsi_proc_host_add
  7. scsi_proc_host_rm
  8. proc_print_scsidevice
  9. scsi_add_single_device
  10. scsi_remove_single_device
  11. proc_scsi_write
  12. next_scsi_device
  13. scsi_seq_start
  14. scsi_seq_next
  15. scsi_seq_stop
  16. scsi_seq_show
  17. proc_scsi_open
  18. scsi_init_procfs
  19. scsi_exit_procfs

   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 static ssize_t proc_scsi_host_write(struct file *file, const char __user *buf,
  50                            size_t count, loff_t *ppos)
  51 {
  52         struct Scsi_Host *shost = PDE_DATA(file_inode(file));
  53         ssize_t ret = -ENOMEM;
  54         char *page;
  55     
  56         if (count > PROC_BLOCK_SIZE)
  57                 return -EOVERFLOW;
  58 
  59         if (!shost->hostt->write_info)
  60                 return -EINVAL;
  61 
  62         page = (char *)__get_free_page(GFP_KERNEL);
  63         if (page) {
  64                 ret = -EFAULT;
  65                 if (copy_from_user(page, buf, count))
  66                         goto out;
  67                 ret = shost->hostt->write_info(shost, page, count);
  68         }
  69 out:
  70         free_page((unsigned long)page);
  71         return ret;
  72 }
  73 
  74 static int proc_scsi_show(struct seq_file *m, void *v)
  75 {
  76         struct Scsi_Host *shost = m->private;
  77         return shost->hostt->show_info(m, shost);
  78 }
  79 
  80 static int proc_scsi_host_open(struct inode *inode, struct file *file)
  81 {
  82         return single_open_size(file, proc_scsi_show, PDE_DATA(inode),
  83                                 4 * PAGE_SIZE);
  84 }
  85 
  86 static const struct file_operations proc_scsi_fops = {
  87         .open = proc_scsi_host_open,
  88         .release = single_release,
  89         .read = seq_read,
  90         .llseek = seq_lseek,
  91         .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 void scsi_proc_hostdir_add(struct scsi_host_template *sht)
 102 {
 103         if (!sht->show_info)
 104                 return;
 105 
 106         mutex_lock(&global_host_template_mutex);
 107         if (!sht->present++) {
 108                 sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi);
 109                 if (!sht->proc_dir)
 110                         printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
 111                                __func__, sht->proc_name);
 112         }
 113         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 void scsi_proc_hostdir_rm(struct scsi_host_template *sht)
 121 {
 122         if (!sht->show_info)
 123                 return;
 124 
 125         mutex_lock(&global_host_template_mutex);
 126         if (!--sht->present && sht->proc_dir) {
 127                 remove_proc_entry(sht->proc_name, proc_scsi);
 128                 sht->proc_dir = NULL;
 129         }
 130         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 void scsi_proc_host_add(struct Scsi_Host *shost)
 139 {
 140         struct scsi_host_template *sht = shost->hostt;
 141         struct proc_dir_entry *p;
 142         char name[10];
 143 
 144         if (!sht->proc_dir)
 145                 return;
 146 
 147         sprintf(name,"%d", shost->host_no);
 148         p = proc_create_data(name, S_IRUGO | S_IWUSR,
 149                 sht->proc_dir, &proc_scsi_fops, shost);
 150         if (!p)
 151                 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 void scsi_proc_host_rm(struct Scsi_Host *shost)
 161 {
 162         char name[10];
 163 
 164         if (!shost->hostt->proc_dir)
 165                 return;
 166 
 167         sprintf(name,"%d", shost->host_no);
 168         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 static int proc_print_scsidevice(struct device *dev, void *data)
 179 {
 180         struct scsi_device *sdev;
 181         struct seq_file *s = data;
 182         int i;
 183 
 184         if (!scsi_is_sdev_device(dev))
 185                 goto out;
 186 
 187         sdev = to_scsi_device(dev);
 188         seq_printf(s,
 189                 "Host: scsi%d Channel: %02d Id: %02d Lun: %02llu\n  Vendor: ",
 190                 sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
 191         for (i = 0; i < 8; i++) {
 192                 if (sdev->vendor[i] >= 0x20)
 193                         seq_putc(s, sdev->vendor[i]);
 194                 else
 195                         seq_putc(s, ' ');
 196         }
 197 
 198         seq_puts(s, " Model: ");
 199         for (i = 0; i < 16; i++) {
 200                 if (sdev->model[i] >= 0x20)
 201                         seq_putc(s, sdev->model[i]);
 202                 else
 203                         seq_putc(s, ' ');
 204         }
 205 
 206         seq_puts(s, " Rev: ");
 207         for (i = 0; i < 4; i++) {
 208                 if (sdev->rev[i] >= 0x20)
 209                         seq_putc(s, sdev->rev[i]);
 210                 else
 211                         seq_putc(s, ' ');
 212         }
 213 
 214         seq_putc(s, '\n');
 215 
 216         seq_printf(s, "  Type:   %s ", scsi_device_type(sdev->type));
 217         seq_printf(s, "               ANSI  SCSI revision: %02x",
 218                         sdev->scsi_level - (sdev->scsi_level > 1));
 219         if (sdev->scsi_level == 2)
 220                 seq_puts(s, " CCS\n");
 221         else
 222                 seq_putc(s, '\n');
 223 
 224 out:
 225         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 static int scsi_add_single_device(uint host, uint channel, uint id, uint lun)
 244 {
 245         struct Scsi_Host *shost;
 246         int error = -ENXIO;
 247 
 248         shost = scsi_host_lookup(host);
 249         if (!shost)
 250                 return error;
 251 
 252         if (shost->transportt->user_scan)
 253                 error = shost->transportt->user_scan(shost, channel, id, lun);
 254         else
 255                 error = scsi_scan_host_selected(shost, channel, id, lun,
 256                                                 SCSI_SCAN_MANUAL);
 257         scsi_host_put(shost);
 258         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 static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
 272 {
 273         struct scsi_device *sdev;
 274         struct Scsi_Host *shost;
 275         int error = -ENXIO;
 276 
 277         shost = scsi_host_lookup(host);
 278         if (!shost)
 279                 return error;
 280         sdev = scsi_device_lookup(shost, channel, id, lun);
 281         if (sdev) {
 282                 scsi_remove_device(sdev);
 283                 scsi_device_put(sdev);
 284                 error = 0;
 285         }
 286 
 287         scsi_host_put(shost);
 288         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 static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
 311                                size_t length, loff_t *ppos)
 312 {
 313         int host, channel, id, lun;
 314         char *buffer, *p;
 315         int err;
 316 
 317         if (!buf || length > PAGE_SIZE)
 318                 return -EINVAL;
 319 
 320         buffer = (char *)__get_free_page(GFP_KERNEL);
 321         if (!buffer)
 322                 return -ENOMEM;
 323 
 324         err = -EFAULT;
 325         if (copy_from_user(buffer, buf, length))
 326                 goto out;
 327 
 328         err = -EINVAL;
 329         if (length < PAGE_SIZE)
 330                 buffer[length] = '\0';
 331         else if (buffer[PAGE_SIZE-1])
 332                 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         if (!strncmp("scsi add-single-device", buffer, 22)) {
 339                 p = buffer + 23;
 340 
 341                 host = simple_strtoul(p, &p, 0);
 342                 channel = simple_strtoul(p + 1, &p, 0);
 343                 id = simple_strtoul(p + 1, &p, 0);
 344                 lun = simple_strtoul(p + 1, &p, 0);
 345 
 346                 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         } else if (!strncmp("scsi remove-single-device", buffer, 25)) {
 353                 p = buffer + 26;
 354 
 355                 host = simple_strtoul(p, &p, 0);
 356                 channel = simple_strtoul(p + 1, &p, 0);
 357                 id = simple_strtoul(p + 1, &p, 0);
 358                 lun = simple_strtoul(p + 1, &p, 0);
 359 
 360                 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         if (!err)
 368                 err = length;
 369 
 370  out:
 371         free_page((unsigned long)buffer);
 372         return err;
 373 }
 374 
 375 static inline struct device *next_scsi_device(struct device *start)
 376 {
 377         struct device *next = bus_find_next_device(&scsi_bus_type, start);
 378 
 379         put_device(start);
 380         return next;
 381 }
 382 
 383 static void *scsi_seq_start(struct seq_file *sfile, loff_t *pos)
 384 {
 385         struct device *dev = NULL;
 386         loff_t n = *pos;
 387 
 388         while ((dev = next_scsi_device(dev))) {
 389                 if (!n--)
 390                         break;
 391                 sfile->private++;
 392         }
 393         return dev;
 394 }
 395 
 396 static void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
 397 {
 398         (*pos)++;
 399         sfile->private++;
 400         return next_scsi_device(v);
 401 }
 402 
 403 static void scsi_seq_stop(struct seq_file *sfile, void *v)
 404 {
 405         put_device(v);
 406 }
 407 
 408 static int scsi_seq_show(struct seq_file *sfile, void *dev)
 409 {
 410         if (!sfile->private)
 411                 seq_puts(sfile, "Attached devices:\n");
 412 
 413         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 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         return seq_open(file, &scsi_seq_ops);
 437 }
 438 
 439 static const struct file_operations proc_scsi_operations = {
 440         .owner          = THIS_MODULE,
 441         .open           = proc_scsi_open,
 442         .read           = seq_read,
 443         .write          = proc_scsi_write,
 444         .llseek         = seq_lseek,
 445         .release        = seq_release,
 446 };
 447 
 448 /**
 449  * scsi_init_procfs - create scsi and scsi/scsi in procfs
 450  */
 451 int __init scsi_init_procfs(void)
 452 {
 453         struct proc_dir_entry *pde;
 454 
 455         proc_scsi = proc_mkdir("scsi", NULL);
 456         if (!proc_scsi)
 457                 goto err1;
 458 
 459         pde = proc_create("scsi/scsi", 0, NULL, &proc_scsi_operations);
 460         if (!pde)
 461                 goto err2;
 462 
 463         return 0;
 464 
 465 err2:
 466         remove_proc_entry("scsi", NULL);
 467 err1:
 468         return -ENOMEM;
 469 }
 470 
 471 /**
 472  * scsi_exit_procfs - Remove scsi/scsi and scsi from procfs
 473  */
 474 void scsi_exit_procfs(void)
 475 {
 476         remove_proc_entry("scsi/scsi", NULL);
 477         remove_proc_entry("scsi", NULL);
 478 }

/* [<][>][^][v][top][bottom][index][help] */