diff options
Diffstat (limited to 'scsi/rz_disk_bbr.c')
-rw-r--r-- | scsi/rz_disk_bbr.c | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/scsi/rz_disk_bbr.c b/scsi/rz_disk_bbr.c new file mode 100644 index 0000000..9d87675 --- /dev/null +++ b/scsi/rz_disk_bbr.c @@ -0,0 +1,259 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * File: rz_disk_bbr.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/91 + * + * Top layer of the SCSI driver: interface with the MI. + * This file contains bad-block management functions + * (retry, replace) for disk-like devices. + */ + +#include <mach/std_types.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> + +#if (NSCSI > 0) + +int scsi_bbr_retries = 10; + +#define BBR_ACTION_COMPLETE 1 +#define BBR_ACTION_RETRY_READ 2 +#define BBR_ACTION_REASSIGN 3 +#define BBR_ACTION_COPY 4 +#define BBR_ACTION_VERIFY 5 + +static void make_msf(); /* forward */ + +/* + * Bad block replacement routine, invoked on + * unrecovereable disk read/write errors. + */ +boolean_t +scdisk_bad_block_repl(tgt, blockno) + target_info_t *tgt; + unsigned int blockno; +{ + register io_req_t ior = tgt->ior; + + if (scsi_no_automatic_bbr || (ior->io_op & IO_INTERNAL)) + return FALSE; + + /* signal we took over */ + tgt->flags |= TGT_BBR_ACTIVE; + + printf("%s", "Attempting bad block replacement.."); + + tgt->dev_info.disk.b.badblockno = blockno; + tgt->dev_info.disk.b.retry_count = 0; + + tgt->dev_info.disk.b.save_rec = ior->io_recnum; + tgt->dev_info.disk.b.save_addr = ior->io_data; + tgt->dev_info.disk.b.save_count = ior->io_count; + tgt->dev_info.disk.b.save_resid = ior->io_residual; + + /* + * On a write all we need is to rewire the offending block. + * Note that the sense data identified precisely which 512 sector + * is bad. At the end we'll retry the entire write, so if there + * is more than one bad sector involved they will be handled one + * at a time. + */ + if ((ior->io_op & IO_READ) == 0) { + char msf[sizeof(int)]; + ior->io_temporary = BBR_ACTION_COMPLETE; + printf("%s", "just reassign.."); + make_msf(msf,blockno); + scsi_reassign_blocks( tgt, msf, 1, ior); + } else + /* + * This is more complicated. We asked for N bytes, and somewhere + * in there there is a chunk of bad data. First off, we should retry + * at least a couple of times to retrieve that data [yes the drive + * should have done its best already so what]. If that fails we + * should recover as much good data as possible (before the bad one). + */ + { + ior->io_temporary = BBR_ACTION_RETRY_READ; + printf("%s", "retry read.."); + ior->io_residual = 0; + scdisk_start_rw(tgt, ior); + } + + return TRUE; +} + +static +void make_msf(buf,val) + unsigned char *buf; + unsigned int val; +{ + *buf++ = val >> 24; + *buf++ = val >> 16; + *buf++ = val >> 8; + *buf++ = val >> 0; +} + +/* + * This effectively replaces the strategy routine during bbr. + */ +void scdisk_bbr_start( tgt, done) + target_info_t *tgt; + boolean_t done; +{ + register io_req_t ior = tgt->ior; + char *msg; + + switch (ior->io_temporary) { + + case BBR_ACTION_COMPLETE: + + /* all done, either way */ +fin: + tgt->flags &= ~TGT_BBR_ACTIVE; + ior->io_recnum = tgt->dev_info.disk.b.save_rec; + ior->io_data = tgt->dev_info.disk.b.save_addr; + ior->io_count = tgt->dev_info.disk.b.save_count; + ior->io_residual = tgt->dev_info.disk.b.save_resid; + + if (tgt->done == SCSI_RET_SUCCESS) { + /* restart normal life */ + register unsigned int xferred; + if (xferred = ior->io_residual) { + ior->io_data -= xferred; + ior->io_count += xferred; + ior->io_recnum -= xferred / tgt->block_size; + ior->io_residual = 0; + } + /* from the beginning */ + ior->io_error = 0; + msg = "done, restarting."; + } else { + /* we could not fix it. Tell user and give up */ + tgt->ior = ior->io_next; + iodone(ior); + msg = "done, but could not recover."; + } + + printf("%s\n", msg); + scdisk_start( tgt, FALSE); + return; + + case BBR_ACTION_RETRY_READ: + + /* see if retry worked, if not do it again */ + if (tgt->done == SCSI_RET_SUCCESS) { + char msf[sizeof(int)]; + + /* whew, retry worked. Now rewire that bad block + * and don't forget to copy the good data over */ + + tgt->dev_info.disk.b.retry_count = 0; + printf("%s", "ok now, reassign.."); + ior->io_temporary = BBR_ACTION_COPY; + make_msf(msf, tgt->dev_info.disk.b.badblockno); + scsi_reassign_blocks( tgt, msf, 1, ior); + return; + } + if (tgt->dev_info.disk.b.retry_count++ < scsi_bbr_retries) { + scdisk_start_rw( tgt, ior); + return; + } + /* retrying was hopeless. Leave the bad block there for maintainance */ + /* because we do not know what to write on it */ + printf("%s%d%s", "failed after ", scsi_bbr_retries, " retries.."); + goto fin; + + + case BBR_ACTION_COPY: + + /* retrying succeded and we rewired the bad block. */ + if (tgt->done == SCSI_RET_SUCCESS) { + unsigned int tmp; + struct diskpart *label; + + printf("%s", "ok, rewrite.."); + + /* writeback only the bad sector */ + + /* map blockno back to partition offset */ +/* !!! partition code changes: */ + tmp = rzpartition(ior->io_unit); +/* label=lookup_part(array, tmp +1); */ + tmp = tgt->dev_info.disk.b.badblockno - +/* label->start; */ +/* #if 0 */ + tgt->dev_info.disk.l.d_partitions[tmp].p_offset; +/* #endif 0 */ + ior->io_data += (tmp - ior->io_recnum) * tgt->block_size; + ior->io_recnum = tmp; + ior->io_count = tgt->block_size; + ior->io_op &= ~IO_READ; + + ior->io_temporary = BBR_ACTION_VERIFY; + scdisk_start_rw( tgt, ior); + } else { + + /* either unsupported command, or repl table full */ + printf("%s", "reassign failed (really needs reformatting), "); + ior->io_error = 0; + goto fin; + } + break; + + case BBR_ACTION_VERIFY: + + if (tgt->done == SCSI_RET_SUCCESS) { + ior->io_op |= IO_READ; + goto fin; + } + + if (tgt->dev_info.disk.b.retry_count++ > scsi_bbr_retries) { + printf("%s%d%s", "failed after ", + scsi_bbr_retries, " retries.."); + ior->io_op |= IO_READ; + goto fin; + } + + /* retry, we are *this* close to success.. */ + scdisk_start_rw( tgt, ior); + + break; + + case BBR_ACTION_REASSIGN: + + /* if we wanted to issue the reassign multiple times */ + /* XXX unimplemented XXX */ + + default: /* snafu */ + panic("scdisk_bbr_start"); + } +} +#endif /* NSCSI > 0 */ |