Subject: Changing block size on SCSI disks
To: None <tech-kern@netbsd.org>
From: Bob Nestor <rnestor@augustmail.com>
List: tech-kern
Date: 05/26/2002 12:06:21
--Apple-Mail-1--971574350
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed

Matthew Jacob suggested I post these code changes here for comment.  
These changes add an optional blocksize parameter to the scsictl format 
command which enables one to permanently change a SCSI disk setup. (I 
also implement the START/STOP commands, but they aren't really to 
useful.)

I've successfully used this to modify the blocksize from 514 to 512 on a 
couple of different IBM disks I have.  After being reformatted this way 
the disks were usable on NetBSD and MacOS.  I've only run the code on 
NetBSD/i386, but I don't see any problems with other ports.

BTW, I'm not on this list so please include me on any e-mail that you 
feel I need to be made aware of on this code.  If nobody objects it 
would be nice to have the changes committed as there are very few other 
options out there for doing this.  If anyone wants to commit the code 
that would be fine with me too.

Thanks,
-bob


--Apple-Mail-1--971574350
Content-Disposition: attachment;
	filename=scsictl.diffs
Content-Transfer-Encoding: 7bit
Content-Type: application/macbinary;
	x-mac-creator=554E4958;
	x-unix-mode=0755;
	x-mac-type=42494E41;
	name="scsictl.diffs"

*** extern.h	Sat May 25 14:59:23 2002
--- extern.h.orig	Sat May 25 14:58:40 2002
***************
*** 47,52 ****
  /* scsi_subr.c */
  void	scsi_command __P((int, void *, size_t, void *, size_t, int, int));
  void	scsi_mode_sense __P((int, u_int8_t, u_int8_t, void *, size_t));
- void	scsi_request_sense __P((int, u_int8_t, u_int8_t, void *, size_t));
  void	scsi_mode_select __P((int, u_int8_t, u_int8_t, void *, size_t));
  void	scsi_strvis __P((char *, size_t, const char *, size_t));
--- 47,51 ----
*** scsi_subr.c	Sat May 25 15:02:32 2002
--- scsi_subr.c.orig	Sat May 25 13:04:39 2002
***************
*** 54,60 ****
  #include <unistd.h>
  
  #include <dev/scsipi/scsi_all.h>
- #include <dev/scsipi/scsipi_all.h>
  
  #include "extern.h"
  
--- 54,59 ----
***************
*** 110,133 ****
  
  	cmd.opcode = SCSI_MODE_SENSE;
  	cmd.page = pgcode | pctl;
