==== //depot/FreeBSD-ken/src/sbin/camcontrol/camcontrol.8#16 - /usr/home/ken/perforce/FreeBSD-ken/src/sbin/camcontrol/camcontrol.8 ==== *** /tmp/tmp.611.0 Mon Apr 28 13:28:21 2003 --- /usr/home/ken/perforce/FreeBSD-ken/src/sbin/camcontrol/camcontrol.8 Mon Feb 10 19:51:51 2003 *************** *** 27,33 **** .\" .\" $FreeBSD: src/sbin/camcontrol/camcontrol.8,v 1.36 2002/12/17 06:05:21 njl Exp $ .\" ! .Dd September 14, 1998 .Dt CAMCONTROL 8 .Os .Sh NAME --- 27,33 ---- .\" .\" $FreeBSD: src/sbin/camcontrol/camcontrol.8,v 1.36 2002/12/17 06:05:21 njl Exp $ .\" ! .Dd October 23, 2002 .Dt CAMCONTROL 8 .Os .Sh NAME *************** *** 141,146 **** --- 141,151 ---- .Op Fl w .Op Fl y .Nm + .Ic reassign + .Op device id + .Op generic args + .Op Fl b Ar blockno Op Ar ,blockno Op Ar \&,... + .Nm .Ic help .Sh DESCRIPTION The *************** *** 623,628 **** --- 628,667 ---- will not be asked about the timeout if a timeout is specified on the command line. .El + .It Ic reassign + Request the device to reassign the specified block(s). + The device must be of type "direct access" (such as a disk drive). + This should cause it to save the contents of the original blocks into + one of the spare blocks if possible, place the specified block into + the grown defect list, and arrange a mapping for the requested logical + block so further requests to it will return the contents of the spare + block instead. + .Pp + That way, a suspicious block can be prevented from being used anymore. + Normally, blocks that experience errors can be remapped automatically + by setting the appropriate bits in the "Read-Write Error Recovery Page" + (mode page 1). + .Pp + The + .Sq reassign + subcommand expects that the list of blocks to be reassigned is given + in one or more + .Fl b + options, followed by the respective logical block numbers. + Multiple logical block numbers can be specified in a single + .Fl b + option, separated by commas. + The numbers can be specified using standard "C" notation, so notably + hexadecimal numbers are acceptable when prefixed with "0x". + This can be useful since the "info" field in SCSI error messages + issued by the kernel contains the block number in hexadecimal form. + .Pp + The + .Sq reassign + subcommand will automatically rearrange the given list of block numbers + in ascending order, as required by the + .Tn SCSI + standard. .It Ic help Print out verbose usage information. .El ==== //depot/FreeBSD-ken/src/sbin/camcontrol/camcontrol.c#29 - /usr/home/ken/perforce/FreeBSD-ken/src/sbin/camcontrol/camcontrol.c ==== *** /tmp/tmp.611.1 Mon Apr 28 13:28:21 2003 --- /usr/home/ken/perforce/FreeBSD-ken/src/sbin/camcontrol/camcontrol.c Fri Feb 21 23:46:17 2003 *************** *** 68,73 **** --- 68,74 ---- CAM_CMD_TAG = 0x0000000e, CAM_CMD_RATE = 0x0000000f, CAM_CMD_DETACH = 0x00000010, + CAM_CMD_REASSIGN = 0x00000011, } cam_cmdmask; typedef enum { *************** *** 144,149 **** --- 145,151 ---- {"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts}, {"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXc"}, {"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qwy"}, + {"reassign", CAM_CMD_REASSIGN, CAM_ARG_NONE, "b:"}, #endif /* MINIMALISTIC */ {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, *************** *** 201,206 **** --- 203,211 ---- 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); + static int reassignblocks(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); + static int compare32(const void *i1, const void *i2); #endif /* MINIMALISTIC */ camcontrol_optret *************** *** 1563,1583 **** return(error); } - #endif /* MINIMALISTIC */ ! #if 0 ! void ! reassignblocks(struct cam_device *device, u_int32_t *blocks, int num_blocks) { union ccb *ccb; ! ccb = cam_getccb(device); cam_freeccb(ccb); } - #endif - #ifndef MINIMALISTIC void mode_sense(struct cam_device *device, int mode_page, int page_control, int dbd, int retry_count, int timeout, u_int8_t *data, int datalen) --- 1568,1717 ---- return(error); } ! static int ! compare32(const void *i1, const void *i2) ! { ! u_int32_t *ui1, *ui2; ! ! ui1 = (u_int32_t *)i1; ! ui2 = (u_int32_t *)i2; ! ! return (*ui1 - *ui2); ! } ! ! static int ! reassignblocks(struct cam_device *device, int argc, char **argv, ! char *combinedopt, int retry_count, int timeout) { union ccb *ccb; ! int c; ! size_t n, blockcount = 0, datalen; ! int error = 0, retval = 0; ! struct scsi_reassign_blocks *rea_cdb; ! struct scsi_reassign_blocks_data *data = NULL; ! u_int32_t *block_ptr = NULL; ! unsigned long block; ! char *cp, *endptr; ! int num_warnings = 0; ! ! while ((c = getopt(argc, argv, combinedopt)) != -1) { ! switch(c) { ! case 'b': ! cp = optarg; ! do { ! block = strtoul(cp, &endptr, 0); ! if (endptr == cp) { ! warnx("reassignblocks: number " ! "expected: %s", cp); ! return(1); ! } ! blockcount++; ! block_ptr = realloc(block_ptr, ! blockcount * ! sizeof(u_int32_t)); ! if (block_ptr == NULL) { ! warnx("reassignblocks: out of memory"); ! return(1); ! } ! block_ptr[blockcount - 1] = block; ! if (*endptr == ',') ! cp = endptr + 1; ! /* ! * Else: fall through. If it is '\0', the ! * loop will just terminate. Otherwise, ! * the next strtoul() will complain. ! */ ! } while (*endptr != '\0'); ! break; ! } ! } ! ! if (blockcount == 0) { ! warnx("reassignblocks: no blocks to reassign"); ! return(1); ! } ! ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("reassignblocks: error allocating ccb"); + return(1); + } + + /* + * REASSIGN BLOCKS requires that the blocks are sent in + * ascending order. + */ + qsort(block_ptr, blockcount, sizeof(u_int32_t), compare32); + + rea_cdb = (struct scsi_reassign_blocks *)&ccb->csio.cdb_io.cdb_bytes; + + /* + * cam_getccb() zeros the CCB header only. So we need to zero the + * payload portion of the ccb. + */ + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + + /* + * Allocate the memory. struct scsi_reassign_blocks_data already + * accounts for one defect_descriptor, but we need blockcount + * copies of it. + */ + datalen = sizeof(struct scsi_reassign_blocks_data) + + (blockcount - 1) * sizeof(data->defect_descriptor[0]); + if ((data = malloc(datalen)) == NULL) { + warnx("reassignblocks: out of memory"); + goto reassignblocks_bailout; + } + bzero(data, sizeof(struct scsi_reassign_blocks_data)); + for (n = 0; n < blockcount; n++) + scsi_ulto4b(block_ptr[n], data->defect_descriptor[n].dlbaddr); + scsi_ulto2b(blockcount * sizeof(data->defect_descriptor[0]), + data->length); + + cam_fill_csio(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*flags*/ CAM_DIR_OUT | ((arglist & CAM_ARG_ERR_RECOVER) ? + CAM_PASS_ERR_RECOVER : 0), + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*data_ptr*/ (u_int8_t *)data, + /*dxfer_len*/ datalen, + /*sense_len*/ SSD_FULL_SIZE, + /*cdb_len*/ sizeof(struct scsi_reassign_blocks), + /*timeout*/ timeout ? timeout : 5000); + + rea_cdb->opcode = REASSIGN_BLOCKS; + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + + if (retval < 0) + warn("error sending reassign blocks command"); + else + warnx("error sending reassign blocks command"); + error = 1; + } + + reassignblocks_bailout: + + free(block_ptr); + if (data != NULL) + free(data); cam_freeccb(ccb); + + return(error); } void mode_sense(struct cam_device *device, int mode_page, int page_control, int dbd, int retry_count, int timeout, u_int8_t *data, int datalen) *************** *** 3171,3176 **** --- 3305,3311 ---- " [-R syncrate][-v][-T ]\n" " [-U][-W bus_width]\n" " camcontrol format [dev_id][generic args][-q][-w][-y]\n" + " camcontrol reassign [dev_id][generic_args] -b blockno[,...]\n" #endif /* MINIMALISTIC */ " camcontrol help\n"); if (!verbose) *************** *** 3195,3200 **** --- 3330,3336 ---- "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" + "reassign reassign (supposedly bad) blocks\n" "help this message\n" "Device Identifiers:\n" "bus:target specify the bus and target, lun defaults to 0\n" *************** *** 3249,3255 **** "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"); #endif /* MINIMALISTIC */ } --- 3385,3393 ---- "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" ! "reassign arguments:\n" ! "-b list of blocks to reassign\n"); #endif /* MINIMALISTIC */ } *************** *** 3523,3528 **** --- 3661,3671 ---- case CAM_CMD_FORMAT: error = scsiformat(cam_dev, argc, argv, combinedopt, retry_count, timeout); + break; + case CAM_CMD_REASSIGN: + error = reassignblocks(cam_dev, argc, argv, + combinedopt, retry_count, + timeout); break; #endif /* MINIMALISTIC */ case CAM_CMD_USAGE: