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