1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Command-line utility for injecting SCSI transport errors in SD targets.
  18  * It utilizes fault injection framework, so make sure both SD and MPTSAS
  19  * drivers support it, or some IOCTLs may fail.
  20  */
  21 
  22 #include <stdio.h>
  23 #include <fcntl.h>
  24 #include <errno.h>
  25 #include <unistd.h>
  26 #include <assert.h>
  27 #include <stdlib.h>
  28 #include <string.h>
  29 #include <ctype.h>
  30 #include <strings.h>
  31 #include <stdarg.h>
  32 #include <sys/types.h>
  33 
  34 #define SDIOC           ('T'<<8)
  35 #define SDIOCSTART      (SDIOC|1)
  36 #define SDIOCSTOP       (SDIOC|2)
  37 #define SDIOCPUSH       (SDIOC|7)
  38 #define SDIOCRETRIEVE   (SDIOC|8)
  39 #define SDIOCRUN        (SDIOC|9)
  40 #define SDIOCINSERTTRAN (SDIOC|0xA)
  41 
  42 enum sd_fi_tran_cmd {
  43 /*
  44  * Reject this command instead of sending it the hardware.
  45  * Results in immediate rejecting the packet by the HBA with the TRAN_BUSY
  46  * reason.
  47  */
  48         SD_FLTINJ_CMD_BUSY,
  49 
  50 /*
  51  * Time-out this command.
  52  * Results in putting the command to a stashed queue in the HBA, returning
  53  * TRAN_ACCEPT to the target driver and calling its completion
  54  * handler with the reason set to CMD_TIMEOUT, when command timeout expires
  55  */
  56         SD_FLTINJ_CMD_TIMEOUT
  57 };
  58 
  59 struct sd_fi_tran {
  60         enum sd_fi_tran_cmd tran_cmd;
  61 };
  62 
  63 void
  64 usage(void)
  65 {
  66         printf("diskfltinj -c command [-s] [-f] [-r] [-a] <devpath>\n");
  67         printf("  -c: Apply fault injection command. The following commands");
  68         printf("      are supported:\n");
  69         printf("       reject    Reject command immediately\n");
  70         printf("       timeout   Cause command timeout expiration\n");
  71         printf("  -s:  Start a new fault injection session.\n");
  72         printf("  -f:  Reset current fault injection session.\n");
  73         printf("  -r:  Run fault injection.\n");
  74         printf("  -a:  Access target device (read one sector).\n");
  75         printf("  -v:  Verbose mode.\n");
  76         exit(1);
  77 }
  78 
  79 int verbose = 0;
  80 
  81 void
  82 v_output(const char *fmt, ...)
  83 {
  84         if (verbose) {
  85                 va_list args;
  86                 va_start(args, fmt);
  87                 vfprintf(stderr, fmt, args);
  88                 va_end(args);
  89         }
  90 }
  91 
  92 int
  93 main(int argc, char **argv)
  94 {
  95         int fd = 0;
  96         unsigned char buf[512];
  97         struct sd_fi_tran tr_err;
  98         int cmd = -1, opt;
  99         int do_access = 0;
 100         int start_inj = 0;
 101         char *devpath = NULL;
 102         int stop_inj = 0;
 103         int run_inj = 0;
 104 
 105         while ((opt = getopt(argc, argv, "asvfrc:")) != -1) {
 106                 switch (opt) {
 107                         case 'v':
 108                                 verbose = 1;
 109                                 break;
 110                         case 'a':
 111                                 do_access = 1;
 112                                 break;
 113                         case 's':
 114                                 start_inj = 1;
 115                                 break;
 116                         case 'r':
 117                                 run_inj = 1;
 118                                 break;
 119                         case 'f':
 120                                 stop_inj = 1;
 121                                 break;
 122                         case 'c':
 123                                 if (strcmp(optarg, "reject") == 0) {
 124                                         cmd = SD_FLTINJ_CMD_BUSY;
 125                                 } else if (strcmp(optarg, "timeout") == 0) {
 126                                         cmd = SD_FLTINJ_CMD_TIMEOUT;
 127                                 } else {
 128                                         usage();
 129                                 }
 130                                 break;
 131                         default:
 132                                 usage();
 133                 }
 134         }
 135 
 136         /* Handle device name. */
 137         if ((argc - optind) == 1) {
 138                 devpath = argv[optind];
 139         } else {
 140                 usage();
 141         }
 142 
 143         /* Action must be specified. */
 144         if (cmd == -1 && !stop_inj && !run_inj && !start_inj) {
 145                 usage();
 146         }
 147 
 148         /* Open device. */
 149         printf("Target device: %s\n", devpath);
 150         if ((fd = open(devpath, O_RDONLY|O_NDELAY)) == -1) {
 151                 perror("open");
 152                 exit(1);
 153         }
 154 
 155         /* Start new fault injection session. */
 156         if (start_inj) {
 157                 v_output("Starting a new fault injection session.\n");
 158                 if (ioctl(fd, SDIOCSTART, NULL) == -1) {
 159                         perror("ioctl SDIOCSTART");
 160                         exit(1);
 161                 }
 162         }
 163 
 164         /* Inject a command. */
 165         if (cmd != -1) {
 166                 tr_err.tran_cmd = cmd;
 167 
 168                 v_output("Injecting fault.\n");
 169                 if (ioctl(fd, SDIOCINSERTTRAN, &tr_err) == -1) {
 170                         perror("ioctl SDIOCINSERTTRAN");
 171                         exit(1);
 172                 }
 173 
 174                 if (ioctl(fd, SDIOCPUSH, NULL) == -1) {
 175                         perror("ioctl SDIOCPUSH");
 176                         exit(1);
 177                 }
 178         }
 179 
 180         /* Run injection sequence. */
 181         if (run_inj) {
 182                 v_output("Activating fault injection sequence.\n");
 183                 if (ioctl(fd, SDIOCRUN, NULL) == -1) {
 184                         perror("ioctl SDIOCRUN");
 185                         exit(1);
 186                 }
 187         }
 188 
 189         /* Access the device, if requested. */
 190         if (do_access) {
 191                 v_output("Accessing target device.\n");
 192                 read(fd, buf, sizeof (buf));
 193         }
 194 
 195         /* Stop fault injection, if requested */
 196         if (stop_inj) {
 197                 v_output("Stopping fault injection session.\n");
 198                 if (ioctl(fd, SDIOCSTOP, NULL) == -1) {
 199                         perror("ioctl SDIOCSTOP");
 200                         exit(1);
 201                 }
 202         }
 203 
 204         return (0);
 205 }