- 	cmd.length = len;
- 
- 	scsi_command(fd, &cmd, sizeof(cmd), buf, len, 10000, SCCMD_READ);
- }
- 
- void
- scsi_request_sense(fd, pgcode, pctl, buf, len)
- 	int fd;
- 	u_int8_t pgcode, pctl;
- 	void *buf;
- 	size_t len;
- {
- 	struct scsipi_sense cmd;
- 
- 	memset(&cmd, 0, sizeof(cmd));
- 	memset(buf, 0, len);
- 
- 	cmd.opcode = REQUEST_SENSE;
  	cmd.length = len;
  
  	scsi_command(fd, &cmd, sizeof(cmd), buf, len, 10000, SCCMD_READ);
--- 109,114 ----
*** scsictl.8	Sun May 26 01:07:08 2002
--- scsictl.8.orig	Sun May 12 10:42:04 2002
***************
*** 69,94 ****
  The following commands are supported for SCSI devices:
  .Pp
  .Nm format
- .Oo blocksize
- .Oc
  .Pp
! (Low level) format the named device. If the optional 
! .Li blocksize
! parameter is provided, the device geometry will be modified to
! use the specified
! .Li blocksize.
! If this parameter is different from the Current or Default Mode Page 3
! parameters, the device will update Mode Page 3 at the successful
! completion of the Format.  Device geometry may change as a result of
! using a new device
! .Li blocksize.
! When the optional
! .Li blocksize
! parameter is specified, the Defect List on the drive will revert to
! the original primary defect list created at the time of manufacture
! if available.  The drive will usually recertify itself
! during the Format and add any other defective blocks to the new Defect
! List.
  .Pp
  .Nm identify
  .Pp
--- 69,76 ----
  The following commands are supported for SCSI devices:
  .Pp
  .Nm format
  .Pp
! (Low level) format the named revice.
  .Pp
  .Nm identify
  .Pp
***************
*** 107,128 ****
  command to the device, adding the specified blocks to the
  grown defect list.  This command is only supported on
  direct access devices.
- .Pp
- .Nm start
- .Pp
- Issues a
- .Li START
- command to the device to spin up the spindle.  This action may be
- overridden by jumpers on most drives.  With the jumper in place
- the spindle starts up when power is applied and the START/STOP
- commands are ignored.
- .Pp
- .Nm stop
- .Pp
- Issues a
- .Li STOP
- command to the device to spin down the drive motor. This action may
- be overridden by jumpers on most drives.
  .Pp
  .Nm reset
  .Pp
--- 89,94 ----
*** scsictl.c	Sun May 26 08:19:01 2002
--- scsictl.c.orig	Sun May 12 10:42:15 2002
***************
*** 54,60 ****
  #include <util.h>
  
  #include <dev/scsipi/scsipi_all.h>
- #include <dev/scsipi/scsipi_disk.h>
  #include <dev/scsipi/scsi_all.h>
  #include <dev/scsipi/scsi_disk.h>
  #include <dev/scsipi/scsipiconf.h>
--- 54,59 ----
***************
*** 83,98 ****
  void	device_identify __P((int, char *[]));
  void	device_reassign __P((int, char *[]));
  void	device_reset __P((int, char *[]));
- void	device_stop __P((int, char *[]));
- void	device_start __P((int, char *[]));
  
  struct command device_commands[] = {
! 	{ "format",	"[blocksize]",		device_format },
  	{ "identify",	"",			device_identify },
  	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
  	{ "reset",	"",			device_reset },
- 	{ "stop",	"",			device_stop },
- 	{ "start",	"",			device_start },
  	{ NULL,		NULL,			NULL },
  };
  
--- 82,93 ----
  void	device_identify __P((int, char *[]));
  void	device_reassign __P((int, char *[]));
  void	device_reset __P((int, char *[]));
  
  struct command device_commands[] = {
! 	{ "format",	"",			device_format },
  	{ "identify",	"",			device_identify },
  	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
  	{ "reset",	"",			device_reset },
  	{ NULL,		NULL,			NULL },
  };
  
***************
*** 198,336 ****
   * device_format:
   *
   *	Format a direct access device.
   */
  void
  device_format(argc, argv)
  	int argc;
  	char *argv[];
  {
- 	u_int32_t blksize;
- 	int i, j;
- #define ten_pc 65536/10
- 	int complete[] = {ten_pc*1, ten_pc*2, ten_pc*3, ten_pc*4, ten_pc*5,
- 		ten_pc*6, ten_pc*7, ten_pc*8, ten_pc*9, 65536};
- 	char *cp, buffer[64];
- 	struct scsipi_sense_data sense;
  	struct scsi_format_unit cmd;
  	struct {
- 		struct scsi_format_unit_defect_list_header header;
- 		/* optional initialization pattern */
- 		/* optional defect list */
- 	} dfl;
- 	struct {
  		struct scsi_mode_header header;
  		struct scsi_blk_desc blk_desc;
  		struct page_disk_format format_page;
! 	} mode_page;
! 	struct {
! 		struct scsi_mode_header header;
! 		struct scsi_blk_desc blk_desc;
! 	} data_select;
  
! 	/* Blocksize is an optional argument */
! 	if (argc > 1)
  		usage();
  
  	/*
- 	 * Loop doing Request Sense to clear any pending Unit Attention.
- 	 * Multiple conditions may exist on the drive which are returned
- 	 *  in priority order.
- 	 */
- 	for (i=0;i<10;i++) {
- 	    scsi_request_sense(fd, 0, 0, &sense, sizeof(sense));
- 	    if ((j = sense.flags & SSD_KEY) == 0)
- 		break;
- 	    cp = scsi_decode_sense((const unsigned char *)&sense,
- 		 2, buffer, sizeof(buffer));
- 	    printf("clearing Unit Attention: %s\n", cp);
- 	}
- 
- 	/*
  	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
  	 * interleave read from this page in the FORMAT UNIT command.
  	 */
! 	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
! 
!         j = (mode_page.format_page.bytes_s[0] << 8)
! 		| mode_page.format_page.bytes_s[1];
! 	if (j != 512)
!             printf("current disk sector size: %hd\n", j);
  
  	memset(&cmd, 0, sizeof(cmd));
  
  	cmd.opcode = SCSI_FORMAT_UNIT;
! 	memcpy(cmd.interleave, mode_page.format_page.interleave,
  	    sizeof(cmd.interleave));
  
! 	/*
! 	 * The blocksize on the device is only changed if the user
! 	 * specified a new blocksize. If not specified the blocksize
! 	 * used for the device will be the Default value in the device.
! 	 * We don't specify the number of blocks since the format
! 	 * command will always reformat the entire drive.  Also by
! 	 * not specifying a block count the drive will reset the
! 	 * block count to the maximum available after the format
! 	 * completes if the blocksize was changed in the format.
! 	 * Finally, the new disk geometry will not but updated on
! 	 * the drive in permanent storage until _AFTER_ the format
! 	 * completes successfully.
! 	 */
! 	if (argc == 1) {
! 	    blksize = strtoul(argv[0], &cp, 10);
! 	    if (*cp != '\0')
! 		errx(1, "invalid block size: %s\n", argv[0]);
! 
! 	    memset(&data_select, 0, sizeof(data_select));
! 
! 	    data_select.header.blk_desc_len = sizeof(struct scsi_blk_desc);
! 	    /*
! 	     * XXX blklen in desc is 3 bytes with a leading reserved byte
! 	     */
! 	    _lto4b(blksize, &data_select.blk_desc.reserved);
! 
! 	    /*
! 	     * Issue Mode Select to modify the device blocksize to be
! 	     *  used on the Format.  The modified device geometry will
! 	     *  be stored as Current and Saved Page 3 parameters when
! 	     *  the Format completes.
! 	     */
! 	    scsi_mode_select(fd, 0, 0, &data_select, sizeof(data_select));
! 
! 	    /*
! 	     * Since user specified a specific block size make sure it
! 	     *  gets stored in the device when the format completes.
! 	     * Also scrub the defect list back to the manufacturers original.
! 	     */
! 	    cmd.flags = SFU_CMPLST | SFU_FMTDATA;
! 	}
! 
! 	memset(&dfl, 0, sizeof(dfl));
! 	/*
! 	 * Signal target for an immediate return from Format. We'll
! 	 *  poll for completion status.
! 	 */
! 	dfl.header.flags = DLH_IMMED;
! 
! 	scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl), 10000, 0);
! 
! 	/*
! 	 * Poll device for completion of Format
! 	 */
! 	i = 0;
! 	printf("formatting.");
! 	fflush(stdout);
! 	do {
! 	    scsi_request_sense(fd, 0, 0, &sense, sizeof(sense));
! 	    if (sense.sense_key_spec_1 == SSD_SCS_VALID) {
! 		j = (sense.sense_key_spec_2 << 8) | sense.sense_key_spec_3;
! 		if (j >= complete[i]) {
! 		    printf(".%d0%%.", ++i);
! 		    fflush(stdout);
! 		}
! 		sleep(5);
! 	    }
! 	} while ((sense.flags & SSD_KEY) == SKEY_NOT_READY);
! 	printf(".100%%..done.\n");
  
  	return;
  }
