aboutsummaryrefslogtreecommitdiff
path: root/src/sg_rdac.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_rdac.c')
-rw-r--r--src/sg_rdac.c516
1 files changed, 516 insertions, 0 deletions
diff --git a/src/sg_rdac.c b/src/sg_rdac.c
new file mode 100644
index 00000000..b0d1cdcf
--- /dev/null
+++ b/src/sg_rdac.c
@@ -0,0 +1,516 @@
+/*
+ * sg_rdac
+ *
+ * Retrieve / set RDAC options.
+ *
+ * Copyright (C) 2006-2018 Hannes Reinecke <hare@suse.de>
+ *
+ * Based on sg_modes.c and sg_emc_trespass.c; credits from there apply.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "1.17 20180512";
+
+uint8_t mode6_hdr[] = {
+ 0x75, /* Length */
+ 0, /* medium */
+ 0, /* params */
+ 8, /* Block descriptor length */
+};
+
+uint8_t mode10_hdr[] = {
+ 0x01, 0x18, /* Length */
+ 0, /* medium */
+ 0, /* params */
+ 0, 0, /* reserved */
+ 0, 0, /* block descriptor length */
+};
+
+uint8_t block_descriptor[] = {
+ 0, /* Density code */
+ 0, 0, 0, /* Number of blocks */
+ 0, /* Reserved */
+ 0, 0x02, 0, /* 512 byte blocks */
+};
+
+struct rdac_page_common {
+ uint8_t current_serial[16];
+ uint8_t alternate_serial[16];
+ uint8_t current_mode_msb;
+ uint8_t current_mode_lsb;
+ uint8_t alternate_mode_msb;
+ uint8_t alternate_mode_lsb;
+ uint8_t quiescence;
+ uint8_t options;
+};
+
+struct rdac_legacy_page {
+ uint8_t page_code;
+ uint8_t page_length;
+ struct rdac_page_common attr;
+ uint8_t lun_table[32];
+ uint8_t lun_table_exp[32];
+ unsigned short reserved;
+};
+
+struct rdac_expanded_page {
+ uint8_t page_code;
+ uint8_t subpage_code;
+ uint8_t page_length[2];
+ struct rdac_page_common attr;
+ uint8_t lun_table[256];
+ uint8_t reserved[2];
+};
+
+static int do_verbose = 0;
+
+static void dump_mode_page( uint8_t *page, int len )
+{
+ int i, k;
+
+ for (k = 0; k < len; k += 16) {
+
+ printf("%x:",k / 16);
+ for (i = 0; i < 16; i++) {
+ printf(" %02x", page[k + i]);
+ if (k + i >= len) {
+ printf("\n");
+ break;
+ }
+ }
+ printf("\n");
+ }
+
+}
+
+#define MX_ALLOC_LEN (1024 * 4)
+#define RDAC_CONTROLLER_PAGE 0x2c
+#define RDAC_CONTROLLER_PAGE_LEN 0x68
+#define LEGACY_PAGE 0x00
+#define EXPANDED_LUN_SPACE_PAGE 0x01
+#define EXPANDED_LUN_SPACE_PAGE_LEN 0x128
+#define RDAC_FAIL_ALL_PATHS 0x1
+#define RDAC_FAIL_SELECTED_PATHS 0x2
+#define RDAC_FORCE_QUIESCENCE 0x2
+#define RDAC_QUIESCENCE_TIME 10
+
+static int fail_all_paths(int fd, bool use_6_byte)
+{
+ struct rdac_legacy_page *rdac_page;
+ struct rdac_expanded_page *rdac_page_exp;
+ struct rdac_page_common *rdac_common = NULL;
+ uint8_t fail_paths_pg[308];
+
+ int res;
+ char b[80];
+
+ memset(fail_paths_pg, 0, 308);
+ if (use_6_byte) {
+ memcpy(fail_paths_pg, mode6_hdr, 4);
+ memcpy(fail_paths_pg + 4, block_descriptor, 8);
+ rdac_page = (struct rdac_legacy_page *)(fail_paths_pg + 4 + 8);
+ rdac_page->page_code = RDAC_CONTROLLER_PAGE;
+ rdac_page->page_length = RDAC_CONTROLLER_PAGE_LEN;
+ rdac_common = &rdac_page->attr;
+ } else {
+ memcpy(fail_paths_pg, mode10_hdr, 8);
+ rdac_page_exp = (struct rdac_expanded_page *)
+ (fail_paths_pg + 8);
+ rdac_page_exp->page_code = RDAC_CONTROLLER_PAGE | 0x40;
+ rdac_page_exp->subpage_code = 0x1;
+ sg_put_unaligned_be16(EXPANDED_LUN_SPACE_PAGE_LEN,
+ rdac_page_exp->page_length + 0);
+ rdac_common = &rdac_page_exp->attr;
+ }
+
+ rdac_common->current_mode_lsb = RDAC_FAIL_ALL_PATHS;
+ rdac_common->quiescence = RDAC_QUIESCENCE_TIME;
+ rdac_common->options = RDAC_FORCE_QUIESCENCE;
+
+ if (use_6_byte) {
+ res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
+ fail_paths_pg, 118,
+ true, (do_verbose ? 2 : 0));
+ } else {
+ res = sg_ll_mode_select10(fd, 1 /* pf */, 0 /* sp */,
+ fail_paths_pg, 308,
+ true, (do_verbose ? 2: 0));
+ }
+
+ switch (res) {
+ case 0:
+ if (do_verbose)
+ pr2serr("fail paths successful\n");
+ break;
+ default:
+ sg_get_category_sense_str(res, sizeof(b), b, do_verbose);
+ pr2serr("fail paths failed: %s\n", b);
+ break;
+ }
+
+ return res;
+}
+
+static int fail_this_path(int fd, int lun, bool use_6_byte)
+{
+ int res;
+ struct rdac_legacy_page *rdac_page;
+ struct rdac_expanded_page *rdac_page_exp;
+ struct rdac_page_common *rdac_common = NULL;
+ uint8_t fail_paths_pg[308];
+ char b[80];
+
+ if (use_6_byte) {
+ if (lun > 31) {
+ pr2serr("must use 10 byte cdb to fail luns over 31\n");
+ return -1;
+ }
+ } else { /* 10 byte cdb case */
+ if (lun > 255) {
+ pr2serr("lun cannot exceed 255\n");
+ return -1;
+ }
+ }
+
+ memset(fail_paths_pg, 0, 308);
+ if (use_6_byte) {
+ memcpy(fail_paths_pg, mode6_hdr, 4);
+ memcpy(fail_paths_pg + 4, block_descriptor, 8);
+ rdac_page = (struct rdac_legacy_page *)(fail_paths_pg + 4 + 8);
+ rdac_page->page_code = RDAC_CONTROLLER_PAGE;
+ rdac_page->page_length = RDAC_CONTROLLER_PAGE_LEN;
+ rdac_common = &rdac_page->attr;
+ memset(rdac_page->lun_table, 0x0, 32);
+ rdac_page->lun_table[lun] = 0x81;
+ } else {
+ memcpy(fail_paths_pg, mode10_hdr, 8);
+ rdac_page_exp = (struct rdac_expanded_page *)
+ (fail_paths_pg + 8);
+ rdac_page_exp->page_code = RDAC_CONTROLLER_PAGE | 0x40;
+ rdac_page_exp->subpage_code = 0x1;
+ sg_put_unaligned_be16(EXPANDED_LUN_SPACE_PAGE_LEN,
+ rdac_page_exp->page_length + 0);
+ rdac_common = &rdac_page_exp->attr;
+ memset(rdac_page_exp->lun_table, 0x0, 256);
+ rdac_page_exp->lun_table[lun] = 0x81;
+ }
+
+ rdac_common->current_mode_lsb = RDAC_FAIL_SELECTED_PATHS;
+ rdac_common->quiescence = RDAC_QUIESCENCE_TIME;
+ rdac_common->options = RDAC_FORCE_QUIESCENCE;
+
+ if (use_6_byte) {
+ res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
+ fail_paths_pg, 118,
+ true, (do_verbose ? 2 : 0));
+ } else {
+ res = sg_ll_mode_select10(fd, 1 /* pf */, 0 /* sp */,
+ fail_paths_pg, 308,
+ true, (do_verbose ? 2: 0));
+ }
+
+ switch (res) {
+ case 0:
+ if (do_verbose)
+ pr2serr("fail paths successful\n");
+ break;
+ default:
+ sg_get_category_sense_str(res, sizeof(b), b, do_verbose);
+ pr2serr("fail paths page (lun=%d) failed: %s\n", lun, b);
+ break;
+ }
+
+ return res;
+}
+
+static void print_rdac_mode(uint8_t *ptr, bool exp_subpg)
+{
+ int i, k, bd_len, lun_table_len;
+ uint8_t * lun_table = NULL;
+ struct rdac_legacy_page *legacy;
+ struct rdac_expanded_page *expanded;
+ struct rdac_page_common *rdac_ptr = NULL;
+
+ if (exp_subpg) {
+ bd_len = ptr[7];
+ expanded = (struct rdac_expanded_page *)(ptr + 8 + bd_len);
+ rdac_ptr = &expanded->attr;
+ lun_table = expanded->lun_table;
+ lun_table_len = 256;
+ } else {
+ bd_len = ptr[3];
+ legacy = (struct rdac_legacy_page *)(ptr + 4 + bd_len);
+ rdac_ptr = &legacy->attr;
+ lun_table = legacy->lun_table;
+ lun_table_len = 32;
+ }
+
+ printf("RDAC %s page\n", exp_subpg ? "Expanded" : "Legacy");
+ printf(" Controller serial: %s\n",
+ rdac_ptr->current_serial);
+ printf(" Alternate controller serial: %s\n",
+ rdac_ptr->alternate_serial);
+ printf(" RDAC mode (redundant processor): ");
+ switch (rdac_ptr->current_mode_msb) {
+ case 0x00:
+ printf("alternate controller not present; ");
+ break;
+ case 0x01:
+ printf("alternate controller present; ");
+ break;
+ default:
+ printf("(Unknown controller status 0x%x); ",
+ rdac_ptr->current_mode_msb);
+ break;
+ }
+ switch (rdac_ptr->current_mode_lsb) {
+ case 0x0:
+ printf("inactive\n");
+ break;
+ case 0x1:
+ printf("active\n");
+ break;
+ case 0x2:
+ printf("Dual active mode\n");
+ break;
+ default:
+ printf("(Unknown mode 0x%x)\n",
+ rdac_ptr->current_mode_lsb);
+ }
+
+ printf(" RDAC mode (alternate processor): ");
+ switch (rdac_ptr->alternate_mode_msb) {
+ case 0x00:
+ printf("alternate controller not present; ");
+ break;
+ case 0x01:
+ printf("alternate controller present; ");
+ break;
+ default:
+ printf("(Unknown status 0x%x); ",
+ rdac_ptr->alternate_mode_msb);
+ break;
+ }
+ switch (rdac_ptr->alternate_mode_lsb) {
+ case 0x0:
+ printf("inactive\n");
+ break;
+ case 0x1:
+ printf("active\n");
+ break;
+ case 0x2:
+ printf("Dual active mode\n");
+ break;
+ case 0x3:
+ printf("Not present\n");
+ break;
+ case 0x4:
+ printf("held in reset\n");
+ break;
+ default:
+ printf("(Unknown mode 0x%x)\n",
+ rdac_ptr->alternate_mode_lsb);
+ }
+ printf(" Quiescence timeout: %d\n", rdac_ptr->quiescence);
+ printf(" RDAC option 0x%x\n", rdac_ptr->options);
+ printf(" ALUA: %s\n", (rdac_ptr->options & 0x4 ? "Enabled" :
+ "Disabled" ));
+ printf(" Force Quiescence: %s\n", (rdac_ptr->options & 0x2 ?
+ "Enabled" : "Disabled" ));
+ printf (" LUN Table: (p = preferred, a = alternate, u = utm lun)\n");
+ printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n");
+ for (k = 0; k < lun_table_len; k += 16) {
+ printf(" 0x%x:",k / 16);
+ for (i = 0; i < 16; i++) {
+ switch (lun_table[k + i]) {
+ case 0x0:
+ printf(" x");
+ break;
+ case 0x1:
+ printf(" p");
+ break;
+ case 0x2:
+ printf(" a");
+ break;
+ case 0x3:
+ printf(" u");
+ break;
+ default:
+ printf(" ?");
+ break;
+ }
+ if (i == 7) {
+ printf(" ");
+ }
+ }
+ printf("\n");
+ }
+}
+
+static void usage()
+{
+ printf("Usage: sg_rdac [-6] [-a] [-f=LUN] [-v] [-V] DEVICE\n"
+ " where:\n"
+ " -6 use 6 byte cdbs for mode sense/select\n"
+ " -a transfer all devices to the controller\n"
+ " serving DEVICE.\n"
+ " -f=LUN transfer the device at LUN to the\n"
+ " controller serving DEVICE\n"
+ " -v verbose\n"
+ " -V print version then exit\n\n"
+ " Display/Modify RDAC Redundant Controller Page 0x2c.\n"
+ " If [-a] or [-f] is not specified the current settings"
+ " are displayed.\n");
+}
+
+int main(int argc, char * argv[])
+{
+ bool fail_all = false;
+ bool fail_path = false;
+ bool use_6_byte = false;
+ int res, fd, k, resid, len, lun = -1;
+ int ret = 0;
+ char **argptr;
+ char * file_name = 0;
+ uint8_t rsp_buff[MX_ALLOC_LEN];
+
+ if (argc < 2) {
+ usage ();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+
+ for (k = 1; k < argc; ++k) {
+ argptr = argv + k;
+ if (!strcmp (*argptr, "-v"))
+ ++do_verbose;
+ else if (!strncmp(*argptr, "-f=",3)) {
+ fail_path = true;
+ lun = strtoul(*argptr + 3, NULL, 0);
+ }
+ else if (!strcmp(*argptr, "-a")) {
+ fail_all = true;
+ }
+ else if (!strcmp(*argptr, "-6")) {
+ use_6_byte = true;
+ }
+ else if (!strcmp(*argptr, "-V")) {
+ pr2serr("sg_rdac version: %s\n", version_str);
+ return 0;
+ }
+ else if (*argv[k] == '-') {
+ pr2serr("Unrecognized switch: %s\n", argv[k]);
+ file_name = 0;
+ break;
+ }
+ else if (0 == file_name)
+ file_name = argv[k];
+ else {
+ pr2serr("too many arguments\n");
+ file_name = 0;
+ break;
+ }
+ }
+ if (0 == file_name) {
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+
+ fd = sg_cmds_open_device(file_name, false /* rw */, do_verbose);
+ if (fd < 0) {
+ pr2serr("open error: %s: %s\n", file_name, safe_strerror(-fd));
+ usage();
+ ret = sg_convert_errno(-fd);
+ goto fini;
+ }
+
+ if (fail_all) {
+ res = fail_all_paths(fd, use_6_byte);
+ } else if (fail_path) {
+ res = fail_this_path(fd, lun, use_6_byte);
+ } else {
+ resid = 0;
+ if (use_6_byte)
+ res = sg_ll_mode_sense6(fd, /* DBD */ false,
+ /* PC */ 0,
+ 0x2c /* page */,
+ 0 /*subpage */,
+ rsp_buff, 252,
+ true, do_verbose);
+ else
+ res = sg_ll_mode_sense10_v2(fd, /* llbaa */ false,
+ /* DBD */ false,
+ /* page control */0,
+ 0x2c, 0x1 /* subpage */,
+ rsp_buff, 308, 0, &resid,
+ true, do_verbose);
+
+ if (! res) {
+ len = sg_msense_calc_length(rsp_buff, 308, use_6_byte,
+ NULL);
+ if (resid > 0) {
+ len = ((308 - resid) < len) ? (308 - resid) :
+ len;
+ if (len < 2)
+ pr2serr("MS(10) residual value (%d) "
+ "a worry\n", resid);
+ }
+ if (do_verbose && (len > 1))
+ dump_mode_page(rsp_buff, len);
+ print_rdac_mode(rsp_buff, ! use_6_byte);
+ } else {
+ if (SG_LIB_CAT_INVALID_OP == res)
+ pr2serr(">>>>>> try again without the '-6' "
+ "switch for a 10 byte MODE SENSE "
+ "command\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ pr2serr("mode sense: invalid field in cdb "
+ "(perhaps subpages or page control "
+ "(PC) not supported)\n");
+ else {
+ char b[80];
+
+ sg_get_category_sense_str(res, sizeof(b), b,
+ do_verbose);
+ pr2serr("mode sense failed: %s\n", b);
+ }
+ }
+ }
+ ret = res;
+
+ res = sg_cmds_close_device(fd);
+ if (res < 0) {
+ pr2serr("close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = sg_convert_errno(res);
+ }
+fini:
+ if (0 == do_verbose) {
+ if (! sg_if_can2stderr("sg_rdac failed: ", ret))
+ pr2serr("Some error occurred, try again with '-v' "
+ "or '-vv' for more information\n");
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}