diff options
Diffstat (limited to 'sbin/camcontrol/camcontrol.c')
-rw-r--r-- | sbin/camcontrol/camcontrol.c | 262 |
1 files changed, 202 insertions, 60 deletions
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index e24fff1f13ef..a3c7f0515c9d 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -123,6 +123,7 @@ typedef enum { CAM_ARG_DEBUG_CDB = 0x08000000, CAM_ARG_DEBUG_XPT = 0x10000000, CAM_ARG_DEBUG_PERIPH = 0x20000000, + CAM_ARG_DEBUG_PROBE = 0x40000000, } cam_argmask; struct camcontrol_opts { @@ -176,7 +177,7 @@ static struct camcontrol_opts option_table[] = { {"tags", CAM_CMD_TAG, CAM_ARG_NONE, "N:q"}, {"negotiate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts}, {"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts}, - {"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXc"}, + {"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXcp"}, {"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qrwy"}, {"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"}, {"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"}, @@ -1025,11 +1026,11 @@ camxferrate(struct cam_device *device) if (sas->valid & CTS_SAS_VALID_SPEED) speed = sas->bitrate; } else if (ccb->cts.transport == XPORT_ATA) { - struct ccb_trans_settings_ata *ata = + struct ccb_trans_settings_pata *pata = &ccb->cts.xport_specific.ata; - if (ata->valid & CTS_ATA_VALID_MODE) - speed = ata_mode2speed(ata->mode); + if (pata->valid & CTS_ATA_VALID_MODE) + speed = ata_mode2speed(pata->mode); } else if (ccb->cts.transport == XPORT_SATA) { struct ccb_trans_settings_sata *sata = &ccb->cts.xport_specific.sata; @@ -1072,16 +1073,16 @@ camxferrate(struct cam_device *device) fprintf(stdout, ")"); } } else if (ccb->cts.transport == XPORT_ATA) { - struct ccb_trans_settings_ata *ata = + struct ccb_trans_settings_pata *pata = &ccb->cts.xport_specific.ata; printf(" ("); - if (ata->valid & CTS_ATA_VALID_MODE) - printf("%s, ", ata_mode2string(ata->mode)); - if ((ata->valid & CTS_ATA_VALID_ATAPI) && ata->atapi != 0) - printf("ATAPI %dbytes, ", ata->atapi); - if (ata->valid & CTS_ATA_VALID_BYTECOUNT) - printf("PIO %dbytes", ata->bytecount); + if (pata->valid & CTS_ATA_VALID_MODE) + printf("%s, ", ata_mode2string(pata->mode)); + if ((pata->valid & CTS_ATA_VALID_ATAPI) && pata->atapi != 0) + printf("ATAPI %dbytes, ", pata->atapi); + if (pata->valid & CTS_ATA_VALID_BYTECOUNT) + printf("PIO %dbytes", pata->bytecount); printf(")"); } else if (ccb->cts.transport == XPORT_SATA) { struct ccb_trans_settings_sata *sata = @@ -1798,13 +1799,14 @@ readdefects(struct cam_device *device, int argc, char **argv, union ccb *ccb = NULL; struct scsi_read_defect_data_10 *rdd_cdb; u_int8_t *defect_list = NULL; - u_int32_t dlist_length = 65000; + u_int32_t max_dlist_length = SRDD10_MAX_LENGTH, dlist_length = 0; u_int32_t returned_length = 0; u_int32_t num_returned = 0; u_int8_t returned_format; unsigned int i; int c, error = 0; - int lists_specified = 0; + int lists_specified; + int get_length = 1; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch(c){ @@ -1841,20 +1843,33 @@ readdefects(struct cam_device *device, int argc, char **argv, ccb = cam_getccb(device); /* - * Hopefully 65000 bytes is enough to hold the defect list. If it - * isn't, the disk is probably dead already. We'd have to go with - * 12 byte command (i.e. alloc_length is 32 bits instead of 16) - * to hold them all. + * Eventually we should probably support the 12 byte READ DEFECT + * DATA command. It supports a longer parameter list, which may be + * necessary on newer drives with lots of defects. According to + * the SBC-3 spec, drives are supposed to return an illegal request + * if they have more defect data than will fit in 64K. */ - defect_list = malloc(dlist_length); + defect_list = malloc(max_dlist_length); if (defect_list == NULL) { warnx("can't malloc memory for defect list"); error = 1; goto defect_bailout; } + /* + * We start off asking for just the header to determine how much + * defect data is available. Some Hitachi drives return an error + * if you ask for more data than the drive has. Once we know the + * length, we retry the command with the returned length. + */ + dlist_length = sizeof(struct scsi_read_defect_data_hdr_10); + rdd_cdb =(struct scsi_read_defect_data_10 *)&ccb->csio.cdb_io.cdb_bytes; +retry: + + lists_specified = 0; + /* * cam_getccb() zeros the CCB header only. So we need to zero the * payload portion of the ccb. @@ -1916,6 +1931,51 @@ readdefects(struct cam_device *device, int argc, char **argv, returned_length = scsi_2btoul(((struct scsi_read_defect_data_hdr_10 *)defect_list)->length); + if (get_length != 0) { + get_length = 0; + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == + CAM_SCSI_STATUS_ERROR) { + struct scsi_sense_data *sense; + int error_code, sense_key, asc, ascq; + + sense = &ccb->csio.sense_data; + scsi_extract_sense_len(sense, ccb->csio.sense_len - + ccb->csio.sense_resid, &error_code, &sense_key, + &asc, &ascq, /*show_errors*/ 1); + + /* + * If the drive is reporting that it just doesn't + * support the defect list format, go ahead and use + * the length it reported. Otherwise, the length + * may not be valid, so use the maximum. + */ + if ((sense_key == SSD_KEY_RECOVERED_ERROR) + && (asc == 0x1c) && (ascq == 0x00) + && (returned_length > 0)) { + dlist_length = returned_length + + sizeof(struct scsi_read_defect_data_hdr_10); + dlist_length = min(dlist_length, + SRDD10_MAX_LENGTH); + } else + dlist_length = max_dlist_length; + } else if ((ccb->ccb_h.status & CAM_STATUS_MASK) != + CAM_REQ_CMP){ + error = 1; + warnx("Error reading defect header"); + if (arglist & CAM_ARG_VERBOSE) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + goto defect_bailout; + } else { + dlist_length = returned_length + + sizeof(struct scsi_read_defect_data_hdr_10); + dlist_length = min(dlist_length, SRDD10_MAX_LENGTH); + } + + goto retry; + } + returned_format = ((struct scsi_read_defect_data_hdr_10 *) defect_list)->format; @@ -2640,6 +2700,10 @@ camdebug(int argc, char **argv, char *combinedopt) arglist |= CAM_ARG_DEBUG_CDB; ccb.cdbg.flags |= CAM_DEBUG_CDB; break; + case 'p': + arglist |= CAM_ARG_DEBUG_PROBE; + ccb.cdbg.flags |= CAM_DEBUG_PROBE; + break; default: break; } @@ -2669,7 +2733,7 @@ camdebug(int argc, char **argv, char *combinedopt) ccb.cdbg.flags = CAM_DEBUG_NONE; arglist &= ~(CAM_ARG_DEBUG_INFO|CAM_ARG_DEBUG_PERIPH| CAM_ARG_DEBUG_TRACE|CAM_ARG_DEBUG_SUBTRACE| - CAM_ARG_DEBUG_XPT); + CAM_ARG_DEBUG_XPT|CAM_ARG_DEBUG_PROBE); } else if (strncmp(tstr, "all", 3) != 0) { tmpstr = (char *)strtok(tstr, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')){ @@ -2891,21 +2955,45 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts) "enabled" : "disabled"); } } + if (cts->transport == XPORT_FC) { + struct ccb_trans_settings_fc *fc = + &cts->xport_specific.fc; + + if (fc->valid & CTS_FC_VALID_WWNN) + fprintf(stdout, "%sWWNN: 0x%llx", pathstr, + (long long) fc->wwnn); + if (fc->valid & CTS_FC_VALID_WWPN) + fprintf(stdout, "%sWWPN: 0x%llx", pathstr, + (long long) fc->wwpn); + if (fc->valid & CTS_FC_VALID_PORT) + fprintf(stdout, "%sPortID: 0x%x", pathstr, fc->port); + if (fc->valid & CTS_FC_VALID_SPEED) + fprintf(stdout, "%stransfer speed: %d.%03dMB/s\n", + pathstr, fc->bitrate / 1000, fc->bitrate % 1000); + } + if (cts->transport == XPORT_SAS) { + struct ccb_trans_settings_sas *sas = + &cts->xport_specific.sas; + + if (sas->valid & CTS_SAS_VALID_SPEED) + fprintf(stdout, "%stransfer speed: %d.%03dMB/s\n", + pathstr, sas->bitrate / 1000, sas->bitrate % 1000); + } if (cts->transport == XPORT_ATA) { - struct ccb_trans_settings_ata *ata = + struct ccb_trans_settings_pata *pata = &cts->xport_specific.ata; - if ((ata->valid & CTS_ATA_VALID_MODE) != 0) { + if ((pata->valid & CTS_ATA_VALID_MODE) != 0) { fprintf(stdout, "%sATA mode: %s\n", pathstr, - ata_mode2string(ata->mode)); + ata_mode2string(pata->mode)); } - if ((ata->valid & CTS_ATA_VALID_ATAPI) != 0) { + if ((pata->valid & CTS_ATA_VALID_ATAPI) != 0) { fprintf(stdout, "%sATAPI packet length: %d\n", pathstr, - ata->atapi); + pata->atapi); } - if ((ata->valid & CTS_ATA_VALID_BYTECOUNT) != 0) { + if ((pata->valid & CTS_ATA_VALID_BYTECOUNT) != 0) { fprintf(stdout, "%sPIO transaction length: %d\n", - pathstr, ata->bytecount); + pathstr, pata->bytecount); } } if (cts->transport == XPORT_SATA) { @@ -2941,12 +3029,22 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts) sata->caps); } } + if (cts->protocol == PROTO_ATA) { + struct ccb_trans_settings_ata *ata= + &cts->proto_specific.ata; + + if (ata->valid & CTS_ATA_VALID_TQ) { + fprintf(stdout, "%stagged queueing: %s\n", pathstr, + (ata->flags & CTS_ATA_FLAGS_TAG_ENB) ? + "enabled" : "disabled"); + } + } if (cts->protocol == PROTO_SCSI) { struct ccb_trans_settings_scsi *scsi= &cts->proto_specific.scsi; if (scsi->valid & CTS_SCSI_VALID_TQ) { - fprintf(stdout, "%stagged queueing is %s\n", pathstr, + fprintf(stdout, "%stagged queueing: %s\n", pathstr, (scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) ? "enabled" : "disabled"); } @@ -3032,6 +3130,26 @@ get_cgd_bailout: return(retval); } +/* return the type of disk (really the command type) */ +static const char * +get_disk_type(struct cam_device *device) +{ + struct ccb_getdev cgd; + + (void) memset(&cgd, 0x0, sizeof(cgd)); + get_cgd(device, &cgd); + switch(cgd.protocol) { + case PROTO_SCSI: + return "scsi"; + case PROTO_ATA: + case PROTO_ATAPI: + case PROTO_SATAPM: + return "ata"; + default: + return "unknown"; + } +} + static void cpi_print(struct ccb_pathinq *cpi) { @@ -3384,16 +3502,19 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout, if (change_settings) { int didsettings = 0; struct ccb_trans_settings_spi *spi = NULL; - struct ccb_trans_settings_ata *ata = NULL; + struct ccb_trans_settings_pata *pata = NULL; struct ccb_trans_settings_sata *sata = NULL; + struct ccb_trans_settings_ata *ata = NULL; struct ccb_trans_settings_scsi *scsi = NULL; if (ccb->cts.transport == XPORT_SPI) spi = &ccb->cts.xport_specific.spi; if (ccb->cts.transport == XPORT_ATA) - ata = &ccb->cts.xport_specific.ata; + pata = &ccb->cts.xport_specific.ata; if (ccb->cts.transport == XPORT_SATA) sata = &ccb->cts.xport_specific.sata; + if (ccb->cts.protocol == PROTO_ATA) + ata = &ccb->cts.proto_specific.ata; if (ccb->cts.protocol == PROTO_SCSI) scsi = &ccb->cts.proto_specific.scsi; ccb->cts.xport_specific.valid = 0; @@ -3406,19 +3527,28 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout, spi->flags |= CTS_SPI_FLAGS_DISC_ENB; didsettings++; } - if (scsi && tag_enable != -1) { + if (tag_enable != -1) { if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0) { warnx("HBA does not support tagged queueing, " "so you cannot modify tag settings"); retval = 1; goto ratecontrol_bailout; } - scsi->valid |= CTS_SCSI_VALID_TQ; - if (tag_enable == 0) - scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; - else - scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; - didsettings++; + if (ata) { + ata->valid |= CTS_SCSI_VALID_TQ; + if (tag_enable == 0) + ata->flags &= ~CTS_ATA_FLAGS_TAG_ENB; + else + ata->flags |= CTS_ATA_FLAGS_TAG_ENB; + didsettings++; + } else if (scsi) { + scsi->valid |= CTS_SCSI_VALID_TQ; + if (tag_enable == 0) + scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; + else + scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; + didsettings++; + } } if (spi && offset != -1) { if ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) { @@ -3465,6 +3595,12 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout, retval = 1; goto ratecontrol_bailout; } + if (!user_settings) { + warnx("You can modify only user rate " + "settings for SATA"); + retval = 1; + goto ratecontrol_bailout; + } sata->revision = ata_speed2revision(syncrate * 100); if (sata->revision < 0) { warnx("Invalid rate %f", syncrate); @@ -3474,16 +3610,22 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout, sata->valid |= CTS_SATA_VALID_REVISION; didsettings++; } - if ((ata || sata) && mode != -1) { + if ((pata || sata) && mode != -1) { if ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) { warnx("HBA is not capable of changing " "transfer rates"); retval = 1; goto ratecontrol_bailout; } - if (ata) { - ata->mode = mode; - ata->valid |= CTS_ATA_VALID_MODE; + if (!user_settings) { + warnx("You can modify only user mode " + "settings for ATA/SATA"); + retval = 1; + goto ratecontrol_bailout; + } + if (pata) { + pata->mode = mode; + pata->valid |= CTS_ATA_VALID_MODE; } else { sata->mode = mode; sata->valid |= CTS_SATA_VALID_MODE; @@ -3530,11 +3672,6 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout, if (didsettings == 0) { goto ratecontrol_bailout; } - if (!user_settings && (ata || sata)) { - warnx("You can modify only user settings for ATA/SATA"); - retval = 1; - goto ratecontrol_bailout; - } ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS; if (cam_send_ccb(device, ccb) < 0) { perror("error sending XPT_SET_TRAN_SETTINGS CCB"); @@ -3566,13 +3703,10 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout, fprintf(stderr, "Test Unit Ready failed\n"); goto ratecontrol_bailout; } - /* - * If the user wants things quiet, there's no sense in - * getting the transfer settings, if we're not going - * to print them. - */ - if (quiet != 0) - goto ratecontrol_bailout; + } + if ((change_settings || send_tur) && !quiet && + (ccb->cts.transport == XPORT_ATA || + ccb->cts.transport == XPORT_SATA || send_tur)) { fprintf(stdout, "New parameters:\n"); retval = get_print_cts(device, user_settings, 0, NULL); } @@ -4330,7 +4464,7 @@ static int smpcmd(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout) { - int c, error; + int c, error = 0; union ccb *ccb; uint8_t *smp_request = NULL, *smp_response = NULL; int request_size = 0, response_size = 0; @@ -4624,7 +4758,10 @@ try_long: smp_report_general_sbuf(response, sizeof(*response), sb); - sbuf_finish(sb); + if (sbuf_finish(sb) != 0) { + warnx("%s: sbuf_finish", __func__); + goto bailout; + } printf("%s", sbuf_data(sb)); @@ -4995,7 +5132,10 @@ smpmaninfo(struct cam_device *device, int argc, char **argv, smp_report_manuf_info_sbuf(&response, sizeof(response), sb); - sbuf_finish(sb); + if (sbuf_finish(sb) != 0) { + warnx("%s: sbuf_finish", __func__); + goto bailout; + } printf("%s", sbuf_data(sb)); @@ -5325,6 +5465,7 @@ smpphylist(struct cam_device *device, int argc, char **argv, bzero(&(&ccb->ccb_h)[1], sizeof(union ccb) - sizeof(struct ccb_hdr)); + STAILQ_INIT(&devlist.dev_queue); rgrequest = malloc(sizeof(*rgrequest)); if (rgrequest == NULL) { @@ -5393,7 +5534,6 @@ smpphylist(struct cam_device *device, int argc, char **argv, goto bailout; } - STAILQ_INIT(&devlist.dev_queue); devlist.path_id = device->path_id; retval = buildbusdevlist(&devlist); @@ -5643,9 +5783,10 @@ bailout: #endif /* MINIMALISTIC */ void -usage(int verbose) +usage(int printlong) { - fprintf(verbose ? stdout : stderr, + + fprintf(printlong ? stdout : stderr, "usage: camcontrol <command> [device id][generic args][command args]\n" " camcontrol devlist [-v]\n" #ifndef MINIMALISTIC @@ -5694,7 +5835,7 @@ usage(int verbose) " camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-y][-s]\n" #endif /* MINIMALISTIC */ " camcontrol help\n"); - if (!verbose) + if (!printlong) return; #ifndef MINIMALISTIC fprintf(stdout, @@ -6142,7 +6283,8 @@ main(int argc, char **argv) break; case CAM_CMD_DOWNLOAD_FW: error = fwdownload(cam_dev, argc, argv, combinedopt, - arglist & CAM_ARG_VERBOSE, retry_count, timeout); + arglist & CAM_ARG_VERBOSE, retry_count, timeout, + get_disk_type(cam_dev)); break; #endif /* MINIMALISTIC */ case CAM_CMD_USAGE: |