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