diff options
Diffstat (limited to 'sbin/camcontrol')
-rw-r--r-- | sbin/camcontrol/camcontrol.8 | 104 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.c | 320 |
2 files changed, 393 insertions, 31 deletions
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index dde3e19..9da0d48 100644 --- a/sbin/camcontrol/camcontrol.8 +++ b/sbin/camcontrol/camcontrol.8 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 1998, 1999 Kenneth D. Merry. +.\" Copyright (c) 1998, 1999, 2000 Kenneth D. Merry. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -128,6 +128,13 @@ negotiate .Op Fl W Ar bus_width .Op Fl v .Nm camcontrol +format +.Op device id +.Op generic args +.Op Fl q +.Op Fl w +.Op Fl y +.Nm camcontrol help .Sh DESCRIPTION .Nm camcontrol @@ -172,7 +179,7 @@ and .Fl u arguments will .Em not -override a specified bus:target or bus:target:lun, howevever. +override a specified bus:target or bus:target:lun, however. .Pp Most of the .Nm camcontrol @@ -333,7 +340,7 @@ function requires the argument to specify the CDB. Other arguments are optional, depending on the command type. The command and data specification syntax is documented in -.Xr cam 3 . +.Xr cam_cdbparse 3 . NOTE: If the CDB specified causes data to be transfered to or from the SCSI device in question, you MUST specify either .Fl i @@ -510,6 +517,70 @@ device until a command has been sent to the device. The .Fl a switch above will automatically send a Test Unit Ready to the device so negotiation parameters will take effect. +.It format +Issue the +.Tn SCSI +FORMAT UNIT command to the named device. +.Pp +.Em WARNING! WARNING! WARNING! +.Pp +Low level formatting a disk will destroy ALL data on the disk. Use +extreme caution when issuing this command. Many users low-level format +disks that do not really need to be low-level formatted. There are +relatively few scenarios that call for low-level formatting a disk. +One reason for +low-level formatting a disk is to initialize the disk after changing +its physical sector size. Another reason for low-level formatting a disk +is to revive the disk if you are getting "medium format corrupted" errors +from the disk in response to read and write requests. +.Pp +Some disks take longer than others to format. Users should specify a +timeout long enough to allow the format to complete. The default format +timeout is 3 hours, which should be long enough for most disks. Some hard +disks will complete a format operation in a very short period of time +(on the order of 5 minutes or less). This is often because the drive +doesn't really support the FORMAT UNIT command -- it just accepts the +command, waits a few minutes and then returns it. +.Pp +The +.Sq format +subcommand takes several arguments that modify its default behavior. The +.Fl q +and +.Fl y +arguments can be useful for scripts. +.Pp +.Bl -tag -width 123456 +.It Fl q +Be quiet, don't print any status messages. This option will not disable +the questions, however. To disable questions, use the +.Fl y +argument, below. +.It Fl w +Issue a non-immediate format command. By default, +.Nm camcontrol +issues the FORMAT UNIT command with the immediate bit set. This tells the +device to immediately return the format command, before the format has +actually completed. Then, +.Nm camcontrol +gathers +.Tn SCSI +sense information from the device every second to determine how far along +in the format process it is. If the +.Fl w +argument is specified, +.Nm camcontrol +will issue a non-immediate format command, and will be unable to print any +information to let the user know what percentage of the disk has been +formatted. +.It Fl y +Don't ask any questions. By default, +.Nm camcontrol +will ask the user if he/she really wants to format the disk in question, +and also if the default format command timeout is acceptable. The user +will not be asked about the timeout if a timeout is specified on the +command line. +.El .It help Print out verbose usage information. .El @@ -611,33 +682,6 @@ camcontrol negotiate -n da -u 3 -R 20.000 -O 15 -a .Pp Negotiate a sync rate of 20MHz and an offset of 15 with da3. Then send a Test Unit Ready command to make the settings take effect. -.Pp -.Bd -literal -offset indent -camcontrol cmd -n da -u 3 -v -t 7200 -c "4 0 0 0 0 0" -.Ed -.Pp -Send the FORMAT UNIT (0x04) command to da3. This will low-level format the -disk. Print sense information if the command fails, and set the timeout to -two hours (or 7200 seconds). -.Pp -.Em WARNING! WARNING! WARNING! -.Pp -Low level formatting a disk will destroy ALL data on the disk. Use -extreme caution when issuing this command. Many users low-level format -disks that do not really need to be low-level formatted. There are -relatively few scenarios that call for low-level formatting a disk. -One reason for -low-level formatting a disk is if you want to change the physical sector -size of the disk. Another reason for low-level formatting a disk is to -revive the disk if you are getting "medium format corrupted" errors from the -disk in response to read and write requests. -.Pp -Some disks take longer than others to format. Users should specify a -timeout long enough to allow the format to complete. Some hard disks -will complete a format operation in a very short period of time (on the -order of 5 minutes or less). This is often because the drive doesn't -really support the FORMAT UNIT command -- it just accepts the command, -waits a few minutes and then returns it. .Sh SEE ALSO .Xr cam 3 , .Xr cam_cdbparse 3 , diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index fe80150..f42b751 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -132,6 +132,7 @@ struct camcontrol_opts option_table[] = { {"negotiate", CAM_ARG_RATE, negotiate_opts}, {"rate", CAM_ARG_RATE, negotiate_opts}, {"debug", CAM_ARG_DEBUG, "ITSc"}, + {"format", CAM_ARG_FORMAT, "qwy"}, {"help", CAM_ARG_USAGE, NULL}, {"-?", CAM_ARG_USAGE, NULL}, {"-h", CAM_ARG_USAGE, NULL}, @@ -181,6 +182,8 @@ static int get_print_cts(struct cam_device *device, int user_settings, int quiet, struct ccb_trans_settings *cts); static int ratecontrol(struct cam_device *device, int retry_count, int timeout, int argc, char **argv, char *combinedopt); +static int scsiformat(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); camcontrol_optret getoption(char *arg, cam_argmask *argnum, char **subopt) @@ -2652,6 +2655,311 @@ ratecontrol_bailout: return(retval); } +static int +scsiformat(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + int c; + int ycount = 0, quiet = 0; + int error = 0, response = 0, retval = 0; + int use_timeout = 10800 * 1000; + int immediate = 1; + struct format_defect_list_header fh; + u_int8_t *data_ptr = NULL; + u_int32_t dxfer_len = 0; + u_int8_t byte2 = 0; + int num_warnings = 0; + + ccb = cam_getccb(device); + + if (ccb == NULL) { + warnx("scsiformat: error allocating ccb"); + return(1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch(c) { + case 'q': + quiet++; + break; + case 'w': + immediate = 0; + break; + case 'y': + ycount++; + break; + } + } + + if (quiet == 0) { + fprintf(stdout, "You are about to REMOVE ALL DATA from the " + "following device:\n"); + + error = scsidoinquiry(device, argc, argv, combinedopt, + retry_count, timeout); + + if (error != 0) { + warnx("scsiformat: error sending inquiry"); + goto scsiformat_bailout; + } + } + + if (ycount == 0) { + + do { + char str[1024]; + + fprintf(stdout, "Are you SURE you want to do " + "this? (yes/no) "); + + if (fgets(str, sizeof(str), stdin) != NULL) { + + if (strncasecmp(str, "yes", 3) == 0) + response = 1; + else if (strncasecmp(str, "no", 2) == 0) + response = -1; + else { + fprintf(stdout, "Please answer" + " \"yes\" or \"no\"\n"); + } + } + } while (response == 0); + + if (response == -1) { + error = 1; + goto scsiformat_bailout; + } + } + + if (timeout != 0) + use_timeout = timeout; + + if (quiet == 0) { + fprintf(stdout, "Current format timeout is %d seconds\n", + use_timeout / 1000); + } + + /* + * If the user hasn't disabled questions and didn't specify a + * timeout on the command line, ask them if they want the current + * timeout. + */ + if ((ycount == 0) + && (timeout == 0)) { + char str[1024]; + int new_timeout = 0; + + fprintf(stdout, "Enter new timeout in seconds or press\n" + "return to keep the current timeout [%d] ", + use_timeout / 1000); + + if (fgets(str, sizeof(str), stdin) != NULL) { + if (str[0] != '\0') + new_timeout = atoi(str); + } + + if (new_timeout != 0) { + use_timeout = new_timeout * 1000; + fprintf(stdout, "Using new timeout value %d\n", + use_timeout / 1000); + } + } + + /* + * Keep this outside the if block below to silence any unused + * variable warnings. + */ + bzero(&fh, sizeof(fh)); + + /* + * If we're in immediate mode, we've got to include the format + * header + */ + if (immediate != 0) { + fh.byte2 = FU_DLH_IMMED; + data_ptr = (u_int8_t *)&fh; + dxfer_len = sizeof(fh); + byte2 = FU_FMT_DATA; + } else if (quiet == 0) { + fprintf(stdout, "Formatting..."); + fflush(stdout); + } + + scsi_format_unit(&ccb->csio, + /* retries */ retry_count, + /* cbfcnp */ NULL, + /* tag_action */ MSG_SIMPLE_Q_TAG, + /* byte2 */ byte2, + /* ileave */ 0, + /* data_ptr */ data_ptr, + /* dxfer_len */ dxfer_len, + /* sense_len */ SSD_FULL_SIZE, + /* timeout */ use_timeout); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (arglist & CAM_ARG_ERR_RECOVER) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((immediate == 0) + && ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP))) { + char *errstr = "error sending format command"; + + if (retval < 0) + warn(errstr); + else + warnx(errstr); + + if (arglist & CAM_ARG_VERBOSE) { + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == + CAM_SCSI_STATUS_ERROR) + scsi_sense_print(device, &ccb->csio, stderr); + else + fprintf(stderr, "CAM status is %#x\n", + ccb->ccb_h.status); + } + error = 1; + goto scsiformat_bailout; + } + + /* + * If we ran in non-immediate mode, we already checked for errors + * above and printed out any necessary information. If we're in + * immediate mode, we need to loop through and get status + * information periodically. + */ + if (immediate == 0) { + if (quiet == 0) { + fprintf(stdout, "Format Complete\n"); + } + goto scsiformat_bailout; + } + + do { + cam_status status; + + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + + /* + * There's really no need to do error recovery or + * retries here, since we're just going to sit in a + * loop and wait for the device to finish formatting. + */ + scsi_test_unit_ready(&ccb->csio, + /* retries */ 0, + /* cbfcnp */ NULL, + /* tag_action */ MSG_SIMPLE_Q_TAG, + /* sense_len */ SSD_FULL_SIZE, + /* timeout */ 5000); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + retval = cam_send_ccb(device, ccb); + + /* + * If we get an error from the ioctl, bail out. SCSI + * errors are expected. + */ + if (retval < 0) { + warn("error sending CAMIOCOMMAND ioctl"); + if (arglist & CAM_ARG_VERBOSE) { + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == + CAM_SCSI_STATUS_ERROR) + scsi_sense_print(device, &ccb->csio, + stderr); + else + fprintf(stderr, "CAM status is %#x\n", + ccb->ccb_h.status); + } + error = 1; + goto scsiformat_bailout; + } + + status = ccb->ccb_h.status & CAM_STATUS_MASK; + + if ((status != CAM_REQ_CMP) + && (status == CAM_SCSI_STATUS_ERROR)) { + struct scsi_sense_data *sense; + int error_code, sense_key, asc, ascq; + + sense = &ccb->csio.sense_data; + scsi_extract_sense(sense, &error_code, &sense_key, + &asc, &ascq); + + /* + * According to the SCSI-2 and SCSI-3 specs, a + * drive that is in the middle of a format should + * return NOT READY with an ASC of "logical unit + * not ready, format in progress". The sense key + * specific bytes will then be a progress indicator. + */ + if ((sense_key == SSD_KEY_NOT_READY) + && (asc == 0x04) && (ascq == 0x04)) { + if ((sense->extra_len >= 10) + && ((sense->sense_key_spec[0] & + SSD_SCS_VALID) != 0) + && (quiet == 0)) { + int val; + u_int64_t percentage; + + val = scsi_2btoul( + &sense->sense_key_spec[1]); + percentage = 10000 * val; + + fprintf(stdout, + "\rFormatting: %qd.%02qd %% " + "(%d/%d) done", + percentage / (0x10000 * 100), + (percentage / 0x10000) % 100, + val, 0x10000); + fflush(stdout); + } else if ((quiet == 0) + && (++num_warnings <= 1)) { + warnx("Unexpected SCSI Sense Key " + "Specific value returned " + "during format:"); + scsi_sense_print(device, &ccb->csio, + stderr); + warnx("Unable to print status " + "information, but format will " + "proceed."); + warnx("will exit when format is " + "complete"); + } + sleep(1); + } else { + warnx("Unexpected SCSI error during format"); + scsi_sense_print(device, &ccb->csio, stderr); + error = 1; + goto scsiformat_bailout; + } + + } else if (status != CAM_REQ_CMP) { + warnx("Unexpected CAM status %#x", status); + error = 1; + goto scsiformat_bailout; + } + + } while((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP); + + if (quiet == 0) + fprintf(stdout, "\nFormat Complete\n"); + +scsiformat_bailout: + + cam_freeccb(ccb); + + return(error); +} + void usage(int verbose) { @@ -2677,6 +2985,7 @@ usage(int verbose) " [-D <enable|disable>][-O offset][-q]\n" " [-R syncrate][-v][-T <enable|disable>]\n" " [-U][-W bus_width]\n" +" camcontrol format [dev_id][generic args][-q][-w][-y]\n" " camcontrol help\n"); if (!verbose) return; @@ -2697,6 +3006,7 @@ usage(int verbose) "debug turn debugging on/off for a bus, target, or lun, or all devices\n" "tags report or set the number of transaction slots for a device\n" "negotiate report or set device negotiation parameters\n" +"format send the SCSI FORMAT UNIT command to the named device\n" "help this message\n" "Device Identifiers:\n" "bus:target specify the bus and target, lun defaults to 0\n" @@ -2745,7 +3055,11 @@ usage(int verbose) "-T <arg> \"enable\" or \"disable\" tagged queueing\n" "-U report/set user negotiation settings\n" "-W bus_width set the bus width in bits (8, 16 or 32)\n" -"-v also print a Path Inquiry CCB for the controller\n", +"-v also print a Path Inquiry CCB for the controller\n" +"format arguments:\n" +"-q be quiet, don't print status messages\n" +"-w don't send immediate format command\n" +"-y don't ask any questions\n", DEFAULT_DEVICE, DEFAULT_UNIT); } @@ -3005,6 +3319,10 @@ main(int argc, char **argv) error = ratecontrol(cam_dev, retry_count, timeout, argc, argv, combinedopt); break; + case CAM_ARG_FORMAT: + error = scsiformat(cam_dev, argc, argv, + combinedopt, retry_count, timeout); + break; case CAM_ARG_USAGE: usage(1); break; |