xref: /aosp_15_r20/external/sg3_utils/src/sg_rdac.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * sg_rdac
3  *
4  * Retrieve / set RDAC options.
5  *
6  * Copyright (C) 2006-2018 Hannes Reinecke <[email protected]>
7  *
8  * Based on sg_modes.c and sg_emc_trespass.c; credits from there apply.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  * SPDX-License-Identifier: GPL-2.0-or-later
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <stdbool.h>
22 #include <unistd.h>
23 #include <string.h>
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "sg_lib.h"
30 #include "sg_cmds_basic.h"
31 #include "sg_unaligned.h"
32 #include "sg_pr2serr.h"
33 
34 
35 static const char * version_str = "1.17 20180512";
36 
37 uint8_t mode6_hdr[] = {
38     0x75, /* Length */
39     0, /* medium */
40     0, /* params */
41     8, /* Block descriptor length */
42 };
43 
44 uint8_t mode10_hdr[] = {
45     0x01, 0x18, /* Length */
46     0, /* medium */
47     0, /* params */
48     0, 0, /* reserved */
49     0, 0, /* block descriptor length */
50 };
51 
52 uint8_t block_descriptor[] = {
53     0, /* Density code */
54     0, 0, 0, /* Number of blocks */
55     0, /* Reserved */
56     0, 0x02, 0, /* 512 byte blocks */
57 };
58 
59 struct rdac_page_common {
60     uint8_t  current_serial[16];
61     uint8_t  alternate_serial[16];
62     uint8_t  current_mode_msb;
63     uint8_t  current_mode_lsb;
64     uint8_t  alternate_mode_msb;
65     uint8_t  alternate_mode_lsb;
66     uint8_t  quiescence;
67     uint8_t  options;
68 };
69 
70 struct rdac_legacy_page {
71     uint8_t  page_code;
72     uint8_t  page_length;
73     struct rdac_page_common attr;
74     uint8_t  lun_table[32];
75     uint8_t  lun_table_exp[32];
76     unsigned short reserved;
77 };
78 
79 struct rdac_expanded_page {
80     uint8_t  page_code;
81     uint8_t  subpage_code;
82     uint8_t  page_length[2];
83     struct rdac_page_common attr;
84     uint8_t  lun_table[256];
85     uint8_t  reserved[2];
86 };
87 
88 static int do_verbose = 0;
89 
dump_mode_page(uint8_t * page,int len)90 static void dump_mode_page( uint8_t *page, int len )
91 {
92         int i, k;
93 
94         for (k = 0; k < len; k += 16) {
95 
96                 printf("%x:",k / 16);
97                 for (i = 0; i < 16; i++) {
98                         printf(" %02x", page[k + i]);
99                         if (k + i >= len) {
100                                 printf("\n");
101                                 break;
102                         }
103                 }
104                 printf("\n");
105         }
106 
107 }
108 
109 #define MX_ALLOC_LEN (1024 * 4)
110 #define RDAC_CONTROLLER_PAGE 0x2c
111 #define RDAC_CONTROLLER_PAGE_LEN 0x68
112 #define LEGACY_PAGE 0x00
113 #define EXPANDED_LUN_SPACE_PAGE 0x01
114 #define EXPANDED_LUN_SPACE_PAGE_LEN 0x128
115 #define RDAC_FAIL_ALL_PATHS 0x1
116 #define RDAC_FAIL_SELECTED_PATHS 0x2
117 #define RDAC_FORCE_QUIESCENCE 0x2
118 #define RDAC_QUIESCENCE_TIME 10
119 
fail_all_paths(int fd,bool use_6_byte)120 static int fail_all_paths(int fd, bool use_6_byte)
121 {
122         struct rdac_legacy_page *rdac_page;
123         struct rdac_expanded_page *rdac_page_exp;
124         struct rdac_page_common *rdac_common = NULL;
125         uint8_t fail_paths_pg[308];
126 
127         int res;
128         char b[80];
129 
130         memset(fail_paths_pg, 0, 308);
131         if (use_6_byte) {
132                 memcpy(fail_paths_pg, mode6_hdr, 4);
133                 memcpy(fail_paths_pg + 4, block_descriptor, 8);
134                 rdac_page = (struct rdac_legacy_page *)(fail_paths_pg + 4 + 8);
135                 rdac_page->page_code = RDAC_CONTROLLER_PAGE;
136                 rdac_page->page_length = RDAC_CONTROLLER_PAGE_LEN;
137                 rdac_common = &rdac_page->attr;
138         } else {
139                 memcpy(fail_paths_pg, mode10_hdr, 8);
140                 rdac_page_exp = (struct rdac_expanded_page *)
141                                 (fail_paths_pg + 8);
142                 rdac_page_exp->page_code = RDAC_CONTROLLER_PAGE | 0x40;
143                 rdac_page_exp->subpage_code = 0x1;
144                 sg_put_unaligned_be16(EXPANDED_LUN_SPACE_PAGE_LEN,
145                                       rdac_page_exp->page_length + 0);
146                 rdac_common = &rdac_page_exp->attr;
147         }
148 
149         rdac_common->current_mode_lsb =  RDAC_FAIL_ALL_PATHS;
150         rdac_common->quiescence = RDAC_QUIESCENCE_TIME;
151         rdac_common->options = RDAC_FORCE_QUIESCENCE;
152 
153         if (use_6_byte) {
154                 res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
155                                         fail_paths_pg, 118,
156                                         true, (do_verbose ? 2 : 0));
157         } else {
158                 res = sg_ll_mode_select10(fd, 1 /* pf */, 0 /* sp */,
159                                         fail_paths_pg, 308,
160                                         true, (do_verbose ? 2: 0));
161         }
162 
163         switch (res) {
164         case 0:
165                 if (do_verbose)
166                         pr2serr("fail paths successful\n");
167                 break;
168         default:
169                 sg_get_category_sense_str(res, sizeof(b), b, do_verbose);
170                 pr2serr("fail paths failed: %s\n", b);
171                 break;
172         }
173 
174         return res;
175 }
176 
fail_this_path(int fd,int lun,bool use_6_byte)177 static int fail_this_path(int fd, int lun, bool use_6_byte)
178 {
179         int res;
180         struct rdac_legacy_page *rdac_page;
181         struct rdac_expanded_page *rdac_page_exp;
182         struct rdac_page_common *rdac_common = NULL;
183         uint8_t fail_paths_pg[308];
184         char b[80];
185 
186         if (use_6_byte) {
187                 if (lun > 31) {
188                         pr2serr("must use 10 byte cdb to fail luns over 31\n");
189                         return -1;
190                 }
191         } else {        /* 10 byte cdb case */
192                 if (lun > 255) {
193                         pr2serr("lun cannot exceed 255\n");
194                         return -1;
195                 }
196         }
197 
198         memset(fail_paths_pg, 0, 308);
199         if (use_6_byte) {
200                 memcpy(fail_paths_pg, mode6_hdr, 4);
201                 memcpy(fail_paths_pg + 4, block_descriptor, 8);
202                 rdac_page = (struct rdac_legacy_page *)(fail_paths_pg + 4 + 8);
203                 rdac_page->page_code = RDAC_CONTROLLER_PAGE;
204                 rdac_page->page_length = RDAC_CONTROLLER_PAGE_LEN;
205                 rdac_common = &rdac_page->attr;
206                 memset(rdac_page->lun_table, 0x0, 32);
207                 rdac_page->lun_table[lun] = 0x81;
208         } else {
209                 memcpy(fail_paths_pg, mode10_hdr, 8);
210                 rdac_page_exp = (struct rdac_expanded_page *)
211                                 (fail_paths_pg + 8);
212                 rdac_page_exp->page_code = RDAC_CONTROLLER_PAGE | 0x40;
213                 rdac_page_exp->subpage_code = 0x1;
214                 sg_put_unaligned_be16(EXPANDED_LUN_SPACE_PAGE_LEN,
215                                       rdac_page_exp->page_length + 0);
216                 rdac_common = &rdac_page_exp->attr;
217                 memset(rdac_page_exp->lun_table, 0x0, 256);
218                 rdac_page_exp->lun_table[lun] = 0x81;
219         }
220 
221         rdac_common->current_mode_lsb =  RDAC_FAIL_SELECTED_PATHS;
222         rdac_common->quiescence = RDAC_QUIESCENCE_TIME;
223         rdac_common->options = RDAC_FORCE_QUIESCENCE;
224 
225         if (use_6_byte) {
226                 res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
227                                         fail_paths_pg, 118,
228                                         true, (do_verbose ? 2 : 0));
229         } else {
230                 res = sg_ll_mode_select10(fd, 1 /* pf */, 0 /* sp */,
231                                         fail_paths_pg, 308,
232                                         true, (do_verbose ? 2: 0));
233         }
234 
235         switch (res) {
236         case 0:
237                 if (do_verbose)
238                         pr2serr("fail paths successful\n");
239                 break;
240         default:
241                 sg_get_category_sense_str(res, sizeof(b), b, do_verbose);
242                 pr2serr("fail paths page (lun=%d) failed: %s\n", lun, b);
243                 break;
244         }
245 
246         return res;
247 }
248 
print_rdac_mode(uint8_t * ptr,bool exp_subpg)249 static void print_rdac_mode(uint8_t *ptr, bool exp_subpg)
250 {
251         int i, k, bd_len, lun_table_len;
252         uint8_t * lun_table = NULL;
253         struct rdac_legacy_page *legacy;
254         struct rdac_expanded_page *expanded;
255         struct rdac_page_common *rdac_ptr = NULL;
256 
257         if (exp_subpg) {
258                 bd_len = ptr[7];
259                 expanded = (struct rdac_expanded_page *)(ptr + 8 + bd_len);
260                 rdac_ptr = &expanded->attr;
261                 lun_table = expanded->lun_table;
262                 lun_table_len = 256;
263         } else {
264                 bd_len = ptr[3];
265                 legacy = (struct rdac_legacy_page *)(ptr + 4 + bd_len);
266                 rdac_ptr = &legacy->attr;
267                 lun_table = legacy->lun_table;
268                 lun_table_len = 32;
269         }
270 
271         printf("RDAC %s page\n", exp_subpg ? "Expanded" : "Legacy");
272         printf("  Controller serial: %s\n",
273                rdac_ptr->current_serial);
274         printf("  Alternate controller serial: %s\n",
275                rdac_ptr->alternate_serial);
276         printf("  RDAC mode (redundant processor): ");
277         switch (rdac_ptr->current_mode_msb) {
278         case 0x00:
279                 printf("alternate controller not present; ");
280                 break;
281         case 0x01:
282                 printf("alternate controller present; ");
283                 break;
284         default:
285                 printf("(Unknown controller status 0x%x); ",
286                        rdac_ptr->current_mode_msb);
287                 break;
288         }
289         switch (rdac_ptr->current_mode_lsb) {
290         case 0x0:
291                 printf("inactive\n");
292                 break;
293         case 0x1:
294                 printf("active\n");
295                 break;
296         case 0x2:
297                 printf("Dual active mode\n");
298                 break;
299         default:
300                 printf("(Unknown mode 0x%x)\n",
301                        rdac_ptr->current_mode_lsb);
302         }
303 
304         printf("  RDAC mode (alternate processor): ");
305         switch (rdac_ptr->alternate_mode_msb) {
306         case 0x00:
307                 printf("alternate controller not present; ");
308                 break;
309         case 0x01:
310                 printf("alternate controller present; ");
311                 break;
312         default:
313                 printf("(Unknown status 0x%x); ",
314                        rdac_ptr->alternate_mode_msb);
315                 break;
316         }
317         switch (rdac_ptr->alternate_mode_lsb) {
318         case 0x0:
319                 printf("inactive\n");
320                 break;
321         case 0x1:
322                 printf("active\n");
323                 break;
324         case 0x2:
325                 printf("Dual active mode\n");
326                 break;
327         case 0x3:
328                 printf("Not present\n");
329                 break;
330         case 0x4:
331                 printf("held in reset\n");
332                 break;
333         default:
334                 printf("(Unknown mode 0x%x)\n",
335                        rdac_ptr->alternate_mode_lsb);
336         }
337         printf("  Quiescence timeout: %d\n", rdac_ptr->quiescence);
338         printf("  RDAC option 0x%x\n", rdac_ptr->options);
339         printf("    ALUA: %s\n", (rdac_ptr->options & 0x4 ? "Enabled" :
340                                                             "Disabled" ));
341         printf("    Force Quiescence: %s\n", (rdac_ptr->options & 0x2 ?
342                                               "Enabled" : "Disabled" ));
343         printf ("  LUN Table: (p = preferred, a = alternate, u = utm lun)\n");
344         printf("         0 1 2 3 4 5 6 7  8 9 a b c d e f\n");
345         for (k = 0; k < lun_table_len; k += 16) {
346                 printf("    0x%x:",k / 16);
347                 for (i = 0; i < 16; i++) {
348                         switch (lun_table[k + i]) {
349                         case 0x0:
350                                 printf(" x");
351                                 break;
352                         case 0x1:
353                                 printf(" p");
354                                 break;
355                         case 0x2:
356                                 printf(" a");
357                                 break;
358                         case 0x3:
359                                 printf(" u");
360                                 break;
361                         default:
362                                 printf(" ?");
363                                 break;
364                         }
365                         if (i == 7) {
366                                 printf(" ");
367                         }
368                 }
369                 printf("\n");
370         }
371 }
372 
usage()373 static void usage()
374 {
375     printf("Usage:  sg_rdac [-6] [-a] [-f=LUN] [-v] [-V] DEVICE\n"
376            "  where:\n"
377            "    -6        use 6 byte cdbs for mode sense/select\n"
378            "    -a        transfer all devices to the controller\n"
379            "              serving DEVICE.\n"
380            "    -f=LUN    transfer the device at LUN to the\n"
381            "              controller serving DEVICE\n"
382            "    -v        verbose\n"
383            "    -V        print version then exit\n\n"
384            " Display/Modify RDAC Redundant Controller Page 0x2c.\n"
385            " If [-a] or [-f] is not specified the current settings"
386            " are displayed.\n");
387 }
388 
main(int argc,char * argv[])389 int main(int argc, char * argv[])
390 {
391         bool fail_all = false;
392         bool fail_path = false;
393         bool use_6_byte = false;
394         int res, fd, k, resid, len, lun = -1;
395         int ret = 0;
396         char **argptr;
397         char * file_name = 0;
398         uint8_t rsp_buff[MX_ALLOC_LEN];
399 
400         if (argc < 2) {
401                 usage ();
402                 return SG_LIB_SYNTAX_ERROR;
403         }
404 
405         for (k = 1; k < argc; ++k) {
406                 argptr = argv + k;
407                 if (!strcmp (*argptr, "-v"))
408                         ++do_verbose;
409                 else if (!strncmp(*argptr, "-f=",3)) {
410                         fail_path = true;
411                         lun = strtoul(*argptr + 3, NULL, 0);
412                 }
413                 else if (!strcmp(*argptr, "-a")) {
414                         fail_all = true;
415                 }
416                 else if (!strcmp(*argptr, "-6")) {
417                         use_6_byte = true;
418                 }
419                 else if (!strcmp(*argptr, "-V")) {
420                         pr2serr("sg_rdac version: %s\n", version_str);
421                         return 0;
422                 }
423                 else if (*argv[k] == '-') {
424                         pr2serr("Unrecognized switch: %s\n", argv[k]);
425                         file_name = 0;
426                         break;
427                 }
428                 else if (0 == file_name)
429                         file_name = argv[k];
430                 else {
431                         pr2serr("too many arguments\n");
432                         file_name = 0;
433                         break;
434                 }
435         }
436         if (0 == file_name) {
437                 usage();
438                 return SG_LIB_SYNTAX_ERROR;
439         }
440 
441         fd = sg_cmds_open_device(file_name, false /* rw */, do_verbose);
442         if (fd < 0) {
443                 pr2serr("open error: %s: %s\n", file_name, safe_strerror(-fd));
444                 usage();
445                 ret = sg_convert_errno(-fd);
446                 goto fini;
447         }
448 
449         if (fail_all) {
450                 res = fail_all_paths(fd, use_6_byte);
451         } else if (fail_path) {
452                 res = fail_this_path(fd, lun, use_6_byte);
453         } else {
454                 resid = 0;
455                 if (use_6_byte)
456                         res = sg_ll_mode_sense6(fd, /* DBD */ false,
457                                                 /* PC */ 0,
458                                                 0x2c /* page */,
459                                                 0 /*subpage */,
460                                                 rsp_buff, 252,
461                                                 true, do_verbose);
462                 else
463                         res = sg_ll_mode_sense10_v2(fd, /* llbaa */ false,
464                                                     /* DBD */ false,
465                                                     /* page control */0,
466                                                     0x2c, 0x1 /* subpage */,
467                                                     rsp_buff, 308, 0, &resid,
468                                                     true, do_verbose);
469 
470                 if (! res) {
471                         len = sg_msense_calc_length(rsp_buff, 308, use_6_byte,
472                                                     NULL);
473                         if (resid > 0) {
474                                 len = ((308 - resid) < len) ? (308 - resid) :
475                                                               len;
476                                 if (len < 2)
477                                         pr2serr("MS(10) residual value (%d) "
478                                                 "a worry\n", resid);
479                         }
480                         if (do_verbose && (len > 1))
481                                 dump_mode_page(rsp_buff, len);
482                         print_rdac_mode(rsp_buff, ! use_6_byte);
483                 } else {
484                         if (SG_LIB_CAT_INVALID_OP == res)
485                                 pr2serr(">>>>>> try again without the '-6' "
486                                         "switch for a 10 byte MODE SENSE "
487                                         "command\n");
488                         else if (SG_LIB_CAT_ILLEGAL_REQ == res)
489                                 pr2serr("mode sense: invalid field in cdb "
490                                         "(perhaps subpages or page control "
491                                         "(PC) not supported)\n");
492                         else {
493                                 char b[80];
494 
495                                 sg_get_category_sense_str(res, sizeof(b), b,
496                                                           do_verbose);
497                                 pr2serr("mode sense failed: %s\n", b);
498                         }
499                 }
500         }
501         ret = res;
502 
503         res = sg_cmds_close_device(fd);
504         if (res < 0) {
505                 pr2serr("close error: %s\n", safe_strerror(-res));
506                 if (0 == ret)
507                         ret = sg_convert_errno(res);
508         }
509 fini:
510         if (0 == do_verbose) {
511                 if (! sg_if_can2stderr("sg_rdac failed: ", ret))
512                         pr2serr("Some error occurred, try again with '-v' "
513                                 "or '-vv' for more information\n");
514         }
515         return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
516 }
517