From: Ronnie Sahlberg Date: Mon, 2 Jul 2007 05:51:38 +0000 (+1000) Subject: add incomplete code fragments to perform SCSI PERSISTENT RESERVATION X-Git-Tag: tevent-0.9.20~348^2~2480^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bf216f8faa678b95367651f122a6a200644cc84e;p=thirdparty%2Fsamba.git add incomplete code fragments to perform SCSI PERSISTENT RESERVATION calls to a scsi device. added to bzr so it will not be lost we need access to an array that supports this optional cdb before we can proceed and toolify it properly (the idea is that ctdb should have a mechanism where it can shut out nodes from accessing the LUNs when it has deemed a node as disconnected) (This used to be ctdb commit 360573b505c6df8a32996efce3813fa95365a206) --- diff --git a/ctdb/scsi/scsi_io.c b/ctdb/scsi/scsi_io.c new file mode 100644 index 00000000000..079aa6f7d4a --- /dev/null +++ b/ctdb/scsi/scsi_io.c @@ -0,0 +1,915 @@ +/* a tool to open a scsi device and issue some useful commands + such as INQUIRY and helpers to call various PERSISTENT RESERVATION + functions + + Copyright ronnie sahlberg 2007 +*/ + +/* very incomplete and needs to be enhanced with noice command line options + to drive it. + we need access to an array that supports the PERSISTENT RESERVATION cdb's + before we can proceed +*/ +/* scsi bugs: + INQUIRY takes a 2 byte allocation_length parameter but it appears that + it only looks at the low byte. If you specify 0x00ff all is well + but if you specify 0x0100 it gets confused and returnes garbage data + for (e.g) SupportedVPDPages. Same goes for UnitSerialNumber and probably all + other inq pages as well. + +*/ + +#include +#include +#include +#include +#include +#include +#include + + +#define SCSI_TIMEOUT 5000 /* ms */ + + +char *sensetable[16]={ + "no sense", + "recovered error", + "not ready", + "medium error", + "hardware error", + "illegal request", + "unit attention", + "data protect", + "blank check", + "vendor specific", + "copy aborted", + "aboreted command", + "unknown", + "unknown", + "unknown", + "unknown" +}; + +int scsi_io(int fd, unsigned char *cdb, unsigned char cdb_size, int xfer_dir, unsigned char *data, unsigned int *data_size, char *sense, unsigned int *sense_len) +{ + sg_io_hdr_t io_hdr; + + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + + /* CDB */ + io_hdr.cmdp = cdb; + io_hdr.cmd_len = cdb_size; + + /* Where to store the sense_data, if there was an error */ + io_hdr.sbp = sense; + io_hdr.mx_sb_len = *sense_len; + *sense_len=0; + + /* Transfer direction, either in or out. Linux does not yet + support bidirectional SCSI transfers ? + */ + io_hdr.dxfer_direction = xfer_dir; + + /* Where to store the DATA IN/OUT from the device and how big the + buffer is + */ + io_hdr.dxferp = data; + io_hdr.dxfer_len = *data_size; + + /* SCSI timeout in ms */ + io_hdr.timeout = SCSI_TIMEOUT; + + + if(ioctl(fd, SG_IO, &io_hdr) < 0){ + perror("SG_IO ioctl failed"); + return -1; + } + + /* now for the error processing */ + if((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK){ + if(io_hdr.sb_len_wr > 0){ + *sense_len=io_hdr.sb_len_wr; + return 0; + } + } + if(io_hdr.masked_status){ + printf("status=0x%x\n", io_hdr.status); + printf("masked_status=0x%x\n", io_hdr.masked_status); + return -2; + } + if(io_hdr.host_status){ + printf("host_status=0x%x\n", io_hdr.host_status); + return -3; + } + if(io_hdr.driver_status){ + printf("driver_status=0x%x\n", io_hdr.driver_status); + return -4; + } + +#if 0 +{int i; +printf("CDB:\n"); +for(i=0;i\n"); +} + +typedef struct _value_string_t { + int value; + char *string; +} value_string_t; + + + +value_string_t peripheral_device_types[] = { + {0, "SBC : Direct Access Block device"}, + {1, "SSC : Sequential Access Device"}, + {5, "MMC : Multimedia Device"}, + {17,"OSD : Object Based Storage"}, + {0,NULL} +}; + +value_string_t scsi_versions[] = { + {0, "No conformance to any standard claimed"}, + {3, "SPC"}, + {4, "SPC-2"}, + {5, "SPC-3"}, + {0,NULL} +}; + +value_string_t vpd_pages[] = { + {0x00, "Supported VPD Pages"}, + {0x80, "Unit Serial number"}, + {0x83, "Device Identification"}, + {0,NULL} +}; + +char *val_to_str(value_string_t *vs, int v) +{ + while(vs && vs->string){ + if(vs->value==v){ + return vs->string; + } + vs++; + } + return ""; +} + +void print_sense_data(unsigned char *sense, int sense_len) +{ + int i; + unsigned char asc, ascq; + + printf("Device returned sense information\n"); + if(sense[0]==0x70){ + printf("filemark:%d eom:%d ili:%d sense-key:0x%02x (%s)\n", + !!(sense[2]&0x80), + !!(sense[2]&0x40), + !!(sense[2]&0x20), + sense[2]&0x0f, + sensetable[sense[2]&0x0f]); + printf("command specific info: 0x%02x 0x%02x 0x%02x 0x%02x\n", + sense[8],sense[9],sense[10],sense[11]); + + asc=sense[12]; + printf("additional sense code:0x%02x\n", asc); + + ascq=sense[13]; + printf("additional sense code qualifier:0x%02x\n", ascq); + + printf("field replacable unit code:0x%02x\n", sense[14]); + + if((asc==0x20)&&(ascq==0x00)) + printf("INVALID COMMAND OPERATION CODE\n"); + } + + printf("Sense data:\n"); + for(i=0;i>8)&0xff; + cdb[4]=data_size&0xff; + + + printf("Standard INQUIRY Data:\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + /* Peripheral Qualifier */ + printf("Peripheral Qualifier:%c%c%cb\n", + '0'+!!(data[0]&0x80), + '0'+!!(data[0]&0x40), + '0'+!!(data[0]&0x20)); + + /* Peripheral Device Type */ + printf("Peripheral Device Type: 0x%02x (%s)\n", + data[0]&0x1f, + val_to_str(peripheral_device_types, data[0]&0x1f)); + + /* RMB */ + printf("RMB: %s device\n", data[1]&0x80?"REMOVABLE":"NON-REMOVABLE"); + + /* SCSI Version */ + printf("SCSI Version: 0x%02x (%s)\n", + data[2], + val_to_str(scsi_versions, data[2])); + + /* NormACA, HiSUP, Response Data Format */ + printf("NormACA:%d HiSup:%d ResponseDataFormat:%d\n", + !!(data[3]&0x20), + !!(data[3]&0x10), + data[3]&0x0f); + + /* Additional Length */ + alen=data[4]; + + switch(data[3]&0x0f){ + /*SPC-2/SPC-3/SPC-4*/ + case 2: + /*SPC (not strictly correct but we print it like 2 anyway)*/ + case 1: + /* SCCS ... */ + printf("SCCS:%d ACC:%d TPGS:%c%cb 3PC:%d PROTECT:%d\n", + !!(data[5]&0x80), + !!(data[5]&0x40), + '0'+!!(data[5]&0x20), + '0'+!!(data[5]&0x10), + !!(data[5]&0x08), + !!(data[5]&0x01)); + + /* Encserv ... */ + printf("Encserv:%d VS:%d MultiP:%d ADDR16:%d\n", + !!(data[6]&0x40), + !!(data[6]&0x20), + !!(data[6]&0x10), + !!(data[6]&0x01)); + + /* WBUS16 ... */ + printf("WBUS16:%d SYNC:%d CmdQue:%d VS:%d\n", + !!(data[7]&0x20), + !!(data[7]&0x10), + !!(data[7]&0x02), + !!(data[7]&0x01)); + + + /* T10 vendor Identification */ + printf("Vendor:"); + for(i=0;i<8;i++)printf("%c",data[8+i]);printf("\n"); + + /* Product Identification */ + printf("Product:"); + for(i=0;i<16;i++)printf("%c",data[16+i]);printf("\n"); + + /* Product Revision Level */ + printf("Product Revision:"); + for(i=0;i<4;i++)printf("%c",data[32+i]);printf("\n"); + + break; + } + + return 0; +} + +int scsi_inquiry_supported_vpd_pages(int fd) +{ + char cdb[]={0x12,0x01,0,0,0,0}; + + unsigned int data_size=0xff; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + + int res, pl, i; + + cdb[3]=(data_size>>8)&0xff; + cdb[4]=data_size&0xff; + + + printf("INQUIRY Supported VPD Pages:\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + /* Page Length */ + pl=data[3]; + + /* Pages */ + for(i=4;i<(pl+4);i++){ + printf("Page:%02xh (%s)\n", + data[i], + val_to_str(vpd_pages, data[i])); + } + + return 0; +} + +int scsi_inquiry_unit_serial_number(int fd) +{ + char cdb[]={0x12,0x01,0x80,0,0,0}; + + unsigned int data_size=0x00ff; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + + int res, pl, i; + + cdb[3]=(data_size>>8)&0xff; + cdb[4]=data_size&0xff; + + + printf("INQUIRY Unit Serial Number:\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + /* Page Length */ + pl=data[3]; + + /* Unit Serial Number */ + printf("Unit Serial Number:"); + for(i=4;i<(pl+4);i++)printf("%c",data[i]&0xff);printf("\n"); + + return 0; +} + +int scsi_persistent_reserve_in_read_keys(int fd) +{ + char cdb[]={0x5e,0,0,0,0,0,0,0,0,0}; + + unsigned int data_size=0x00ff; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + unsigned char service_action=0; + int res, i; + unsigned long prgeneration, additional_length; + + cdb[1]=service_action; + cdb[7]=(data_size>>8)&0xff; + cdb[8]=data_size&0xff; + + + printf("PRESISTENT RESERVE IN: READ KEYS\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + /* PRGeneration */ + prgeneration=data[0]; + prgeneration<<=8;prgeneration|=data[1]; + prgeneration<<=8;prgeneration|=data[2]; + prgeneration<<=8;prgeneration|=data[3]; + printf("PRGeneration:%d\n", prgeneration); + + /* Additional Length */ + additional_length=data[4]; + additional_length<<=8;additional_length|=data[5]; + additional_length<<=8;additional_length|=data[6]; + additional_length<<=8;additional_length|=data[7]; + printf("Additional Length:%d\n", additional_length); + + /* print the registered keys */ + for(i=0;i>8)&0xff; + cdb[8]=data_size&0xff; + + + printf("PRESISTENT RESERVE IN: READ RESERVATION\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + /* PRGeneration */ + prgeneration=data[0]; + prgeneration<<=8;prgeneration|=data[1]; + prgeneration<<=8;prgeneration|=data[2]; + prgeneration<<=8;prgeneration|=data[3]; + printf("PRGeneration:%d\n", prgeneration); + + /* Additional Length */ + additional_length=data[4]; + additional_length<<=8;additional_length|=data[5]; + additional_length<<=8;additional_length|=data[6]; + additional_length<<=8;additional_length|=data[7]; + printf("Additional Length:%d\n", additional_length); + + if(additional_length==16){ + printf("Key:%02x%02x%02x%02x%02x%02x%02x%02x\n", + data[8], + data[9], + data[10], + data[11], + data[12], + data[13], + data[14], + data[15]); + printf("Scope:%xh Type:%xh\n",data[21]>>4,data[21]&0x0f); + } + + return 0; +} + +int scsi_persistent_reserve_in_report_capabilities(int fd) +{ + char cdb[]={0x5e,0,0,0,0,0,0,0,0,0}; + + unsigned int data_size=0x00ff; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + unsigned char service_action=2; + int res; + unsigned short length, type_mask; + + cdb[1]=service_action; + cdb[7]=(data_size>>8)&0xff; + cdb[8]=data_size&0xff; + + + printf("PRESISTENT RESERVE IN: REPORT CAPABILITIES\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + /* Length */ + length=data[0]; + length<<=8;length|=data[1]; + printf("Length:%d\n", length); + + /* CRH ... */ + printf("CRH:%d SIP_C:%d ATP_C:%d PTPL_C:%d\n", + !!(data[2]&0x10), + !!(data[2]&0x08), + !!(data[2]&0x04), + !!(data[2]&0x01)); + + /* TMV ... */ + printf("TMV:%d ALLOW_COMMANDS:%c%c%cb PTPL_A:%d\n", + !!(data[3]&0x80), + '0'+(!!(data[3]&0x40)), + '0'+(!!(data[3]&0x20)), + '0'+(!!(data[3]&0x10)), + !!(data[3]&0x01)); + + /* Persistent Reservation Type Mask */ + type_mask=data[4]; + type_mask<<=8;type_mask|=data[5]; + printf("Presistent Reservation Type Mask:0x%04x\n", type_mask); + printf("WR_EX_AR:%d EX_AC_RO:%d WR_EX_RO:%d EX_AC:%d WR_EX:%d EX_AC_AR:%d\n", + !!(data[4]&0x80), + !!(data[4]&0x40), + !!(data[4]&0x20), + !!(data[4]&0x08), + !!(data[4]&0x02), + !!(data[4]&0x01)); + + return 0; +} + +int scsi_persistent_reserve_in_read_full_status(int fd) +{ + char cdb[]={0x5e,0,0,0,0,0,0,0,0,0}; + + unsigned int data_size=0x00ff; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + unsigned char service_action=3; + int res; + unsigned long prgeneration, additional_length; + + cdb[1]=service_action; + cdb[7]=(data_size>>8)&0xff; + cdb[8]=data_size&0xff; + + + printf("PRESISTENT RESERVE IN: READ FULL STATUS\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + /* PRGeneration */ + prgeneration=data[0]; + prgeneration<<=8;prgeneration|=data[1]; + prgeneration<<=8;prgeneration|=data[2]; + prgeneration<<=8;prgeneration|=data[3]; + printf("PRGeneration:%d\n", prgeneration); + + /* Additional Length */ + additional_length=data[4]; + additional_length<<=8;additional_length|=data[5]; + additional_length<<=8;additional_length|=data[6]; + additional_length<<=8;additional_length|=data[7]; + printf("Additional Length:%d\n", additional_length); + +/*XXX*/ + + return 0; +} + +int scsi_persistent_reserve_out_clear(int fd) +{ + char cdb[]={0x5f,0,0,0,0,0,0,0,0,0}; + + unsigned int data_size=24; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + unsigned char service_action=3; + unsigned char scope=0; + unsigned char type=8; + int res; + + cdb[1]=service_action; + cdb[2]=(scope<<4)|type; + cdb[7]=(data_size>>8)&0xff; + cdb[8]=data_size&0xff; + + memset(data, 0, data_size); + + /* Reservation Key */ + data[0]='C'; + data[1]='T'; + data[2]='D'; + data[3]='B'; + data[4]='#'; + data[5]='0'; + data[6]=' '; + data[7]=' '; + + /* Service Action Key */ + data[8]=0; + data[9]=0; + data[10]=0; + data[11]=0; + data[12]=0; + data[13]=0; + data[14]=0; + data[15]=0; + + /* Spec_ip_ti=0 all_tg_pt=1 aptpl=0 */ + data[20]=0x04; + + printf("PRESISTENT RESERVE IN: CLEAR\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_TO_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + return 0; +} + +int scsi_persistent_reserve_out_reserve(int fd) +{ + char cdb[]={0x5f,0,0,0,0,0,0,0,0,0}; + + unsigned int data_size=24; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + unsigned char service_action=1; + unsigned char scope=0; + unsigned char type=8; + int res; + + cdb[1]=service_action; + cdb[2]=(scope<<4)|type; + cdb[7]=(data_size>>8)&0xff; + cdb[8]=data_size&0xff; + + memset(data, 0, data_size); + + /* Reservation Key */ + data[0]='C'; + data[1]='T'; + data[2]='D'; + data[3]='B'; + data[4]='#'; + data[5]='0'; + data[6]=' '; + data[7]=' '; + + /* Service Action Key */ + data[8]=0; + data[9]=0; + data[10]=0; + data[11]=0; + data[12]=0; + data[13]=0; + data[14]=0; + data[15]=0; + + /* Spec_ip_ti=0 all_tg_pt=1 aptpl=0 */ + data[20]=0x04; + + printf("PRESISTENT RESERVE IN: RESERVE\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_TO_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + return 0; +} + +int scsi_persistent_reserve_out_register_and_ignore_existing_key(int fd) +{ + char cdb[]={0x5f,0,0,0,0,0,0,0,0,0}; + + unsigned int data_size=24; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + unsigned char service_action=6; + unsigned char scope=0; + unsigned char type=8; + int res; + + cdb[1]=service_action; + cdb[2]=(scope<<4)|type; + cdb[7]=(data_size>>8)&0xff; + cdb[8]=data_size&0xff; + + memset(data, 0, data_size); + + /* Reservation Key */ + data[0]=0; + data[1]=0; + data[2]=0; + data[3]=0; + data[4]=0; + data[5]=0; + data[6]=0; + data[7]=0; + + /* Service Action Key */ + data[8]='C'; + data[9]='T'; + data[10]='D'; + data[11]='B'; + data[12]='#'; + data[13]='0'; + data[14]=' '; + data[15]=' '; + + /* Spec_ip_ti=0 all_tg_pt=1 aptpl=0 */ + data[20]=0x04; + + printf("PRESISTENT RESERVE IN: REGISTER AND IGNORE EXISTING KEY\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_TO_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + return 0; +} + +int scsi_persistent_reserve_out_unregister_key(int fd) +{ + char cdb[]={0x5f,0,0,0,0,0,0,0,0,0}; + + unsigned int data_size=24; + unsigned char data[data_size]; + + unsigned int sense_len=32; + unsigned char sense[sense_len]; + unsigned char service_action=6; + unsigned char scope=0; + unsigned char type=8; + int res; + + cdb[1]=service_action; + cdb[2]=(scope<<4)|type; + cdb[7]=(data_size>>8)&0xff; + cdb[8]=data_size&0xff; + + memset(data, 0, data_size); + + /* Reservation Key */ + data[0]='C'; + data[1]='T'; + data[2]='D'; + data[3]='B'; + data[4]='#'; + data[5]='0'; + data[6]=' '; + data[7]=' '; + + /* Service Action Key */ + data[8]=0; + data[9]=0; + data[10]=0; + data[11]=0; + data[12]=0; + data[13]=0; + data[14]=0; + data[15]=0; + + /* Spec_ip_ti=0 all_tg_pt=1 aptpl=0 */ + data[20]=0x04; + + printf("PRESISTENT RESERVE IN: UNREGISTER KEY\n"); + + res=scsi_io(fd, cdb, sizeof(cdb), SG_DXFER_TO_DEV, data, &data_size, sense, &sense_len); + if(res){ + printf("SCSI_IO failed\n"); + return -1; + } + if(sense_len){ + print_sense_data(sense, sense_len); + return -1; + } + + return 0; +} + + + + +int open_scsi_device(const char *dev) +{ + int fd, vers; + + if((fd=open(dev, O_RDWR))<0){ + printf("ERROR could not open device %s\n", dev); + return -1; + } + if ((ioctl(fd, SG_GET_VERSION_NUM, &vers) < 0) || (vers < 30000)) { + printf("/dev is not an sg device, or old sg driver\n"); + close(fd); + return -1; + } + + return fd; +} + +int main(int argc, char *argv[]) +{ + int fd; + + if(argc!=2){ + usage(); + exit(10); + } + fd=open_scsi_device(argv[1]); + if(fd<0){ + printf("Could not open SCSI device %s\n",argv[1]); + exit(10); + } + + scsi_inquiry(fd); + scsi_inquiry_supported_vpd_pages(fd); + scsi_inquiry_unit_serial_number(fd); + scsi_persistent_reserve_in_read_keys(fd); + scsi_persistent_reserve_in_read_reservation(fd); + scsi_persistent_reserve_in_report_capabilities(fd); + scsi_persistent_reserve_in_read_full_status(fd); +#if 0 + scsi_persistent_reserve_out_register_and_ignore_existing_key(fd); + scsi_persistent_reserve_in_read_keys(fd); + + scsi_persistent_reserve_out_reserve(fd); + scsi_persistent_reserve_in_read_reservation(fd); + + scsi_persistent_reserve_out_clear(fd); + scsi_persistent_reserve_in_read_reservation(fd); + + scsi_persistent_reserve_out_unregister_key(fd); + scsi_persistent_reserve_in_read_keys(fd); +#endif + return 0; +}