Subject: Re: BAD BLOCKS WITH SCSI DISKS, HOW TO REMAP
To: Brian Buhrow <buhrow@cats.ucsc.edu>
From: Jason Thorpe <thorpej@nas.nasa.gov>
List: current-users
Date: 06/03/1996 23:04:38
On Mon, 3 Jun 1996 16:17:53 -0700 
 buhrow@cats.ucsc.edu (Brian Buhrow) wrote:

 > 	Hello NetBSD folks.  I'm running a machine with NetBSD 1.1A (circa 
 > February 1996 sources) which has a Bus Logic BT946C and a Seagate
 > Technologies ST31230N 1.X gig disk attached to it.  The machine (an i386
 > Pentium) runs fine, except that I'm getting lines of the form:
 > 

A few months ago, I wrote a quick little program to do this for a friend 
of mine.  It's appended below...

Usage is:

	sdremap /dev/rsd1d 45532

...assuming that sd1 has the bad block, and the bad block number is 
45532.  Note, you must use the "raw" partition, d on i386, c on all others.

 > Jun  3 02:01:53 rumpleteazer /netbsd: sd0(bt0:0:0):
 > medium error, info = 842458 (decimal),
 > data = 00 00 00 00 11 00 00 00 00 00 06 22 00 53

Hmm, might want to look up that sense key in the SCSI specs...

 > 	Is there a utility under NetBSD, like SunOS's format command, which 
 > will let me issue the remap scsi request to this drive so it will use a new
 > block?  I've had this discussion before, but seem to remember that in order
 > to do this, I had to become a scsi byte coder and issue raw requests to the
 > scsi disk.  I'd prefer something with a little higher level interface if
 > possible.

Oh, it's actually a lot easier than sending raw requests ... Anyhow, my 
quick little hack is appended below ... Gee, a libscsi sure would be nice :-)

----save the ancient forests - http://www.bayarea.net/~thorpej/forest/----
Jason R. Thorpe                                       thorpej@nas.nasa.gov
NASA Ames Research Center                               Home: 408.866.1912
NAS: M/S 258-6                                          Work: 415.604.0935
Moffett Field, CA 94035                                Pager: 415.428.6939

----- snip -----
/*
 * sdremap <dev> <blkno> - tell a SCSI disk to remap the bad block
 *                         specified on the command line.
 *
 * Compile this on your favorite NetBSD 1.1 system with:
 *
 *	cc -I/sys -DOLD_SCSI_BYTES -o sdremap sdremap.c
 *
 * or on newer NetBSD systems with:
 *
 *	cc -I/sys -o sdremap sdremap.c
 *
 * It will be pretty obvious if you need to add or remove the OLD_SCSI_BYTES
 * define; this program won't compile if the setting is incorrect.
 *
 * When you use this program, make sure the disk you're mucking with
 * has no mounted filesystems (unless that's just not possible, in which
 * case, make sure they're mounted read-only, and you're in single-user
 * mode).  You must use the "raw partition" of the disk.  On an i386, this
 * is `d', and everywhere else `c'.
 *
 * I.e.:
 *	./sdremap /dev/rsd1d 356642
 *
 * Written by Jason R. Thorpe <thorpej@NetBSD.ORG>.
 * This program is in the public domain.
 *
 * Use this software at your own risk.  At no time shall Jason R. Thorpe
 * be liable for any damages it may cause.
 *
 * TODO:
 *	Handle multiple blocks with a single command.  I'm just lazy.
 */

#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/scsiio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <scsi/scsi_all.h>
#include <scsi/scsi_disk.h>

extern	char *__progname;		/* from crt0.o */

static	void usage __P((void));
static	void cleanup __P((void));

static	int disk_fd;

int
main(argc, argv)
	int argc;
	char **argv;
{
	struct scsi_reassign_blocks cmd;
	struct scsi_reassign_blocks_data desc;
	scsireq_t req;
	char *disk, *cp;
	u_int32_t blkno;
	u_int16_t desc_data_len;

	if (argc != 3)
		usage();

	disk = argv[1];
	blkno = (u_int32_t)strtol(argv[2], &cp, 10);
	if (*cp != '\0')
		errx(1, "invalid block number `%s'", argv[2]);

	/* Register cleanup function. */
	if (atexit(cleanup))
		err(1, "can't register cleanup function");

	/* Open the device... */
	if ((disk_fd = open(disk, O_RDWR, 0600)) == -1)
		err(1, "%s: open", disk);

	/* Build SCSI command. */
	bzero(&cmd, sizeof(cmd));
	cmd.opcode = REASSIGN_BLOCKS;

	/*
	 * Build the block descriptor.
	 */
	desc_data_len = sizeof(desc.defect_descriptor[0]);
	bzero(&desc, sizeof(desc));
	/* Descriptor length. */
#ifdef OLD_SCSI_BYTES
	desc.length_msb = (desc_data_len >> 8) & 0xff;
	desc.length_lsb = desc_data_len & 0xff;
#else
	desc.length[0] = (desc_data_len >> 8) & 0xff;
	desc.length[1] = desc_data_len & 0xff;
#endif
	/* Block number. */
#ifdef OLD_SCSI_BYTES
	desc.defect_descriptor[0].dlbaddr_3 = (blkno >> 24) & 0xff;
	desc.defect_descriptor[0].dlbaddr_2 = (blkno >> 16) & 0xff;
	desc.defect_descriptor[0].dlbaddr_1 = (blkno >> 8) & 0xff;
	desc.defect_descriptor[0].dlbaddr_0 = blkno & 0xff;
#else
	desc.defect_descriptor[0].dlbaddr[0] = (blkno >> 24) & 0xff;
	desc.defect_descriptor[0].dlbaddr[1] = (blkno >> 16) & 0xff;
	desc.defect_descriptor[0].dlbaddr[2] = (blkno >> 8) & 0xff;
	desc.defect_descriptor[0].dlbaddr[3] = blkno & 0xff;
#endif

	/* Fill out user-level SCSI request. */
	bzero(&req, sizeof(req));
	bcopy((struct scsi_generic *)&cmd, &req.cmd, sizeof(cmd));
	req.cmdlen = (u_char)sizeof(cmd);
	req.databuf = (void *)&desc;
	req.datalen = (u_long)sizeof(desc);
	req.flags |= SCCMD_WRITE;
	req.timeout = 20000;			/* XXX */

	/* Send the request to the SCSI subsystem. */
	if (ioctl(disk_fd, SCIOCCOMMAND, (char *)&req) == -1)
		err(1, "SCIOCCOMMAND");

	/*
	 * XXX should check for error condition and print
	 * sense data.
	 */

	exit(0);
}

static void
cleanup()
{

	/* ...simple enough... */
	(void)close(disk_fd);
}

static void
usage()
{

	fprintf(stderr, "usage: %s disk blkno\n", __progname);
	exit(1);
}