--- 193,230 ----
   * device_format:
   *
   *	Format a direct access device.
+  *
+  *	XXX Does not handle defect list management or geometry settings.
   */
  void
  device_format(argc, argv)
  	int argc;
  	char *argv[];
  {
  	struct scsi_format_unit cmd;
  	struct {
  		struct scsi_mode_header header;
  		struct scsi_blk_desc blk_desc;
  		struct page_disk_format format_page;
! 	} data;
  
! 	/* No arguments. */
! 	if (argc != 0)
  		usage();
  
  	/*
  	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
  	 * interleave read from this page in the FORMAT UNIT command.
  	 */
! 	scsi_mode_sense(fd, 0x03, 0x00, &data, sizeof(data));
  
  	memset(&cmd, 0, sizeof(cmd));
  
  	cmd.opcode = SCSI_FORMAT_UNIT;
! 	memcpy(cmd.interleave, data.format_page.interleave,
  	    sizeof(cmd.interleave));
  
! 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 21600000, 0);
  
  	return;
  }
***************
*** 457,513 ****
  	return;
  }
  
- /*
-  * device_stop:
-  *
-  *	Issue a stop to a SCSI device.
-  */
- void
- device_stop(argc, argv)
- 	int argc;
- 	char *argv[];
- {
- 	struct scsipi_start_stop cmd;
- 
- 	/* No arguments. */
- 	if (argc != 0)
- 		usage();
- 
- 	memset(&cmd, 0, sizeof(cmd));
- 
- 	cmd.opcode = START_STOP;
- 	cmd.how = SSS_STOP;
- 
- 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
- 
- 	return;
- }
- 
- /*
-  * device_start:
-  *
-  *	Issue a start to a SCSI device.
-  */
- void
- device_start(argc, argv)
- 	int argc;
- 	char *argv[];
- {
- 	struct scsipi_start_stop cmd;
- 
- 	/* No arguments. */
- 	if (argc != 0)
- 		usage();
- 
- 	memset(&cmd, 0, sizeof(cmd));
- 
- 	cmd.opcode = START_STOP;
- 	cmd.how = SSS_START;
- 
- 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
- 
- 	return;
- }
  /*
   * BUS COMMANDS
   */
--- 351,356 ----

--Apple-Mail-1--971574350--