/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg") device driver. * Copyright (C) 1999 - 2002 D. Gilbert * 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. This program scans the "sg" device space (ie actual + simulated SCSI generic devices). Options: -w open writable (new driver opens readable unless -i) -n numeric scan: scan /dev/sg0,1,2, .... -a alpha scan: scan /dev/sga,b,c, .... -i do SCSI inquiry on device (implies -w) -x extra information output By default this program will look for /dev/sg0 first (i.e. numeric scan) Note: This program is written to work under both the original and the new sg driver. Version 1.00 20031022 F. Jansen - modification to extend beyond 26 sg devices. M. Ridgeway - Roll code together for SCSI testing with one command line 6 byte INQUIRY command: [0x12][ |lu][pg cde][res ][al len][cntrl ] */ #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <dirent.h> #include <limits.h> #include <time.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #include <ctype.h> #include <pthread.h> #include <sys/sysmacros.h> #include <sys/time.h> #include <sys/mman.h> #include <linux/major.h> #include "sg_include.h" #include "sg_err.h" #include "llseek.h" #define ME "scsimain: " #ifndef O_DIRECT #define O_DIRECT 040000 #endif #define NUMERIC_SCAN_DEF 1 /* change to 0 to make alpha scan default */ //static char * version_str = "0.21 20030513"; #define BPI (signed)(sizeof(int)) #define READWRITE_BASE_NUM 0x12345678 #define DEF_BLOCK_SIZE 512 #define DEF_NUM_THREADS 16 #define MAX_NUM_THREADS SG_MAX_QUEUE #define DEF_BLOCKS_PER_TRANSFER 128 #define DEF_SCSI_CDBSZ 10 #define MAX_SCSI_CDBSZ 16 #define TUR_CMD_LEN 6 #define DEVNAME_SZ 256 #define MAX_HOLES 4 #define OFF sizeof(struct sg_header) #define INQ_REPLY_LEN 96 /* logic assumes >= sizeof(inqCmdBlk) */ #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ #define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */ #define REASON_SZ 128 #define SENSE_BUFF_SZ 64 #define RCAP_REPLY_LEN 8 #define LOG_SENSE_CMD 0x4d #define LOG_SENSE_CMDLEN 10 #define MX_ALLOC_LEN (1024 * 17) #define D_ROOT_SZ 512 #define STR_SZ 1024 #define INOUTF_SZ 512 #define EBUFF_SZ 512 #define MDEV_NAME_SZ 256 #define PG_CODE_ALL 0x00 #define TRUE 1 #define FALSE 0 #define MAX_DEVICES 50 #define NAME_LEN_MAX 256 #define LEVELS 4 #define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ #define INQ_ALLOC_LEN 255 #ifndef SCSI_IOCTL_GET_PCI #define SCSI_IOCTL_GET_PCI 0x5387 #endif #define READ_CAP_REPLY_LEN 8 #ifndef RAW_MAJOR #define RAW_MAJOR 255 /*unlikey value */ #endif #define FT_OTHER 1 /* filetype is probably normal */ #define FT_SG 2 /* filetype is sg char device or supports SG_IO ioctl */ #define FT_RAW 4 /* filetype is raw char device */ #define FT_DEV_NULL 8 /* either "/dev/null" or "." as filename */ #define FT_ST 16 /* filetype is st char device (tape) */ #define FT_BLOCK 32 /* filetype is block device */ #define DEV_NULL_MINOR_NUM 3 #ifdef SG_GET_RESERVED_SIZE #define OPEN_FLAG O_RDONLY #else #define OPEN_FLAG O_RDWR #endif #ifndef SG_MAX_SENSE #define SG_MAX_SENSE 16 #endif #define TEST_START 0 #define TEST_BREAK 1 #define TEST_STOP 2 #define MAX_SG_DEVS 128 #define MAX_SD_DEVS 128 #define MAX_SR_DEVS 128 #define MAX_ST_DEVS 128 #define MAX_OSST_DEVS 128 #define MAX_ERRORS 5 #define LIN_DEV_TYPE_UNKNOWN 0 #define LIN_DEV_TYPE_SD 1 #define LIN_DEV_TYPE_SR 2 #define LIN_DEV_TYPE_ST 3 #define LIN_DEV_TYPE_SCD 4 #define LIN_DEV_TYPE_OSST 5 #define MODE_SENSE6_CMD 0x1a #define MODE_SENSE6_CMDLEN 6 #define MODE_SENSE10_CMD 0x5a #define MODE_SENSE10_CMDLEN 10 #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define MODE_ALLOC_LEN (1024 * 4) #define MODE_CODE_ALL 0x3f #define RB_MODE_DESC 3 #define RB_MODE_DATA 2 #define RB_DESC_LEN 4 #define RB_MB_TO_READ 200 #define RB_OPCODE 0x3C #define RB_CMD_LEN 10 /* #define SG_DEBUG */ #ifndef SG_FLAG_MMAP_IO #define SG_FLAG_MMAP_IO 4 #endif #ifndef SG_SCSI_RESET #define SG_SCSI_RESET 0x2284 #endif #ifndef SG_SCSI_RESET_NOTHING #define SG_SCSI_RESET_NOTHING 0 #define SG_SCSI_RESET_DEVICE 1 #define SG_SCSI_RESET_BUS 2 #define SG_SCSI_RESET_HOST 3 #endif #define LONG_TIMEOUT 2400000 /* 2,400,000 millisecs == 40 minutes */ #define SEND_DIAGNOSTIC_CMD 0x1d #define SEND_DIAGNOSTIC_CMDLEN 6 #define RECEIVE_DIAGNOSTIC_CMD 0x1c #define RECEIVE_DIAGNOSTIC_CMDLEN 6 #define START_STOP 0x1b #define SYNCHRONIZE_CACHE 0x35 #define DEF_START_TIMEOUT 120000 /* 120,000 millisecs == 2 minutes */ #define DEVICE_RESET 0 #define HOST_RESET 1 #define BUS_RESET 2 #define SG_HSZ sizeof(struct sg_header) #define OFFSET_HEADER (SG_HSZ - (2 * sizeof(int))) #define SIZEOF_BUFFER (256*1024) #define SIZEOF_BUFFER1 (16*1024) #define MAXPARM 32 #define SETUP_MODE_PAGE(NPAGE, NPARAM) \ status = get_mode_page(NPAGE, page_code); \ if (status) { printf("\n"); return status; } \ bdlen = buffer[11]; \ pagestart = buffer + 12 + bdlen; typedef struct request_collection { /* one instance visible to all threads */ int infd; int skip; int in_type; int in_scsi_type; int in_blk; /* -\ next block address to read */ int in_count; /* | blocks remaining for next read */ int in_done_count; /* | count of completed in blocks */ int in_partial; /* | */ int in_stop; /* | */ pthread_mutex_t in_mutex; /* -/ */ int outfd; int seek; int out_type; int out_scsi_type; int out_blk; /* -\ next block address to write */ int out_count; /* | blocks remaining for next write */ int out_done_count; /* | count of completed out blocks */ int out_partial; /* | */ int out_stop; /* | */ pthread_mutex_t out_mutex; /* | */ pthread_cond_t out_sync_cv; /* -/ hold writes until "in order" */ int bs; int bpt; int fua_mode; int dio; int dio_incomplete; /* -\ */ int sum_of_resids; /* | */ pthread_mutex_t aux_mutex; /* -/ (also serializes some printf()s */ int coe; int cdbsz; int debug; } Rq_coll; typedef struct request_element { /* one instance per worker thread */ int infd; int outfd; int wr; int blk; int num_blks; unsigned char *buffp; unsigned char *alloc_bp; sg_io_hdr_t io_hdr; unsigned char cmd[MAX_SCSI_CDBSZ]; unsigned char sb[SENSE_BUFF_LEN]; int bs; int fua_mode; int dio; int dio_incomplete; int resid; int in_scsi_type; int out_scsi_type; int cdbsz; int debug; } Rq_elem; typedef struct my_map_info { int active; int lin_dev_type; int oth_dev_num; struct sg_scsi_id sg_dat; char vendor[8]; char product[16]; char revision[4]; } my_map_info_t; typedef struct sg_map { int bus; int channel; int target_id; int lun; char *dev_name; } Sg_map; typedef struct my_scsi_idlun { /* why can't userland see this structure ??? */ int dev_id; int host_unique_id; } My_scsi_idlun; struct page_code_desc { int page_code; const char *desc; }; static const char *pg_control_str_arr[] = { "current", "changeable", "default", "saved" }; char *devices[] = { "/dev/sda", "/dev/sdb", "/dev/sdc", "/dev/sdd", "/dev/sde", "/dev/sdf", "/dev/sdg", "/dev/sdh", "/dev/sdi", "/dev/sdj", "/dev/sdk", "/dev/sdl", "/dev/sdm", "/dev/sdn", "/dev/sdo", "/dev/sdp", "/dev/sdq", "/dev/sdr", "/dev/sds", "/dev/sdt", "/dev/sdu", "/dev/sdv", "/dev/sdw", "/dev/sdx", "/dev/sdy", "/dev/sdz", "/dev/sdaa", "/dev/sdab", "/dev/sdac", "/dev/sdad", "/dev/scd0", "/dev/scd1", "/dev/scd2", "/dev/scd3", "/dev/scd4", "/dev/scd5", "/dev/scd6", "/dev/scd7", "/dev/scd8", "/dev/scd9", "/dev/scd10", "/dev/scd11", "/dev/sr0", "/dev/sr1", "/dev/sr2", "/dev/sr3", "/dev/sr4", "/dev/sr5", "/dev/sr6", "/dev/sr7", "/dev/sr8", "/dev/sr9", "/dev/sr10", "/dev/sr11", "/dev/nst0", "/dev/nst1", "/dev/nst2", "/dev/nst3", "/dev/nst4", "/dev/nst5", "/dev/nosst0", "/dev/nosst1", "/dev/nosst2", "/dev/nosst3", "/dev/nosst4" }; static char *page_names[] = { NULL, "Read-Write Error Recovery", "Disconnect-Reconnect", "Format Device", "Rigid Disk Geometry", /* "Flexible Disk" */ NULL, NULL, "Verify Error Recovery", "Caching", "Peripheral Device", "Control Mode", /* "Medium Types Supported" */ NULL, "Notch and Partition", /* "CD-ROM" */ NULL, /* "CD-ROM Audio Control" */ NULL, NULL, /* "Medium Partition (1)" */ NULL, /* "Medium Partition (2)" */ NULL, /* "Medium Partition (3)" */ NULL, /* "Medium Partition (4)" */ NULL }; #define MAX_PAGENO (sizeof(page_names)/sizeof(char *)) /* Following 2 macros from D.R. Butenhof's POSIX threads book: ISBN 0-201-63392-2 . [Highly recommended book.] */ #define err_exit(code,text) do { \ fprintf(stderr, "%s at \"%s\":%d: %s\n", \ text, __FILE__, __LINE__, strerror(code)); \ exit(1); \ } while (0) static Sg_map sg_map_arr[(sizeof(devices) / sizeof(char *)) + 1]; static const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 }; const unsigned char rbCmdBlk[10] = { READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static const char *level_arr[LEVELS] = { "host", "bus", "target", "lun" }; static const char *proc_allow_dio = "/proc/scsi/sg/allow_dio"; static const char *devfs_id = "/dev/.devfsd"; static my_map_info_t map_arr[MAX_SG_DEVS]; static char ebuff[EBUFF_SZ]; static int glob_fd; static char defectformat = 0x4; static sigset_t signal_set; static pthread_t sig_listen_thread_id; static int do_ide = 0; static int do_inq = 1; static int do_leaf = 1; static int do_extra = 1; static int do_quiet = 0; static int checked_sg = 1; static int sum_of_resids = 0; static int dd_count = -1; static int in_full = 0; static int in_partial = 0; static int out_full = 0; static int out_partial = 0; static int do_coe = 0; int base = READWRITE_BASE_NUM; unsigned char *cmpbuf = 0; static unsigned char buff_a[SIZEOF_BUFFER + SG_HSZ + 12]; static unsigned char *buffer = buff_a + OFFSET_HEADER; typedef struct my_sg_scsi_id { int host_no; /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */ int channel; int scsi_id; /* scsi id of target device */ int lun; int scsi_type; /* TYPE_... defined in scsi/scsi.h */ short h_cmd_per_lun; /* host (adapter) maximum commands per lun */ short d_queue_depth; /* device (or adapter) maximum queue length */ int unused1; /* probably find a good use, set 0 for now */ int unused2; /* ditto */ } My_sg_scsi_id; // Prototypes int do_scsi_sgp_read_write(char *device); int do_scsi_sgm_read_write(char *device); void sg_in_operation(Rq_coll * clp, Rq_elem * rep); void sg_out_operation(Rq_coll * clp, Rq_elem * rep); int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks); void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks); int sg_start_io(Rq_elem * rep); int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp); int run_sg_scan_tests(void); int show_scsi_logs(char *device); int validate_device(char *device); int show_devfs_devices(void); void usage(void); int do_scsi_device_read_write(char *device); int do_scsi_inquiry(char *device, int hex_flag); int show_scsi_maps(void); int show_scsi_modes(char *device); int do_scsi_read_buffer(char *device); int show_scsi_read_capacity(char *device); int do_scsi_reset_devices(char *device, int reset_opts); int do_scsi_send_diagnostics(char *device); int do_scsi_start_stop(char *device, int startstop); int do_scsi_read_write_buffer(char *device); int do_scsi_test_unit_ready(char *device); int show_scsi_info(char *device); void print_msg(int msg_num, const char *msg); static void scan_dev_type(const char *leadin, int max_dev, int do_numeric, int lin_dev_type, int last_sg_ind); #ifdef SG_IO int sg3_inq(int sg_fd, unsigned char *inqBuff, int do_extra); #endif static unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { 0x12, 0, 0, 0, INQ_REPLY_LEN, 0 }; void print_msg(int msg_num, const char *msg) { switch (msg_num) { case TEST_START: printf ("\n****************** Starting Tests ***************************\n"); break; case TEST_STOP: printf ("\n****************** Tests Complete ***************************\n"); break; case TEST_BREAK: printf("\n------------------ %s Test ------------------\n\n", msg); break; } } int main(int argc, char *argv[]) { int rc = 0; if (argc < 2) { printf("\n\nERROR:No device passed to test\n\n"); usage(); return 1; } rc = validate_device(argv[1]); if (rc == 0) { print_msg(TEST_START, NULL); rc = run_sg_scan_tests(); if (rc != 0) { printf("ERROR: run_sg_scan_tests failed %d\n", rc); } rc = show_scsi_logs(argv[1]); if (rc != 0) { printf("ERROR: show_scsi_logs failed %d\n", rc); } rc = show_devfs_devices(); if (rc != 0) { printf("ERROR: show_devfs_devices failed %d\n", rc); } rc = do_scsi_device_read_write(argv[1]); if (rc != 0) { printf("ERROR: do_scsi_devices_read_write failed %d\n", rc); } rc = do_scsi_inquiry(argv[1], TRUE); if (rc != 0) { printf("ERROR: do_scsi_inquiry HEX failed %d\n", rc); } else { rc = do_scsi_inquiry(argv[1], FALSE); if (rc != 0) { printf("ERROR: do_scsi_inquiry PCI failed %d\n", rc); } } rc = show_scsi_maps(); if (rc != 0) { printf("ERROR: show_scsi_maps failed %d\n", rc); } rc = show_scsi_modes(argv[1]); if (rc != 0) { printf("ERROR: show_scsi_modes failed %d\n", rc); } rc = do_scsi_read_buffer(argv[1]); if (rc != 0 && rc != 1) { printf("ERROR: do_scsi_read_buffer failed %d\n", rc); } rc = show_scsi_read_capacity(argv[1]); if (rc != 0) { printf("ERROR: show_scsi_read_capacity failed %d\n", rc); } rc |= do_scsi_reset_devices(argv[1], DEVICE_RESET); rc |= do_scsi_reset_devices(argv[1], BUS_RESET); rc |= do_scsi_reset_devices(argv[1], HOST_RESET); if (rc != 0) { printf("ERROR: do_scsi_reset_devices failed %d\n", rc); } rc = do_scsi_send_diagnostics(argv[1]); if (rc != 0) { printf("ERROR: do_scsi_send_diagnostics failed %d\n", rc); } rc |= do_scsi_start_stop(argv[1], FALSE); rc |= do_scsi_start_stop(argv[1], TRUE); if (rc != 0) { printf("ERROR: do_scsi_start_top failed %d\n", rc); } rc = do_scsi_read_write_buffer(argv[1]); if (rc != 0 && rc != 1) { printf("ERROR: do_scsi_read_write_buffer failed %d\n", rc); } rc = do_scsi_test_unit_ready(argv[1]); if (rc != 0) { printf("ERROR: do_scsi_test_unit_ready failed %d\n", rc); } rc = show_scsi_info(argv[1]); if (rc != 0) { printf("ERROR: show_scsi_info failed %d\n", rc); } rc = do_scsi_sgp_read_write(argv[1]); if (rc != 0) { printf("ERROR: do_scsi_sgp_read_write failed %d\n", rc); } rc = do_scsi_sgm_read_write(argv[1]); if (rc != 0) { printf("ERROR: do_scsi_sgm_read_write failed %d\n", rc); } print_msg(TEST_STOP, NULL); } else { printf("\nERROR: Invalid device passed to test\n\n\n"); usage(); } return 0; } int validate_device(char *device) { int rc = 0; int i, found = FALSE; char device_string[25]; for (i = 0; i < MAX_DEVICES && !found; i++) { sprintf(device_string, "/dev/sg%d", i); //printf("checking %s \n", device_string); if (strcmp(device, device_string) == 0) { found = TRUE; } } return rc; } void usage() { printf("Usage: 'sg_scan [-a] [-n] [-w] [-i] [-x]'\n"); printf(" where: -a do alpha scan (ie sga, sgb, sgc)\n"); printf(" -n do numeric scan (ie sg0, sg1...) [default]\n"); printf(" -w force open with read/write flag\n"); printf(" -i do SCSI INQUIRY, output results\n"); printf(" -x extra information output about queuing\n\n\n"); } void make_dev_name(char *fname, const char *leadin, int k, int do_numeric) { char buff[64]; int big, little; strcpy(fname, leadin ? leadin : "/dev/sg"); if (do_numeric) { sprintf(buff, "%d", k); strcat(fname, buff); } else { if (k < 26) { buff[0] = 'a' + (char)k; buff[1] = '\0'; strcat(fname, buff); } else if (k <= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */ big = k / 26; little = k - (26 * big); big = big - 1; buff[0] = 'a' + (char)big; buff[1] = 'a' + (char)little; buff[2] = '\0'; strcat(fname, buff); } else strcat(fname, "xxxx"); } } int run_sg_scan_tests() { int sg_fd, res, k, f; unsigned char inqBuff[OFF + INQ_REPLY_LEN]; int inqInLen = OFF + sizeof(inqCmdBlk); int inqOutLen = OFF + INQ_REPLY_LEN; unsigned char *buffp = inqBuff + OFF; struct sg_header *isghp = (struct sg_header *)inqBuff; int do_numeric = NUMERIC_SCAN_DEF; int do_inquiry = 0; int do_extra = 1; int writeable = 0; int num_errors = 0; int num_silent = 0; int eacces_err = 0; char fname[64]; My_scsi_idlun my_idlun; int host_no; int flags; int emul; print_msg(TEST_BREAK, __FUNCTION__); flags = writeable ? O_RDWR : OPEN_FLAG; do_numeric = 1; writeable = O_RDONLY; do_inquiry = 1; do_extra = 1; for (k = 0, res = 0; (k < 1000) && (num_errors < MAX_ERRORS); ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) { if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ", fname); perror(ME "close error"); return 1; } make_dev_name(fname, NULL, k, do_numeric); sg_fd = open(fname, flags | O_NONBLOCK); if (sg_fd < 0) { if (EBUSY == errno) { printf ("%s: device busy (O_EXCL lock), skipping\n", fname); continue; } else if ((ENODEV == errno) || (ENOENT == errno) || (ENXIO == errno)) { ++num_errors; ++num_silent; continue; } else { if (EACCES == errno) eacces_err = 1; snprintf(ebuff, EBUFF_SZ, ME "Error opening %s ", fname); perror(ebuff); ++num_errors; continue; } } res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun); if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi ioctl, skip", fname); perror(ebuff); ++num_errors; continue; } res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no); if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi " "ioctl(2), skip", fname); perror(ebuff); ++num_errors; continue; } #ifdef SG_EMULATED_HOST res = ioctl(sg_fd, SG_EMULATED_HOST, &emul); if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "device %s failed on sg ioctl(3), skip", fname); perror(ebuff); ++num_errors; continue; } #else emul = 0; #endif printf("%s: scsi%d channel=%d id=%d lun=%d", fname, host_no, (my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff, (my_idlun.dev_id >> 8) & 0xff); if (emul) printf(" [em]"); #if 0 printf(", huid=%d", my_idlun.host_unique_id); #endif #ifdef SG_GET_RESERVED_SIZE { My_sg_scsi_id m_id; /* compatible with sg_scsi_id_t in sg.h */ res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id); if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "device %s ioctls(4), skip", fname); perror(ebuff); ++num_errors; continue; } printf(" type=%d", m_id.scsi_type); if (do_extra) printf(" cmd_per_lun=%hd queue_depth=%hd\n", m_id.h_cmd_per_lun, m_id.d_queue_depth); else printf("\n"); } #else printf("\n"); #endif if (!do_inquiry) continue; #ifdef SG_IO if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &f) >= 0) && (f >= 30000)) { res = sg3_inq(sg_fd, inqBuff, do_extra); continue; } #endif memset(isghp, 0, sizeof(struct sg_header)); isghp->reply_len = inqOutLen; memcpy(inqBuff + OFF, inqCmdBlk, INQUIRY_CMDLEN); if (O_RDWR == (flags & O_ACCMODE)) { /* turn on blocking */ f = fcntl(sg_fd, F_GETFL); fcntl(sg_fd, F_SETFL, f & (~O_NONBLOCK)); } else { close(sg_fd); sg_fd = open(fname, O_RDWR); } res = write(sg_fd, inqBuff, inqInLen); if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "device %s writing, skip", fname); perror(ebuff); ++num_errors; continue; } res = read(sg_fd, inqBuff, inqOutLen); if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "device %s reading, skip", fname); perror(ebuff); ++num_errors; continue; } #ifdef SG_GET_RESERVED_SIZE if (!sg_chk_n_print("Error from Inquiry", isghp->target_status, isghp->host_status, isghp->driver_status, isghp->sense_buffer, SG_MAX_SENSE)) continue; #else if ((isghp->result != 0) || (0 != isghp->sense_buffer[0])) { printf("Error from Inquiry: result=%d\n", isghp->result); if (0 != isghp->sense_buffer[0]) sg_print_sense("Error from Inquiry", isghp->sense_buffer, SG_MAX_SENSE); continue; } #endif f = (int)*(buffp + 7); printf(" %.8s %.16s %.4s ", buffp + 8, buffp + 16, buffp + 32); printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x]\n", ! !(f & 0x20), ! !(f & 0x10), ! !(f & 2), ! !(f & 1), (*buffp & 0xe0) >> 5); } if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) { printf("Stopping because there are too many error\n"); if (eacces_err) printf(" root access may be required\n"); } return 0; } #ifdef SG_IO int sg3_inq(int sg_fd, unsigned char *inqBuff, int do_extra) { sg_io_hdr_t io_hdr; unsigned char sense_buffer[32]; int ok; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(inqCmdBlk); io_hdr.mx_sb_len = sizeof(sense_buffer); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = INQ_REPLY_LEN; io_hdr.dxferp = inqBuff; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror(ME "Inquiry SG_IO ioctl error"); return 1; } /* now for the error processing */ ok = 0; switch (sg_err_category3(&io_hdr)) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: ok = 1; break; default: /* won't bother decoding other categories */ sg_chk_n_print3("INQUIRY command error", &io_hdr); break; } if (ok) { /* output result if it is available */ char *p = (char *)inqBuff; int f = (int)*(p + 7); printf(" %.8s %.16s %.4s ", p + 8, p + 16, p + 32); printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x] ", ! !(f & 0x20), ! !(f & 0x10), ! !(f & 2), ! !(f & 1), (*p & 0xe0) >> 5); if (do_extra) printf("dur=%ums\n", io_hdr.duration); else printf("\n"); } return 0; } #endif static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code, int paramp, void *resp, int mx_resp_len, int noisy) { int res; unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] = { LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; logsCmdBlk[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0)); logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); logsCmdBlk[5] = (unsigned char)((paramp >> 8) & 0xff); logsCmdBlk[6] = (unsigned char)(paramp & 0xff); if (mx_resp_len > 0xffff) { printf(ME "mx_resp_len too big\n"); return -1; } logsCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); logsCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(logsCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = logsCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO (log sense) error"); return -1; } #if 0 printf("SG_IO ioctl: status=%d, info=%d, sb_len_wr=%d\n", io_hdr.status, io_hdr.info, io_hdr.sb_len_wr); #endif res = sg_err_category3(&io_hdr); switch (res) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: return 0; default: if (noisy) { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, ME "ppc=%d, sp=%d, " "pc=%d, page_code=%x, paramp=%x\n ", ppc, sp, pc, pg_code, paramp); sg_chk_n_print3(ebuff, &io_hdr); } return -1; } } static void dStrHex(const char *str, int len, int no_ascii) { const char *p = str; unsigned char c; char buff[82]; int a = 0; const int bpstart = 5; const int cpstart = 60; int cpos = cpstart; int bpos = bpstart; int i, k; if (len <= 0) return; memset(buff, ' ', 80); buff[80] = '\0'; k = sprintf(buff + 1, "%.2x", a); buff[k + 1] = ' '; if (bpos >= ((bpstart + (9 * 3)))) bpos++; for (i = 0; i < len; i++) { c = *p++; bpos += 3; if (bpos == (bpstart + (9 * 3))) bpos++; sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); buff[bpos + 2] = ' '; if (no_ascii) buff[cpos++] = ' '; else { if ((c < ' ') || (c >= 0x7f)) c = '.'; buff[cpos++] = c; } if (cpos > (cpstart + 15)) { printf("%s\n", buff); bpos = bpstart; cpos = cpstart; a += 16; memset(buff, ' ', 80); k = sprintf(buff + 1, "%.2x", a); buff[k + 1] = ' '; } } if (cpos > cpstart) { printf("%s\n", buff); } } static void show_page_name(int page_no) { switch (page_no) { case 0x0: printf(" 0x00 Supported log pages\n"); break; case 0x1: printf(" 0x01 Buffer over-run/under-run\n"); break; case 0x2: printf(" 0x02 Error counters (write)\n"); break; case 0x3: printf(" 0x03 Error counters (read)\n"); break; case 0x4: printf(" 0x04 Error counters (read reverse)\n"); break; case 0x5: printf(" 0x05 Error counters (verify)\n"); break; case 0x6: printf(" 0x06 Non-medium errors\n"); break; case 0x7: printf(" 0x07 Last n error events\n"); break; case 0x8: printf(" 0x08 Format status (sbc2)\n"); break; case 0xb: printf(" 0x0b Last n deferred errors of " "asynchronous events\n"); break; case 0xc: printf(" 0x0c Sequential Access (ssc-2)\n"); break; case 0xd: printf(" 0x0d Temperature\n"); break; case 0xe: printf(" 0x0e Start-stop cycle counter\n"); break; case 0xf: printf(" 0x0f Application client\n"); break; case 0x10: printf(" 0x10 Self-test results\n"); break; case 0x18: printf(" 0x18 Protocol specific port\n"); break; case 0x2e: printf(" 0x2e Tape alerts (ssc-2)\n"); break; case 0x2f: printf(" 0x2f Informational exceptions (SMART)\n"); break; default: printf(" 0x%.2x\n", page_no); break; } } static void show_buffer_under_overrun_page(unsigned char *resp, int len) { int k, j, num, pl, count_basis, cause; unsigned char *ucp; unsigned char *xp; unsigned long long ull; printf("Buffer over-run/under-run page\n"); num = len - 4; ucp = &resp[0] + 4; while (num > 3) { pl = ucp[3] + 4; count_basis = (ucp[1] >> 5) & 0x7; printf(" Count basis: "); switch (count_basis) { case 0: printf("undefined"); break; case 1: printf("per command"); break; case 2: printf("per failed reconnect"); break; case 3: printf("per unit of time"); break; default: printf("reserved [0x%x]", count_basis); break; } cause = (ucp[1] >> 1) & 0xf; printf(", Cause: "); switch (cause) { case 0: printf("bus busy"); break; case 1: printf("transfer rate too slow"); break; default: printf("reserved [0x%x]", cause); break; } printf(", Type: "); if (ucp[1] & 1) printf("over-run"); else printf("under-run"); printf(", count"); k = pl - 4; xp = ucp + 4; if (k > sizeof(ull)) { xp += (k - sizeof(ull)); k = sizeof(ull); } ull = 0; for (j = 0; j < k; ++j) { if (j > 0) ull <<= 8; ull |= xp[j]; } printf(" = %llu\n", ull); num -= pl; ucp += pl; } } static void show_error_counter_page(unsigned char *resp, int len) { int k, j, num, pl, pc; unsigned char *ucp; unsigned char *xp; unsigned long long ull; switch (resp[0]) { case 2: printf("Write error counter page\n"); break; case 3: printf("Read error counter page\n"); break; case 4: printf("Read Reverse error counter page\n"); break; case 5: printf("Verify error counter page\n"); break; default: printf("expecting error counter page, got page=0x%x\n", resp[0]); return; } num = len - 4; ucp = &resp[0] + 4; while (num > 3) { pc = (ucp[0] << 8) | ucp[1]; pl = ucp[3] + 4; switch (pc) { case 0: printf(" Errors corrected without substantion delay"); break; case 1: printf(" Errors corrected with possible delays"); break; case 2: printf(" Total operations"); break; case 3: printf(" Total errors corrected"); break; case 4: printf(" Total times correction algorithm processed"); break; case 5: printf(" Total bytes processed"); break; case 6: printf(" Total uncorrected errors"); break; default: printf(" Reserved or vendor specific [0x%x]", pc); break; } k = pl - 4; xp = ucp + 4; if (k > sizeof(ull)) { xp += (k - sizeof(ull)); k = sizeof(ull); } ull = 0; for (j = 0; j < k; ++j) { if (j > 0) ull <<= 8; ull |= xp[j]; } printf(" = %llu\n", ull); num -= pl; ucp += pl; } } static void show_non_medium_error_page(unsigned char *resp, int len) { int k, j, num, pl, pc; unsigned char *ucp; unsigned char *xp; unsigned long long ull; printf("Non-medium error page\n"); num = len - 4; ucp = &resp[0] + 4; while (num > 3) { pc = (ucp[0] << 8) | ucp[1]; pl = ucp[3] + 4; switch (pc) { case 0: printf(" Non-medium error count"); break; default: if (pc <= 0x7fff) printf(" Reserved [0x%x]", pc); else printf(" Vendor specific [0x%x]", pc); break; } k = pl - 4; xp = ucp + 4; if (k > sizeof(ull)) { xp += (k - sizeof(ull)); k = sizeof(ull); } ull = 0; for (j = 0; j < k; ++j) { if (j > 0) ull <<= 8; ull |= xp[j]; } printf(" = %llu\n", ull); num -= pl; ucp += pl; } } const char *self_test_code[] = { "default", "background short", "background extended", "reserved", "aborted background", "foreground short", "foreground extended", "reserved" }; const char *self_test_result[] = { "completed without error", "aborted by SEND DIAGNOSTIC", "aborted other than by SEND DIAGNOSTIC", "unknown error, unable to complete", "self test completed with failure in test segment (which one unkown)", "first segment in self test failed", "second segment in self test failed", "another segment in self test failed", "reserved", "reserved", "reserved", "reserved", "reserved", "reserved", "reserved", "self test in progress" }; static void show_self_test_page(unsigned char *resp, int len) { int k, num, n, res; unsigned char *ucp; unsigned long long ull; num = len - 4; if (num < 0x190) { printf("badly formed self-test results page\n"); return; } printf("Self-test results page\n"); for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20) { n = (ucp[6] << 8) | ucp[7]; if ((0 == n) && (0 == ucp[4])) break; printf(" Parameter code=%d, accumulated power-on hours=%d\n", (ucp[0] << 8) | ucp[1], n); printf(" self test code: %s [%d]\n", self_test_code[(ucp[4] >> 5) & 0x7], (ucp[4] >> 5) & 0x7); res = ucp[4] & 0xf; printf(" self test result: %s [%d]\n", self_test_result[res], res); if (ucp[5]) printf(" self-test number=%d\n", (int)ucp[5]); ull = ucp[8]; ull <<= 8; ull |= ucp[9]; ull <<= 8; ull |= ucp[10]; ull <<= 8; ull |= ucp[11]; ull <<= 8; ull |= ucp[12]; ull <<= 8; ull |= ucp[13]; ull <<= 8; ull |= ucp[14]; ull <<= 8; ull |= ucp[14]; ull <<= 8; ull |= ucp[15]; if ((0xffffffffffffffffULL != ull) && (res > 0) && (res < 0xf)) printf(" address of first error=0x%llx\n", ull); if (ucp[16] & 0xf) printf(" sense key=0x%x, asc=0x%x, asq=0x%x\n", ucp[16] & 0xf, ucp[17], ucp[18]); } } static void show_Temperature_page(unsigned char *resp, int len, int hdr) { int k, num, extra, pc; unsigned char *ucp; num = len - 4; ucp = &resp[0] + 4; if (num < 4) { printf("badly formed Temperature log page\n"); return; } if (hdr) printf("Temperature log page\n"); for (k = num; k > 0; k -= extra, ucp += extra) { if (k < 3) { printf("short Temperature log page\n"); return; } extra = ucp[3] + 4; pc = ((ucp[0] << 8) & 0xff) + ucp[1]; if (0 == pc) { if (extra > 5) { if (ucp[5] < 0xff) printf(" Current temperature= %d C\n", ucp[5]); else printf (" Current temperature=<not available>\n"); } } else if (1 == pc) { if (extra > 5) { if (ucp[5] < 0xff) printf (" Reference temperature= %d C\n", ucp[5]); else printf (" Reference temperature=<not available>\n"); } } else { printf(" parameter code=0x%x, contents in hex:\n", pc); dStrHex((const char *)ucp, extra, 1); } } } static void show_IE_page(unsigned char *resp, int len, int full) { int k, num, extra, pc; unsigned char *ucp; num = len - 4; ucp = &resp[0] + 4; if (num < 4) { printf("badly formed Informational Exceptions log page\n"); return; } if (full) printf("Informational Exceptions log page\n"); for (k = num; k > 0; k -= extra, ucp += extra) { if (k < 3) { printf("short Informational Exceptions log page\n"); return; } extra = ucp[3] + 4; pc = ((ucp[0] << 8) & 0xff) + ucp[1]; if (0 == pc) { if (extra > 5) { if (full) printf(" IE asc=0x%x, ascq=0x%x", ucp[4], ucp[5]); if (extra > 6) { if (full) printf(","); if (ucp[6] < 0xff) printf (" Current temperature=%d C", ucp[6]); else printf (" Current temperature=<not available>"); } printf("\n"); } } else if (full) { printf(" parameter code=0x%x, contents in hex:\n", pc); dStrHex((const char *)ucp, extra, 1); } } } static void show_ascii_page(unsigned char *resp, int len) { int k, n, num; if (len < 0) { printf("response has bad length\n"); return; } num = len - 4; switch (resp[0]) { case 0: printf("Supported pages:\n"); for (k = 0; k < num; ++k) show_page_name((int)resp[4 + k]); break; case 0x1: show_buffer_under_overrun_page(resp, len); break; case 0x2: case 0x3: case 0x4: case 0x5: show_error_counter_page(resp, len); break; case 0x6: show_non_medium_error_page(resp, len); break; case 0xd: show_Temperature_page(resp, len, 1); break; case 0xe: if (len < 40) { printf("badly formed start-stop cycle counter page\n"); break; } printf("Start-stop cycle counter page\n"); printf(" Date of manufacture, year: %.4s, week: %.2s\n", &resp[8], &resp[12]); printf(" Accounting date, year: %.4s, week: %.2s\n", &resp[18], &resp[22]); n = (resp[28] << 24) | (resp[29] << 16) | (resp[30] << 8) | resp[31]; printf(" Specified cycle count over device lifetime=%d\n", n); n = (resp[36] << 24) | (resp[37] << 16) | (resp[38] << 8) | resp[39]; printf(" Accumulated start-stop cycles=%d\n", n); break; case 0x10: show_self_test_page(resp, len); break; case 0x2f: show_IE_page(resp, len, 1); break; default: printf("No ascii information for page=0x%x, here is hex:\n", resp[0]); dStrHex((const char *)resp, len, 1); break; } } static int fetchTemperature(int sg_fd, int do_hex, unsigned char *resp, int max_len) { int res = 0; if (0 == do_logs(sg_fd, 0, 0, 1, 0xd, 0, resp, max_len, 0)) show_Temperature_page(resp, (resp[2] << 8) + resp[3] + 4, 0); else if (0 == do_logs(sg_fd, 0, 0, 1, 0x2f, 0, resp, max_len, 0)) show_IE_page(resp, (resp[2] << 8) + resp[3] + 4, 0); else { printf ("Unable to find temperature in either log page (temperature " "or IE)\n"); res = 1; } close(sg_fd); return res; } int show_scsi_logs(char *device) { int sg_fd, k, pg_len; char *file_name = 0; unsigned char rsp_buff[MX_ALLOC_LEN]; int pg_code = 0; int pc = 1; /* N.B. some disks only give data for current cumulative */ int paramp = 0; int do_list = 0; int do_ppc = 0; int do_sp = 0; int do_hex = 0; int do_all = 1; int do_temp = 0; int oflags = O_RDWR | O_NONBLOCK; file_name = device; print_msg(TEST_BREAK, __FUNCTION__); if ((sg_fd = open(file_name, oflags)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", file_name); perror(ebuff); return 1; } /* Just to be safe, check we have a new sg device by trying an ioctl */ if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { printf(ME "%s doesn't seem to be a version 3 sg device\n", file_name); close(sg_fd); return 1; } if (do_list || do_all) pg_code = PG_CODE_ALL; pg_len = 0; if (1 == do_temp) return fetchTemperature(sg_fd, do_hex, rsp_buff, MX_ALLOC_LEN); if (0 == do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp, rsp_buff, MX_ALLOC_LEN, 1)) { pg_len = (rsp_buff[2] << 8) + rsp_buff[3]; if ((pg_len + 4) > MX_ALLOC_LEN) { printf ("Only fetched %d bytes of response, truncate output\n", MX_ALLOC_LEN); pg_len = MX_ALLOC_LEN - 4; } if (do_hex) { printf("Returned log page code=0x%x, page len=0x%x\n", rsp_buff[0], pg_len); dStrHex((const char *)rsp_buff, pg_len + 4, 1); } else show_ascii_page(rsp_buff, pg_len + 4); } if (do_all && (pg_len > 1)) { int my_len = pg_len - 1; unsigned char parr[256]; memcpy(parr, rsp_buff + 5, my_len); for (k = 0; k < my_len; ++k) { printf("\n"); pg_code = parr[k]; if (0 == do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp, rsp_buff, MX_ALLOC_LEN, 1)) { pg_len = (rsp_buff[2] << 8) + rsp_buff[3]; if ((pg_len + 4) > MX_ALLOC_LEN) { printf ("Only fetched %d bytes of response, truncate " "output\n", MX_ALLOC_LEN); pg_len = MX_ALLOC_LEN - 4; } if (do_hex) { printf ("Returned log page code=0x%x, page len=0x%x\n", rsp_buff[0], pg_len); dStrHex((const char *)rsp_buff, pg_len + 4, 1); } else show_ascii_page(rsp_buff, pg_len + 4); } } } close(sg_fd); return 0; } static int do_inquiry(int sg_fd, void *resp, int mx_resp_len) { int res; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; inqCmdBlk[4] = (unsigned char)mx_resp_len; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(inqCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_TO_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO (inquiry) error"); return -1; } res = sg_err_category3(&io_hdr); switch (res) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: return 0; default: sg_chk_n_print3("Failed INQUIRY", &io_hdr); return -1; } } void leaf_dir(const char *lf, unsigned int *larr) { char name[NAME_LEN_MAX * 2]; int res; if (do_quiet) { printf("%u\t%u\t%u\t%u\n", larr[0], larr[1], larr[2], larr[3]); return; } printf("%u\t%u\t%u\t%u\t%s\n", larr[0], larr[1], larr[2], larr[3], lf); if (do_leaf) { struct dirent *de_entry; struct dirent *de_result; DIR *sdir; int outpos; if (NULL == (sdir = opendir(lf))) { fprintf(stderr, "leaf_dir: opendir of %s: failed\n", lf); return; } de_entry = malloc(sizeof(struct dirent) + NAME_LEN_MAX); if (NULL == de_entry) return; res = 0; printf("\t"); outpos = 8; while (1) { res = readdir_r(sdir, de_entry, &de_result); if (0 != res) { fprintf(stderr, "leaf_dir: readdir_r of %s: %s\n", lf, strerror(res)); res = -2; break; } if (de_result == NULL) break; strncpy(name, de_entry->d_name, NAME_LEN_MAX * 2); if ((0 == strcmp("..", name)) || (0 == strcmp(".", name))) continue; if (do_extra) { struct stat st; char devname[NAME_LEN_MAX * 2]; strncpy(devname, lf, NAME_LEN_MAX * 2); strcat(devname, "/"); strcat(devname, name); if (stat(devname, &st) < 0) return; if (S_ISCHR(st.st_mode)) { strcat(name, "(c "); sprintf(name + strlen(name), "%d %d)", major(st.st_rdev), minor(st.st_rdev)); } else if (S_ISBLK(st.st_mode)) { strcat(name, "(b "); sprintf(name + strlen(name), "%d %d)", major(st.st_rdev), minor(st.st_rdev)); } } res = strlen(name); if ((outpos + res + 2) > 80) { printf("\n\t"); outpos = 8; } printf("%s ", name); outpos += res + 2; } printf("\n"); } if (do_inq) { int sg_fd; char buff[64]; memset(buff, 0, sizeof(buff)); strncpy(name, lf, NAME_LEN_MAX * 2); strcat(name, "/generic"); if ((sg_fd = open(name, O_RDONLY)) < 0) { if (!checked_sg) { checked_sg = 1; if ((sg_fd = open("/dev/sg0", O_RDONLY)) >= 0) close(sg_fd); /* try and get sg module loaded */ sg_fd = open(name, O_RDONLY); } if (sg_fd < 0) { printf("Unable to open sg device: %s, %s\n", name, strerror(errno)); return; } } if (0 != do_inquiry(sg_fd, buff, 64)) return; close(sg_fd); dStrHex(buff, 64, 0); } } /* Return 0 -> ok, -1 -> opendir() error, -2 -> readdir_r error, -3 -> malloc error */ int hbtl_scan(const char *path, int level, unsigned int *larr) { struct dirent *de_entry; struct dirent *de_result; char new_path[NAME_LEN_MAX * 2]; DIR *sdir; int res; size_t level_slen; level_slen = strlen(level_arr[level]); if (NULL == (sdir = opendir(path))) { fprintf(stderr, "hbtl_scan: opendir of %s: failed\n", path); return -1; } de_entry = malloc(sizeof(struct dirent) + NAME_LEN_MAX); if (NULL == de_entry) return -3; res = 0; while (1) { res = readdir_r(sdir, de_entry, &de_result); if (0 != res) { fprintf(stderr, "hbtl_scan: readdir_r of %s: %s\n", path, strerror(res)); res = -2; break; } if (de_result == NULL) break; if (0 == strncmp(level_arr[level], de_entry->d_name, level_slen)) { if (1 != sscanf(de_entry->d_name + level_slen, "%u", larr + level)) larr[level] = UINT_MAX; strncpy(new_path, path, NAME_LEN_MAX * 2); strcat(new_path, "/"); strcat(new_path, de_entry->d_name); if ((level + 1) < LEVELS) { res = hbtl_scan(new_path, level + 1, larr); if (res < 0) break; } else leaf_dir(new_path, larr); } } free(de_entry); closedir(sdir); return res; } int show_devfs_devices() { int res; char ds_root[D_ROOT_SZ]; char di_root[D_ROOT_SZ]; unsigned int larr[LEVELS]; struct stat st; print_msg(TEST_BREAK, __FUNCTION__); strncpy(ds_root, "/dev", D_ROOT_SZ); strncpy(di_root, ds_root, D_ROOT_SZ); strcat(di_root, "/.devfsd"); if (stat(di_root, &st) < 0) { printf("Didn't find %s so perhaps devfs is not present," " attempting to continue ...\n", di_root); } strncpy(di_root, ds_root, D_ROOT_SZ); strcat(ds_root, "/scsi"); strcat(di_root, "/ide"); if (!do_ide) printf("SCSI scan:\n"); res = hbtl_scan(ds_root, 0, larr); if (res < 0) printf("main: scsi hbtl_scan res=%d\n", res); do_ide = TRUE; do_inq = 0; /* won't try SCSI INQUIRY on IDE devices */ if (do_ide) { printf("\nIDE scan:\n"); res = hbtl_scan(di_root, 0, larr); if (res < 0) printf("main: ide hbtl_scan res=%d\n", res); } return 0; } static void install_handler(int sig_num, void (*sig_handler) (int sig)) { struct sigaction sigact; sigaction(sig_num, NULL, &sigact); if (sigact.sa_handler != SIG_IGN) { sigact.sa_handler = sig_handler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(sig_num, &sigact, NULL); } } void print_stats() { if (0 != dd_count) fprintf(stderr, " remaining block count=%d\n", dd_count); fprintf(stderr, "%d+%d records in\n", in_full - in_partial, in_partial); fprintf(stderr, "%d+%d records out\n", out_full - out_partial, out_partial); } static void interrupt_handler(int sig) { struct sigaction sigact; sigact.sa_handler = SIG_DFL; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(sig, &sigact, NULL); fprintf(stderr, "Interrupted by signal,"); print_stats(); kill(getpid(), sig); } static void siginfo_handler(int sig) { fprintf(stderr, "Progress report, continuing ...\n"); print_stats(); } int dd_filetype(const char *filename) { struct stat st; size_t len = strlen(filename); if ((1 == len) && ('.' == filename[0])) return FT_DEV_NULL; if (stat(filename, &st) < 0) return FT_OTHER; if (S_ISCHR(st.st_mode)) { if ((MEM_MAJOR == major(st.st_rdev)) && (DEV_NULL_MINOR_NUM == minor(st.st_rdev))) return FT_DEV_NULL; if (RAW_MAJOR == major(st.st_rdev)) return FT_RAW; if (SCSI_GENERIC_MAJOR == major(st.st_rdev)) return FT_SG; if (SCSI_TAPE_MAJOR == major(st.st_rdev)) return FT_ST; } else if (S_ISBLK(st.st_mode)) return FT_BLOCK; return FT_OTHER; } int read_capacity(int sg_fd, int *num_sect, int *sect_sz) { int res; unsigned char rcCmdBlk[10] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char rcBuff[READ_CAP_REPLY_LEN]; unsigned char sense_b[64]; sg_io_hdr_t io_hdr; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(rcCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof(rcBuff); io_hdr.dxferp = rcBuff; io_hdr.cmdp = rcCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("read_capacity (SG_IO) error"); return -1; } res = sg_err_category3(&io_hdr); if (SG_ERR_CAT_MEDIA_CHANGED == res) return 2; /* probably have another go ... */ else if (SG_ERR_CAT_CLEAN != res) { sg_chk_n_print3("read capacity", &io_hdr); return -1; } *num_sect = 1 + ((rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) | rcBuff[3]); *sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) | (rcBuff[6] << 8) | rcBuff[7]; return 0; } /* Return of 0 -> success, -1 -> failure, 2 -> try again */ int sync_cache(int sg_fd) { int res; unsigned char scCmdBlk[10] = { SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char sense_b[64]; sg_io_hdr_t io_hdr; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(scCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.dxfer_len = 0; io_hdr.dxferp = NULL; io_hdr.cmdp = scCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("synchronize_cache (SG_IO) error"); return -1; } res = sg_err_category3(&io_hdr); if (SG_ERR_CAT_MEDIA_CHANGED == res) return 2; /* probably have another go ... */ else if (SG_ERR_CAT_CLEAN != res) { sg_chk_n_print3("synchronize cache", &io_hdr); return -1; } return 0; } int sg_build_scsi_cdb(unsigned char *cdbp, int cdb_sz, unsigned int blocks, unsigned int start_block, int write_true, int fua, int dpo) { int rd_opcode[] = { 0x8, 0x28, 0xa8, 0x88 }; int wr_opcode[] = { 0xa, 0x2a, 0xaa, 0x8a }; int sz_ind; memset(cdbp, 0, cdb_sz); if (dpo) cdbp[1] |= 0x10; if (fua) cdbp[1] |= 0x8; switch (cdb_sz) { case 6: sz_ind = 0; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); cdbp[1] = (unsigned char)((start_block >> 16) & 0x1f); cdbp[2] = (unsigned char)((start_block >> 8) & 0xff); cdbp[3] = (unsigned char)(start_block & 0xff); cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks; if (blocks > 256) { fprintf(stderr, ME "for 6 byte commands, maximum number of " "blocks is 256\n"); return 1; } if ((start_block + blocks - 1) & (~0x1fffff)) { fprintf(stderr, ME "for 6 byte commands, can't address blocks" " beyond %d\n", 0x1fffff); return 1; } if (dpo || fua) { fprintf(stderr, ME "for 6 byte commands, neither dpo nor fua" " bits supported\n"); return 1; } break; case 10: sz_ind = 1; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); cdbp[2] = (unsigned char)((start_block >> 24) & 0xff); cdbp[3] = (unsigned char)((start_block >> 16) & 0xff); cdbp[4] = (unsigned char)((start_block >> 8) & 0xff); cdbp[5] = (unsigned char)(start_block & 0xff); cdbp[7] = (unsigned char)((blocks >> 8) & 0xff); cdbp[8] = (unsigned char)(blocks & 0xff); if (blocks & (~0xffff)) { fprintf(stderr, ME "for 10 byte commands, maximum number of " "blocks is %d\n", 0xffff); return 1; } break; case 12: sz_ind = 2; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); cdbp[2] = (unsigned char)((start_block >> 24) & 0xff); cdbp[3] = (unsigned char)((start_block >> 16) & 0xff); cdbp[4] = (unsigned char)((start_block >> 8) & 0xff); cdbp[5] = (unsigned char)(start_block & 0xff); cdbp[6] = (unsigned char)((blocks >> 24) & 0xff); cdbp[7] = (unsigned char)((blocks >> 16) & 0xff); cdbp[8] = (unsigned char)((blocks >> 8) & 0xff); cdbp[9] = (unsigned char)(blocks & 0xff); break; case 16: sz_ind = 3; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); /* can't cope with block number > 32 bits (yet) */ cdbp[6] = (unsigned char)((start_block >> 24) & 0xff); cdbp[7] = (unsigned char)((start_block >> 16) & 0xff); cdbp[8] = (unsigned char)((start_block >> 8) & 0xff); cdbp[9] = (unsigned char)(start_block & 0xff); cdbp[10] = (unsigned char)((blocks >> 24) & 0xff); cdbp[11] = (unsigned char)((blocks >> 16) & 0xff); cdbp[12] = (unsigned char)((blocks >> 8) & 0xff); cdbp[13] = (unsigned char)(blocks & 0xff); break; default: fprintf(stderr, ME "expected cdb size of 6, 10, 12, or 16 but got" "=%d\n", cdb_sz); return 1; } return 0; } /* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM), 2 -> try again */ int sg_read(int sg_fd, unsigned char *buff, int blocks, int from_block, int bs, int cdbsz, int fua, int *diop) { unsigned char rdCmd[MAX_SCSI_CDBSZ]; unsigned char senseBuff[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) { fprintf(stderr, ME "bad rd cdb build, from_block=%d, blocks=%d\n", from_block, blocks); return -1; } memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = cdbsz; io_hdr.cmdp = rdCmd; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = bs * blocks; io_hdr.dxferp = buff; io_hdr.mx_sb_len = SENSE_BUFF_LEN; io_hdr.sbp = senseBuff; io_hdr.timeout = DEF_TIMEOUT; io_hdr.pack_id = from_block; if (diop && *diop) io_hdr.flags |= SG_FLAG_DIRECT_IO; if (ioctl(sg_fd, SG_IO, &io_hdr)) { if (ENOMEM == errno) return 1; perror("reading (SG_IO) on sg device, error"); return -1; } switch (sg_err_category3(&io_hdr)) { case SG_ERR_CAT_CLEAN: break; case SG_ERR_CAT_RECOVERED: fprintf(stderr, "Recovered error while reading block=%d, num=%d\n", from_block, blocks); break; case SG_ERR_CAT_MEDIA_CHANGED: return 2; default: sg_chk_n_print3("reading", &io_hdr); if (do_coe) { memset(buff, 0, bs * blocks); fprintf(stderr, ">> unable to read at blk=%d for " "%d bytes, use zeros\n", from_block, bs * blocks); return 0; /* fudge success */ } else return -1; } if (diop && *diop && ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) *diop = 0; /* flag that dio not done (completely) */ sum_of_resids += io_hdr.resid; #if SG_DEBUG fprintf(stderr, "duration=%u ms\n", io_hdr.duration); #endif return 0; } /* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM), 2 -> try again */ int sg_write(int sg_fd, unsigned char *buff, int blocks, int to_block, int bs, int cdbsz, int fua, int *diop) { unsigned char wrCmd[MAX_SCSI_CDBSZ]; unsigned char senseBuff[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) { fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n", to_block, blocks); return -1; } memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = cdbsz; io_hdr.cmdp = wrCmd; io_hdr.dxfer_direction = SG_DXFER_TO_DEV; io_hdr.dxfer_len = bs * blocks; io_hdr.dxferp = buff; io_hdr.mx_sb_len = SENSE_BUFF_LEN; io_hdr.sbp = senseBuff; io_hdr.timeout = DEF_TIMEOUT; io_hdr.pack_id = to_block; if (diop && *diop) io_hdr.flags |= SG_FLAG_DIRECT_IO; if (ioctl(sg_fd, SG_IO, &io_hdr)) { if (ENOMEM == errno) return 1; perror("writing (SG_IO) on sg device, error"); return -1; } switch (sg_err_category3(&io_hdr)) { case SG_ERR_CAT_CLEAN: break; case SG_ERR_CAT_RECOVERED: fprintf(stderr, "Recovered error while writing block=%d, num=%d\n", to_block, blocks); break; case SG_ERR_CAT_MEDIA_CHANGED: return 2; default: sg_chk_n_print3("writing", &io_hdr); if (do_coe) { fprintf(stderr, ">> ignored errors for out blk=%d for " "%d bytes\n", to_block, bs * blocks); return 0; /* fudge success */ } else return -1; } if (diop && *diop && ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) *diop = 0; /* flag that dio not done (completely) */ return 0; } int get_num(char *buf) { int res, num; char c; res = sscanf(buf, "%d%c", &num, &c); if (0 == res) return -1; else if (1 == res) return num; else { switch (c) { case 'c': case 'C': return num; case 'b': case 'B': return num * 512; case 'k': return num * 1024; case 'K': return num * 1000; case 'm': return num * 1024 * 1024; case 'M': return num * 1000000; case 'g': return num * 1024 * 1024 * 1024; case 'G': return num * 1000000000; default: fprintf(stderr, "unrecognized multiplier\n"); return -1; } } } int do_scsi_device_read_write(char *device) { int skip = 0; int seek = 0; int bs = 0; int ibs = 0; int obs = 0; int bpt = DEF_BLOCKS_PER_TRANSFER; char inf[INOUTF_SZ]; int in_type = FT_OTHER; char outf[INOUTF_SZ]; int out_type = FT_OTHER; int dio = 0; int dio_incomplete = 0; int do_time = 1; int do_odir = 1; int scsi_cdbsz = DEF_SCSI_CDBSZ; int fua_mode = 0; int do_sync = 1; int do_blk_sgio = 1; int do_append = 1; int res, t, buf_sz, dio_tmp; int infd, outfd, blocks; unsigned char *wrkBuff; unsigned char *wrkPos; int in_num_sect = 0; int out_num_sect = 0; int in_sect_sz, out_sect_sz; char ebuff[EBUFF_SZ]; int blocks_per; int req_count; struct timeval start_tm, end_tm; print_msg(TEST_BREAK, __FUNCTION__); strcpy(inf, "/dev/zero"); strcpy(outf, device); if (bs <= 0) { bs = DEF_BLOCK_SIZE; fprintf(stderr, "Assume default 'bs' (block size) of %d bytes\n", bs); } if ((ibs && (ibs != bs)) || (obs && (obs != bs))) { fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n"); usage(); return 1; } if ((skip < 0) || (seek < 0)) { fprintf(stderr, "skip and seek cannot be negative\n"); return 1; } if ((do_append > 0) && (seek > 0)) { fprintf(stderr, "Can't use both append and seek switches\n"); return 1; } #ifdef SG_DEBUG fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n", inf, skip, outf, seek, dd_count); #endif install_handler(SIGINT, interrupt_handler); install_handler(SIGQUIT, interrupt_handler); install_handler(SIGPIPE, interrupt_handler); install_handler(SIGUSR1, siginfo_handler); infd = STDIN_FILENO; outfd = STDOUT_FILENO; if (inf[0] && ('-' != inf[0])) { in_type = dd_filetype(inf); if ((FT_BLOCK & in_type) && do_blk_sgio) in_type |= FT_SG; if (FT_ST == in_type) { fprintf(stderr, ME "unable to use scsi tape device %s\n", inf); return 1; } else if (FT_SG & in_type) { if ((infd = open(inf, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for sg reading", inf); perror(ebuff); return 1; } t = bs * bpt; res = ioctl(infd, SG_SET_RESERVED_SIZE, &t); if (res < 0) perror(ME "SG_SET_RESERVED_SIZE error"); res = ioctl(infd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30000)) { if (FT_BLOCK & in_type) fprintf(stderr, ME "SG_IO unsupported on this block" " device\n"); else fprintf(stderr, ME "sg driver prior to 3.x.y\n"); return 1; } } else { if (do_odir && (FT_BLOCK == in_type)) infd = open(inf, O_RDONLY | O_DIRECT); else infd = open(inf, O_RDONLY); if (infd < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for reading", inf); perror(ebuff); return 1; } else if (skip > 0) { llse_loff_t offset = skip; offset *= bs; /* could exceed 32 bits here! */ if (llse_llseek(infd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to required position on %s", inf); perror(ebuff); return 1; } } } } if (outf[0] && ('-' != outf[0])) { out_type = dd_filetype(outf); if ((FT_BLOCK & out_type) && do_blk_sgio) out_type |= FT_SG; if (FT_ST == out_type) { fprintf(stderr, ME "unable to use scsi tape device %s\n", outf); return 1; } else if (FT_SG & out_type) { if ((outfd = open(outf, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for sg writing", outf); perror(ebuff); return 1; } t = bs * bpt; res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t); if (res < 0) perror(ME "SG_SET_RESERVED_SIZE error"); res = ioctl(outfd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30000)) { fprintf(stderr, ME "sg driver prior to 3.x.y\n"); return 1; } } else if (FT_DEV_NULL & out_type) outfd = -1; /* don't bother opening */ else { if (FT_RAW != out_type) { int flags = O_WRONLY | O_CREAT; if (do_odir && (FT_BLOCK == out_type)) flags |= O_DIRECT; else if (do_append) flags |= O_APPEND; if ((outfd = open(outf, flags, 0666)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for writing", outf); perror(ebuff); return 1; } } else { if ((outfd = open(outf, O_WRONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for raw writing", outf); perror(ebuff); return 1; } } if (seek > 0) { llse_loff_t offset = seek; offset *= bs; /* could exceed 32 bits here! */ if (llse_llseek(outfd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't seek to required position on %s", outf); perror(ebuff); return 1; } } } } if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) { fprintf(stderr, "Can't have both 'if' as stdin _and_ 'of' as stdout\n"); return 1; } if (dd_count < 0) { if (FT_SG & in_type) { res = read_capacity(infd, &in_num_sect, &in_sect_sz); if (2 == res) { fprintf(stderr, "Unit attention, media changed(in), continuing\n"); res = read_capacity(infd, &in_num_sect, &in_sect_sz); } if (0 != res) { fprintf(stderr, "Unable to read capacity on %s\n", inf); in_num_sect = -1; } else { if (in_num_sect > skip) in_num_sect -= skip; } } if (FT_SG & out_type) { res = read_capacity(outfd, &out_num_sect, &out_sect_sz); if (2 == res) { fprintf(stderr, "Unit attention, media changed(out), continuing\n"); res = read_capacity(outfd, &out_num_sect, &out_sect_sz); } if (0 != res) { fprintf(stderr, "Unable to read capacity on %s\n", outf); out_num_sect = -1; } else { if (out_num_sect > seek) out_num_sect -= seek; } } #ifdef SG_DEBUG fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n", dd_count, in_num_sect, out_num_sect); #endif if (in_num_sect > 0) { if (out_num_sect > 0) dd_count = (in_num_sect > out_num_sect) ? out_num_sect : in_num_sect; else dd_count = in_num_sect; } else dd_count = out_num_sect; } if (dd_count < 0) { fprintf(stderr, "Couldn't calculate count, please give one\n"); return 1; } if (dio || do_odir || (FT_RAW == in_type) || (FT_RAW == out_type)) { size_t psz = getpagesize(); wrkBuff = malloc(bs * bpt + psz); if (0 == wrkBuff) { fprintf(stderr, "Not enough user memory for raw\n"); return 1; } wrkPos = (unsigned char *)(((unsigned long)wrkBuff + psz - 1) & (~(psz - 1))); } else { wrkBuff = malloc(bs * bpt); if (0 == wrkBuff) { fprintf(stderr, "Not enough user memory\n"); return 1; } wrkPos = wrkBuff; } blocks_per = bpt; #ifdef SG_DEBUG fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n", dd_count, blocks_per); #endif if (do_time) { start_tm.tv_sec = 0; start_tm.tv_usec = 0; gettimeofday(&start_tm, NULL); } req_count = dd_count; while (dd_count > 0) { blocks = (dd_count > blocks_per) ? blocks_per : dd_count; if (FT_SG & in_type) { int fua = fua_mode & 2; dio_tmp = dio; res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, fua, &dio_tmp); if (1 == res) { /* ENOMEM, find what's available+try that */ if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) { perror("RESERVED_SIZE ioctls failed"); break; } blocks_per = (buf_sz + bs - 1) / bs; blocks = blocks_per; fprintf(stderr, "Reducing read to %d blocks per loop\n", blocks_per); res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, fua, &dio_tmp); } else if (2 == res) { fprintf(stderr, "Unit attention, media changed, continuing (r)\n"); res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, fua, &dio_tmp); } if (0 != res) { fprintf(stderr, "sg_read failed, skip=%d\n", skip); break; } else { in_full += blocks; if (dio && (0 == dio_tmp)) dio_incomplete++; } } else { while (((res = read(infd, wrkPos, blocks * bs)) < 0) && (EINTR == errno)) ; if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%d ", skip); perror(ebuff); break; } else if (res < blocks * bs) { dd_count = 0; blocks = res / bs; if ((res % bs) > 0) { blocks++; in_partial++; } } in_full += blocks; } if (FT_SG & out_type) { int fua = fua_mode & 1; dio_tmp = dio; res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz, fua, &dio_tmp); if (1 == res) { /* ENOMEM, find what's available+try that */ if (ioctl(outfd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) { perror("RESERVED_SIZE ioctls failed"); break; } blocks_per = (buf_sz + bs - 1) / bs; blocks = blocks_per; fprintf(stderr, "Reducing write to %d blocks per loop\n", blocks); res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz, fua, &dio_tmp); } else if (2 == res) { fprintf(stderr, "Unit attention, media changed, continuing (w)\n"); res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz, fua, &dio_tmp); } else if (0 != res) { fprintf(stderr, "sg_write failed, seek=%d\n", seek); break; } else { out_full += blocks; if (dio && (0 == dio_tmp)) dio_incomplete++; } } else if (FT_DEV_NULL & out_type) out_full += blocks; /* act as if written out without error */ else { while (((res = write(outfd, wrkPos, blocks * bs)) < 0) && (EINTR == errno)) ; if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%d ", seek); perror(ebuff); break; } else if (res < blocks * bs) { fprintf(stderr, "output file probably full, seek=%d ", seek); blocks = res / bs; out_full += blocks; if ((res % bs) > 0) out_partial++; break; } else out_full += blocks; } if (dd_count > 0) dd_count -= blocks; skip += blocks; seek += blocks; } if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { struct timeval res_tm; double a, b; gettimeofday(&end_tm, NULL); res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; if (res_tm.tv_usec < 0) { --res_tm.tv_sec; res_tm.tv_usec += 1000000; } a = res_tm.tv_sec; a += (0.000001 * res_tm.tv_usec); b = (double)bs *(req_count - dd_count); printf("time to transfer data was %d.%06d secs", (int)res_tm.tv_sec, (int)res_tm.tv_usec); if ((a > 0.00001) && (b > 511)) printf(", %.2f MB/sec\n", b / (a * 1000000.0)); else printf("\n"); } if (do_sync) { if (FT_SG & out_type) { fprintf(stderr, ">> Synchronizing cache on %s\n", outf); res = sync_cache(outfd); if (2 == res) { fprintf(stderr, "Unit attention, media changed(in), continuing\n"); res = sync_cache(outfd); } if (0 != res) fprintf(stderr, "Unable to synchronize cache\n"); } } free(wrkBuff); if (STDIN_FILENO != infd) close(infd); if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type)) close(outfd); res = 0; if (0 != dd_count) { fprintf(stderr, "Some error occurred,"); res = 2; } print_stats(); if (dio_incomplete) { int fd; char c; fprintf(stderr, ">> Direct IO requested but incomplete %d times\n", dio_incomplete); if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { if (1 == read(fd, &c, 1)) { if ('0' == c) fprintf(stderr, ">>> %s set to '0' but should be set " "to '1' for direct IO\n", proc_allow_dio); } close(fd); } } if (sum_of_resids) fprintf(stderr, ">> Non-zero sum of residual counts=%d\n", sum_of_resids); return res; } /* Returns 0 when successful, else -1 */ static int do_scsi_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, void *resp, int mx_resp_len, int noisy) { int res; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; if (cmddt) inqCmdBlk[1] |= 2; if (evpd) inqCmdBlk[1] |= 1; inqCmdBlk[2] = (unsigned char)pg_op; inqCmdBlk[4] = (unsigned char)mx_resp_len; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(inqCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO (inquiry) error"); return -1; } res = sg_err_category3(&io_hdr); switch (res) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: return 0; default: if (noisy) { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "Inquiry error, CmdDt=%d, " "EVPD=%d, page_opcode=%x ", cmddt, evpd, pg_op); sg_chk_n_print3(ebuff, &io_hdr); } return -1; } } int do_scsi_inquiry(char *device, int hex_flag) { int sg_fd, k, j, num, len, act_len; int support_num; char *file_name = 0; char buff[MX_ALLOC_LEN + 1]; unsigned char rsp_buff[MX_ALLOC_LEN + 1]; unsigned int num_opcode = 0; int do_evpd = 0; int do_cmddt = 0; int do_cmdlst = 0; int do_hex = 0; int do_raw = 0; int do_pci = 0; int do_36 = 0; int oflags = O_RDONLY | O_NONBLOCK; int ansi_version = 0; int ret = 0; file_name = device; if (hex_flag) { do_hex = TRUE; print_msg(TEST_BREAK, __FUNCTION__); } else { do_pci = TRUE; } if (do_pci) oflags = O_RDWR | O_NONBLOCK; if ((sg_fd = open(file_name, oflags)) < 0) { snprintf(ebuff, EBUFF_SZ, "sg_inq: error opening file: %s", file_name); perror(ebuff); return 1; } /* Just to be safe, check we have a new sg device by trying an ioctl */ if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { fprintf(stderr, "sg_inq: %s doesn't seem to be a version 3 sg device\n", file_name); close(sg_fd); return 1; } memset(rsp_buff, 0, MX_ALLOC_LEN + 1); if (!(do_cmddt || do_evpd)) { if (!do_raw) printf("standard INQUIRY:\n"); if (num_opcode > 0) printf (" <<given opcode or page_code is being ignored>>\n"); if (0 == do_scsi_inq(sg_fd, 0, 0, 0, rsp_buff, 36, 1)) { len = rsp_buff[4] + 5; ansi_version = rsp_buff[2] & 0x7; if ((len > 36) && (len < 256) && (!do_36)) { if (do_scsi_inq (sg_fd, 0, 0, 0, rsp_buff, len, 1)) { fprintf(stderr, "second INQUIRY (%d byte) failed\n", len); return 1; } if (len != (rsp_buff[4] + 5)) { fprintf(stderr, "strange, twin INQUIRYs yield different " "'additional length'\n"); ret = 2; } } if (do_36) { act_len = len; len = 36; } else act_len = len; if (do_hex) dStrHex((const char *)rsp_buff, len, 0); else { printf (" PQual=%d, Device type=%d, RMB=%d, ANSI version=%d, ", (rsp_buff[0] & 0xe0) >> 5, rsp_buff[0] & 0x1f, ! !(rsp_buff[1] & 0x80), ansi_version); printf("[full version=0x%02x]\n", (unsigned int)rsp_buff[2]); printf (" AERC=%d, TrmTsk=%d, NormACA=%d, HiSUP=%d, " "Resp data format=%d, SCCS=%d\n", ! !(rsp_buff[3] & 0x80), ! !(rsp_buff[3] & 0x40), ! !(rsp_buff[3] & 0x20), ! !(rsp_buff[3] & 0x10), rsp_buff[3] & 0x0f, ! !(rsp_buff[5] & 0x80)); printf (" BQue=%d, EncServ=%d, MultiP=%d, MChngr=%d, " "ACKREQQ=%d, ", ! !(rsp_buff[6] & 0x80), ! !(rsp_buff[6] & 0x40), ! !(rsp_buff[6] & 0x10), ! !(rsp_buff[6] & 0x08), ! !(rsp_buff[6] & 0x04)); printf("Addr16=%d\n RelAdr=%d, ", ! !(rsp_buff[6] & 0x01), ! !(rsp_buff[7] & 0x80)); printf ("WBus16=%d, Sync=%d, Linked=%d, TranDis=%d, ", ! !(rsp_buff[7] & 0x20), ! !(rsp_buff[7] & 0x10), ! !(rsp_buff[7] & 0x08), ! !(rsp_buff[7] & 0x04)); printf("CmdQue=%d\n", ! !(rsp_buff[7] & 0x02)); if (len > 56) printf (" Clocking=0x%x, QAS=%d, IUS=%d\n", (rsp_buff[56] & 0x0c) >> 2, ! !(rsp_buff[56] & 0x2), ! !(rsp_buff[56] & 0x1)); if (act_len == len) printf(" length=%d (0x%x)", len, len); else printf (" length=%d (0x%x), but only read 36 bytes", len, len); if ((ansi_version >= 2) && (len < 36)) printf (" [for SCSI>=2, len>=36 is expected]\n"); else printf("\n"); if (len <= 8) printf (" Inquiry response length=%d\n, no vendor, " "product or revision data\n", len); else { if (len < 36) rsp_buff[len] = '\0'; memcpy(buff, &rsp_buff[8], 8); buff[8] = '\0'; printf(" Vendor identification: %s\n", buff); if (len <= 16) printf (" Product identification: <none>\n"); else { memcpy(buff, &rsp_buff[16], 16); buff[16] = '\0'; printf (" Product identification: %s\n", buff); } if (len <= 32) printf (" Product revision level: <none>\n"); else { memcpy(buff, &rsp_buff[32], 4); buff[4] = '\0'; printf (" Product revision level: %s\n", buff); } } } if (!do_raw && (0 == do_scsi_inq(sg_fd, 0, 1, 0x80, rsp_buff, MX_ALLOC_LEN, 0))) { len = rsp_buff[3]; if (len > 0) { memcpy(buff, rsp_buff + 4, len); buff[len] = '\0'; printf(" Product serial number: %s\n", buff); } } } else { printf("36 byte INQUIRY failed\n"); return 1; } } else if (do_cmddt) { int reserved_cmddt; char op_name[128]; if (do_cmdlst) { printf("Supported command list:\n"); for (k = 0; k < 256; ++k) { if (0 == do_scsi_inq(sg_fd, 1, 0, k, rsp_buff, MX_ALLOC_LEN, 1)) { support_num = rsp_buff[1] & 7; reserved_cmddt = rsp_buff[4]; if ((3 == support_num) || (5 == support_num)) { num = rsp_buff[5]; for (j = 0; j < num; ++j) printf(" %.2x", (int)rsp_buff[6 + j]); if (5 == support_num) printf (" [vendor specific manner (5)]"); sg_get_command_name((unsigned char)k, sizeof (op_name) - 1, op_name); op_name[sizeof(op_name) - 1] = '\0'; printf(" %s\n", op_name); } else if ((4 == support_num) || (6 == support_num)) printf (" opcode=0x%.2x vendor specific (%d)\n", k, support_num); else if ((0 == support_num) && (reserved_cmddt > 0)) { printf (" opcode=0x%.2x ignored cmddt bit, " "given standard INQUIRY response, stop\n", k); break; } } else { fprintf(stderr, "CmdDt INQUIRY on opcode=0x%.2x: failed\n", k); break; } } } else { if (!do_raw) { printf("CmdDt INQUIRY, opcode=0x%.2x: [", num_opcode); sg_get_command_name((unsigned char)num_opcode, sizeof(op_name) - 1, op_name); op_name[sizeof(op_name) - 1] = '\0'; printf("%s]\n", op_name); } if (0 == do_scsi_inq(sg_fd, 1, 0, num_opcode, rsp_buff, MX_ALLOC_LEN, 1)) { len = rsp_buff[5] + 6; reserved_cmddt = rsp_buff[4]; if (do_hex) dStrHex((const char *)rsp_buff, len, 0); else { const char *desc_p; int prnt_cmd = 0; support_num = rsp_buff[1] & 7; num = rsp_buff[5]; switch (support_num) { case 0: if (0 == reserved_cmddt) desc_p = "no data available"; else desc_p = "ignored cmddt bit, standard INQUIRY " "response"; break; case 1: desc_p = "not supported"; break; case 2: desc_p = "reserved (2)"; break; case 3: desc_p = "supported as per standard"; prnt_cmd = 1; break; case 4: desc_p = "vendor specific (4)"; break; case 5: desc_p = "supported in vendor specific way"; prnt_cmd = 1; break; case 6: desc_p = "vendor specific (6)"; break; case 7: desc_p = "reserved (7)"; break; default: desc_p = "impossible value > 7"; break; } if (prnt_cmd) { printf(" Support field: %s [", desc_p); for (j = 0; j < num; ++j) printf(" %.2x", (int)rsp_buff[6 + j]); printf(" ]\n"); } else printf(" Support field: %s\n", desc_p); } } else { fprintf(stderr, "CmdDt INQUIRY on opcode=0x%.2x: failed\n", num_opcode); return 1; } } } else if (do_evpd) { if (!do_raw) printf("EVPD INQUIRY, page code=0x%.2x:\n", num_opcode); if (0 == do_scsi_inq(sg_fd, 0, 1, num_opcode, rsp_buff, MX_ALLOC_LEN, 1)) { len = rsp_buff[3] + 4; if (num_opcode != rsp_buff[1]) printf ("non evpd respone; probably a STANDARD INQUIRY " "response\n"); else { if (!do_hex) printf(" Only hex output supported\n"); dStrHex((const char *)rsp_buff, len, 0); } } else { fprintf(stderr, "EVPD INQUIRY, page code=0x%.2x: failed\n", num_opcode); return 1; } } if (do_pci) { unsigned char slot_name[16]; printf("\n"); memset(slot_name, '\0', sizeof(slot_name)); if (ioctl(sg_fd, SCSI_IOCTL_GET_PCI, slot_name) < 0) { if (EINVAL == errno) printf ("ioctl(SCSI_IOCTL_GET_PCI) not supported by this " "kernel\n"); else if (ENXIO == errno) printf ("associated adapter not a PCI device?\n"); else perror("ioctl(SCSI_IOCTL_GET_PCI) failed"); } else printf("PCI:slot_name: %s\n", slot_name); } close(sg_fd); return ret; } int show_scsi_maps() { int sg_fd, res, k; int do_numeric = NUMERIC_SCAN_DEF; int do_all_s = 1; int do_sd = 0; int do_st = 0; int do_osst = 0; int do_sr = 0; int do_scd = 0; int do_extra = 1; int do_inquiry = 0; char fname[64]; int num_errors = 0; int num_silent = 0; int eacces_err = 0; int last_sg_ind = -1; struct stat stat_buf; print_msg(TEST_BREAK, __FUNCTION__); if (stat(devfs_id, &stat_buf) == 0) printf("# Note: the devfs pseudo file system is present\n"); for (k = 0, res = 0; (k < MAX_SG_DEVS) && (num_errors < MAX_ERRORS); ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) { if (res < 0) { snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname); perror("sg_map: close error"); return 1; } make_dev_name(fname, "/dev/sg", k, do_numeric); sg_fd = open(fname, O_RDONLY | O_NONBLOCK); if (sg_fd < 0) { if (EBUSY == errno) { map_arr[k].active = -2; continue; } else if ((ENODEV == errno) || (ENOENT == errno) || (ENXIO == errno)) { ++num_errors; ++num_silent; map_arr[k].active = -1; continue; } else { if (EACCES == errno) eacces_err = 1; snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname); perror(ebuff); ++num_errors; continue; } } res = ioctl(sg_fd, SG_GET_SCSI_ID, &map_arr[k].sg_dat); if (res < 0) { snprintf(ebuff, EBUFF_SZ, "device %s failed on sg ioctl, skip", fname); perror(ebuff); ++num_errors; continue; } if (do_inquiry) { char buff[36]; if (0 == do_scsi_inq(sg_fd, 0, 0, 0, buff, sizeof(buff), 1)) { memcpy(map_arr[k].vendor, &buff[8], 8); memcpy(map_arr[k].product, &buff[16], 16); memcpy(map_arr[k].revision, &buff[32], 4); } } map_arr[k].active = 1; map_arr[k].oth_dev_num = -1; last_sg_ind = k; } if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) { printf("Stopping because there are too many error\n"); if (eacces_err) printf(" root access may be required\n"); return 1; } if (last_sg_ind < 0) { printf("Stopping because no sg devices found\n"); } if (do_all_s || do_sd) scan_dev_type("/dev/sd", MAX_SD_DEVS, 0, LIN_DEV_TYPE_SD, last_sg_ind); if (do_all_s || do_sr) scan_dev_type("/dev/sr", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SR, last_sg_ind); if (do_all_s || do_scd) scan_dev_type("/dev/scd", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SCD, last_sg_ind); if (do_all_s || do_st) scan_dev_type("/dev/st", MAX_ST_DEVS, 1, LIN_DEV_TYPE_ST, last_sg_ind); if (do_all_s || do_osst) scan_dev_type("/dev/osst", MAX_OSST_DEVS, 1, LIN_DEV_TYPE_OSST, last_sg_ind); for (k = 0; k <= last_sg_ind; ++k) { make_dev_name(fname, "/dev/sg", k, do_numeric); printf("%s", fname); switch (map_arr[k].active) { case -2: printf(do_extra ? " -2 -2 -2 -2 -2" : " busy"); break; case -1: printf(do_extra ? " -1 -1 -1 -1 -1" : " not present"); break; case 0: printf(do_extra ? " -3 -3 -3 -3 -3" : " some error\n"); break; case 1: if (do_extra) printf(" %d %d %d %d %d", map_arr[k].sg_dat.host_no, map_arr[k].sg_dat.channel, map_arr[k].sg_dat.scsi_id, map_arr[k].sg_dat.lun, map_arr[k].sg_dat.scsi_type); switch (map_arr[k].lin_dev_type) { case LIN_DEV_TYPE_SD: make_dev_name(fname, "/dev/sd", map_arr[k].oth_dev_num, 0); printf(" %s", fname); break; case LIN_DEV_TYPE_ST: make_dev_name(fname, "/dev/st", map_arr[k].oth_dev_num, 1); printf(" %s", fname); break; case LIN_DEV_TYPE_OSST: make_dev_name(fname, "/dev/osst", map_arr[k].oth_dev_num, 1); printf(" %s", fname); break; case LIN_DEV_TYPE_SR: make_dev_name(fname, "/dev/sr", map_arr[k].oth_dev_num, 1); printf(" %s", fname); break; case LIN_DEV_TYPE_SCD: make_dev_name(fname, "/dev/scd", map_arr[k].oth_dev_num, 1); printf(" %s", fname); break; default: break; } if (do_inquiry) printf(" %.8s %.16s %.4s", map_arr[k].vendor, map_arr[k].product, map_arr[k].revision); break; default: printf(" bad logic\n"); break; } printf("\n"); } return 0; } static int find_dev_in_sg_arr(My_scsi_idlun * my_idlun, int host_no, int last_sg_ind) { int k; struct sg_scsi_id *sidp; for (k = 0; k <= last_sg_ind; ++k) { sidp = &(map_arr[k].sg_dat); if ((host_no == sidp->host_no) && ((my_idlun->dev_id & 0xff) == sidp->scsi_id) && (((my_idlun->dev_id >> 8) & 0xff) == sidp->lun) && (((my_idlun->dev_id >> 16) & 0xff) == sidp->channel)) return k; } return -1; } static void scan_dev_type(const char *leadin, int max_dev, int do_numeric, int lin_dev_type, int last_sg_ind) { int k, res, ind, sg_fd = 0; int num_errors = 0; int num_silent = 0; int host_no = -1; int nonMappedDevicesPresent = FALSE; My_scsi_idlun my_idlun; char fname[64]; for (k = 0, res = 0; (k < max_dev) && (num_errors < MAX_ERRORS); ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) { /* ignore close() errors */ #if 0 if (res < 0) { snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname); perror("sg_map: close error"); #ifndef IGN_CLOSE_ERR return; #else ++num_errors; sg_fd = 0; #endif } #endif make_dev_name(fname, leadin, k, do_numeric); #ifdef DEBUG printf("Trying %s: ", fname); #endif sg_fd = open(fname, O_RDONLY | O_NONBLOCK); if (sg_fd < 0) { #ifdef DEBUG printf("ERROR %i\n", errno); #endif if (EBUSY == errno) { printf("Device %s is busy\n", fname); ++num_errors; continue; } else if ((ENODEV == errno) || (ENOENT == errno) || (ENXIO == errno)) { ++num_errors; ++num_silent; continue; } else { snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname); perror(ebuff); ++num_errors; continue; } } res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun); if (res < 0) { snprintf(ebuff, EBUFF_SZ, "device %s failed on scsi ioctl(idlun), skip", fname); perror(ebuff); ++num_errors; #ifdef DEBUG printf("Couldn't get IDLUN!\n"); #endif continue; } res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no); if (res < 0) { snprintf(ebuff, EBUFF_SZ, "device %s failed on scsi ioctl(bus_number), skip", fname); perror(ebuff); ++num_errors; #ifdef DEBUG printf("Couldn't get BUS!\n"); #endif continue; } #ifdef DEBUG printf("%i(%x) %i %i %i %i\n", host_no, my_idlun.host_unique_id, (my_idlun.dev_id >> 24) & 0xff, (my_idlun.dev_id >> 16) & 0xff, (my_idlun.dev_id >> 8) & 0xff, my_idlun.dev_id & 0xff); #endif ind = find_dev_in_sg_arr(&my_idlun, host_no, last_sg_ind); if (ind >= 0) { map_arr[ind].oth_dev_num = k; map_arr[ind].lin_dev_type = lin_dev_type; } else if (ind != -1) { printf ("Strange, could not find device %s mapped to sg device error %d??\n", fname, ind); } else { nonMappedDevicesPresent = TRUE; } } if (nonMappedDevicesPresent) { printf("Unmapped Devices found...\n\n"); } } /* Returns 0 when successful, else -1 */ static int do_simple_inq(int sg_fd, void *resp, int mx_resp_len, int noisy) { int res; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; inqCmdBlk[4] = (unsigned char)mx_resp_len; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(inqCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO (inquiry) error"); return -1; } res = sg_err_category3(&io_hdr); switch (res) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: return 0; default: if (noisy) { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "Inquiry error "); sg_chk_n_print3(ebuff, &io_hdr); } return -1; } } static int do_modes(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code, void *resp, int mx_resp_len, int noisy, int mode6) { int res; unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = { MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0); modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff); if (mx_resp_len > (mode6 ? 0xff : 0xffff)) { printf(ME "mx_resp_len too big\n"); return -1; } if (mode6) { modesCmdBlk[0] = MODE_SENSE6_CMD; modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); } else { modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); } memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); memset(sense_b, 0, sizeof(sense_b)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN; io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = modesCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO (mode sense) error"); return -1; } res = sg_err_category3(&io_hdr); switch (res) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: return 0; default: if (noisy) { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d " "pc=%d page_code=%x sub_page_code=%x\n ", dbd, pc, pg_code, sub_pg_code); sg_chk_n_print3(ebuff, &io_hdr); } if ((0x70 == (0x7f & sense_b[0])) && (0x20 == sense_b[12]) && (0x0 == sense_b[13])) { if (mode6) fprintf(stderr, ">>>>>> drop '-6' switch and try again with " "a 10 byte MODE SENSE\n"); else fprintf(stderr, ">>>>>> add '-6' switch and try again with " "a 6 byte MODE SENSE\n"); } return -1; } } const char *scsi_ptype_strs[] = { "disk", "tape", "printer", "processor", "write once optical disk", "cd/dvd", "scanner", "optical memory device", "medium changer", "communications", "graphics", "graphics", "storage array controller", "enclosure services device", "simplified direct access device", "optical card reader/writer device", }; const char *get_ptype_str(int scsi_ptype) { int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]); return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : ""; } static struct page_code_desc pc_desc_all[] = { {0x0, "Unit Attention condition [vendor: page format not required]"}, {0x2, "Disconnect-Reconnect"}, {0xa, "Control"}, {0x15, "Extended"}, {0x16, "Extended device-type specific"}, {0x18, "Protocol specific LUN"}, {0x19, "Protocol specific port"}, {0x1a, "Power condition"}, {0x1c, "Informational exceptions control"}, {0x3f, "[yields all supported pages]"}, }; static struct page_code_desc pc_desc_disk[] = { {0x1, "Read-Write error recovery"}, {0x3, "Format"}, {0x4, "Rigid disk geometry"}, {0x5, "Flexible geometry"}, {0x7, "Verify error recovery"}, {0x8, "Caching"}, {0x9, "Peripheral device (spc-2 ?)"}, {0xb, "Medium types supported"}, {0xc, "Notch and partition"}, {0xd, "Power condition (obsolete)"}, {0x10, "XOR control"}, }; static struct page_code_desc pc_desc_tape[] = { {0xf, "Data Compression"}, {0x10, "Device config"}, {0x11, "Medium Partition [1]"}, {0x12, "Medium Partition [2]"}, {0x13, "Medium Partition [3]"}, {0x14, "Medium Partition [4]"}, {0x1c, "Informational exceptions control (tape version)"}, }; static struct page_code_desc pc_desc_cddvd[] = { {0x1, "Read-Write error recovery"}, {0x3, "MRW"}, {0x5, "Write parameters"}, {0xd, "CD device parameters (obsolete)"}, {0xe, "CD audio"}, {0x1a, "Power condition"}, {0x1c, "Fault/failure reporting control"}, {0x1d, "Timeout and protect"}, {0x2a, "MM capabilities and mechanical status (obsolete)"}, }; static struct page_code_desc pc_desc_smc[] = { {0x1d, "Element address assignment"}, {0x1e, "Transport geometry parameters"}, {0x1f, "Device capabilities"}, }; static struct page_code_desc pc_desc_scc[] = { {0x1b, "LUN mapping"}, }; static struct page_code_desc pc_desc_ses[] = { {0x14, "Enclosure services management"}, }; struct page_code_desc *find_mode_page_table(int scsi_ptype, int *size) { switch (scsi_ptype) { case 0: /* disk (direct access) type devices */ case 4: case 7: case 0xe: *size = sizeof(pc_desc_disk) / sizeof(pc_desc_disk[0]); return &pc_desc_disk[0]; case 1: /* tape devices */ case 2: *size = sizeof(pc_desc_tape) / sizeof(pc_desc_tape[0]); return &pc_desc_tape[0]; case 5: /* cd/dvd devices */ *size = sizeof(pc_desc_cddvd) / sizeof(pc_desc_cddvd[0]); return &pc_desc_cddvd[0]; case 8: /* medium changer devices */ *size = sizeof(pc_desc_smc) / sizeof(pc_desc_smc[0]); return &pc_desc_smc[0]; case 0xc: /* storage array devices */ *size = sizeof(pc_desc_scc) / sizeof(pc_desc_scc[0]); return &pc_desc_scc[0]; case 0xd: /* enclosure services devices */ *size = sizeof(pc_desc_ses) / sizeof(pc_desc_ses[0]); return &pc_desc_ses[0]; } *size = 0; return NULL; } const char *find_page_code_desc(int page_num, int scsi_ptype) { int k; int num; const struct page_code_desc *pcdp; pcdp = find_mode_page_table(scsi_ptype, &num); if (pcdp) { for (k = 0; k < num; ++k, ++pcdp) { if (page_num == pcdp->page_code) return pcdp->desc; else if (page_num < pcdp->page_code) break; } } pcdp = &pc_desc_all[0]; num = sizeof(pc_desc_all) / sizeof(pc_desc_all[0]); for (k = 0; k < num; ++k, ++pcdp) { if (page_num == pcdp->page_code) return pcdp->desc; else if (page_num < pcdp->page_code) break; } return NULL; } static void list_page_codes(int scsi_ptype) { int k; int num = sizeof(pc_desc_all) / sizeof(pc_desc_all[0]); const struct page_code_desc *pcdp = &pc_desc_all[0]; int num_ptype; const struct page_code_desc *pcd_ptypep; pcd_ptypep = find_mode_page_table(scsi_ptype, &num_ptype); printf("Page_Code Description\n"); for (k = 0; k < 0x3f; ++k) { if (pcd_ptypep && (num_ptype > 0)) { if (k == pcd_ptypep->page_code) { printf(" 0x%02x %s\n", pcd_ptypep->page_code, pcd_ptypep->desc); ++pcd_ptypep; --num_ptype; continue; } else if (k > pcd_ptypep->page_code) { pcd_ptypep++; --num_ptype; } } if (pcdp && (num > 0)) { if (k == pcdp->page_code) { printf(" 0x%02x %s\n", pcdp->page_code, pcdp->desc); ++pcdp; --num; continue; } else if (k > pcdp->page_code) { pcdp++; --num; } } } } int show_scsi_modes(char *device) { int sg_fd, k, num, len, md_len, bd_len, longlba, page_num; char *file_name = 0; char ebuff[EBUFF_SZ]; const char *descp; unsigned char rsp_buff[MODE_ALLOC_LEN]; int rsp_buff_size = MODE_ALLOC_LEN; int pg_code = 0; int sub_pg_code = 0; int pc = 0; int do_all = 1; int do_dbd = 0; int do_hex = 0; int do_mode6 = 0; /* Use MODE SENSE(6) instead of MODE SENSE(10) */ int oflags = O_RDONLY | O_NONBLOCK; struct sg_scsi_id a_sid; int scsi_ptype, density_code_off; unsigned char *ucp; unsigned char uc; print_msg(TEST_BREAK, __FUNCTION__); file_name = device; list_page_codes(0); /* The 6 bytes command only allows up to 255 bytes of response data */ if (do_mode6) rsp_buff_size = 255; if ((sg_fd = open(file_name, oflags)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", file_name); perror(ebuff); return 1; } /* Just to be safe, check we have a new sg device by trying an ioctl */ if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { printf(ME "%s doesn't seem to be a version 3 sg device\n", file_name); close(sg_fd); return 1; } if (ioctl(sg_fd, SG_GET_SCSI_ID, &a_sid) < 0) { unsigned char inqBuff[36]; if (do_simple_inq(sg_fd, inqBuff, sizeof(inqBuff), 1)) { printf(ME "%s doesn't respond to a SCSI INQUIRY\n", file_name); close(sg_fd); return 1; } scsi_ptype = inqBuff[0] & 0x1f; /* fetch peripheral device type */ } else scsi_ptype = a_sid.scsi_type; printf(" SCSI peripheral type: %s [0x%x] (from INQUIRY)\n", get_ptype_str(scsi_ptype), scsi_ptype); if (do_all) pg_code = MODE_CODE_ALL; if (0 == do_modes(sg_fd, do_dbd, pc, pg_code, sub_pg_code, rsp_buff, rsp_buff_size, 1, do_mode6)) { int medium_type, specific, headerlen; printf("Mode parameter header from %s byte MODE SENSE:\n", (do_mode6 ? "6" : "10")); if (do_mode6) { headerlen = 4; if (do_hex) dStrHex((const char *)rsp_buff, headerlen, 1); md_len = rsp_buff[0] + 1; bd_len = rsp_buff[3]; medium_type = rsp_buff[1]; specific = rsp_buff[2]; longlba = 0; /* what is this field? */ } else { headerlen = 8; md_len = (rsp_buff[0] << 8) + rsp_buff[1] + 2; bd_len = (rsp_buff[6] << 8) + rsp_buff[7]; medium_type = rsp_buff[2]; specific = rsp_buff[3]; longlba = rsp_buff[4] & 1; } if (do_hex) dStrHex((const char *)rsp_buff, headerlen, 1); printf(" Mode data length=%d, medium type=0x%.2x, specific" " param=0x%.2x, longlba=%d\n", md_len, medium_type, specific, longlba); if (md_len > rsp_buff_size) { printf ("Only fetched %d bytes of response, truncate output\n", rsp_buff_size); md_len = rsp_buff_size; if (bd_len + headerlen > rsp_buff_size) bd_len = rsp_buff_size - headerlen; } printf(" Block descriptor length=%d\n", bd_len); if (bd_len > 0) { len = 8; density_code_off = 0; num = bd_len; if (longlba) { printf("> longlba block descriptors:\n"); len = 16; density_code_off = 8; } else if (0 == scsi_ptype) { printf ("> Direct access device block descriptors:\n"); density_code_off = 4; } else printf ("> General mode parameter block descriptors:\n"); ucp = rsp_buff + headerlen; while (num > 0) { printf(" Density code=0x%x\n", *(ucp + density_code_off)); dStrHex((const char *)ucp, len, 1); ucp += len; num -= len; } printf("\n"); } ucp = rsp_buff + bd_len + headerlen; /* start of mode page(s) */ md_len -= bd_len + headerlen; /* length of mode page(s) */ while (md_len > 0) { /* got mode page(s) */ uc = *ucp; page_num = ucp[0] & 0x3f; if (do_hex) descp = NULL; else { descp = find_page_code_desc(page_num, scsi_ptype); if (NULL == descp) snprintf(ebuff, EBUFF_SZ, "vendor[0x%x]", page_num); } if (uc & 0x40) { len = (ucp[2] << 8) + ucp[3] + 4; if (do_hex) printf (">> page_code=0x%x, subpage_code=0x%x, " "page_control=%d\n", page_num, ucp[1], pc); else printf (">> page_code: %s, subpage_code=0x%x, " "page_control: %s\n", (descp ? descp : ebuff), ucp[1], pg_control_str_arr[pc]); } else { len = ucp[1] + 2; if (do_hex) printf (">> page_code=0x%x, page_control=%d\n", page_num, pc); else printf (">> page_code: %s, page_control: %s\n", (descp ? descp : ebuff), pg_control_str_arr[pc]); } dStrHex((const char *)ucp, len, 1); ucp += len; md_len -= len; } } close(sg_fd); return 0; } int do_scsi_read_buffer(char *device) { int sg_fd, res; unsigned int k, num; unsigned char rbCmdBlk[RB_CMD_LEN]; unsigned char *rbBuff = NULL; void *rawp = NULL; unsigned char sense_buffer[32]; int buf_capacity = 0; int do_quick = 0; int do_dio = 0; int do_mmap = 1; int do_time = 0; int buf_size = 0; unsigned int total_size_mb = RB_MB_TO_READ; char *file_name = 0; size_t psz = getpagesize(); int dio_incomplete = 0; sg_io_hdr_t io_hdr; struct timeval start_tm, end_tm; #ifdef SG_DEBUG int clear = 1; #endif print_msg(TEST_BREAK, __FUNCTION__); file_name = device; sg_fd = open(file_name, O_RDONLY); if (sg_fd < 0) { perror(ME "open error"); return 1; } /* Don't worry, being very careful not to write to a none-sg file ... */ res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k); if ((res < 0) || (k < 30000)) { printf(ME "not a sg device, or driver prior to 3.x\n"); return 1; } if (do_mmap) { do_dio = 0; do_quick = 0; } if (NULL == (rawp = malloc(512))) { printf(ME "out of memory (query)\n"); return 1; } rbBuff = rawp; memset(rbCmdBlk, 0, RB_CMD_LEN); rbCmdBlk[0] = RB_OPCODE; rbCmdBlk[1] = RB_MODE_DESC; rbCmdBlk[8] = RB_DESC_LEN; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(rbCmdBlk); io_hdr.mx_sb_len = sizeof(sense_buffer); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = RB_DESC_LEN; io_hdr.dxferp = rbBuff; io_hdr.cmdp = rbCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */ /* do normal IO to find RB size (not dio or mmap-ed at this stage) */ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror(ME "SG_IO READ BUFFER descriptor error"); if (rawp) free(rawp); return 1; } /* now for the error processing */ switch (sg_err_category3(&io_hdr)) { case SG_ERR_CAT_CLEAN: break; case SG_ERR_CAT_RECOVERED: printf ("Recovered error on READ BUFFER descriptor, continuing\n"); break; default: /* won't bother decoding other categories */ sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr); if (rawp) free(rawp); return 1; } buf_capacity = ((rbBuff[1] << 16) | (rbBuff[2] << 8) | rbBuff[3]); printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n", buf_capacity, (int)rbBuff[0]); if (0 == buf_size) buf_size = buf_capacity; else if (buf_size > buf_capacity) { printf ("Requested buffer size=%d exceeds reported capacity=%d\n", buf_size, buf_capacity); if (rawp) free(rawp); return 1; } if (rawp) { free(rawp); rawp = NULL; } if (!do_dio) { k = buf_size; if (do_mmap && (0 != (k % psz))) k = ((k / psz) + 1) * psz; /* round up to page size */ res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, &k); if (res < 0) perror(ME "SG_SET_RESERVED_SIZE error"); } if (do_mmap) { rbBuff = mmap(NULL, buf_size, PROT_READ, MAP_SHARED, sg_fd, 0); if (MAP_FAILED == rbBuff) { if (ENOMEM == errno) printf(ME "mmap() out of memory, try a smaller " "buffer size than %d KB\n", buf_size / 1024); else perror(ME "error using mmap()"); return 1; } } else { /* non mmap-ed IO */ rawp = malloc(buf_size + (do_dio ? psz : 0)); if (NULL == rawp) { printf(ME "out of memory (data)\n"); return 1; } if (do_dio) /* align to page boundary */ rbBuff = (unsigned char *)(((unsigned long)rawp + psz - 1) & (~(psz - 1))); else rbBuff = rawp; } num = (total_size_mb * 1024U * 1024U) / (unsigned int)buf_size; if (do_time) { start_tm.tv_sec = 0; start_tm.tv_usec = 0; gettimeofday(&start_tm, NULL); } /* main data reading loop */ for (k = 0; k < num; ++k) { memset(rbCmdBlk, 0, RB_CMD_LEN); rbCmdBlk[0] = RB_OPCODE; rbCmdBlk[1] = RB_MODE_DATA; rbCmdBlk[6] = 0xff & (buf_size >> 16); rbCmdBlk[7] = 0xff & (buf_size >> 8); rbCmdBlk[8] = 0xff & buf_size; #ifdef SG_DEBUG memset(rbBuff, 0, buf_size); #endif memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(rbCmdBlk); io_hdr.mx_sb_len = sizeof(sense_buffer); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = buf_size; if (!do_mmap) io_hdr.dxferp = rbBuff; io_hdr.cmdp = rbCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */ io_hdr.pack_id = k; if (do_mmap) io_hdr.flags |= SG_FLAG_MMAP_IO; else if (do_dio) io_hdr.flags |= SG_FLAG_DIRECT_IO; else if (do_quick) io_hdr.flags |= SG_FLAG_NO_DXFER; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { if (ENOMEM == errno) printf(ME "SG_IO data; out of memory, try a smaller " "buffer size than %d KB\n", buf_size / 1024); else perror(ME "SG_IO READ BUFFER data error"); if (rawp) free(rawp); return 1; } /* now for the error processing */ switch (sg_err_category3(&io_hdr)) { case SG_ERR_CAT_CLEAN: break; case SG_ERR_CAT_RECOVERED: printf ("Recovered error on READ BUFFER data, continuing\n"); break; default: /* won't bother decoding other categories */ sg_chk_n_print3("READ BUFFER data error", &io_hdr); if (rawp) free(rawp); return 1; } if (do_dio && ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) dio_incomplete = 1; /* flag that dio not done (completely) */ #ifdef SG_DEBUG if (clear) { for (j = 0; j < buf_size; ++j) { if (rbBuff[j] != 0) { clear = 0; break; } } } #endif } if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { struct timeval res_tm; double a, b; gettimeofday(&end_tm, NULL); res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; if (res_tm.tv_usec < 0) { --res_tm.tv_sec; res_tm.tv_usec += 1000000; } a = res_tm.tv_sec; a += (0.000001 * res_tm.tv_usec); b = (double)buf_size *num; printf("time to read data from buffer was %d.%06d secs", (int)res_tm.tv_sec, (int)res_tm.tv_usec); if ((a > 0.00001) && (b > 511)) printf(", %.2f MB/sec\n", b / (a * 1000000.0)); else printf("\n"); } if (dio_incomplete) printf(">> direct IO requested but not done\n"); printf ("Read %u MBytes (actual %u MB, %u bytes), buffer size=%d KBytes\n", total_size_mb, (num * buf_size) / 1048576, num * buf_size, buf_size / 1024); if (rawp) free(rawp); res = close(sg_fd); if (res < 0) { perror(ME "close error"); return 0; } #ifdef SG_DEBUG if (clear) printf("read buffer always zero\n"); else printf("read buffer non-zero\n"); #endif return 0; } /* Performs a 10 byte READ CAPACITY command and fetches response. There is * evidently a 16 byte READ CAPACITY command coming. * Return of 0 -> success, -1 -> failure */ int do_readcap_10(int sg_fd, int pmi, unsigned int lba, unsigned int *last_sect, unsigned int *sect_sz) { int res; unsigned char rcCmdBlk[10] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char rcBuff[RCAP_REPLY_LEN]; unsigned char sense_b[SENSE_BUFF_SZ]; sg_io_hdr_t io_hdr; if (pmi) { /* lbs only valid when pmi set */ rcCmdBlk[8] |= 1; rcCmdBlk[2] = (lba >> 24) & 0xff; rcCmdBlk[3] = (lba >> 16) & 0xff; rcCmdBlk[4] = (lba >> 8) & 0xff; rcCmdBlk[5] = lba & 0xff; } memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(rcCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof(rcBuff); io_hdr.dxferp = rcBuff; io_hdr.cmdp = rcCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = 60000; while (1) { if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("read_capacity (SG_IO) error"); return -1; } res = sg_err_category3(&io_hdr); if (SG_ERR_CAT_MEDIA_CHANGED == res) continue; else if (SG_ERR_CAT_CLEAN != res) { sg_chk_n_print3("READ CAPACITY command error", &io_hdr); return -1; } else break; } *last_sect = ((rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) | rcBuff[3]); *sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) | (rcBuff[6] << 8) | rcBuff[7]; return 0; } int show_scsi_read_capacity(char *device) { int sg_fd, k, res; unsigned int lba = 0; int pmi = 1; unsigned int last_blk_addr, block_size; char ebuff[EBUFF_SZ]; const char *file_name = 0; print_msg(TEST_BREAK, __FUNCTION__); file_name = device; if ((0 == pmi) && (lba > 0)) { fprintf(stderr, ME "lba can only be non-zero when pmi is set\n"); usage(); return 1; } if ((sg_fd = open(file_name, O_RDONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", file_name); perror(ebuff); return 1; } /* Just to be safe, check we have a new sg device by trying an ioctl */ if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { printf(ME "%s doesn't seem to be a version 3 sg device\n", file_name); close(sg_fd); return 1; } res = do_readcap_10(sg_fd, pmi, lba, &last_blk_addr, &block_size); if (0 == res) { printf("Read Capacity results:\n"); if (pmi) printf(" PMI mode: given lba=0x%x, last block before " "delay=0x%x\n", lba, last_blk_addr); else printf (" Last block address=%u (0x%x), Number of blocks=%u\n", last_blk_addr, last_blk_addr, last_blk_addr + 1); printf(" Block size = %u bytes\n", block_size); } close(sg_fd); return 0; } int do_scsi_reset_devices(char *device, int reset_opt) { int sg_fd, res, k; int do_device_reset = 0; int do_bus_reset = 0; int do_host_reset = 0; char *file_name = 0; switch (reset_opt) { case DEVICE_RESET: print_msg(TEST_BREAK, __FUNCTION__); do_device_reset = 1; break; case HOST_RESET: do_host_reset = 1; break; case BUS_RESET: do_bus_reset = 1; break; } file_name = device; sg_fd = open(file_name, O_RDWR | O_NONBLOCK); if (sg_fd < 0) { perror("sg_reset: open error"); return 1; } k = SG_SCSI_RESET_NOTHING; if (do_device_reset) { printf("sg_reset: starting device reset\n"); k = SG_SCSI_RESET_DEVICE; } else if (do_bus_reset) { printf("sg_reset: starting bus reset\n"); k = SG_SCSI_RESET_BUS; } else if (do_host_reset) { printf("sg_reset: starting host reset\n"); k = SG_SCSI_RESET_HOST; } res = ioctl(sg_fd, SG_SCSI_RESET, &k); if (res < 0) { if (EBUSY == errno) printf("sg_reset: BUSY, may be resetting now\n"); else if (EIO == errno) printf ("sg_reset: requested type of reset may not be available\n"); else if (EACCES == errno) printf("sg_reset: reset requires CAP_SYS_ADMIN (root) " "permission\n"); else if (EINVAL == errno) printf("sg_reset: SG_SCSI_RESET not supported\n"); else if (EIO == errno) printf("sg_reset: scsi_reset_provider() call failed\n"); else perror("sg_reset: SG_SCSI_RESET failed"); return 1; } if (SG_SCSI_RESET_NOTHING == k) printf("sg_reset: did nothing, device is normal mode\n"); else if (SG_SCSI_RESET_DEVICE == k) printf("sg_reset: completed device reset\n"); else if (SG_SCSI_RESET_BUS == k) printf("sg_reset: completed bus reset\n"); else if (SG_SCSI_RESET_HOST == k) printf("sg_reset: completed host reset\n"); if (close(sg_fd) < 0) { perror("sg_reset: close error"); return 1; } return 0; } static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit, int devofl_bit, int unitofl_bit, void *outgoing_pg, int outgoing_len, int noisy) { int res; unsigned char senddiagCmdBlk[SEND_DIAGNOSTIC_CMDLEN] = { SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; senddiagCmdBlk[1] = (unsigned char)((sf_code << 5) | (pf_bit << 4) | (sf_bit << 2) | (devofl_bit << 1) | unitofl_bit); senddiagCmdBlk[3] = (unsigned char)((outgoing_len >> 8) & 0xff); senddiagCmdBlk[4] = (unsigned char)(outgoing_len & 0xff); memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = SEND_DIAGNOSTIC_CMDLEN; io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = outgoing_len ? SG_DXFER_TO_DEV : SG_DXFER_NONE; io_hdr.dxfer_len = outgoing_len; io_hdr.dxferp = outgoing_pg; io_hdr.cmdp = senddiagCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = LONG_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO (send diagnostic) error"); return -1; } res = sg_err_category3(&io_hdr); switch (res) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: return 0; default: if (noisy) { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "Send diagnostic error, sf_code=0x%x, " "pf_bit=%d, sf_bit=%d ", sf_code, pf_bit, sf_bit); sg_chk_n_print3(ebuff, &io_hdr); } return -1; } } static int do_rcvdiag(int sg_fd, int pcv, int pg_code, void *resp, int mx_resp_len, int noisy) { int res; unsigned char rcvdiagCmdBlk[RECEIVE_DIAGNOSTIC_CMDLEN] = { RECEIVE_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; rcvdiagCmdBlk[1] = (unsigned char)(pcv ? 0x1 : 0); rcvdiagCmdBlk[2] = (unsigned char)(pg_code); rcvdiagCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); rcvdiagCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = RECEIVE_DIAGNOSTIC_CMDLEN; io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = rcvdiagCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO (receive diagnostic) error"); return -1; } res = sg_err_category3(&io_hdr); switch (res) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: return 0; default: if (noisy) { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "Receive diagnostic error, pcv=%d, " "page_code=%x ", pcv, pg_code); sg_chk_n_print3(ebuff, &io_hdr); } return -1; } } /* Get last extended self-test time from mode page 0xa (for '-e' option) */ static int do_modes_0a(int sg_fd, void *resp, int mx_resp_len, int noisy, int mode6) { int res; unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = { MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; int dbd = 1; int pc = 0; int pg_code = 0xa; modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0); modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); if (mx_resp_len > (mode6 ? 0xff : 0xffff)) { printf(ME "mx_resp_len too big\n"); return -1; } if (mode6) { modesCmdBlk[0] = MODE_SENSE6_CMD; modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); } else { modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); } memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN; io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = modesCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO (mode sense) error"); return -1; } res = sg_err_category3(&io_hdr); switch (res) { case SG_ERR_CAT_CLEAN: case SG_ERR_CAT_RECOVERED: return 0; default: if (noisy) { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d, " "pc=%d, page_code=%x ", dbd, pc, pg_code); sg_chk_n_print3(ebuff, &io_hdr); } return -1; } } int do_scsi_send_diagnostics(char *device) { int sg_fd, k, num, rsp_len; char *file_name = 0; unsigned char rsp_buff[MODE_ALLOC_LEN]; int rsp_buff_size = MODE_ALLOC_LEN; int self_test_code = 6; int do_pf = 0; int do_doff = 0; int do_def_test = 0; int do_uoff = 0; int oflags = O_RDWR; print_msg(TEST_BREAK, __FUNCTION__); file_name = device; if ((sg_fd = open(file_name, oflags)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", file_name); perror(ebuff); return 1; } /* Just to be safe, check we have a new sg device by trying an ioctl */ if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { printf(ME "%s doesn't seem to be a version 3 sg device\n", file_name); close(sg_fd); return 1; } if (0 == do_modes_0a(sg_fd, rsp_buff, 32, 1, 0)) { /* Assume mode sense(10) response without block descriptors */ num = (rsp_buff[0] << 8) + rsp_buff[1] - 6; if (num >= 0xc) { int secs; secs = (rsp_buff[18] << 8) + rsp_buff[19]; printf ("Previous extended self-test duration=%d seconds " "(%.2f minutes)\n", secs, secs / 60.0); } else printf("Extended self-test duration not available\n"); } else printf("Extended self-test duration (mode page 0xa) failed\n"); memset(rsp_buff, 0, sizeof(rsp_buff)); if (0 == do_senddiag(sg_fd, 0, do_pf, 0, 0, 0, rsp_buff, 4, 1)) { if (0 == do_rcvdiag(sg_fd, 0, 0, rsp_buff, rsp_buff_size, 1)) { printf("Supported diagnostic pages response:\n"); rsp_len = (rsp_buff[2] << 8) + rsp_buff[3] + 4; for (k = 0; k < (rsp_len - 4); ++k) printf(" %s\n", find_page_code_desc(rsp_buff[k + 4], 0)); } } if (0 == do_senddiag(sg_fd, self_test_code, do_pf, do_def_test, do_doff, do_uoff, NULL, 0, 1)) { if ((5 == self_test_code) || (6 == self_test_code)) printf("Foreground self test returned GOOD status\n"); else if (do_def_test && (!do_doff) && (!do_uoff)) printf("Default self test returned GOOD status\n"); } close(sg_fd); return 0; } static void do_start_stop(int fd, int start, int immed, int loej, int power_conditions) { unsigned char cmdblk[6] = { START_STOP, /* Command */ 0, /* Resvd/Immed */ 0, /* Reserved */ 0, /* Reserved */ 0, /* PowCond/Resvd/LoEj/Start */ 0 }; /* Reserved/Flag/Link */ unsigned char sense_b[32]; sg_io_hdr_t io_hdr; int k, res, debug = 1; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); cmdblk[1] = immed & 1; cmdblk[4] = ((power_conditions & 0xf) << 4) | ((loej & 1) << 1) | (start & 1); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(cmdblk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.dxfer_len = 0; io_hdr.dxferp = NULL; io_hdr.cmdp = cmdblk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_START_TIMEOUT; if (debug) { printf(" Start/Stop command:"); for (k = 0; k < 6; ++k) printf(" %02x", cmdblk[k]); printf("\n"); } if (ioctl(fd, SG_IO, &io_hdr) < 0) { perror("start_stop (SG_IO) error"); return; } res = sg_err_category3(&io_hdr); if (SG_ERR_CAT_MEDIA_CHANGED == res) { fprintf(stderr, "media change report, try start_stop again\n"); if (ioctl(fd, SG_IO, &io_hdr) < 0) { perror("start_stop (SG_IO) error"); return; } } if (SG_ERR_CAT_CLEAN != res) { sg_chk_n_print3("start_stop", &io_hdr); return; } if (debug) fprintf(stderr, "start_stop [%s] successful\n", start ? "start" : "stop"); } static void do_sync_cache(int fd) { unsigned char cmdblk[10] = { SYNCHRONIZE_CACHE, /* Command */ 0, /* Immed (2) */ 0, 0, 0, 0, /* LBA */ 0, /* Reserved */ 0, 0, /* No of blocks */ 0 }; /* Reserved/Flag/Link */ unsigned char sense_b[32]; sg_io_hdr_t io_hdr; int res, debug = 1; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(cmdblk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.dxfer_len = 0; io_hdr.dxferp = NULL; io_hdr.cmdp = cmdblk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_START_TIMEOUT; if (ioctl(fd, SG_IO, &io_hdr) < 0) { perror("sync_cache (SG_IO) error"); return; } res = sg_err_category3(&io_hdr); if (SG_ERR_CAT_MEDIA_CHANGED == res) { fprintf(stderr, "media change report, try sync_cache again\n"); if (ioctl(fd, SG_IO, &io_hdr) < 0) { perror("sync_cache (SG_IO) error"); return; } } if (SG_ERR_CAT_CLEAN != res) { sg_chk_n_print3("sync_cache", &io_hdr); return; } if (debug) fprintf(stderr, "synchronize cache successful\n"); } int do_scsi_start_stop(char *device, int startstop) { int synccache = 1; char *file_name = 0; int fd; int immed = 1; int loej = 0; int power_conds = 0; print_msg(TEST_BREAK, __FUNCTION__); file_name = device; fd = open(file_name, O_RDWR | O_NONBLOCK); if (fd < 0) { fprintf(stderr, "Error trying to open %s\n", file_name); perror(""); usage(); return 2; } if (ioctl(fd, SG_GET_TIMEOUT, 0) < 0) { fprintf(stderr, "Given file not block or SCSI " "generic device\n"); close(fd); return 3; } if (synccache) do_sync_cache(fd); if (power_conds > 0) do_start_stop(fd, 0, immed, 0, power_conds); else if (startstop != -1) do_start_stop(fd, startstop, immed, loej, 0); close(fd); return 0; } int find_out_about_buffer(int sg_fd, int *buf_capacity, char *file_name) { int res, buf_granul = 255; unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + 512); struct sg_header *rsghp = (struct sg_header *)rbBuff; int rbInLen = OFF + RB_DESC_LEN; int rbOutLen = OFF + sizeof(rbCmdBlk); unsigned char *buffp = rbBuff + OFF; rsghp->pack_len = 0; /* don't care */ rsghp->pack_id = 0; rsghp->reply_len = rbInLen; rsghp->twelve_byte = 0; rsghp->result = 0; #ifndef SG_GET_RESERVED_SIZE rsghp->sense_buffer[0] = 0; #endif memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk)); rbBuff[OFF + 1] = RB_MODE_DESC; rbBuff[OFF + 8] = RB_DESC_LEN; res = write(sg_fd, rbBuff, rbOutLen); if (res < 0) { perror("sg_test_rwbuf: write (desc) error"); if (rbBuff) free(rbBuff); return 1; } if (res < rbOutLen) { printf("sg_test_rwbuf: wrote less (desc), ask=%d, got=%d\n", rbOutLen, res); if (rbBuff) free(rbBuff); return 1; } memset(rbBuff + OFF, 0, RB_DESC_LEN); res = read(sg_fd, rbBuff, rbInLen); if (res < 0) { perror("sg_test_rwbuf: read (desc) error"); if (rbBuff) free(rbBuff); return 1; } if (res < rbInLen) { printf("sg_test_rwbuf: read less (desc), ask=%d, got=%d\n", rbInLen, res); if (rbBuff) free(rbBuff); return 1; } #ifdef SG_GET_RESERVED_SIZE if (!sg_chk_n_print("sg_test_rwbuf: desc", rsghp->target_status, rsghp->host_status, rsghp->driver_status, rsghp->sense_buffer, SG_MAX_SENSE)) { printf ("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n", file_name); if (rbBuff) free(rbBuff); return 1; } #else if ((rsghp->result != 0) || (0 != rsghp->sense_buffer[0])) { printf("sg_test_rwbuf: read(desc) result=%d\n", rsghp->result); if (0 != rsghp->sense_buffer[0]) sg_print_sense("sg_test_rwbuf: desc", rsghp->sense_buffer, SG_MAX_SENSE); printf ("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n", file_name); if (rbBuff) free(rbBuff); return 1; } #endif *(buf_capacity) = ((buffp[1] << 16) | (buffp[2] << 8) | buffp[3]); buf_granul = (unsigned char)buffp[0]; printf("READ BUFFER reports: %02x %02x %02x %02x %02x %02x %02x %02x\n", buffp[0], buffp[1], buffp[2], buffp[3], buffp[4], buffp[5], buffp[6], buffp[7]); printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n", *(buf_capacity), buf_granul); #ifdef SG_DEF_RESERVED_SIZE res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, buf_capacity); if (res < 0) perror("sg_test_rwbuf: SG_SET_RESERVED_SIZE error"); #endif return 0; } int mymemcmp(unsigned char *bf1, unsigned char *bf2, int len) { int df; for (df = 0; df < len; df++) if (bf1[df] != bf2[df]) return df; return 0; } int do_checksum(int *buf, int len, int quiet) { int sum = base; int i; int rln = len; for (i = 0; i < len / BPI; i++) sum += buf[i]; while (rln % BPI) sum += ((char *)buf)[--rln]; if (sum != READWRITE_BASE_NUM) { if (!quiet) printf("sg_test_rwbuf: Checksum error (sz=%i): %08x\n", len, sum); if (cmpbuf && !quiet) { int diff = mymemcmp(cmpbuf, (unsigned char *)buf, len); printf("Differ at pos %i/%i:\n", diff, len); for (i = 0; i < 24 && i + diff < len; i++) printf(" %02x", cmpbuf[i + diff]); printf("\n"); for (i = 0; i < 24 && i + diff < len; i++) printf(" %02x", ((unsigned char *)buf)[i + diff]); printf("\n"); } return 2; } else return 0; } void do_fill_buffer(int *buf, int len) { int sum; int i; int rln = len; srand(time(0)); retry: if (len >= BPI) base = READWRITE_BASE_NUM + rand(); else base = READWRITE_BASE_NUM + (char)rand(); sum = base; for (i = 0; i < len / BPI - 1; i++) { /* we rely on rand() giving full range of int */ buf[i] = rand(); sum += buf[i]; } while (rln % BPI) { ((char *)buf)[--rln] = rand(); sum += ((char *)buf)[rln]; } if (len >= BPI) buf[len / BPI - 1] = READWRITE_BASE_NUM - sum; else ((char *)buf)[0] = READWRITE_BASE_NUM + ((char *)buf)[0] - sum; if (do_checksum(buf, len, 1)) { if (len < BPI) goto retry; printf("sg_test_rwbuf: Memory corruption?\n"); exit(1); } if (cmpbuf) memcpy(cmpbuf, (char *)buf, len); } int read_buffer(int sg_fd, unsigned size) { int res; unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size); struct sg_header *rsghp = (struct sg_header *)rbBuff; int rbInLen = OFF + size; int rbOutLen = OFF + sizeof(rbCmdBlk); memset(rbBuff, 0, OFF + sizeof(rbCmdBlk) + size); rsghp->pack_len = 0; /* don't care */ rsghp->reply_len = rbInLen; rsghp->twelve_byte = 0; rsghp->result = 0; memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk)); rbBuff[OFF + 1] = RB_MODE_DATA; rbBuff[OFF + 6] = 0xff & ((size) >> 16); rbBuff[OFF + 7] = 0xff & ((size) >> 8); rbBuff[OFF + 8] = 0xff & (size); rsghp->pack_id = 2; res = write(sg_fd, rbBuff, rbOutLen); if (res < 0) { perror("sg_test_rwbuf: write (data) error"); if (rbBuff) free(rbBuff); return 1; } if (res < rbOutLen) { printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n", rbOutLen, res); if (rbBuff) free(rbBuff); return 1; } res = read(sg_fd, rbBuff, rbInLen); if (res < 0) { perror("sg_test_rwbuf: read (data) error"); if (rbBuff) free(rbBuff); return 1; } if (res < rbInLen) { printf("sg_test_rwbuf: read less (data), ask=%d, got=%d\n", rbInLen, res); if (rbBuff) free(rbBuff); return 1; } res = do_checksum((int *)(rbBuff + OFF), size, 0); if (rbBuff) free(rbBuff); return res; } int write_buffer(int sg_fd, unsigned size) { int res; unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size); struct sg_header *rsghp = (struct sg_header *)rbBuff; //unsigned char * buffp = rbBuff + OFF; int rbInLen = OFF; int rbOutLen = OFF + sizeof(rbCmdBlk) + size; do_fill_buffer((int *)(rbBuff + OFF + sizeof(rbCmdBlk)), size); rsghp->pack_len = 0; /* don't care */ rsghp->reply_len = rbInLen; rsghp->twelve_byte = 0; rsghp->result = 0; memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk)); rbBuff[OFF + 0] = WRITE_BUFFER; rbBuff[OFF + 1] = RB_MODE_DATA; rbBuff[OFF + 6] = 0xff & ((size) >> 16); rbBuff[OFF + 7] = 0xff & ((size) >> 8); rbBuff[OFF + 8] = 0xff & (size); rsghp->pack_id = 1; res = write(sg_fd, rbBuff, rbOutLen); if (res < 0) { perror("sg_test_rwbuf: write (data) error"); if (rbBuff) free(rbBuff); return 1; } if (res < rbOutLen) { printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n", rbOutLen, res); if (rbBuff) free(rbBuff); return 1; } res = read(sg_fd, rbBuff, rbInLen); if (res < 0) { perror("sg_test_rwbuf: read (status) error"); if (rbBuff) free(rbBuff); return 1; } if (rbBuff) free(rbBuff); return 0; } int do_scsi_read_write_buffer(char *device) { int sg_fd; int res, buf_capacity; char *file_name = device; struct stat a_st; int block_dev = 0; print_msg(TEST_BREAK, __FUNCTION__); sg_fd = open(file_name, O_RDWR); if (sg_fd < 0) { perror("sg_test_rwbuf: open error"); return 1; } if (fstat(sg_fd, &a_st) < 0) { fprintf(stderr, "could do fstat() on fd ??\n"); close(sg_fd); return 1; } if (S_ISBLK(a_st.st_mode)) block_dev = 1; /* Don't worry, being very careful not to write to a none-sg file ... */ if (block_dev || (ioctl(sg_fd, SG_GET_TIMEOUT, 0) < 0)) { /* perror("ioctl on generic device, error"); */ printf("sg_test_rwbuf: not a sg device, or wrong driver\n"); return 1; } if (find_out_about_buffer(sg_fd, &buf_capacity, file_name)) return 1; cmpbuf = malloc(buf_capacity); if (write_buffer(sg_fd, buf_capacity)) return 3; res = read_buffer(sg_fd, buf_capacity); if (res) return (res + 4); res = close(sg_fd); if (res < 0) { perror("sg_test_rwbuf: close error"); return 6; } printf("Success\n"); return 0; } int do_scsi_test_unit_ready(char *device) { int sg_fd, k; unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; sg_io_hdr_t io_hdr; char *file_name = device; char ebuff[EBUFF_SZ]; unsigned char sense_buffer[32]; int num_turs = 10240; int num_errs = 0; int do_time = 1; struct timeval start_tm, end_tm; print_msg(TEST_BREAK, __FUNCTION__); if ((sg_fd = open(file_name, O_RDONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, "sg_turs: error opening file: %s", file_name); perror(ebuff); return 1; } /* Just to be safe, check we have a new sg driver by trying an ioctl */ if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { printf ("sg_turs: %s isn't an sg device (or the sg driver is old)\n", file_name); close(sg_fd); return 1; } /* Prepare TEST UNIT READY command */ memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(turCmdBlk); io_hdr.mx_sb_len = sizeof(sense_buffer); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */ if (do_time) { start_tm.tv_sec = 0; start_tm.tv_usec = 0; gettimeofday(&start_tm, NULL); } for (k = 0; k < num_turs; ++k) { io_hdr.pack_id = k; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("sg_turs: Test Unit Ready SG_IO ioctl error"); close(sg_fd); return 1; } if (io_hdr.info & SG_INFO_OK_MASK) { ++num_errs; if (1 == num_turs) { /* then print out the error message */ if (SG_ERR_CAT_CLEAN != sg_err_category3(&io_hdr)) sg_chk_n_print3("tur", &io_hdr); } } } if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { struct timeval res_tm; double a, b; gettimeofday(&end_tm, NULL); res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; if (res_tm.tv_usec < 0) { --res_tm.tv_sec; res_tm.tv_usec += 1000000; } a = res_tm.tv_sec; a += (0.000001 * res_tm.tv_usec); b = (double)num_turs; printf("time to perform commands was %d.%06d secs", (int)res_tm.tv_sec, (int)res_tm.tv_usec); if (a > 0.00001) printf("; %.2f operations/sec\n", b / a); else printf("\n"); } printf("Completed %d Test Unit Ready commands with %d errors\n", num_turs, num_errs); close(sg_fd); return 0; } /* Returns 0 -> ok, 1 -> err, 2 -> recovered error */ static int do_sg_io(int sg_fd, unsigned char *buff) { /* N.B. Assuming buff contains pointer 'buffer' or 'buffer1' */ struct sg_header *sghp = (struct sg_header *)(buff - OFF); int res; sghp->pack_len = 0; sghp->reply_len = SG_HSZ + *(((int *)buff) + 1); sghp->pack_id = 0; sghp->twelve_byte = 0; sghp->other_flags = 0; #ifndef SG_GET_RESERVED_SIZE sghp->sense_buffer[0] = 0; #endif #if 0 sg_print_command(buff + 8); printf(" write_len=%d, read_len=%d\n", SG_HSZ + sg_get_command_size(buff[8]) + *((int *)buff), sghp->reply_len); #endif res = write(sg_fd, (const void *)sghp, SG_HSZ + sg_get_command_size(buff[8]) + *((int *)buff)); if (res < 0) { #ifdef SG_IO_DEBUG perror("write to sg failed"); #endif return 1; } res = read(sg_fd, (void *)sghp, sghp->reply_len); if (res < 0) { #ifdef SG_IO_DEBUG perror("read from sg failed"); #endif return 1; } #ifdef SG_GET_RESERVED_SIZE res = sg_err_category(sghp->target_status, sghp->host_status, sghp->driver_status, sghp->sense_buffer, SG_MAX_SENSE); switch (res) { case SG_ERR_CAT_CLEAN: return 0; case SG_ERR_CAT_RECOVERED: return 2; default: #ifdef SG_IO_DEBUG sg_chk_n_print("read from sg", sghp->target_status, sghp->host_status, sghp->driver_status, sghp->sense_buffer, SG_MAX_SENSE); #endif return 1; } #else if (0 != sghp->sense_buffer[0]) { #ifdef SG_IO_DEBUG int k; printf("read from sg, sense buffer (in hex):\n "); for (k = 0; k < 16; ++k) printf("%02x ", (int)sghp->sense_buffer[k]); printf("\n"); #endif return 1; } else if (0 != sghp->result) { #ifdef SG_IO_DEBUG printf("read from sg, bad result=%d\n", sghp->result); #endif return 1; } else return 0; #endif } static char *get_page_name(int pageno) { if ((pageno <= 0) || (pageno >= MAX_PAGENO) || (!page_names[pageno])) return "Mode"; return page_names[pageno]; } static int getnbyte(unsigned char *pnt, int nbyte) { unsigned int result; int i; result = 0; for (i = 0; i < nbyte; i++) result = (result << 8) | (pnt[i] & 0xff); return result; } static void bitfield(unsigned char *pageaddr, char *text, int mask, int shift) { printf("%-35s%d\n", text, (*pageaddr >> shift) & mask); } static void notbitfield(unsigned char *pageaddr, char *text, int mask, int shift) { printf("%-35s%d\n", text, !((*pageaddr >> shift) & mask)); } static void intfield(unsigned char *pageaddr, int nbytes, char *text) { printf("%-35s%d\n", text, getnbyte(pageaddr, nbytes)); } static void hexfield(unsigned char *pageaddr, int nbytes, char *text) { printf("%-35s0x%x\n", text, getnbyte(pageaddr, nbytes)); } static void hexdatafield(unsigned char *pageaddr, int nbytes, char *text) { printf("%-35s0x", text); while (nbytes-- > 0) printf("%02x", *pageaddr++); putchar('\n'); } static int get_mode_page(int page, int page_code) { int status, quiet; unsigned char *cmd; memset(buffer, 0, SIZEOF_BUFFER); quiet = page_code & ~3; page_code &= 3; *((int *)buffer) = 0; /* length of input data */ *(((int *)buffer) + 1) = 0xff; /* length of output data */ cmd = (unsigned char *)(((int *)buffer) + 2); cmd[0] = MODE_SENSE; /* MODE SENSE (6) */ cmd[1] = 0x00; /* lun = 0, inhibitting BD makes this fail for me */ cmd[2] = (page_code << 6) | page; cmd[3] = 0x00; /* (reserved) */ cmd[4] = (unsigned char)0xff; /* allocation length */ cmd[5] = 0x00; /* control */ status = do_sg_io(glob_fd, buffer); if (status && (!quiet)) fprintf(stdout, ">>> Unable to read %s Page %02xh\n", get_page_name(page), page); //dump (buffer+2, 46); return status; } /* Same as above, but this time with MODE_SENSE_10 */ static int get_mode_page10(int page, int page_code) { int status, quiet; unsigned char *cmd; memset(buffer, 0, SIZEOF_BUFFER); quiet = page_code & ~3; page_code &= 3; *((int *)buffer) = 0; /* length of input data */ *(((int *)buffer) + 1) = 0xffff; /* length of output buffer */ cmd = (unsigned char *)(((int *)buffer) + 2); cmd[0] = MODE_SENSE_10; /* MODE SENSE (10) */ cmd[1] = 0x00; /* lun = 0, inhibitting BD makes this fail for me */ cmd[2] = (page_code << 6) | page; cmd[3] = 0x00; /* (reserved) */ cmd[4] = 0x00; /* (reserved) */ cmd[5] = 0x00; /* (reserved) */ cmd[6] = 0x00; /* (reserved) */ cmd[7] = 0xff; /* allocation length hi */ cmd[8] = 0xff; /* allocation length lo */ cmd[9] = 0x00; /* control */ status = do_sg_io(glob_fd, buffer); if (status && (!quiet)) fprintf(stdout, ">>> Unable to read %s Page %02xh with MODESENSE(10)\n", get_page_name(page), page); return status; } /* Contents should point to the mode parameter header that we obtained in a prior read operation. This way we do not have to work out the format of the beast */ static int read_geometry(int page_code) { int status; int bdlen; unsigned char *pagestart; SETUP_MODE_PAGE(4, 9); printf("Data from Rigid Disk Drive Geometry Page\n"); printf("----------------------------------------\n"); intfield(pagestart + 2, 3, "Number of cylinders"); intfield(pagestart + 5, 1, "Number of heads"); intfield(pagestart + 6, 3, "Starting write precomp"); intfield(pagestart + 9, 3, "Starting reduced current"); intfield(pagestart + 12, 2, "Drive step rate"); intfield(pagestart + 14, 3, "Landing Zone Cylinder"); bitfield(pagestart + 17, "RPL", 3, 0); intfield(pagestart + 18, 1, "Rotational Offset"); intfield(pagestart + 20, 2, "Rotational Rate"); printf("\n"); return 0; } static int read_disconnect_reconnect_data(int page_code) { int status; int bdlen; unsigned char *pagestart; SETUP_MODE_PAGE(2, 7); printf("Data from Disconnect-Reconnect Page\n"); printf("-----------------------------------\n"); intfield(pagestart + 2, 1, "Buffer full ratio"); intfield(pagestart + 3, 1, "Buffer empty ratio"); intfield(pagestart + 4, 2, "Bus Inactivity Limit"); intfield(pagestart + 6, 2, "Disconnect Time Limit"); intfield(pagestart + 8, 2, "Connect Time Limit"); intfield(pagestart + 10, 2, "Maximum Burst Size"); hexfield(pagestart + 12, 1, "DTDC"); printf("\n"); return 0; } static int read_control_page(int page_code) { int status; int bdlen; unsigned char *pagestart; SETUP_MODE_PAGE(10, 9); printf("Data from Control Page\n"); printf("----------------------\n"); bitfield(pagestart + 2, "RLEC", 1, 0); bitfield(pagestart + 3, "QErr", 1, 1); bitfield(pagestart + 3, "DQue", 1, 0); bitfield(pagestart + 4, "EECA", 1, 7); bitfield(pagestart + 4, "RAENP", 1, 2); bitfield(pagestart + 4, "UUAENP", 1, 1); bitfield(pagestart + 4, "EAENP", 1, 0); bitfield(pagestart + 3, "Queue Algorithm Modifier", 0xf, 4); intfield(pagestart + 6, 2, "Ready AEN Holdoff Period"); printf("\n"); return 0; } static int error_recovery_page(int page_code) { int status; int bdlen; unsigned char *pagestart; SETUP_MODE_PAGE(1, 14); printf("Data from Error Recovery Page\n"); printf("-----------------------------\n"); bitfield(pagestart + 2, "AWRE", 1, 7); bitfield(pagestart + 2, "ARRE", 1, 6); bitfield(pagestart + 2, "TB", 1, 5); bitfield(pagestart + 2, "RC", 1, 4); bitfield(pagestart + 2, "EER", 1, 3); bitfield(pagestart + 2, "PER", 1, 2); bitfield(pagestart + 2, "DTE", 1, 1); bitfield(pagestart + 2, "DCR", 1, 0); intfield(pagestart + 3, 1, "Read Retry Count"); intfield(pagestart + 4, 1, "Correction Span"); intfield(pagestart + 5, 1, "Head Offset Count"); intfield(pagestart + 6, 1, "Data Strobe Offset Count"); intfield(pagestart + 8, 1, "Write Retry Count"); intfield(pagestart + 10, 2, "Recovery Time Limit"); printf("\n"); return 0; } static int notch_parameters_page(int page_code) { int status; int bdlen; unsigned char *pagestart; SETUP_MODE_PAGE(0xc, 7); printf("Data from Notch Parameters Page\n"); printf("-------------------------------\n"); bitfield(pagestart + 2, "Notched Drive", 1, 7); bitfield(pagestart + 2, "Logical or Physical Notch", 1, 6); intfield(pagestart + 4, 2, "Max # of notches"); intfield(pagestart + 6, 2, "Active Notch"); if (pagestart[2] & 0x40) { intfield(pagestart + 8, 4, "Starting Boundary"); intfield(pagestart + 12, 4, "Ending Boundary"); } else { /* Hex is more meaningful for physical notches */ hexfield(pagestart + 8, 4, "Starting Boundary"); hexfield(pagestart + 12, 4, "Ending Boundary"); } printf("0x%8.8x%8.8x", getnbyte(pagestart + 16, 4), getnbyte(pagestart + 20, 4)); printf("\n"); return 0; } static char *formatname(int format) { switch (format) { case 0x0: return "logical blocks"; case 0x4: return "bytes from index [Cyl:Head:Off]\n" "Offset -1 marks whole track as bad.\n"; case 0x5: return "physical blocks [Cyl:Head:Sect]\n" "Sector -1 marks whole track as bad.\n"; } return "Weird, unknown format"; } static int read_defect_list(int page_code) { int status = 0, i, len, reallen, table, k; unsigned char *cmd, *df = 0; int trunc; printf("Data from Defect Lists\n" "----------------------\n"); for (table = 0; table < 2; table++) { memset(buffer, 0, SIZEOF_BUFFER); trunc = 0; *((int *)buffer) = 0; /* length of input data */ *(((int *)buffer) + 1) = 4; /* length of output buffer */ cmd = (unsigned char *)(((int *)buffer) + 2); cmd[0] = 0x37; /* READ DEFECT DATA */ cmd[1] = 0x00; /* lun=0 */ cmd[2] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */ cmd[3] = 0x00; /* (reserved) */ cmd[4] = 0x00; /* (reserved) */ cmd[5] = 0x00; /* (reserved) */ cmd[6] = 0x00; /* (reserved) */ cmd[7] = 0x00; /* Alloc len */ cmd[8] = 0x04; /* Alloc len */ cmd[9] = 0x00; /* control */ i = do_sg_io(glob_fd, buffer); if (2 == i) i = 0; /* Recovered error, probably returned a different format */ if (i) { fprintf(stdout, ">>> Unable to read %s defect data.\n", (table ? "grown" : "manufacturer")); status |= i; continue; } len = (buffer[10] << 8) | buffer[11]; reallen = len; if (len > 0) { if (len >= 0xfff8) { len = SIZEOF_BUFFER - 8; k = len + 8; /* length of defect list */ *((int *)buffer) = 0; /* length of input data */ *(((int *)buffer) + 1) = k; /* length of output buffer */ ((struct sg_header *)buffer)->twelve_byte = 1; cmd[0] = 0xB7; /* READ DEFECT DATA */ cmd[1] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */ cmd[2] = 0x00; /* (reserved) */ cmd[3] = 0x00; /* (reserved) */ cmd[4] = 0x00; /* (reserved) */ cmd[5] = 0x00; /* (reserved) */ cmd[6] = 0x00; /* Alloc len */ cmd[7] = (k >> 16); /* Alloc len */ cmd[8] = (k >> 8); /* Alloc len */ cmd[9] = (k & 0xff); /* Alloc len */ cmd[10] = 0x00; /* reserved */ cmd[11] = 0x00; /* control */ i = do_sg_io(glob_fd, buffer); if (i == 2) i = 0; if (i) goto trytenbyte; reallen = (buffer[12] << 24 | buffer[13] << 16 | buffer[14] << 8 | buffer[15]); len = reallen; if (len > SIZEOF_BUFFER - 8) { len = SIZEOF_BUFFER - 8; trunc = 1; } df = (unsigned char *)(buffer + 16); } else { trytenbyte: if (len > 0xfff8) { len = 0xfff8; trunc = 1; } k = len + 4; /* length of defect list */ *((int *)buffer) = 0; /* length of input data */ *(((int *)buffer) + 1) = k; /* length of output buffer */ cmd[0] = 0x37; /* READ DEFECT DATA */ cmd[1] = 0x00; /* lun=0 */ cmd[2] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */ cmd[3] = 0x00; /* (reserved) */ cmd[4] = 0x00; /* (reserved) */ cmd[5] = 0x00; /* (reserved) */ cmd[6] = 0x00; /* (reserved) */ cmd[7] = (k >> 8); /* Alloc len */ cmd[8] = (k & 0xff); /* Alloc len */ cmd[9] = 0x00; /* control */ i = do_sg_io(glob_fd, buffer); df = (unsigned char *)(buffer + 12); } } if (2 == i) i = 0; /* Recovered error, probably returned a different format */ if (i) { fprintf(stdout, ">>> Unable to read %s defect data.\n", (table ? "grown" : "manufacturer")); status |= i; continue; } else { if (table && !status) printf("\n"); printf("%d entries (%d bytes) in %s table.\n" "Format (%x) is: %s\n", reallen / ((buffer[9] & 7) ? 8 : 4), reallen, (table ? "grown" : "manufacturer"), buffer[9] & 7, formatname(buffer[9] & 7)); i = 0; if ((buffer[9] & 7) == 4) { while (len > 0) { snprintf((char *)buffer, 40, "%6d:%3u:%8d", getnbyte(df, 3), df[3], getnbyte(df + 4, 4)); printf("%19s", (char *)buffer); len -= 8; df += 8; i++; if (i >= 4) { printf("\n"); i = 0; } else printf("|"); } } else if ((buffer[9] & 7) == 5) { while (len > 0) { snprintf((char *)buffer, 40, "%6d:%2u:%5d", getnbyte(df, 3), df[3], getnbyte(df + 4, 4)); printf("%15s", (char *)buffer); len -= 8; df += 8; i++; if (i >= 5) { printf("\n"); i = 0; } else printf("|"); } } else { while (len > 0) { printf("%10d", getnbyte(df, 4)); len -= 4; df += 4; i++; if (i >= 7) { printf("\n"); i = 0; } else printf("|"); } } if (i) printf("\n"); } if (trunc) printf("[truncated]\n"); } printf("\n"); return status; } static int read_cache(int page_code) { int status; int bdlen; unsigned char *pagestart; SETUP_MODE_PAGE(8, 9); printf("Data from Caching Page\n"); printf("----------------------\n"); bitfield(pagestart + 2, "Write Cache", 1, 2); notbitfield(pagestart + 2, "Read Cache", 1, 0); bitfield(pagestart + 2, "Prefetch units", 1, 1); bitfield(pagestart + 3, "Demand Read Retention Priority", 0xf, 4); bitfield(pagestart + 3, "Demand Write Retention Priority", 0xf, 0); intfield(pagestart + 4, 2, "Disable Pre-fetch Transfer Length"); intfield(pagestart + 6, 2, "Minimum Pre-fetch"); intfield(pagestart + 8, 2, "Maximum Pre-fetch"); intfield(pagestart + 10, 2, "Maximum Pre-fetch Ceiling"); printf("\n"); return 0; } static int read_format_info(int page_code) { int status; int bdlen; unsigned char *pagestart; SETUP_MODE_PAGE(3, 13); printf("Data from Format Device Page\n"); printf("----------------------------\n"); bitfield(pagestart + 20, "Removable Medium", 1, 5); bitfield(pagestart + 20, "Supports Hard Sectoring", 1, 6); bitfield(pagestart + 20, "Supports Soft Sectoring", 1, 7); bitfield(pagestart + 20, "Addresses assigned by surface", 1, 4); intfield(pagestart + 2, 2, "Tracks per Zone"); intfield(pagestart + 4, 2, "Alternate sectors per zone"); intfield(pagestart + 6, 2, "Alternate tracks per zone"); intfield(pagestart + 8, 2, "Alternate tracks per lun"); intfield(pagestart + 10, 2, "Sectors per track"); intfield(pagestart + 12, 2, "Bytes per sector"); intfield(pagestart + 14, 2, "Interleave"); intfield(pagestart + 16, 2, "Track skew factor"); intfield(pagestart + 18, 2, "Cylinder skew factor"); printf("\n"); return 0; } static int verify_error_recovery(int page_code) { int status; int bdlen; unsigned char *pagestart; SETUP_MODE_PAGE(7, 7); printf("Data from Verify Error Recovery Page\n"); printf("------------------------------------\n"); bitfield(pagestart + 2, "EER", 1, 3); bitfield(pagestart + 2, "PER", 1, 2); bitfield(pagestart + 2, "DTE", 1, 1); bitfield(pagestart + 2, "DCR", 1, 0); intfield(pagestart + 3, 1, "Verify Retry Count"); intfield(pagestart + 4, 1, "Verify Correction Span (bits)"); intfield(pagestart + 10, 2, "Verify Recovery Time Limit (ms)"); printf("\n"); return 0; } static int peripheral_device_page(int page_code) { static char *idents[] = { "X3.131: Small Computer System Interface", "X3.91M-1987: Storage Module Interface", "X3.170: Enhanced Small Device Interface", "X3.130-1986; X3T9.3/87-002: IPI-2", "X3.132-1987; X3.147-1988: IPI-3" }; int status; int bdlen; unsigned ident; unsigned char *pagestart; char *name; SETUP_MODE_PAGE(9, 2); printf("Data from Peripheral Device Page\n"); printf("--------------------------------\n"); ident = getnbyte(pagestart + 2, 2); if (ident < (sizeof(idents) / sizeof(char *))) name = idents[ident]; else if (ident < 0x8000) name = "Reserved"; else name = "Vendor Specific"; bdlen = pagestart[1] - 6; if (bdlen < 0) bdlen = 0; else SETUP_MODE_PAGE(9, 2); hexfield(pagestart + 2, 2, "Interface Identifier"); for (ident = 0; ident < 35; ident++) putchar(' '); puts(name); hexdatafield(pagestart + 8, bdlen, "Vendor Specific Data"); printf("\n"); return 0; } /* end */ static int do_user_page(int page_code, int page_no) { int status; int bdlen; int i; //unsigned ident; unsigned char *pagestart; char *name; SETUP_MODE_PAGE(page_no, 0); //printf ("Page 0x%02x len: %i\n", page_code, pagestart[1]); name = "Vendor specific"; for (i = 2; i < pagestart[1] + 2; i++) { char nm[8]; snprintf(nm, 8, "%02x", i); hexdatafield(pagestart + i, 1, nm); } printf("\n"); puts(name); return 0; } /* end */ static int do_scsi_info_inquiry(int page_code) { int status, i, x_interface = 0; unsigned char *cmd; unsigned char *pagestart; unsigned char tmp; for (i = 0; i < 1024; i++) { buffer[i] = 0; } *((int *)buffer) = 0; /* length of input data */ *(((int *)buffer) + 1) = 36; /* length of output buffer */ cmd = (unsigned char *)(((int *)buffer) + 2); cmd[0] = 0x12; /* INQUIRY */ cmd[1] = 0x00; /* lun=0, evpd=0 */ cmd[2] = 0x00; /* page code = 0 */ cmd[3] = 0x00; /* (reserved) */ cmd[4] = 0x24; /* allocation length */ cmd[5] = 0x00; /* control */ status = do_sg_io(glob_fd, buffer); if (status) { printf("Error doing INQUIRY (1)"); return status; } pagestart = buffer + 8; printf("Inquiry command\n"); printf("---------------\n"); bitfield(pagestart + 7, "Relative Address", 1, 7); bitfield(pagestart + 7, "Wide bus 32", 1, 6); bitfield(pagestart + 7, "Wide bus 16", 1, 5); bitfield(pagestart + 7, "Synchronous neg.", 1, 4); bitfield(pagestart + 7, "Linked Commands", 1, 3); bitfield(pagestart + 7, "Command Queueing", 1, 1); bitfield(pagestart + 7, "SftRe", 1, 0); bitfield(pagestart + 0, "Device Type", 0x1f, 0); bitfield(pagestart + 0, "Peripheral Qualifier", 0x7, 5); bitfield(pagestart + 1, "Removable?", 1, 7); bitfield(pagestart + 1, "Device Type Modifier", 0x7f, 0); bitfield(pagestart + 2, "ISO Version", 3, 6); bitfield(pagestart + 2, "ECMA Version", 7, 3); bitfield(pagestart + 2, "ANSI Version", 7, 0); bitfield(pagestart + 3, "AENC", 1, 7); bitfield(pagestart + 3, "TrmIOP", 1, 6); bitfield(pagestart + 3, "Response Data Format", 0xf, 0); tmp = pagestart[16]; pagestart[16] = 0; printf("%s%s\n", (!x_interface ? "Vendor: " : ""), pagestart + 8); pagestart[16] = tmp; tmp = pagestart[32]; pagestart[32] = 0; printf("%s%s\n", (!x_interface ? "Product: " : ""), pagestart + 16); pagestart[32] = tmp; printf("%s%s\n", (!x_interface ? "Revision level: " : ""), pagestart + 32); printf("\n"); return status; } static int do_serial_number(int page_code) { int status, i, pagelen; unsigned char *cmd; unsigned char *pagestart; for (i = 0; i < 1024; i++) { buffer[i] = 0; } *((int *)buffer) = 0; /* length of input data */ *(((int *)buffer) + 1) = 4; /* length of output buffer */ cmd = (unsigned char *)(((int *)buffer) + 2); cmd[0] = 0x12; /* INQUIRY */ cmd[1] = 0x01; /* lun=0, evpd=1 */ cmd[2] = 0x80; /* page code = 0x80, serial number */ cmd[3] = 0x00; /* (reserved) */ cmd[4] = 0x04; /* allocation length */ cmd[5] = 0x00; /* control */ status = do_sg_io(glob_fd, buffer); if (status) { printf("Error doing INQUIRY (evpd=1, serial number)\n"); return status; } pagestart = buffer + 8; pagelen = 4 + pagestart[3]; *((int *)buffer) = 0; /* length of input data */ *(((int *)buffer) + 1) = pagelen; /* length of output buffer */ cmd[0] = 0x12; /* INQUIRY */ cmd[1] = 0x01; /* lun=0, evpd=1 */ cmd[2] = 0x80; /* page code = 0x80, serial number */ cmd[3] = 0x00; /* (reserved) */ cmd[4] = (unsigned char)pagelen; /* allocation length */ cmd[5] = 0x00; /* control */ status = do_sg_io(glob_fd, buffer); if (status) { printf("Error doing INQUIRY (evpd=1, serial number, len)\n"); return status; } printf("Serial Number '"); for (i = 0; i < pagestart[3]; i++) printf("%c", pagestart[4 + i]); printf("'\n"); printf("\n"); return status; } /* Print out a list of the known devices on the system */ static void show_devices() { int k, j, fd, err, bus; My_scsi_idlun m_idlun; char name[MDEV_NAME_SZ]; char ebuff[EBUFF_SZ]; int do_numeric = 1; int max_holes = MAX_HOLES; for (k = 0, j = 0; k < sizeof(devices) / sizeof(char *); k++) { fd = open(devices[k], O_RDONLY | O_NONBLOCK); if (fd < 0) continue; err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &(sg_map_arr[j].bus)); if (err < 0) { snprintf(ebuff, EBUFF_SZ, "SCSI(1) ioctl on %s failed", devices[k]); perror(ebuff); close(fd); continue; } err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun); if (err < 0) { snprintf(ebuff, EBUFF_SZ, "SCSI(2) ioctl on %s failed", devices[k]); perror(ebuff); close(fd); continue; } sg_map_arr[j].channel = (m_idlun.dev_id >> 16) & 0xff; sg_map_arr[j].lun = (m_idlun.dev_id >> 8) & 0xff; sg_map_arr[j].target_id = m_idlun.dev_id & 0xff; sg_map_arr[j].dev_name = devices[k]; printf("[scsi%d ch=%d id=%d lun=%d %s] ", sg_map_arr[j].bus, sg_map_arr[j].channel, sg_map_arr[j].target_id, sg_map_arr[j].lun, sg_map_arr[j].dev_name); ++j; printf("%s ", devices[k]); close(fd); }; printf("\n"); for (k = 0; k < MAX_SG_DEVS; k++) { make_dev_name(name, NULL, k, do_numeric); fd = open(name, O_RDWR | O_NONBLOCK); if (fd < 0) { if ((ENOENT == errno) && (0 == k)) { do_numeric = 0; make_dev_name(name, NULL, k, do_numeric); fd = open(name, O_RDWR | O_NONBLOCK); } if (fd < 0) { if (EBUSY == errno) continue; /* step over if O_EXCL already on it */ else { #if 0 snprintf(ebuff, EBUFF_SZ, "open on %s failed (%d)", name, errno); perror(ebuff); #endif if (max_holes-- > 0) continue; else break; } } } max_holes = MAX_HOLES; err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus); if (err < 0) { snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed", name); perror(ebuff); close(fd); continue; } err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun); if (err < 0) { snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed", name); perror(ebuff); close(fd); continue; } printf("[scsi%d ch=%d id=%d lun=%d %s]", bus, (m_idlun.dev_id >> 16) & 0xff, m_idlun.dev_id & 0xff, (m_idlun.dev_id >> 8) & 0xff, name); for (j = 0; sg_map_arr[j].dev_name; ++j) { if ((bus == sg_map_arr[j].bus) && ((m_idlun.dev_id & 0xff) == sg_map_arr[j].target_id) && (((m_idlun.dev_id >> 16) & 0xff) == sg_map_arr[j].channel) && (((m_idlun.dev_id >> 8) & 0xff) == sg_map_arr[j].lun)) { printf("%s [=%s scsi%d ch=%d id=%d lun=%d]\n", name, sg_map_arr[j].dev_name, bus, ((m_idlun.dev_id >> 16) & 0xff), m_idlun.dev_id & 0xff, ((m_idlun.dev_id >> 8) & 0xff)); break; } } if (NULL == sg_map_arr[j].dev_name) printf("%s [scsi%d ch=%d id=%d lun=%d]\n", name, bus, ((m_idlun.dev_id >> 16) & 0xff), m_idlun.dev_id & 0xff, ((m_idlun.dev_id >> 8) & 0xff)); close(fd); } printf("\n"); } static int show_pages(int page_code) { int offset; int length; int i; unsigned long long pages_sup = 0; unsigned long long pages_mask = 0; if (!get_mode_page10(0x3f, page_code | 0x10)) { length = 9 + getnbyte(buffer + 8, 2); offset = 16 + getnbyte(buffer + 14, 2); } else if (!get_mode_page(0x3f, page_code | 0x10)) { length = 9 + buffer[8]; offset = 12 + buffer[11]; } else { /* Assume SCSI-1 and fake settings to report NO pages */ offset = 10; length = 0; } /* Get mask of pages supported by prog: */ for (i = 0; i < MAX_PAGENO; i++) if (page_names[i]) pages_mask |= (1LL << i); /* Get pages listed in mode_pages */ while (offset < length) { pages_sup |= (1LL << (buffer[offset] & 0x3f)); offset += 2 + buffer[offset + 1]; } /* Mask out pages unsupported by this binary */ pages_sup &= pages_mask; /* Notch page supported? */ if (pages_sup & (1LL << 12)) { if (get_mode_page(12, 0)) return 2; offset = 12 + buffer[11]; } else { /* Fake empty notch page */ memset(buffer, 0, SIZEOF_BUFFER); offset = 0; } pages_mask = getnbyte(buffer + offset + 16, 4); pages_mask <<= 32; pages_mask += getnbyte(buffer + offset + 20, 4); puts("Mode Pages supported by this binary and target:"); puts("-----------------------------------------------"); for (i = 0; i < MAX_PAGENO; i++) if (pages_sup & (1LL << i)) printf("%02xh: %s Page%s\n", i, get_page_name(i), (pages_mask & (1LL << i)) ? " (notched)" : ""); if (pages_sup & (1LL << 12)) { printf("\nCurrent notch is %d.\n", getnbyte(buffer + offset + 6, 2)); } if (!pages_sup) puts("No mode pages supported (SCSI-1?)."); return 0; } static int open_sg_dev(char *devname) { int fd, err, bus, bbus, k; My_scsi_idlun m_idlun, mm_idlun; int do_numeric = 1; char name[DEVNAME_SZ]; struct stat a_st; int block_dev = 0; strncpy(name, devname, DEVNAME_SZ); name[DEVNAME_SZ - 1] = '\0'; fd = open(name, O_RDONLY); if (fd < 0) return fd; if (fstat(fd, &a_st) < 0) { fprintf(stderr, "could do fstat() on fd ??\n"); close(fd); return -9999; } if (S_ISBLK(a_st.st_mode)) block_dev = 1; if (block_dev || (ioctl(fd, SG_GET_TIMEOUT, 0) < 0)) { err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus); if (err < 0) { perror("A SCSI device name is required\n"); close(fd); return -9999; } err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun); if (err < 0) { perror("A SCSI device name is required\n"); close(fd); return -9999; } close(fd); for (k = 0; k < MAX_SG_DEVS; k++) { make_dev_name(name, NULL, k, do_numeric); fd = open(name, O_RDWR | O_NONBLOCK); if (fd < 0) { if ((ENOENT == errno) && (0 == k)) { do_numeric = 0; make_dev_name(name, NULL, k, do_numeric); fd = open(name, O_RDWR | O_NONBLOCK); } if (fd < 0) { if (EBUSY == errno) continue; /* step over if O_EXCL already on it */ else break; } } err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bbus); if (err < 0) { perror("sg ioctl failed"); close(fd); fd = -9999; } err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &mm_idlun); if (err < 0) { perror("sg ioctl failed"); close(fd); fd = -9999; } if ((bus == bbus) && ((m_idlun.dev_id & 0xff) == (mm_idlun.dev_id & 0xff)) && (((m_idlun.dev_id >> 8) & 0xff) == ((mm_idlun.dev_id >> 8) & 0xff)) && (((m_idlun.dev_id >> 16) & 0xff) == ((mm_idlun.dev_id >> 16) & 0xff))) break; else { close(fd); fd = -9999; } } } if (fd >= 0) { #ifdef SG_GET_RESERVED_SIZE int size; if (ioctl(fd, SG_GET_RESERVED_SIZE, &size) < 0) { fprintf(stderr, "Compiled with new driver, running on old!!\n"); close(fd); return -9999; } #endif close(fd); return open(name, O_RDWR); } else return fd; } int show_scsi_info(char *device) { int page_code = 0; int status = 0; print_msg(TEST_BREAK, __FUNCTION__); show_devices(); glob_fd = open_sg_dev(device); if (glob_fd < 0) { if (-9999 == glob_fd) fprintf(stderr, "Couldn't find sg device corresponding to %s\n", device); else { perror("sginfo(open)"); fprintf(stderr, "file=%s, or no corresponding sg device found\n", device); fprintf(stderr, "Is sg driver loaded?\n"); } return 1; } status |= do_scsi_info_inquiry(page_code); status |= do_serial_number(page_code); status |= read_geometry(page_code); status |= read_cache(page_code); status |= read_format_info(page_code); status |= error_recovery_page(page_code); status |= read_control_page(page_code); status |= read_disconnect_reconnect_data(page_code); status |= read_defect_list(page_code); status |= notch_parameters_page(page_code); status |= verify_error_recovery(page_code); status |= peripheral_device_page(page_code); status |= do_user_page(page_code, 0); status |= show_pages(page_code); return status; } /* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM), 2 -> try again */ int sg_read2(int sg_fd, unsigned char *buff, int blocks, int from_block, int bs, int cdbsz, int fua, int do_mmap) { unsigned char rdCmd[MAX_SCSI_CDBSZ]; unsigned char senseBuff[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; int res; if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) { fprintf(stderr, ME "bad rd cdb build, from_block=%d, blocks=%d\n", from_block, blocks); return -1; } memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = cdbsz; io_hdr.cmdp = rdCmd; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = bs * blocks; if (!do_mmap) io_hdr.dxferp = buff; io_hdr.mx_sb_len = SENSE_BUFF_LEN; io_hdr.sbp = senseBuff; io_hdr.timeout = DEF_TIMEOUT; io_hdr.pack_id = from_block; if (do_mmap) io_hdr.flags |= SG_FLAG_MMAP_IO; while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) && (EINTR == errno)) ; if (res < 0) { if (ENOMEM == errno) return 1; perror("reading (wr) on sg device, error"); return -1; } while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) && (EINTR == errno)) ; if (res < 0) { perror("reading (rd) on sg device, error"); return -1; } switch (sg_err_category3(&io_hdr)) { case SG_ERR_CAT_CLEAN: break; case SG_ERR_CAT_RECOVERED: fprintf(stderr, "Recovered error while reading block=%d, num=%d\n", from_block, blocks); break; case SG_ERR_CAT_MEDIA_CHANGED: return 2; default: sg_chk_n_print3("reading", &io_hdr); return -1; } sum_of_resids += io_hdr.resid; #if SG_DEBUG fprintf(stderr, "duration=%u ms\n", io_hdr.duration); #endif return 0; } /* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM), 2 -> try again */ int sg_write2(int sg_fd, unsigned char *buff, int blocks, int to_block, int bs, int cdbsz, int fua, int do_mmap, int *diop) { unsigned char wrCmd[MAX_SCSI_CDBSZ]; unsigned char senseBuff[SENSE_BUFF_LEN]; sg_io_hdr_t io_hdr; int res; if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) { fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n", to_block, blocks); return -1; } memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = cdbsz; io_hdr.cmdp = wrCmd; io_hdr.dxfer_direction = SG_DXFER_TO_DEV; io_hdr.dxfer_len = bs * blocks; if (!do_mmap) io_hdr.dxferp = buff; io_hdr.mx_sb_len = SENSE_BUFF_LEN; io_hdr.sbp = senseBuff; io_hdr.timeout = DEF_TIMEOUT; io_hdr.pack_id = to_block; if (do_mmap) io_hdr.flags |= SG_FLAG_MMAP_IO; if (diop && *diop) io_hdr.flags |= SG_FLAG_DIRECT_IO; while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) && (EINTR == errno)) ; if (res < 0) { if (ENOMEM == errno) return 1; perror("writing (wr) on sg device, error"); return -1; } while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) && (EINTR == errno)) ; if (res < 0) { perror("writing (rd) on sg device, error"); return -1; } switch (sg_err_category3(&io_hdr)) { case SG_ERR_CAT_CLEAN: break; case SG_ERR_CAT_RECOVERED: fprintf(stderr, "Recovered error while writing block=%d, num=%d\n", to_block, blocks); break; case SG_ERR_CAT_MEDIA_CHANGED: return 2; default: sg_chk_n_print3("writing", &io_hdr); return -1; } if (diop && *diop && ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) *diop = 0; /* flag that dio not done (completely) */ return 0; } int do_scsi_sgm_read_write(char *device) { int skip = 0; int seek = 0; int bs = 0; int bpt = DEF_BLOCKS_PER_TRANSFER; char inf[INOUTF_SZ]; int in_type = FT_OTHER; char outf[INOUTF_SZ]; int out_type = FT_OTHER; int res, t; int infd, outfd, blocks; unsigned char *wrkPos; unsigned char *wrkBuff = NULL; unsigned char *wrkMmap = NULL; int in_num_sect = 0; int in_res_sz = 0; int out_num_sect = 0; int out_res_sz = 0; int do_time = 1; int scsi_cdbsz = DEF_SCSI_CDBSZ; int do_sync = 1; int do_dio = 0; int num_dio_not_done = 0; int fua_mode = 0; int in_sect_sz, out_sect_sz; char ebuff[EBUFF_SZ]; int blocks_per; int req_count; size_t psz = getpagesize(); struct timeval start_tm, end_tm; print_msg(TEST_BREAK, __FUNCTION__); strcpy(inf, "/dev/zero"); strcpy(outf, device); install_handler(SIGINT, interrupt_handler); install_handler(SIGQUIT, interrupt_handler); install_handler(SIGPIPE, interrupt_handler); install_handler(SIGUSR1, siginfo_handler); infd = STDIN_FILENO; outfd = STDOUT_FILENO; in_type = dd_filetype(inf); if (FT_ST == in_type) { fprintf(stderr, ME "unable to use scsi tape device %s\n", inf); return 1; } else if (FT_SG == in_type) { if ((infd = open(inf, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for sg reading", inf); perror(ebuff); return 1; } res = ioctl(infd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30122)) { fprintf(stderr, ME "sg driver prior to 3.1.22\n"); return 1; } in_res_sz = bs * bpt; if (0 != (in_res_sz % psz)) /* round up to next page */ in_res_sz = ((in_res_sz / psz) + 1) * psz; if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) { perror(ME "SG_GET_RESERVED_SIZE error"); return 1; } if (in_res_sz > t) { if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) { perror(ME "SG_SET_RESERVED_SIZE error"); return 1; } } wrkMmap = mmap(NULL, in_res_sz, PROT_READ | PROT_WRITE, MAP_SHARED, infd, 0); if (MAP_FAILED == wrkMmap) { snprintf(ebuff, EBUFF_SZ, ME "error using mmap() on file: %s", inf); perror(ebuff); return 1; } } else { if ((infd = open(inf, O_RDONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for reading", inf); perror(ebuff); return 1; } else if (skip > 0) { llse_loff_t offset = skip; offset *= bs; /* could exceed 32 bits here! */ if (llse_llseek(infd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to " "required position on %s", inf); perror(ebuff); return 1; } } } if (outf[0] && ('-' != outf[0])) { out_type = dd_filetype(outf); if (FT_ST == out_type) { fprintf(stderr, ME "unable to use scsi tape device %s\n", outf); return 1; } else if (FT_SG == out_type) { if ((outfd = open(outf, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for " "sg writing", outf); perror(ebuff); return 1; } res = ioctl(outfd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30122)) { fprintf(stderr, ME "sg driver prior to 3.1.22\n"); return 1; } if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) { perror(ME "SG_GET_RESERVED_SIZE error"); return 1; } out_res_sz = bs * bpt; if (out_res_sz > t) { if (ioctl (outfd, SG_SET_RESERVED_SIZE, &out_res_sz) < 0) { perror(ME "SG_SET_RESERVED_SIZE error"); return 1; } } if (NULL == wrkMmap) { wrkMmap = mmap(NULL, out_res_sz, PROT_READ | PROT_WRITE, MAP_SHARED, outfd, 0); if (MAP_FAILED == wrkMmap) { snprintf(ebuff, EBUFF_SZ, ME "error using mmap() on file: %s", outf); perror(ebuff); return 1; } } } else if (FT_DEV_NULL == out_type) outfd = -1; /* don't bother opening */ else { if (FT_RAW != out_type) { if ((outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for writing", outf); perror(ebuff); return 1; } } else { if ((outfd = open(outf, O_WRONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s " "for raw writing", outf); perror(ebuff); return 1; } } if (seek > 0) { llse_loff_t offset = seek; offset *= bs; /* could exceed 32 bits here! */ if (llse_llseek(outfd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't seek to " "required position on %s", outf); perror(ebuff); return 1; } } } } if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) { fprintf(stderr, "Can't have both 'if' as stdin _and_ 'of' as stdout\n"); return 1; } #if 0 if ((FT_OTHER == in_type) && (FT_OTHER == out_type)) { fprintf(stderr, "Both 'if' and 'of' can't be ordinary files\n"); return 1; } #endif if (dd_count < 0) { if (FT_SG == in_type) { res = read_capacity(infd, &in_num_sect, &in_sect_sz); if (2 == res) { fprintf(stderr, "Unit attention, media changed(in), continuing\n"); res = read_capacity(infd, &in_num_sect, &in_sect_sz); } if (0 != res) { fprintf(stderr, "Unable to read capacity on %s\n", inf); in_num_sect = -1; } else { #if 0 if (0 == in_sect_sz) in_sect_sz = bs; else if (in_sect_sz > bs) in_num_sect *= (in_sect_sz / bs); else if (in_sect_sz < bs) in_num_sect /= (bs / in_sect_sz); #endif if (in_num_sect > skip) in_num_sect -= skip; } } if (FT_SG == out_type) { res = read_capacity(outfd, &out_num_sect, &out_sect_sz); if (2 == res) { fprintf(stderr, "Unit attention, media changed(out), continuing\n"); res = read_capacity(outfd, &out_num_sect, &out_sect_sz); } if (0 != res) { fprintf(stderr, "Unable to read capacity on %s\n", outf); out_num_sect = -1; } else { if (out_num_sect > seek) out_num_sect -= seek; } } #ifdef SG_DEBUG fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n", dd_count, in_num_sect, out_num_sect); #endif if (in_num_sect > 0) { if (out_num_sect > 0) dd_count = (in_num_sect > out_num_sect) ? out_num_sect : in_num_sect; else dd_count = in_num_sect; } else dd_count = out_num_sect; } if (dd_count < 0) { fprintf(stderr, "Couldn't calculate count, please give one\n"); return 1; } if (do_dio && (FT_SG != in_type)) { do_dio = 0; fprintf(stderr, ">>> dio only performed on 'of' side when 'if' is" " an sg device\n"); } if (do_dio) { int fd; char c; if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { if (1 == read(fd, &c, 1)) { if ('0' == c) fprintf(stderr, ">>> %s set to '0' but should be set " "to '1' for direct IO\n", proc_allow_dio); } close(fd); } } if (wrkMmap) wrkPos = wrkMmap; else { if ((FT_RAW == in_type) || (FT_RAW == out_type)) { wrkBuff = malloc(bs * bpt + psz); if (0 == wrkBuff) { fprintf(stderr, "Not enough user memory for raw\n"); return 1; } wrkPos = (unsigned char *)(((unsigned long)wrkBuff + psz - 1) & (~(psz - 1))); } else { wrkBuff = malloc(bs * bpt); if (0 == wrkBuff) { fprintf(stderr, "Not enough user memory\n"); return 1; } wrkPos = wrkBuff; } } blocks_per = bpt; #ifdef SG_DEBUG fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n", dd_count, blocks_per); #endif if (do_time) { start_tm.tv_sec = 0; start_tm.tv_usec = 0; gettimeofday(&start_tm, NULL); } req_count = dd_count; while (dd_count > 0) { blocks = (dd_count > blocks_per) ? blocks_per : dd_count; if (FT_SG == in_type) { int fua = fua_mode & 2; res = sg_read2(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, fua, 1); if (2 == res) { fprintf(stderr, "Unit attention, media changed, continuing (r)\n"); res = sg_read2(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, fua, 1); } if (0 != res) { fprintf(stderr, "sg_read2 failed, skip=%d\n", skip); break; } else in_full += blocks; } else { while (((res = read(infd, wrkPos, blocks * bs)) < 0) && (EINTR == errno)) ; if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%d ", skip); perror(ebuff); break; } else if (res < blocks * bs) { dd_count = 0; blocks = res / bs; if ((res % bs) > 0) { blocks++; in_partial++; } } in_full += blocks; } if (FT_SG == out_type) { int do_mmap = (FT_SG == in_type) ? 0 : 1; int fua = fua_mode & 1; int dio_res = do_dio; res = sg_write2(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz, fua, do_mmap, &dio_res); if (2 == res) { fprintf(stderr, "Unit attention, media changed, continuing (w)\n"); res = sg_write2(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz, fua, do_mmap, &dio_res); } else if (0 != res) { fprintf(stderr, "sg_write2 failed, seek=%d\n", seek); break; } else { out_full += blocks; if (do_dio && (0 == dio_res)) num_dio_not_done++; } } else if (FT_DEV_NULL == out_type) out_full += blocks; /* act as if written out without error */ else { while (((res = write(outfd, wrkPos, blocks * bs)) < 0) && (EINTR == errno)) ; if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%d ", seek); perror(ebuff); break; } else if (res < blocks * bs) { fprintf(stderr, "output file probably full, seek=%d ", seek); blocks = res / bs; out_full += blocks; if ((res % bs) > 0) out_partial++; break; } else out_full += blocks; } if (dd_count > 0) dd_count -= blocks; skip += blocks; seek += blocks; } if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { struct timeval res_tm; double a, b; gettimeofday(&end_tm, NULL); res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; if (res_tm.tv_usec < 0) { --res_tm.tv_sec; res_tm.tv_usec += 1000000; } a = res_tm.tv_sec; a += (0.000001 * res_tm.tv_usec); b = (double)bs *(req_count - dd_count); printf("time to transfer data was %d.%06d secs", (int)res_tm.tv_sec, (int)res_tm.tv_usec); if ((a > 0.00001) && (b > 511)) printf(", %.2f MB/sec\n", b / (a * 1000000.0)); else printf("\n"); } if (do_sync) { if (FT_SG == out_type) { fprintf(stderr, ">> Synchronizing cache on %s\n", outf); res = sync_cache(outfd); if (2 == res) { fprintf(stderr, "Unit attention, media changed(in), continuing\n"); res = sync_cache(outfd); } if (0 != res) fprintf(stderr, "Unable to synchronize cache\n"); } } if (wrkBuff) free(wrkBuff); if (STDIN_FILENO != infd) close(infd); if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type)) close(outfd); res = 0; if (0 != dd_count) { fprintf(stderr, "Some error occurred,"); res = 2; } print_stats(); if (sum_of_resids) fprintf(stderr, ">> Non-zero sum of residual counts=%d\n", sum_of_resids); if (num_dio_not_done) fprintf(stderr, ">> dio requested but _not done %d times\n", num_dio_not_done); return res; } static void guarded_stop_in(Rq_coll * clp) { pthread_mutex_lock(&clp->in_mutex); clp->in_stop = 1; pthread_mutex_unlock(&clp->in_mutex); } static void guarded_stop_out(Rq_coll * clp) { pthread_mutex_lock(&clp->out_mutex); clp->out_stop = 1; pthread_mutex_unlock(&clp->out_mutex); } static void guarded_stop_both(Rq_coll * clp) { guarded_stop_in(clp); guarded_stop_out(clp); } void *sig_listen_thread(void *v_clp) { Rq_coll *clp = (Rq_coll *) v_clp; int sig_number; while (1) { sigwait(&signal_set, &sig_number); if (SIGINT == sig_number) { fprintf(stderr, ME "interrupted by SIGINT\n"); guarded_stop_both(clp); pthread_cond_broadcast(&clp->out_sync_cv); } } return NULL; } void cleanup_in(void *v_clp) { Rq_coll *clp = (Rq_coll *) v_clp; fprintf(stderr, "thread cancelled while in mutex held\n"); clp->in_stop = 1; pthread_mutex_unlock(&clp->in_mutex); guarded_stop_out(clp); pthread_cond_broadcast(&clp->out_sync_cv); } void cleanup_out(void *v_clp) { Rq_coll *clp = (Rq_coll *) v_clp; fprintf(stderr, "thread cancelled while out mutex held\n"); clp->out_stop = 1; pthread_mutex_unlock(&clp->out_mutex); guarded_stop_in(clp); pthread_cond_broadcast(&clp->out_sync_cv); } void *read_write_thread(void *v_clp) { Rq_coll *clp = (Rq_coll *) v_clp; Rq_elem rel; Rq_elem *rep = &rel; size_t psz = 0; int sz = clp->bpt * clp->bs; int stop_after_write = 0; int seek_skip = clp->seek - clp->skip; int blocks, status; memset(rep, 0, sizeof(Rq_elem)); psz = getpagesize(); if (NULL == (rep->alloc_bp = malloc(sz + psz))) err_exit(ENOMEM, "out of memory creating user buffers\n"); rep->buffp = (unsigned char *)(((unsigned long)rep->alloc_bp + psz - 1) & (~(psz - 1))); /* Follow clp members are constant during lifetime of thread */ rep->bs = clp->bs; rep->fua_mode = clp->fua_mode; rep->dio = clp->dio; rep->infd = clp->infd; rep->outfd = clp->outfd; rep->debug = clp->debug; rep->in_scsi_type = clp->in_scsi_type; rep->out_scsi_type = clp->out_scsi_type; rep->cdbsz = clp->cdbsz; while (1) { status = pthread_mutex_lock(&clp->in_mutex); if (0 != status) err_exit(status, "lock in_mutex"); if (clp->in_stop || (clp->in_count <= 0)) { /* no more to do, exit loop then thread */ status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); break; } blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count; rep->wr = 0; rep->blk = clp->in_blk; rep->num_blks = blocks; clp->in_blk += blocks; clp->in_count -= blocks; pthread_cleanup_push(cleanup_in, (void *)clp); if (FT_SG == clp->in_type) sg_in_operation(clp, rep); /* lets go of in_mutex mid operation */ else { stop_after_write = normal_in_operation(clp, rep, blocks); status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); } pthread_cleanup_pop(0); status = pthread_mutex_lock(&clp->out_mutex); if (0 != status) err_exit(status, "lock out_mutex"); if (FT_DEV_NULL != clp->out_type) { while ((!clp->out_stop) && ((rep->blk + seek_skip) != clp->out_blk)) { /* if write would be out of sequence then wait */ pthread_cleanup_push(cleanup_out, (void *)clp); status = pthread_cond_wait(&clp->out_sync_cv, &clp->out_mutex); if (0 != status) err_exit(status, "cond out_sync_cv"); pthread_cleanup_pop(0); } } if (clp->out_stop || (clp->out_count <= 0)) { if (!clp->out_stop) clp->out_stop = 1; status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); break; } if (stop_after_write) clp->out_stop = 1; rep->wr = 1; rep->blk = clp->out_blk; /* rep->num_blks = blocks; */ clp->out_blk += blocks; clp->out_count -= blocks; pthread_cleanup_push(cleanup_out, (void *)clp); if (FT_SG == clp->out_type) sg_out_operation(clp, rep); /* releases out_mutex mid operation */ else if (FT_DEV_NULL == clp->out_type) { /* skip actual write operation */ clp->out_done_count -= blocks; status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); } else { normal_out_operation(clp, rep, blocks); status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); } pthread_cleanup_pop(0); if (stop_after_write) break; pthread_cond_broadcast(&clp->out_sync_cv); } /* end of while loop */ if (rep->alloc_bp) free(rep->alloc_bp); status = pthread_mutex_lock(&clp->in_mutex); if (0 != status) err_exit(status, "lock in_mutex"); if (!clp->in_stop) clp->in_stop = 1; /* flag other workers to stop */ status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); pthread_cond_broadcast(&clp->out_sync_cv); return stop_after_write ? NULL : v_clp; } int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks) { int res; int stop_after_write = 0; /* enters holding in_mutex */ while (((res = read(clp->infd, rep->buffp, blocks * clp->bs)) < 0) && (EINTR == errno)) ; if (res < 0) { if (clp->coe) { memset(rep->buffp, 0, rep->num_blks * rep->bs); fprintf(stderr, ">> substituted zeros for in blk=%d for " "%d bytes, %s\n", rep->blk, rep->num_blks * rep->bs, strerror(errno)); res = rep->num_blks * clp->bs; } else { fprintf(stderr, "error in normal read, %s\n", strerror(errno)); clp->in_stop = 1; guarded_stop_out(clp); return 1; } } if (res < blocks * clp->bs) { int o_blocks = blocks; stop_after_write = 1; blocks = res / clp->bs; if ((res % clp->bs) > 0) { blocks++; clp->in_partial++; } /* Reverse out + re-apply blocks on clp */ clp->in_blk -= o_blocks; clp->in_count += o_blocks; rep->num_blks = blocks; clp->in_blk += blocks; clp->in_count -= blocks; } clp->in_done_count -= blocks; return stop_after_write; } void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks) { int res; /* enters holding out_mutex */ while (((res = write(clp->outfd, rep->buffp, rep->num_blks * clp->bs)) < 0) && (EINTR == errno)) ; if (res < 0) { if (clp->coe) { fprintf(stderr, ">> ignored error for out blk=%d for " "%d bytes, %s\n", rep->blk, rep->num_blks * rep->bs, strerror(errno)); res = rep->num_blks * clp->bs; } else { fprintf(stderr, "error normal write, %s\n", strerror(errno)); guarded_stop_in(clp); clp->out_stop = 1; return; } } if (res < blocks * clp->bs) { blocks = res / clp->bs; if ((res % clp->bs) > 0) { blocks++; clp->out_partial++; } rep->num_blks = blocks; } clp->out_done_count -= blocks; } void sg_in_operation(Rq_coll * clp, Rq_elem * rep) { int res; int status; /* enters holding in_mutex */ while (1) { res = sg_start_io(rep); if (1 == res) err_exit(ENOMEM, "sg starting in command"); else if (res < 0) { fprintf(stderr, ME "inputting to sg failed, blk=%d\n", rep->blk); status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); guarded_stop_both(clp); return; } /* Now release in mutex to let other reads run in parallel */ status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); res = sg_finish_io(rep->wr, rep, &clp->aux_mutex); if (res < 0) { if (clp->coe) { memset(rep->buffp, 0, rep->num_blks * rep->bs); fprintf(stderr, ">> substituted zeros for in blk=%d for " "%d bytes\n", rep->blk, rep->num_blks * rep->bs); } else { fprintf(stderr, "error finishing sg in command\n"); guarded_stop_both(clp); return; } } if (res <= 0) { /* looks good, going to return */ if (rep->dio_incomplete || rep->resid) { status = pthread_mutex_lock(&clp->aux_mutex); if (0 != status) err_exit(status, "lock aux_mutex"); clp->dio_incomplete += rep->dio_incomplete; clp->sum_of_resids += rep->resid; status = pthread_mutex_unlock(&clp->aux_mutex); if (0 != status) err_exit(status, "unlock aux_mutex"); } status = pthread_mutex_lock(&clp->in_mutex); if (0 != status) err_exit(status, "lock in_mutex"); clp->in_done_count -= rep->num_blks; status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); return; } /* else assume 1 == res so try again with same addr, count info */ /* now re-acquire read mutex for balance */ /* N.B. This re-read could now be out of read sequence */ status = pthread_mutex_lock(&clp->in_mutex); if (0 != status) err_exit(status, "lock in_mutex"); } } void sg_out_operation(Rq_coll * clp, Rq_elem * rep) { int res; int status; /* enters holding out_mutex */ while (1) { res = sg_start_io(rep); if (1 == res) err_exit(ENOMEM, "sg starting out command"); else if (res < 0) { fprintf(stderr, ME "outputting from sg failed, blk=%d\n", rep->blk); status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); guarded_stop_both(clp); return; } /* Now release in mutex to let other reads run in parallel */ status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); res = sg_finish_io(rep->wr, rep, &clp->aux_mutex); if (res < 0) { if (clp->coe) fprintf(stderr, ">> ignored error for out blk=%d for " "%d bytes\n", rep->blk, rep->num_blks * rep->bs); else { fprintf(stderr, "error finishing sg out command\n"); guarded_stop_both(clp); return; } } if (res <= 0) { if (rep->dio_incomplete || rep->resid) { status = pthread_mutex_lock(&clp->aux_mutex); if (0 != status) err_exit(status, "lock aux_mutex"); clp->dio_incomplete += rep->dio_incomplete; clp->sum_of_resids += rep->resid; status = pthread_mutex_unlock(&clp->aux_mutex); if (0 != status) err_exit(status, "unlock aux_mutex"); } status = pthread_mutex_lock(&clp->out_mutex); if (0 != status) err_exit(status, "lock out_mutex"); clp->out_done_count -= rep->num_blks; status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); return; } /* else assume 1 == res so try again with same addr, count info */ /* now re-acquire out mutex for balance */ /* N.B. This re-write could now be out of write sequence */ status = pthread_mutex_lock(&clp->out_mutex); if (0 != status) err_exit(status, "lock out_mutex"); } } int sg_start_io(Rq_elem * rep) { sg_io_hdr_t *hp = &rep->io_hdr; int fua = rep->wr ? (rep->fua_mode & 1) : (rep->fua_mode & 2); int res; if (sg_build_scsi_cdb(rep->cmd, rep->cdbsz, rep->num_blks, rep->blk, rep->wr, fua, 0)) { fprintf(stderr, ME "bad cdb build, start_blk=%d, blocks=%d\n", rep->blk, rep->num_blks); return -1; } memset(hp, 0, sizeof(sg_io_hdr_t)); hp->interface_id = 'S'; hp->cmd_len = rep->cdbsz; hp->cmdp = rep->cmd; hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; hp->dxfer_len = rep->bs * rep->num_blks; hp->dxferp = rep->buffp; hp->mx_sb_len = sizeof(rep->sb); hp->sbp = rep->sb; hp->timeout = DEF_TIMEOUT; hp->usr_ptr = rep; hp->pack_id = rep->blk; if (rep->dio) hp->flags |= SG_FLAG_DIRECT_IO; if (rep->debug > 8) { fprintf(stderr, "sg_start_io: SCSI %s, blk=%d num_blks=%d\n", rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks); sg_print_command(hp->cmdp); fprintf(stderr, "dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n", hp->dxfer_direction, hp->dxfer_len, hp->dxferp, hp->cmd_len); } while (((res = write(rep->wr ? rep->outfd : rep->infd, hp, sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ; if (res < 0) { if (ENOMEM == errno) return 1; perror("starting io on sg device, error"); return -1; } return 0; } /* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */ int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp) { int res, status; sg_io_hdr_t io_hdr; sg_io_hdr_t *hp; #if 0 static int testing = 0; /* thread dubious! */ #endif memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); /* FORCE_PACK_ID active set only read packet with matching pack_id */ io_hdr.interface_id = 'S'; io_hdr.dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; io_hdr.pack_id = rep->blk; while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr, sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ; if (res < 0) { perror("finishing io on sg device, error"); return -1; } if (rep != (Rq_elem *) io_hdr.usr_ptr) err_exit(0, "sg_finish_io: bad usr_ptr, request-response mismatch\n"); memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t)); hp = &rep->io_hdr; switch (sg_err_category3(hp)) { case SG_ERR_CAT_CLEAN: break; case SG_ERR_CAT_RECOVERED: fprintf(stderr, "Recovered error on block=%d, num=%d\n", rep->blk, rep->num_blks); break; case SG_ERR_CAT_MEDIA_CHANGED: return 1; default: { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "%s blk=%d", rep->wr ? "writing" : "reading", rep->blk); status = pthread_mutex_lock(a_mutp); if (0 != status) err_exit(status, "lock aux_mutex"); sg_chk_n_print3(ebuff, hp); status = pthread_mutex_unlock(a_mutp); if (0 != status) err_exit(status, "unlock aux_mutex"); return -1; } } #if 0 if (0 == (++testing % 100)) return -1; #endif if (rep->dio && ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) rep->dio_incomplete = 1; /* count dios done as indirect IO */ else rep->dio_incomplete = 0; rep->resid = hp->resid; if (rep->debug > 8) fprintf(stderr, "sg_finish_io: completed %s\n", wr ? "WRITE" : "READ"); return 0; } int sg_prepare(int fd, int bs, int bpt, int *scsi_typep) { int res, t; res = ioctl(fd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30000)) { fprintf(stderr, ME "sg driver prior to 3.x.y\n"); return 1; } res = 0; t = bs * bpt; res = ioctl(fd, SG_SET_RESERVED_SIZE, &t); if (res < 0) perror(ME "SG_SET_RESERVED_SIZE error"); t = 1; res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t); if (res < 0) perror(ME "SG_SET_FORCE_PACK_ID error"); if (scsi_typep) { struct sg_scsi_id info; res = ioctl(fd, SG_GET_SCSI_ID, &info); if (res < 0) perror(ME "SG_SET_SCSI_ID error"); *scsi_typep = info.scsi_type; } return 0; } int do_scsi_sgp_read_write(char *device) { int skip = 0; int seek = 0; int count = -1; char inf[INOUTF_SZ]; char outf[INOUTF_SZ]; int res, k; int in_num_sect = 0; int out_num_sect = 0; int num_threads = DEF_NUM_THREADS; pthread_t threads[MAX_NUM_THREADS]; int do_time = 1; int do_sync = 1; int in_sect_sz, out_sect_sz, status, infull, outfull; void *vp; char ebuff[EBUFF_SZ]; struct timeval start_tm, end_tm; Rq_coll rcoll; print_msg(TEST_BREAK, __FUNCTION__); memset(&rcoll, 0, sizeof(Rq_coll)); rcoll.bpt = DEF_BLOCKS_PER_TRANSFER; rcoll.in_type = FT_OTHER; rcoll.out_type = FT_OTHER; rcoll.cdbsz = DEF_SCSI_CDBSZ; strcpy(inf, "/dev/zero"); strcpy(outf, device); if (rcoll.bs <= 0) { rcoll.bs = DEF_BLOCK_SIZE; fprintf(stderr, "Assume default 'bs' (block size) of %d bytes\n", rcoll.bs); } if (rcoll.debug) fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n", inf, skip, outf, seek, count); rcoll.infd = STDIN_FILENO; rcoll.outfd = STDOUT_FILENO; if (inf[0] && ('-' != inf[0])) { rcoll.in_type = dd_filetype(inf); if (FT_ST == rcoll.in_type) { fprintf(stderr, ME "unable to use scsi tape device %s\n", inf); return 1; } else if (FT_SG == rcoll.in_type) { if ((rcoll.infd = open(inf, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for sg reading", inf); perror(ebuff); return 1; } if (sg_prepare(rcoll.infd, rcoll.bs, rcoll.bpt, &rcoll.in_scsi_type)) return 1; } else { if ((rcoll.infd = open(inf, O_RDONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for reading", inf); perror(ebuff); return 1; } else if (skip > 0) { llse_loff_t offset = skip; offset *= rcoll.bs; /* could exceed 32 here! */ if (llse_llseek(rcoll.infd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to required position on %s", inf); perror(ebuff); return 1; } } } } if (outf[0] && ('-' != outf[0])) { rcoll.out_type = dd_filetype(outf); if (FT_ST == rcoll.out_type) { fprintf(stderr, ME "unable to use scsi tape device %s\n", outf); return 1; } else if (FT_SG == rcoll.out_type) { if ((rcoll.outfd = open(outf, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for sg writing", outf); perror(ebuff); return 1; } if (sg_prepare(rcoll.outfd, rcoll.bs, rcoll.bpt, &rcoll.out_scsi_type)) return 1; } else if (FT_DEV_NULL == rcoll.out_type) rcoll.outfd = -1; /* don't bother opening */ else { if (FT_RAW != rcoll.out_type) { if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for writing", outf); perror(ebuff); return 1; } } else { if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for raw writing", outf); perror(ebuff); return 1; } } if (seek > 0) { llse_loff_t offset = seek; offset *= rcoll.bs; /* could exceed 32 bits here! */ if (llse_llseek(rcoll.outfd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't seek to required position on %s", outf); perror(ebuff); return 1; } } } } if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) { fprintf(stderr, "Disallow both if and of to be stdin and stdout"); return 1; } if (count < 0) { if (FT_SG == rcoll.in_type) { res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz); if (2 == res) { fprintf(stderr, "Unit attention, media changed(in), continuing\n"); res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz); } if (0 != res) { fprintf(stderr, "Unable to read capacity on %s\n", inf); in_num_sect = -1; } else { if (in_num_sect > skip) in_num_sect -= skip; } } if (FT_SG == rcoll.out_type) { res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz); if (2 == res) { fprintf(stderr, "Unit attention, media changed(out), continuing\n"); res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz); } if (0 != res) { fprintf(stderr, "Unable to read capacity on %s\n", outf); out_num_sect = -1; } else { if (out_num_sect > seek) out_num_sect -= seek; } } if (in_num_sect > 0) { if (out_num_sect > 0) count = (in_num_sect > out_num_sect) ? out_num_sect : in_num_sect; else count = in_num_sect; } else count = out_num_sect; } if (rcoll.debug > 1) fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, " "out_num_sect=%d\n", count, in_num_sect, out_num_sect); if (count < 0) { fprintf(stderr, "Couldn't calculate count, please give one\n"); return 1; } rcoll.in_count = count; rcoll.in_done_count = count; rcoll.skip = skip; rcoll.in_blk = skip; rcoll.out_count = count; rcoll.out_done_count = count; rcoll.seek = seek; rcoll.out_blk = seek; status = pthread_mutex_init(&rcoll.in_mutex, NULL); if (0 != status) err_exit(status, "init in_mutex"); status = pthread_mutex_init(&rcoll.out_mutex, NULL); if (0 != status) err_exit(status, "init out_mutex"); status = pthread_mutex_init(&rcoll.aux_mutex, NULL); if (0 != status) err_exit(status, "init aux_mutex"); status = pthread_cond_init(&rcoll.out_sync_cv, NULL); if (0 != status) err_exit(status, "init out_sync_cv"); sigemptyset(&signal_set); sigaddset(&signal_set, SIGINT); status = pthread_sigmask(SIG_BLOCK, &signal_set, NULL); if (0 != status) err_exit(status, "pthread_sigmask"); status = pthread_create(&sig_listen_thread_id, NULL, sig_listen_thread, (void *)&rcoll); if (0 != status) err_exit(status, "pthread_create, sig..."); if (do_time) { start_tm.tv_sec = 0; start_tm.tv_usec = 0; gettimeofday(&start_tm, NULL); } /* vvvvvvvvvvv Start worker threads vvvvvvvvvvvvvvvvvvvvvvvv */ if ((rcoll.out_done_count > 0) && (num_threads > 0)) { /* Run 1 work thread to shake down infant retryable stuff */ status = pthread_mutex_lock(&rcoll.out_mutex); if (0 != status) err_exit(status, "lock out_mutex"); status = pthread_create(&threads[0], NULL, read_write_thread, (void *)&rcoll); if (0 != status) err_exit(status, "pthread_create"); if (rcoll.debug) fprintf(stderr, "Starting worker thread k=0\n"); /* wait for any broadcast */ pthread_cleanup_push(cleanup_out, (void *)&rcoll); status = pthread_cond_wait(&rcoll.out_sync_cv, &rcoll.out_mutex); if (0 != status) err_exit(status, "cond out_sync_cv"); pthread_cleanup_pop(0); status = pthread_mutex_unlock(&rcoll.out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); /* now start the rest of the threads */ for (k = 1; k < num_threads; ++k) { status = pthread_create(&threads[k], NULL, read_write_thread, (void *)&rcoll); if (0 != status) err_exit(status, "pthread_create"); if (rcoll.debug) fprintf(stderr, "Starting worker thread k=%d\n", k); } /* now wait for worker threads to finish */ for (k = 0; k < num_threads; ++k) { status = pthread_join(threads[k], &vp); if (0 != status) err_exit(status, "pthread_join"); if (rcoll.debug) fprintf(stderr, "Worker thread k=%d terminated\n", k); } } if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { struct timeval res_tm; double a, b; gettimeofday(&end_tm, NULL); res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; if (res_tm.tv_usec < 0) { --res_tm.tv_sec; res_tm.tv_usec += 1000000; } a = res_tm.tv_sec; a += (0.000001 * res_tm.tv_usec); b = (double)rcoll.bs * (count - rcoll.out_done_count); printf("time to transfer data was %d.%06d secs", (int)res_tm.tv_sec, (int)res_tm.tv_usec); if ((a > 0.00001) && (b > 511)) printf(", %.2f MB/sec\n", b / (a * 1000000.0)); else printf("\n"); } if (do_sync) { if (FT_SG == rcoll.out_type) { fprintf(stderr, ">> Synchronizing cache on %s\n", outf); res = sync_cache(rcoll.outfd); if (2 == res) { fprintf(stderr, "Unit attention, media changed(in), continuing\n"); res = sync_cache(rcoll.outfd); } if (0 != res) fprintf(stderr, "Unable to synchronize cache\n"); } } status = pthread_cancel(sig_listen_thread_id); if (0 != status) err_exit(status, "pthread_cancel"); if (STDIN_FILENO != rcoll.infd) close(rcoll.infd); if ((STDOUT_FILENO != rcoll.outfd) && (FT_DEV_NULL != rcoll.out_type)) close(rcoll.outfd); res = 0; if (0 != rcoll.out_count) { fprintf(stderr, ">>>> Some error occurred, remaining blocks=%d\n", rcoll.out_count); res = 2; } infull = count - rcoll.in_done_count - rcoll.in_partial; fprintf(stderr, "%d+%d records in\n", infull, rcoll.in_partial); outfull = count - rcoll.out_done_count - rcoll.out_partial; fprintf(stderr, "%d+%d records out\n", outfull, rcoll.out_partial); if (rcoll.dio_incomplete) { int fd; char c; fprintf(stderr, ">> Direct IO requested but incomplete %d times\n", rcoll.dio_incomplete); if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { if (1 == read(fd, &c, 1)) { if ('0' == c) fprintf(stderr, ">>> %s set to '0' but should be set " "to '1' for direct IO\n", proc_allow_dio); } close(fd); } } if (rcoll.sum_of_resids) fprintf(stderr, ">> Non-zero sum of residual counts=%d\n", rcoll.sum_of_resids); return res; }