LCOV - code coverage report
Current view: top level - lkbce/drivers/scsi - scsi_proc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 91 168 54.2 %
Date: 2017-01-25 Functions: 8 14 57.1 %

          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 : }

Generated by: LCOV version 1.10