#include #include #include #include #include #include #include #include #include #include #include #include #include "mpath_pr_ioctl.h" #include "mpath_persist.h" #include "unaligned.h" #include "debug.h" #define FILE_NAME_SIZE 256 #define TIMEOUT 2000 #define MAXRETRY 5 int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp *resp, int noisy); int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr, SenseData_t *Sensedata); void dumpHex(const char* str, int len, int no_ascii); int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy); uint32_t format_transportids(struct prout_param_descriptor *paramp); void convert_be32_to_cpu(uint32_t *num); void convert_be16_to_cpu(uint16_t *num); void decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length); int get_prin_length(int rq_servact); int mpath_isLittleEndian(void); unsigned int mpath_mx_alloc_len; int prout_do_scsi_ioctl(char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy) { int status, paramlen = 24, ret = 0; uint32_t translen=0; int retry = MAXRETRY; SenseData_t Sensedata; struct sg_io_hdr io_hdr; char devname[FILE_NAME_SIZE]; int fd = -1; snprintf(devname, FILE_NAME_SIZE, "/dev/%s",dev); fd = open(devname, O_RDONLY); if(fd < 0){ condlog (1, "%s: unable to open device.", dev); return MPATH_PR_FILE_ERROR; } unsigned char cdb[MPATH_PROUT_CMDLEN] = {MPATH_PROUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if (paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK) { translen = format_transportids(paramp); paramlen = 24 + translen; } else paramlen = 24; if ( rq_servact > 0) cdb[1] = (unsigned char)(rq_servact & 0x1f); cdb[2] = (((rq_scope & 0xf) << 4) | (rq_type & 0xf)); cdb[7] = (unsigned char)((paramlen >> 8) & 0xff); cdb[8] = (unsigned char)(paramlen & 0xff); retry : condlog(4, "%s: rq_servact = %d", dev, rq_servact); condlog(4, "%s: rq_scope = %d ", dev, rq_scope); condlog(4, "%s: rq_type = %d ", dev, rq_type); condlog(4, "%s: paramlen = %d", dev, paramlen); if (noisy) { condlog(4, "%s: Persistent Reservation OUT parameter:", dev); dumpHex((const char *)paramp, paramlen,1); } memset(&Sensedata, 0, sizeof(SenseData_t)); memset(&io_hdr,0 , sizeof( struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = MPATH_PROUT_CMDLEN; io_hdr.cmdp = cdb; io_hdr.sbp = (void *)&Sensedata; io_hdr.mx_sb_len = sizeof (SenseData_t); io_hdr.timeout = TIMEOUT; if (paramlen > 0) { io_hdr.dxferp = (void *)paramp; io_hdr.dxfer_len = paramlen; io_hdr.dxfer_direction = SG_DXFER_TO_DEV ; } else { io_hdr.dxfer_direction = SG_DXFER_NONE; } ret = ioctl(fd, SG_IO, &io_hdr); if (ret < 0) { condlog(0, "%s: ioctl failed %d", dev, ret); close(fd); return ret; } condlog(4, "%s: Duration=%u (ms)", dev, io_hdr.duration); status = mpath_translate_response(dev, io_hdr, &Sensedata); condlog(3, "%s: status = %d", dev, status); if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0)) { --retry; condlog(3, "%s: retrying for Unit Attention. Remaining retries = %d", dev, retry); goto retry; } if (((status == MPATH_PR_SENSE_NOT_READY )&& (Sensedata.ASC == 0x04)&& (Sensedata.ASCQ == 0x07))&& (retry > 0)) { usleep(1000); --retry; condlog(3, "%s: retrying for sense 02/04/07." " Remaining retries = %d", dev, retry); goto retry; } close(fd); return status; } uint32_t format_transportids(struct prout_param_descriptor *paramp) { unsigned int i = 0, len; uint32_t buff_offset = 4; memset(paramp->private_buffer, 0, MPATH_MAX_PARAM_LEN); for (i=0; i < paramp->num_transportid; i++ ) { paramp->private_buffer[buff_offset] = (uint8_t)((paramp->trnptid_list[i]->format_code & 0xff)| (paramp->trnptid_list[i]->protocol_id & 0xff)); buff_offset += 1; switch(paramp->trnptid_list[i]->protocol_id) { case MPATH_PROTOCOL_ID_FC: buff_offset += 7; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->n_port_name, 8); buff_offset +=8 ; buff_offset +=8 ; break; case MPATH_PROTOCOL_ID_SAS: buff_offset += 3; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->sas_address, 8); buff_offset += 12; break; case MPATH_PROTOCOL_ID_ISCSI: buff_offset += 1; len = (paramp->trnptid_list[i]->iscsi_name[1] & 0xff)+2; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->iscsi_name,len); buff_offset += len ; break; } } buff_offset -= 4; paramp->private_buffer[0] = (unsigned char)((buff_offset >> 24) & 0xff); paramp->private_buffer[1] = (unsigned char)((buff_offset >> 16) & 0xff); paramp->private_buffer[2] = (unsigned char)((buff_offset >> 8) & 0xff); paramp->private_buffer[3] = (unsigned char)(buff_offset & 0xff); buff_offset += 4; return buff_offset; } static void mpath_format_readkeys(struct prin_resp *pr_buff) { convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.additional_length); } static void mpath_format_readresv(struct prin_resp *pr_buff) { convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readresv.prgeneration); convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readresv.additional_length); return; } static void mpath_format_reportcapabilities(struct prin_resp *pr_buff) { convert_be16_to_cpu(&pr_buff->prin_descriptor.prin_readcap.length); convert_be16_to_cpu(&pr_buff->prin_descriptor.prin_readcap.pr_type_mask); return; } static void mpath_format_readfullstatus(struct prin_resp *pr_buff) { int num; uint32_t fdesc_count=0; unsigned char *p; char *ppbuff; uint32_t additional_length, k, tid_len_len = 0; char tempbuff[MPATH_MAX_PARAM_LEN]; struct prin_fulldescr fdesc; convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readfd.prgeneration); convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readfd.number_of_descriptor); if (pr_buff->prin_descriptor.prin_readfd.number_of_descriptor == 0) { condlog(3, "No registration or reservation found."); return; } additional_length = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor; if (additional_length > MPATH_MAX_PARAM_LEN) { condlog(3, "PRIN length %u exceeds max length %d", additional_length, MPATH_MAX_PARAM_LEN); return; } memset(&fdesc, 0, sizeof(struct prin_fulldescr)); memcpy( tempbuff, pr_buff->prin_descriptor.prin_readfd.private_buffer,MPATH_MAX_PARAM_LEN ); memset(&pr_buff->prin_descriptor.prin_readfd.private_buffer, 0, MPATH_MAX_PARAM_LEN); p =(unsigned char *)tempbuff; ppbuff = (char *)pr_buff->prin_descriptor.prin_readfd.private_buffer; for (k = 0; k < additional_length; k += num, p += num) { memcpy(&fdesc.key, p, 8 ); fdesc.flag = p[12]; fdesc.scope_type = p[13]; fdesc.rtpi = get_unaligned_be16(&p[18]); tid_len_len = get_unaligned_be32(&p[20]); if (tid_len_len + 24 + k > additional_length) { condlog(0, "%s: corrupt PRIN response: status descriptor end %d exceeds length %d", __func__, tid_len_len + k + 24, additional_length); tid_len_len = additional_length - k - 24; } if (tid_len_len > 0) decode_transport_id( &fdesc, &p[24], tid_len_len); num = 24 + tid_len_len; memcpy(ppbuff, &fdesc, sizeof(struct prin_fulldescr)); pr_buff->prin_descriptor.prin_readfd.descriptors[fdesc_count]= (struct prin_fulldescr *)ppbuff; ppbuff += sizeof(struct prin_fulldescr); ++fdesc_count; } pr_buff->prin_descriptor.prin_readfd.number_of_descriptor = fdesc_count; return; } void decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length) { unsigned int num; int jump, k; for (k = 0, jump = 24; k < length; k += jump, p += jump) { fdesc->trnptid.format_code = ((p[0] >> 6) & 0x3); fdesc->trnptid.protocol_id = (p[0] & 0xf); switch (fdesc->trnptid.protocol_id) { case MPATH_PROTOCOL_ID_FC: memcpy(&fdesc->trnptid.n_port_name, &p[8], 8); jump = 24; break; case MPATH_PROTOCOL_ID_ISCSI: num = get_unaligned_be16(&p[2]); if (num >= sizeof(fdesc->trnptid.iscsi_name)) num = sizeof(fdesc->trnptid.iscsi_name); memcpy(&fdesc->trnptid.iscsi_name, &p[4], num); jump = (((num + 4) < 24) ? 24 : num + 4); break; case MPATH_PROTOCOL_ID_SAS: memcpy(&fdesc->trnptid.sas_address, &p[4], 8); jump = 24; break; default: jump = 24; break; } } } int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy) { int ret, status, got, fd; int mx_resp_len; SenseData_t Sensedata; int retry = MAXRETRY; struct sg_io_hdr io_hdr; char devname[FILE_NAME_SIZE]; unsigned char cdb[MPATH_PRIN_CMDLEN] = {MPATH_PRIN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; snprintf(devname, FILE_NAME_SIZE, "/dev/%s",dev); fd = open(devname, O_RDONLY); if(fd < 0){ condlog(0, "%s: Unable to open device ", dev); return MPATH_PR_FILE_ERROR; } if (mpath_mx_alloc_len) mx_resp_len = mpath_mx_alloc_len; else mx_resp_len = get_prin_length(rq_servact); if (mx_resp_len == 0) { status = MPATH_PR_SYNTAX_ERROR; goto out; } cdb[1] = (unsigned char)(rq_servact & 0x1f); cdb[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); cdb[8] = (unsigned char)(mx_resp_len & 0xff); retry : memset(&Sensedata, 0, sizeof(SenseData_t)); memset(&io_hdr,0 , sizeof( struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = MPATH_PRIN_CMDLEN; io_hdr.mx_sb_len = sizeof (SenseData_t); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.cmdp = cdb; io_hdr.sbp = (void *)&Sensedata; io_hdr.timeout = TIMEOUT; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = (void *)resp; ret =ioctl(fd, SG_IO, &io_hdr); if (ret < 0){ condlog(0, "%s: IOCTL failed %d", dev, ret); status = MPATH_PR_OTHER; goto out; } got = mx_resp_len - io_hdr.resid; condlog(3, "%s: duration = %u (ms)", dev, io_hdr.duration); condlog(4, "%s: persistent reservation in: requested %d bytes but got %d bytes)", dev, mx_resp_len, got); status = mpath_translate_response(dev, io_hdr, &Sensedata); if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0)) { --retry; condlog(3, "%s: retrying for Unit Attention. Remaining retries = %d", dev, retry); goto retry; } if (((status == MPATH_PR_SENSE_NOT_READY )&& (Sensedata.ASC == 0x04)&& (Sensedata.ASCQ == 0x07))&& (retry > 0)) { usleep(1000); --retry; condlog(3, "%s: retrying for 02/04/07. Remaining retries = %d", dev, retry); goto retry; } if (status != MPATH_PR_SUCCESS) goto out; if (noisy) dumpHex((const char *)resp, got , 1); switch (rq_servact) { case MPATH_PRIN_RKEY_SA : mpath_format_readkeys(resp); break; case MPATH_PRIN_RRES_SA : mpath_format_readresv(resp); break; case MPATH_PRIN_RCAP_SA : mpath_format_reportcapabilities(resp); break; case MPATH_PRIN_RFSTAT_SA : mpath_format_readfullstatus(resp); } out: close(fd); return status; } int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr, SenseData_t *Sensedata) { condlog(3, "%s: status driver:%02x host:%02x scsi:%02x", dev, io_hdr.driver_status, io_hdr.host_status ,io_hdr.status); io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) return MPATH_PR_SUCCESS; switch(io_hdr.status) { case SAM_STAT_GOOD: break; case SAM_STAT_CHECK_CONDITION: condlog(3, "%s: Sense_Key=%02x, ASC=%02x ASCQ=%02x", dev, Sensedata->Sense_Key, Sensedata->ASC, Sensedata->ASCQ); switch(Sensedata->Sense_Key) { case NO_SENSE: return MPATH_PR_NO_SENSE; case RECOVERED_ERROR: return MPATH_PR_SUCCESS; case NOT_READY: return MPATH_PR_SENSE_NOT_READY; case MEDIUM_ERROR: return MPATH_PR_SENSE_MEDIUM_ERROR; case BLANK_CHECK: return MPATH_PR_OTHER; case HARDWARE_ERROR: return MPATH_PR_SENSE_HARDWARE_ERROR; case ILLEGAL_REQUEST: return MPATH_PR_ILLEGAL_REQ; case UNIT_ATTENTION: return MPATH_PR_SENSE_UNIT_ATTENTION; case DATA_PROTECT: case COPY_ABORTED: return MPATH_PR_OTHER; case ABORTED_COMMAND: return MPATH_PR_SENSE_ABORTED_COMMAND; default : return MPATH_PR_OTHER; } case SAM_STAT_RESERVATION_CONFLICT: return MPATH_PR_RESERV_CONFLICT; default : return MPATH_PR_OTHER; } switch(io_hdr.host_status) { case DID_OK : break; default : return MPATH_PR_OTHER; } switch(io_hdr.driver_status) { case DRIVER_OK: break; default : return MPATH_PR_OTHER; } return MPATH_PR_SUCCESS; } void convert_be16_to_cpu(uint16_t *num) { *num = get_unaligned_be16(num); } void convert_be32_to_cpu(uint32_t *num) { *num = get_unaligned_be32(num); } void dumpHex(const char* str, int len, int log) { const char * p = str; unsigned char c; char buff[82]; const int bpstart = 5; int bpos = bpstart; int k; if (len <= 0) return; memset(buff, ' ', 80); buff[80] = '\0'; for (k = 0; k < len; k++) { c = *p++; bpos += 3; if (bpos == (bpstart + (9 * 3))) bpos++; sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); buff[bpos + 2] = ' '; if ((k > 0) && (0 == ((k + 1) % 16))) { if (log) condlog(0, "%.76s" , buff); else printf("%.76s" , buff); bpos = bpstart; memset(buff, ' ', 80); } } if (bpos > bpstart) { buff[bpos + 2] = '\0'; if (log) condlog(0, "%s", buff); else printf("%s\n" , buff); } return; } int get_prin_length(int rq_servact) { int mx_resp_len; switch (rq_servact) { case MPATH_PRIN_RKEY_SA: mx_resp_len = sizeof(struct prin_readdescr); break; case MPATH_PRIN_RRES_SA : mx_resp_len = sizeof(struct prin_resvdescr); break; case MPATH_PRIN_RCAP_SA : mx_resp_len = sizeof(struct prin_capdescr); break; case MPATH_PRIN_RFSTAT_SA: mx_resp_len = sizeof(struct print_fulldescr_list) + sizeof(struct prin_fulldescr *)*32; break; default: condlog(0, "invalid service action, %d", rq_servact); mx_resp_len = 0; break; } return mx_resp_len; }