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);
}