xref: /aosp_15_r20/external/sg3_utils/src/sg_reset.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /* A utility program originally written for the Linux OS SCSI subsystem.
2*44704f69SBart Van Assche  *  Copyright (C) 1999-2022 D. Gilbert
3*44704f69SBart Van Assche  *  This program is free software; you can redistribute it and/or modify
4*44704f69SBart Van Assche  *  it under the terms of the GNU General Public License as published by
5*44704f69SBart Van Assche  *  the Free Software Foundation; either version 2, or (at your option)
6*44704f69SBart Van Assche  *  any later version.
7*44704f69SBart Van Assche  *
8*44704f69SBart Van Assche  * SPDX-License-Identifier: GPL-2.0-or-later
9*44704f69SBart Van Assche  *
10*44704f69SBart Van Assche  * This program send either device, bus or host resets to device,
11*44704f69SBart Van Assche  * or bus or host associated with the given sg device. This is a Linux
12*44704f69SBart Van Assche  * only utility (perhaps Android as well).
13*44704f69SBart Van Assche  */
14*44704f69SBart Van Assche 
15*44704f69SBart Van Assche #include <unistd.h>
16*44704f69SBart Van Assche #include <fcntl.h>
17*44704f69SBart Van Assche #include <stdio.h>
18*44704f69SBart Van Assche #include <stdlib.h>
19*44704f69SBart Van Assche #include <stdarg.h>
20*44704f69SBart Van Assche #include <stdbool.h>
21*44704f69SBart Van Assche #include <string.h>
22*44704f69SBart Van Assche #include <errno.h>
23*44704f69SBart Van Assche #include <sys/ioctl.h>
24*44704f69SBart Van Assche #include <sys/types.h>
25*44704f69SBart Van Assche #include <sys/stat.h>
26*44704f69SBart Van Assche #include <getopt.h>
27*44704f69SBart Van Assche 
28*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
29*44704f69SBart Van Assche #include "config.h"
30*44704f69SBart Van Assche #endif
31*44704f69SBart Van Assche #include "sg_io_linux.h"
32*44704f69SBart Van Assche 
33*44704f69SBart Van Assche 
34*44704f69SBart Van Assche #define ME "sg_reset: "
35*44704f69SBart Van Assche 
36*44704f69SBart Van Assche static const char * version_str = "0.68 20220308";
37*44704f69SBart Van Assche 
38*44704f69SBart Van Assche #ifndef SG_SCSI_RESET
39*44704f69SBart Van Assche #define SG_SCSI_RESET 0x2284
40*44704f69SBart Van Assche #endif
41*44704f69SBart Van Assche 
42*44704f69SBart Van Assche #ifndef SG_SCSI_RESET_NOTHING
43*44704f69SBart Van Assche #define SG_SCSI_RESET_NOTHING 0
44*44704f69SBart Van Assche #define SG_SCSI_RESET_DEVICE 1
45*44704f69SBart Van Assche #define SG_SCSI_RESET_BUS 2
46*44704f69SBart Van Assche #define SG_SCSI_RESET_HOST 3
47*44704f69SBart Van Assche #endif
48*44704f69SBart Van Assche 
49*44704f69SBart Van Assche #ifndef SG_SCSI_RESET_TARGET
50*44704f69SBart Van Assche #define SG_SCSI_RESET_TARGET 4
51*44704f69SBart Van Assche #endif
52*44704f69SBart Van Assche 
53*44704f69SBart Van Assche #ifndef SG_SCSI_RESET_NO_ESCALATE
54*44704f69SBart Van Assche #define SG_SCSI_RESET_NO_ESCALATE 0x100
55*44704f69SBart Van Assche #endif
56*44704f69SBart Van Assche 
57*44704f69SBart Van Assche static struct option long_options[] = {
58*44704f69SBart Van Assche         {"bus", no_argument, 0, 'b'},
59*44704f69SBart Van Assche         {"device", no_argument, 0, 'd'},
60*44704f69SBart Van Assche         {"help", no_argument, 0, 'z'},
61*44704f69SBart Van Assche         {"host", no_argument, 0, 'H'},
62*44704f69SBart Van Assche         {"no-esc", no_argument, 0, 'N'},
63*44704f69SBart Van Assche         {"no_esc", no_argument, 0, 'N'},
64*44704f69SBart Van Assche         {"no-escalate", no_argument, 0, 'N'},
65*44704f69SBart Van Assche         {"target", no_argument, 0, 't'},
66*44704f69SBart Van Assche         {"verbose", no_argument, 0, 'v'},
67*44704f69SBart Van Assche         {"version", no_argument, 0, 'V'},
68*44704f69SBart Van Assche         {0, 0, 0, 0},
69*44704f69SBart Van Assche };
70*44704f69SBart Van Assche 
71*44704f69SBart Van Assche #if defined(__GNUC__) || defined(__clang__)
72*44704f69SBart Van Assche static int pr2serr(const char * fmt, ...)
73*44704f69SBart Van Assche         __attribute__ ((format (printf, 1, 2)));
74*44704f69SBart Van Assche #else
75*44704f69SBart Van Assche static int pr2serr(const char * fmt, ...);
76*44704f69SBart Van Assche #endif
77*44704f69SBart Van Assche 
78*44704f69SBart Van Assche 
79*44704f69SBart Van Assche static int
pr2serr(const char * fmt,...)80*44704f69SBart Van Assche pr2serr(const char * fmt, ...)
81*44704f69SBart Van Assche {
82*44704f69SBart Van Assche     va_list args;
83*44704f69SBart Van Assche     int n;
84*44704f69SBart Van Assche 
85*44704f69SBart Van Assche     va_start(args, fmt);
86*44704f69SBart Van Assche     n = vfprintf(stderr, fmt, args);
87*44704f69SBart Van Assche     va_end(args);
88*44704f69SBart Van Assche     return n;
89*44704f69SBart Van Assche }
90*44704f69SBart Van Assche 
91*44704f69SBart Van Assche static void
usage(int compat_mode)92*44704f69SBart Van Assche usage(int compat_mode)
93*44704f69SBart Van Assche {
94*44704f69SBart Van Assche     pr2serr("Usage: sg_reset [--bus] [--device] [--help] [--host] [--no-esc] "
95*44704f69SBart Van Assche             "[--no-escalate] [--target]\n"
96*44704f69SBart Van Assche             "                [--verbose] [--version] DEVICE\n"
97*44704f69SBart Van Assche             "  where:\n"
98*44704f69SBart Van Assche             "    --bus|-b        SCSI bus reset (SPI concept), might be all "
99*44704f69SBart Van Assche             "targets\n"
100*44704f69SBart Van Assche             "    --device|-d     device (logical unit) reset\n");
101*44704f69SBart Van Assche     if (compat_mode) {
102*44704f69SBart Van Assche         pr2serr("    --help|-z       print usage information then exit\n"
103*44704f69SBart Van Assche                 "    --host|-h|-H    host (bus adapter: HBA) reset\n");
104*44704f69SBart Van Assche     } else {
105*44704f69SBart Van Assche         pr2serr("    --help|-h       print usage information then exit\n"
106*44704f69SBart Van Assche                 "    --host|-H       host (bus adapter: HBA) reset\n");
107*44704f69SBart Van Assche     }
108*44704f69SBart Van Assche     pr2serr("    --no-esc|-N     overrides default action and only does "
109*44704f69SBart Van Assche             "reset requested\n"
110*44704f69SBart Van Assche             "    --no-escalate   The same as --no-esc|-N\n"
111*44704f69SBart Van Assche             "    --target|-t     target reset. The target holds the DEVICE "
112*44704f69SBart Van Assche             "and perhaps\n"
113*44704f69SBart Van Assche             "                    other LUs\n"
114*44704f69SBart Van Assche             "    --verbose|-v    increase the level of verbosity\n"
115*44704f69SBart Van Assche             "    --version|-V    print version number then exit\n\n"
116*44704f69SBart Van Assche             "Use SG_SCSI_RESET ioctl to send a reset to the "
117*44704f69SBart Van Assche             "host/bus/target/device\nalong the DEVICE path. The DEVICE "
118*44704f69SBart Van Assche             "itself is known as a logical unit (LU)\nin SCSI terminology.\n"
119*44704f69SBart Van Assche             "Be warned: if the '-N' option is not given then if '-d' "
120*44704f69SBart Van Assche             "fails then a\ntarget reset ('-t') is instigated. And it "
121*44704f69SBart Van Assche             "'-t' fails then a bus reset\n('-b') is instigated. And if "
122*44704f69SBart Van Assche             "'-b' fails then a host reset ('h') is\ninstigated. It is "
123*44704f69SBart Van Assche             "recommended to use '-N' to stop the reset escalation.\n"
124*44704f69SBart Van Assche            );
125*44704f69SBart Van Assche }
126*44704f69SBart Van Assche 
127*44704f69SBart Van Assche 
main(int argc,char * argv[])128*44704f69SBart Van Assche int main(int argc, char * argv[])
129*44704f69SBart Van Assche {
130*44704f69SBart Van Assche     bool do_device_reset = false;
131*44704f69SBart Van Assche     bool do_bus_reset = false;
132*44704f69SBart Van Assche     bool do_host_reset = false;
133*44704f69SBart Van Assche     bool no_escalate = false;
134*44704f69SBart Van Assche     bool do_target_reset = false;
135*44704f69SBart Van Assche     int c, sg_fd, res, k, hold_errno;
136*44704f69SBart Van Assche     int verbose = 0;
137*44704f69SBart Van Assche     char * device_name = NULL;
138*44704f69SBart Van Assche     char * cp = NULL;
139*44704f69SBart Van Assche 
140*44704f69SBart Van Assche     cp = getenv("SG3_UTILS_OLD_OPTS");
141*44704f69SBart Van Assche     if (NULL == cp)
142*44704f69SBart Van Assche         cp = getenv("SG_RESET_OLD_OPTS");
143*44704f69SBart Van Assche 
144*44704f69SBart Van Assche     while (1) {
145*44704f69SBart Van Assche         int option_index = 0;
146*44704f69SBart Van Assche 
147*44704f69SBart Van Assche         c = getopt_long(argc, argv, "bdhHNtvVz", long_options,
148*44704f69SBart Van Assche                         &option_index);
149*44704f69SBart Van Assche         if (c == -1)
150*44704f69SBart Van Assche             break;
151*44704f69SBart Van Assche 
152*44704f69SBart Van Assche         switch (c) {
153*44704f69SBart Van Assche         case 'b':
154*44704f69SBart Van Assche             do_bus_reset = true;
155*44704f69SBart Van Assche             break;
156*44704f69SBart Van Assche         case 'd':
157*44704f69SBart Van Assche             do_device_reset = true;
158*44704f69SBart Van Assche             break;
159*44704f69SBart Van Assche         case 'h':
160*44704f69SBart Van Assche             if (cp) {
161*44704f69SBart Van Assche                 do_host_reset = true;
162*44704f69SBart Van Assche                 break;
163*44704f69SBart Van Assche             } else {
164*44704f69SBart Van Assche                 usage(!!cp);
165*44704f69SBart Van Assche                 return 0;
166*44704f69SBart Van Assche             }
167*44704f69SBart Van Assche         case 'H':
168*44704f69SBart Van Assche             do_host_reset = true;
169*44704f69SBart Van Assche             break;
170*44704f69SBart Van Assche         case 'N':
171*44704f69SBart Van Assche             no_escalate = true;
172*44704f69SBart Van Assche             break;
173*44704f69SBart Van Assche         case 't':
174*44704f69SBart Van Assche             do_target_reset = true;
175*44704f69SBart Van Assche             break;
176*44704f69SBart Van Assche         case 'v':
177*44704f69SBart Van Assche             ++verbose;
178*44704f69SBart Van Assche             break;
179*44704f69SBart Van Assche         case 'V':
180*44704f69SBart Van Assche             pr2serr(ME "version: %s\n", version_str);
181*44704f69SBart Van Assche             return 0;
182*44704f69SBart Van Assche         case 'z':
183*44704f69SBart Van Assche             usage(!!cp);
184*44704f69SBart Van Assche             return 0;
185*44704f69SBart Van Assche         default:
186*44704f69SBart Van Assche             pr2serr("unrecognised option code 0x%x ??\n", c);
187*44704f69SBart Van Assche             usage(!!cp);
188*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
189*44704f69SBart Van Assche         }
190*44704f69SBart Van Assche     }
191*44704f69SBart Van Assche 
192*44704f69SBart Van Assche     if (optind < argc) {
193*44704f69SBart Van Assche         if (NULL == device_name) {
194*44704f69SBart Van Assche             device_name = argv[optind];
195*44704f69SBart Van Assche             ++optind;
196*44704f69SBart Van Assche         }
197*44704f69SBart Van Assche         if (optind < argc) {
198*44704f69SBart Van Assche             for (; optind < argc; ++optind)
199*44704f69SBart Van Assche                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
200*44704f69SBart Van Assche             usage(!!cp);
201*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
202*44704f69SBart Van Assche         }
203*44704f69SBart Van Assche     }
204*44704f69SBart Van Assche     if (NULL == device_name) {
205*44704f69SBart Van Assche         pr2serr("Missing DEVICE name. Use '--help' to see usage.\n");
206*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
207*44704f69SBart Van Assche     }
208*44704f69SBart Van Assche 
209*44704f69SBart Van Assche     if (cp && (0 == verbose))
210*44704f69SBart Van Assche         ++verbose;      // older behaviour was more verbose
211*44704f69SBart Van Assche 
212*44704f69SBart Van Assche     if (((int)do_device_reset + (int)do_target_reset + (int)do_bus_reset +
213*44704f69SBart Van Assche          (int)do_host_reset) > 1) {
214*44704f69SBart Van Assche         pr2serr("Can only request one type of reset per invocation\n");
215*44704f69SBart Van Assche         return 1;
216*44704f69SBart Van Assche     }
217*44704f69SBart Van Assche 
218*44704f69SBart Van Assche     sg_fd = open(device_name, O_RDWR | O_NONBLOCK);
219*44704f69SBart Van Assche     if (sg_fd < 0) {
220*44704f69SBart Van Assche         pr2serr(ME "open error: %s: ", device_name);
221*44704f69SBart Van Assche         perror("");
222*44704f69SBart Van Assche         return 1;
223*44704f69SBart Van Assche     }
224*44704f69SBart Van Assche 
225*44704f69SBart Van Assche     k = SG_SCSI_RESET_NOTHING;
226*44704f69SBart Van Assche     if (do_device_reset) {
227*44704f69SBart Van Assche         if (verbose)
228*44704f69SBart Van Assche             printf(ME "starting device reset\n");
229*44704f69SBart Van Assche         k = SG_SCSI_RESET_DEVICE;
230*44704f69SBart Van Assche     }
231*44704f69SBart Van Assche     else if (do_target_reset) {
232*44704f69SBart Van Assche         if (verbose)
233*44704f69SBart Van Assche             printf(ME "starting target reset\n");
234*44704f69SBart Van Assche         k = SG_SCSI_RESET_TARGET;
235*44704f69SBart Van Assche     }
236*44704f69SBart Van Assche     else if (do_bus_reset) {
237*44704f69SBart Van Assche         if (verbose)
238*44704f69SBart Van Assche             printf(ME "starting bus reset\n");
239*44704f69SBart Van Assche         k = SG_SCSI_RESET_BUS;
240*44704f69SBart Van Assche     }
241*44704f69SBart Van Assche     else if (do_host_reset) {
242*44704f69SBart Van Assche         if (verbose)
243*44704f69SBart Van Assche             printf(ME "starting host reset\n");
244*44704f69SBart Van Assche         k = SG_SCSI_RESET_HOST;
245*44704f69SBart Van Assche     }
246*44704f69SBart Van Assche     if (no_escalate)
247*44704f69SBart Van Assche         k += SG_SCSI_RESET_NO_ESCALATE;
248*44704f69SBart Van Assche     if (verbose > 2)
249*44704f69SBart Van Assche         pr2serr("    third argument to ioctl(SG_SCSI_RESET) is 0x%x\n", k);
250*44704f69SBart Van Assche 
251*44704f69SBart Van Assche     res = ioctl(sg_fd, SG_SCSI_RESET, &k);
252*44704f69SBart Van Assche     if (res < 0) {
253*44704f69SBart Van Assche         hold_errno = errno;
254*44704f69SBart Van Assche         switch (errno) {
255*44704f69SBart Van Assche         case EBUSY:
256*44704f69SBart Van Assche             pr2serr(ME "BUSY, may be resetting now\n");
257*44704f69SBart Van Assche             break;
258*44704f69SBart Van Assche         case ENODEV:
259*44704f69SBart Van Assche             pr2serr(ME "'no device' error, may be temporary while device is "
260*44704f69SBart Van Assche                     "resetting\n");
261*44704f69SBart Van Assche             break;
262*44704f69SBart Van Assche         case EAGAIN:
263*44704f69SBart Van Assche             pr2serr(ME "try again later, may be resetting now\n");
264*44704f69SBart Van Assche             break;
265*44704f69SBart Van Assche         case EIO:
266*44704f69SBart Van Assche             pr2serr(ME "reset (for value=0x%x) may not be available\n", k);
267*44704f69SBart Van Assche             break;
268*44704f69SBart Van Assche         case EPERM:
269*44704f69SBart Van Assche         case EACCES:
270*44704f69SBart Van Assche             pr2serr(ME "reset requires CAP_SYS_ADMIN (root) permission\n");
271*44704f69SBart Van Assche             break;
272*44704f69SBart Van Assche         case EINVAL:
273*44704f69SBart Van Assche             pr2serr(ME "SG_SCSI_RESET not supported (for value=0x%x)\n", k);
274*44704f69SBart Van Assche #if defined(__GNUC__)
275*44704f69SBart Van Assche #if (__GNUC__ >= 7)
276*44704f69SBart Van Assche             __attribute__((fallthrough));
277*44704f69SBart Van Assche             /* FALL THROUGH */
278*44704f69SBart Van Assche #endif
279*44704f69SBart Van Assche #endif
280*44704f69SBart Van Assche         default:
281*44704f69SBart Van Assche             perror(ME "SG_SCSI_RESET failed");
282*44704f69SBart Van Assche             break;
283*44704f69SBart Van Assche         }
284*44704f69SBart Van Assche         if (verbose > 1)
285*44704f69SBart Van Assche             pr2serr(ME "ioctl(SG_SCSI_RESET) returned %d, errno=%d\n", res,
286*44704f69SBart Van Assche                     hold_errno);
287*44704f69SBart Van Assche         close(sg_fd);
288*44704f69SBart Van Assche         return 1;
289*44704f69SBart Van Assche     }
290*44704f69SBart Van Assche 
291*44704f69SBart Van Assche     if (no_escalate)
292*44704f69SBart Van Assche         k -= SG_SCSI_RESET_NO_ESCALATE;
293*44704f69SBart Van Assche     if (verbose) {
294*44704f69SBart Van Assche         if (SG_SCSI_RESET_NOTHING == k)
295*44704f69SBart Van Assche             printf(ME "did nothing, device is normal mode\n");
296*44704f69SBart Van Assche         else if (SG_SCSI_RESET_DEVICE == k)
297*44704f69SBart Van Assche             printf(ME "completed device %sreset\n", (no_escalate ?
298*44704f69SBart Van Assche                     "" : "(or target or bus or host) "));
299*44704f69SBart Van Assche         else if (SG_SCSI_RESET_TARGET == k)
300*44704f69SBart Van Assche             printf(ME "completed target %sreset\n", (no_escalate ?
301*44704f69SBart Van Assche                     "" : "(or bus or host) "));
302*44704f69SBart Van Assche         else if (SG_SCSI_RESET_BUS == k)
303*44704f69SBart Van Assche             printf(ME "completed bus %sreset\n", (no_escalate ?
304*44704f69SBart Van Assche                     "" : "(or host) "));
305*44704f69SBart Van Assche         else if (SG_SCSI_RESET_HOST == k)
306*44704f69SBart Van Assche             printf(ME "completed host reset\n");
307*44704f69SBart Van Assche     }
308*44704f69SBart Van Assche 
309*44704f69SBart Van Assche     if (close(sg_fd) < 0) {
310*44704f69SBart Van Assche         perror(ME "close error");
311*44704f69SBart Van Assche         return 1;
312*44704f69SBart Van Assche     }
313*44704f69SBart Van Assche     return 0;
314*44704f69SBart Van Assche }
315