diff options
author | ken <ken@FreeBSD.org> | 2007-09-08 20:24:12 +0000 |
---|---|---|
committer | ken <ken@FreeBSD.org> | 2007-09-08 20:24:12 +0000 |
commit | 544cc9b1b68f08726bb2e6f1c18b8b50846d5b19 (patch) | |
tree | b7e7727ea4b03c60d934a7c27dbbb10b9e79ceaf /sbin/camcontrol | |
parent | 7a6912794da91b4181a5c452c0f28bd0a8a25c11 (diff) | |
download | FreeBSD-src-544cc9b1b68f08726bb2e6f1c18b8b50846d5b19.zip FreeBSD-src-544cc9b1b68f08726bb2e6f1c18b8b50846d5b19.tar.gz |
Add SCSI READ CAPACITY support to camcontrol. The new 'readcap' subcommand
will automatically issue the 16 byte verison of read capacity if the device
in question is larger than 2TB.
There are also a number of output options here (last block, number of
blocks, human readable) that should meet most needs, and also aid in
scripting.
Approved by: re (bmah)
MFC after: 1 week
Diffstat (limited to 'sbin/camcontrol')
-rw-r--r-- | sbin/camcontrol/Makefile | 4 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.8 | 52 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.c | 232 |
3 files changed, 283 insertions, 5 deletions
diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile index bf2ba4a..7a3d421 100644 --- a/sbin/camcontrol/Makefile +++ b/sbin/camcontrol/Makefile @@ -12,8 +12,8 @@ WARNS?= 3 .else WARNS?= 6 .endif -DPADD= ${LIBCAM} ${LIBSBUF} -LDADD= -lcam -lsbuf +DPADD= ${LIBCAM} ${LIBSBUF} ${LIBUTIL} +LDADD= -lcam -lsbuf -lutil MAN= camcontrol.8 .include <bsd.prog.mk> diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index c6691fc..7925e94 100644 --- a/sbin/camcontrol/camcontrol.8 +++ b/sbin/camcontrol/camcontrol.8 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 1998, 1999, 2000, 2002, 2005, 2006 Kenneth D. Merry. +.\" Copyright (c) 1998, 1999, 2000, 2002, 2005, 2006, 2007 Kenneth D. Merry. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -66,6 +66,16 @@ .Op Fl l .Op Fl r Ar reporttype .Nm +.Ic readcap +.Op device id +.Op generic args +.Op Fl b +.Op Fl h +.Op Fl H +.Op Fl N +.Op Fl q +.Op Fl s +.Nm .Ic start .Op device id .Op generic args @@ -304,6 +314,46 @@ Return all available LUNs. .Nm will try to print out LUN numbers in a reasonable format. It can understand the peripheral, flat, LUN and extended LUN formats. +.It Ic readcap +Send the SCSI READ CAPACITY command to the given device and display +the results. +If the device is larger than 2TB, the SCSI READ CAPACITY (16) service +action will be sent to obtain the full size of the device. +By default, +.Nm +will print out the last logical block of the device, and the blocksize of +the device in bytes. +To modify the output format, use the following options: +.Bl -tag -width 5n +.It Fl b +Just print out the blocksize, not the last block or device size. +This cannot be used with +.Fl N +or +.Fl s . +.It Fl h +Print out the device size in human readable (base 2, 1K == 1024) format. +This implies +.Fl N +and cannot be used with +.Fl q +or +.Fl b . +.It Fl H +Print out the device size in human readable (base 10, 1K == 1000) format. +.It Fl N +Print out the number of blocks in the device instead of the last logical +block. +.It Fl q +Quiet, print out the numbers only (separated by a comma if +.Fl b +or +.Fl s +are not specified). +.It Fl s +Print out the last logical block or the size of the device only, and omit +the blocksize. +.El .It Ic start Send the SCSI Start/Stop Unit (0x1B) command to the given device with the start bit set. diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 87dc030..9eab41c 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2006 Kenneth D. Merry + * Copyright (c) 1997-2007 Kenneth D. Merry * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <fcntl.h> #include <ctype.h> #include <err.h> +#include <libutil.h> #include <cam/cam.h> #include <cam/cam_debug.h> @@ -69,7 +70,8 @@ typedef enum { CAM_CMD_TAG = 0x0000000e, CAM_CMD_RATE = 0x0000000f, CAM_CMD_DETACH = 0x00000010, - CAM_CMD_REPORTLUNS = 0x00000011 + CAM_CMD_REPORTLUNS = 0x00000011, + CAM_CMD_READCAP = 0x00000012 } cam_cmdmask; typedef enum { @@ -129,6 +131,7 @@ struct camcontrol_opts option_table[] = { {"load", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT | CAM_ARG_EJECT, NULL}, {"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL}, {"reportluns", CAM_CMD_REPORTLUNS, CAM_ARG_NONE, "clr:"}, + {"readcapacity", CAM_CMD_READCAP, CAM_ARG_NONE, "bhHNqs"}, #endif /* MINIMALISTIC */ {"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL}, {"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL}, @@ -207,6 +210,8 @@ static int scsiformat(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); static int scsireportluns(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); +static int scsireadcapacity(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); #endif /* MINIMALISTIC */ camcontrol_optret @@ -3450,6 +3455,214 @@ bailout: return (retval); } +static int +scsireadcapacity(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + int blocksizeonly, humanize, numblocks, quiet, sizeonly, baseten; + struct scsi_read_capacity_data rcap; + struct scsi_read_capacity_data_long rcaplong; + uint64_t maxsector; + uint32_t block_len; + int retval; + int c; + + blocksizeonly = 0; + humanize = 0; + numblocks = 0; + quiet = 0; + sizeonly = 0; + baseten = 0; + retval = 0; + + ccb = cam_getccb(device); + + if (ccb == NULL) { + warnx("%s: error allocating ccb", __func__); + 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 'b': + blocksizeonly++; + break; + case 'h': + humanize++; + baseten = 0; + break; + case 'H': + humanize++; + baseten++; + break; + case 'N': + numblocks++; + break; + case 'q': + quiet++; + break; + case 's': + sizeonly++; + break; + default: + break; + } + } + + if ((blocksizeonly != 0) + && (numblocks != 0)) { + warnx("%s: you can only specify one of -b or -N", __func__); + retval = 1; + goto bailout; + } + + if ((blocksizeonly != 0) + && (sizeonly != 0)) { + warnx("%s: you can only specify one of -b or -s", __func__); + retval = 1; + goto bailout; + } + + if ((humanize != 0) + && (quiet != 0)) { + warnx("%s: you can only specify one of -h/-H or -q", __func__); + retval = 1; + goto bailout; + } + + if ((humanize != 0) + && (blocksizeonly != 0)) { + warnx("%s: you can only specify one of -h/-H or -b", __func__); + retval = 1; + goto bailout; + } + + scsi_read_capacity(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + &rcap, + SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 5000); + + /* 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 (cam_send_ccb(device, ccb) < 0) { + warn("error sending READ CAPACITY command"); + + if (arglist & CAM_ARG_VERBOSE) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + + retval = 1; + goto bailout; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + retval = 1; + goto bailout; + } + + maxsector = scsi_4btoul(rcap.addr); + block_len = scsi_4btoul(rcap.length); + + /* + * A last block of 2^32-1 means that the true capacity is over 2TB, + * and we need to issue the long READ CAPACITY to get the real + * capacity. Otherwise, we're all set. + */ + if (maxsector != 0xffffffff) + goto do_print; + + scsi_read_capacity_16(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*lba*/ 0, + /*reladdr*/ 0, + /*pmi*/ 0, + &rcaplong, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 5000); + + /* 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 (cam_send_ccb(device, ccb) < 0) { + warn("error sending READ CAPACITY (16) command"); + + if (arglist & CAM_ARG_VERBOSE) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + + retval = 1; + goto bailout; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + retval = 1; + goto bailout; + } + + maxsector = scsi_8btou64(rcaplong.addr); + block_len = scsi_4btoul(rcaplong.length); + +do_print: + if (blocksizeonly == 0) { + /* + * Humanize implies !quiet, and also implies numblocks. + */ + if (humanize != 0) { + char tmpstr[6]; + int64_t tmpbytes; + int ret; + + tmpbytes = (maxsector + 1) * block_len; + ret = humanize_number(tmpstr, sizeof(tmpstr), + tmpbytes, "", HN_AUTOSCALE, + HN_B | HN_DECIMAL | + ((baseten != 0) ? + HN_DIVISOR_1000 : 0)); + if (ret == -1) { + warnx("%s: humanize_number failed!", __func__); + retval = 1; + goto bailout; + } + fprintf(stdout, "Device Size: %s%s", tmpstr, + (sizeonly == 0) ? ", " : "\n"); + } else if (numblocks != 0) { + fprintf(stdout, "%s%ju%s", (quiet == 0) ? + "Blocks: " : "", (uintmax_t)maxsector + 1, + (sizeonly == 0) ? ", " : "\n"); + } else { + fprintf(stdout, "%s%ju%s", (quiet == 0) ? + "Last Block: " : "", (uintmax_t)maxsector, + (sizeonly == 0) ? ", " : "\n"); + } + } + if (sizeonly == 0) + fprintf(stdout, "%s%u%s\n", (quiet == 0) ? + "Block Length: " : "", block_len, (quiet == 0) ? + " bytes" : ""); +bailout: + cam_freeccb(ccb); + + return (retval); +} + #endif /* MINIMALISTIC */ void @@ -3463,6 +3676,8 @@ usage(int verbose) " camcontrol tur [dev_id][generic args]\n" " camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n" " camcontrol reportluns [dev_id][generic args] [-c] [-l] [-r report]\n" +" camcontrol readcap [dev_id][generic args] [-b] [-h] [-H] [-N]\n" +" [-q] [-s]\n" " camcontrol start [dev_id][generic args]\n" " camcontrol stop [dev_id][generic args]\n" " camcontrol load [dev_id][generic args]\n" @@ -3496,6 +3711,7 @@ usage(int verbose) "tur send a test unit ready to the named device\n" "inquiry send a SCSI inquiry command to the named device\n" "reportluns send a SCSI report luns command to the device\n" +"readcap send a SCSI read capacity command to the device\n" "start send a Start Unit command to the device\n" "stop send a Stop Unit command to the device\n" "load send a Start Unit command to the device with the load bit set\n" @@ -3540,6 +3756,13 @@ usage(int verbose) "-c only report a count of available LUNs\n" "-l only print out luns, and not a count\n" "-r <reporttype> specify \"default\", \"wellknown\" or \"all\"\n" +"readcap arguments\n" +"-b only report the blocksize\n" +"-h human readable device size, base 2\n" +"-H human readable device size, base 10\n" +"-N print the number of blocks instead of last block\n" +"-q quiet, print numbers only\n" +"-s only report the last block/device size\n" "cmd arguments:\n" "-c cdb [args] specify the SCSI CDB\n" "-i len fmt specify input data and input data format\n" @@ -3856,6 +4079,11 @@ main(int argc, char **argv) combinedopt, retry_count, timeout); break; + case CAM_CMD_READCAP: + error = scsireadcapacity(cam_dev, argc, argv, + combinedopt, retry_count, + timeout); + break; #endif /* MINIMALISTIC */ case CAM_CMD_USAGE: usage(1); |