diff options
Diffstat (limited to 'path_priority/pp_alua/rtpg.c')
-rw-r--r-- | path_priority/pp_alua/rtpg.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/path_priority/pp_alua/rtpg.c b/path_priority/pp_alua/rtpg.c new file mode 100644 index 0000000..5eb1a1c --- /dev/null +++ b/path_priority/pp_alua/rtpg.c @@ -0,0 +1,280 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * rtpg.c + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader <shbader@de.ibm.com> + * + * This file is released under the GPL. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#define __user +#include <scsi/sg.h> + +#include "rtpg.h" + +#define SENSE_BUFF_LEN 32 +#define DEF_TIMEOUT 60000 + +/* + * Macro used to print debug messaged. + */ +#if DEBUG > 0 +#define PRINT_DEBUG(f, a...) \ + fprintf(stderr, "DEBUG: " f, ##a) +#else +#define PRINT_DEBUG(f, a...) +#endif + +/* + * Optionally print the commands sent and the data received a hex dump. + */ +#if DEBUG > 0 +#if DEBUG_DUMPHEX > 0 +#define PRINT_HEX(p, l) print_hex(p, l) +void +print_hex(unsigned char *p, unsigned long len) +{ + int i; + + for(i = 0; i < len; i++) { + if (i % 16 == 0) + printf("%04x: ", i); + printf("%02x%s", p[i], (((i + 1) % 16) == 0) ? "\n" : " "); + } + printf("\n"); +} +#else +#define PRINT_HEX(p, l) +#endif +#else +#define PRINT_HEX(p, l) +#endif + +/* + * Returns 0 if the SCSI command either was successful or if the an error was + * recovered, otherwise 1. (definitions taken from sg_err.h) + */ +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 + +static int +scsi_error(struct sg_io_hdr *hdr) +{ + /* Treat SG_ERR here to get rid of sg_err.[ch] */ + hdr->status &= 0x7e; + + if ( + (hdr->status == 0) && + (hdr->host_status == 0) && + (hdr->driver_status == 0) + ) { + return 0; + } + + if ( + (hdr->status == SCSI_CHECK_CONDITION) || + (hdr->status == SCSI_COMMAND_TERMINATED) || + ((hdr->driver_status & 0xf) == SG_ERR_DRIVER_SENSE) + ) { + if (hdr->sbp && (hdr->sb_len_wr > 2)) { + int sense_key; + unsigned char * sense_buffer = hdr->sbp; + + if (sense_buffer[0] & 0x2) + sense_key = sense_buffer[1] & 0xf; + else + sense_key = sense_buffer[2] & 0xf; + + if (sense_key == RECOVERED_ERROR) + return 0; + } + } + + return 1; +} + +/* + * Helper function to setup and run a SCSI inquiry command. + */ +int +do_inquiry(int fd, int evpd, unsigned int codepage, void *resp, int resplen) +{ + struct inquiry_command cmd; + struct sg_io_hdr hdr; + unsigned char sense[SENSE_BUFF_LEN]; + + memset(&cmd, 0, sizeof(cmd)); + cmd.op = OPERATION_CODE_INQUIRY; + if (evpd) { + cmd.evpd = 1; + cmd.page = codepage; + } + set_uint16(cmd.length, resplen); + PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); + + memset(&hdr, 0, sizeof(hdr)); + hdr.interface_id = 'S'; + hdr.cmdp = (unsigned char *) &cmd; + hdr.cmd_len = sizeof(cmd); + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.dxferp = resp; + hdr.dxfer_len = resplen; + hdr.sbp = sense; + hdr.mx_sb_len = sizeof(sense); + hdr.timeout = DEF_TIMEOUT; + + if (ioctl(fd, SG_IO, &hdr) < 0) { + PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); + return -RTPG_INQUIRY_FAILED; + } + + if (scsi_error(&hdr)) { + PRINT_DEBUG("do_inquiry: SCSI error!\n"); + return -RTPG_INQUIRY_FAILED; + } + PRINT_HEX((unsigned char *) resp, resplen); + + return 0; +} + +/* + * This function returns the support for target port groups by evaluating the + * data returned by the standard inquiry command. + */ +int +get_target_port_group_support(int fd) +{ + struct inquiry_data inq; + int rc; + + rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq)); + if (!rc) { + rc = inq.tpgs; + } + + return rc; +} + +int +get_target_port_group(int fd) +{ + unsigned char buf[128]; + struct vpd83_data * vpd83; + struct vpd83_dscr * dscr; + int rc; + + rc = do_inquiry(fd, 1, 0x83, buf, sizeof(buf)); + if (!rc) { + vpd83 = (struct vpd83_data *) buf; + + rc = -RTPG_NO_TPG_IDENTIFIER; + FOR_EACH_VPD83_DSCR(vpd83, dscr) { + if ((((char *) dscr) - ((char *) vpd83)) > sizeof(buf)) + break; + + if (dscr->id_type == IDTYPE_TARGET_PORT_GROUP) { + struct vpd83_tpg_dscr * p; + + if (rc != -RTPG_NO_TPG_IDENTIFIER) { + PRINT_DEBUG("get_target_port_group: " + "more than one TPG identifier " + "found!\n"); + continue; + } + + p = (struct vpd83_tpg_dscr *) dscr->data; + rc = get_uint16(p->tpg); + } + } + if (rc == -RTPG_NO_TPG_IDENTIFIER) { + PRINT_DEBUG("get_target_port_group: " + "no TPG identifier found!\n"); + } + } + + return rc; +} + +int +do_rtpg(int fd, void* resp, long resplen) +{ + struct rtpg_command cmd; + struct sg_io_hdr hdr; + unsigned char sense[SENSE_BUFF_LEN]; + + memset(&cmd, 0, sizeof(cmd)); + cmd.op = OPERATION_CODE_RTPG; + cmd.service_action = SERVICE_ACTION_RTPG; + set_uint32(cmd.length, resplen); + PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); + + memset(&hdr, 0, sizeof(hdr)); + hdr.interface_id = 'S'; + hdr.cmdp = (unsigned char *) &cmd; + hdr.cmd_len = sizeof(cmd); + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.dxferp = resp; + hdr.dxfer_len = resplen; + hdr.mx_sb_len = sizeof(sense); + hdr.sbp = sense; + hdr.timeout = DEF_TIMEOUT; + + if (ioctl(fd, SG_IO, &hdr) < 0) + return -RTPG_RTPG_FAILED; + + if (scsi_error(&hdr)) { + PRINT_DEBUG("do_rtpg: SCSI error!\n"); + return -RTPG_RTPG_FAILED; + } + PRINT_HEX(resp, resplen); + + return 0; +} + +int +get_asymmetric_access_state(int fd, unsigned int tpg) +{ + unsigned char buf[128]; + struct rtpg_data * tpgd; + struct rtpg_tpg_dscr * dscr; + int rc; + + rc = do_rtpg(fd, buf, sizeof(buf)); + if (rc < 0) + return rc; + + tpgd = (struct rtpg_data *) buf; + rc = -RTPG_TPG_NOT_FOUND; + RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) { + if (get_uint16(dscr->tpg) == tpg) { + if (rc != -RTPG_TPG_NOT_FOUND) { + PRINT_DEBUG("get_asymmetric_access_state: " + "more than one entry with same port " + "group.\n"); + } else { + PRINT_DEBUG("pref=%i\n", dscr->pref); + rc = dscr->aas; + } + } + } + + return rc; +} + |