LCOV - code coverage report
Current view: top level - drivers/scsi - scsi_proc.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 14 177 7.9 %
Date: 2022-04-01 14:35:51 Functions: 3 19 15.8 %
Branches: 4 80 5.0 %

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

Generated by: LCOV version 1.14