669 lines
16 KiB
C
669 lines
16 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)defect.c 1.1 94/10/31";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1991 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* This file contains routines that manipulate the defect list.
|
|
*/
|
|
#include "global.h"
|
|
#include <assert.h>
|
|
#include <sys/dkbad.h>
|
|
#include "param.h"
|
|
|
|
|
|
|
|
/*
|
|
* This structure is the bad block table for the current disk if the disk
|
|
* uses bad-144 defect mapping.
|
|
*/
|
|
struct dkbad badmap;
|
|
|
|
/*
|
|
* This routine reads the defect list off the disk. It also reads in the
|
|
* bad block table if the disk is a BAD144 type. The defect list is
|
|
* located on the first 2 tracks of the 2nd alternate cylinder of all disks.
|
|
* The bad block map is located on the first 5 even sectors of the last
|
|
* track of the last cylinder.
|
|
*/
|
|
read_list(list)
|
|
struct defect_list *list;
|
|
{
|
|
int size, head, sec, status;
|
|
struct bt_bad *bt;
|
|
|
|
|
|
/* Panther's working list is maintained by the controller */
|
|
if (cur_ctype->ctype_flags & CF_WLIST) {
|
|
|
|
if ((status = (*cur_ops->ex_cur)(list) == 0)) {
|
|
if (list->header.magicno != DEFECT_MAGIC) {
|
|
printf("Defect list BAD\n");
|
|
}
|
|
printf("ISP-80 working list found\n");
|
|
return(0);
|
|
}
|
|
|
|
if ((status = (*cur_ops->ex_man)(list) == 0)) {
|
|
if (list->header.magicno != DEFECT_MAGIC) {
|
|
printf("Defect list BAD\n");
|
|
}
|
|
printf("MANUFACTURER's list found\n");
|
|
return(0);
|
|
}
|
|
|
|
if (status == -1) {
|
|
printf("No defect list found\n");
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Loop for each copy of the defect list until we get a good one.
|
|
*/
|
|
for (head = 0; head < LISTCOUNT; head++) {
|
|
/*
|
|
* Try to read the list header.
|
|
*/
|
|
if ((*cur_ops->rdwr)(DIR_READ, cur_file,
|
|
chs2bn(ncyl + 1, head, 0), 1,
|
|
(char *)&list->header), F_NORMAL)
|
|
continue;
|
|
/*
|
|
* If the magic number is wrong, this copy is corrupt.
|
|
*/
|
|
if (list->header.magicno != DEFECT_MAGIC)
|
|
continue;
|
|
/*
|
|
* Allocate space for the rest of the list.
|
|
*/
|
|
size = LISTSIZE(list->header.count);
|
|
list->list = (struct defect_entry *)zalloc(size * SECSIZE);
|
|
/*
|
|
* Try to read in the rest of the list. If there is an
|
|
* error, or the checksum is wrong, this copy is corrupt.
|
|
*/
|
|
if ((*cur_ops->rdwr)(DIR_READ, cur_file,
|
|
chs2bn(ncyl + 1, head, 1), size,
|
|
(char *)list->list, F_NORMAL) ||
|
|
checkdefsum(list, CK_CHECKSUM)) {
|
|
/*
|
|
* Destroy the list and go on.
|
|
*/
|
|
kill_deflist(list);
|
|
continue;
|
|
}
|
|
/*
|
|
* For SCSI, a valid defect list consists of the
|
|
* the grown list.
|
|
*/
|
|
if (EMBEDDED_SCSI) {
|
|
list->flags |= LIST_PGLIST;
|
|
}
|
|
/*
|
|
* Got a good copy, stop searching.
|
|
*/
|
|
break;
|
|
}
|
|
/*
|
|
* Check the list for non-bfi defects (SMD and Adaptec disks only).
|
|
* This is required because the logical sectors in 451 defect lists
|
|
* are off by the head skew.
|
|
*/
|
|
(void) makebfi(list,(struct defect_entry *)NULL);
|
|
if (!(cur_ctlr->ctlr_flags & DKI_BAD144))
|
|
return;
|
|
/*
|
|
* The disk uses BAD144, read in the bad-block table.
|
|
*/
|
|
for (sec = 0; sec < BAD_LISTCNT * 2; sec += 2) {
|
|
status = (*cur_ops->rdwr)(DIR_READ, cur_file,
|
|
chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
|
|
&badmap, F_NORMAL);
|
|
if (status)
|
|
continue;
|
|
/*
|
|
* Do a sanity check on the list read in. If it passes,
|
|
* stop searching.
|
|
*/
|
|
if (badmap.bt_mbz != 0)
|
|
continue;
|
|
for (bt = badmap.bt_bad; bt - badmap.bt_bad < NDKBAD; bt++) {
|
|
if (bt->bt_cyl < 0)
|
|
break;
|
|
if (bt->bt_trksec < 0)
|
|
continue;
|
|
head = bt->bt_trksec >> 8;
|
|
if ((bt->bt_cyl >= pcyl) || (head >= nhead) ||
|
|
((bt->bt_trksec & 0xff) >= sectors(head))) {
|
|
status = -1;
|
|
break;
|
|
}
|
|
}
|
|
if (status)
|
|
continue;
|
|
return;
|
|
}
|
|
/*
|
|
* If we couldn't find the bad block table, initialize it to
|
|
* zero entries.
|
|
*/
|
|
for (bt = badmap.bt_bad; bt - badmap.bt_bad < NDKBAD; bt++)
|
|
bt->bt_cyl = bt->bt_trksec = -1;
|
|
badmap.bt_mbz = badmap.bt_csn = badmap.bt_flag = 0;
|
|
}
|
|
|
|
/*
|
|
* Put all logical sector defects into Bytes From Index (bfi) format.
|
|
* This is necessary
|
|
* because 450/451 formatted drives have all their logical sectors off
|
|
* by the head skew but the 7053 does not.
|
|
* We are called by add_ldef() and d_add() when a defect is being added
|
|
* to the list, or by read_list() when a defect list is read in.
|
|
*/
|
|
int
|
|
makebfi(list,def)
|
|
struct defect_list *list;
|
|
struct defect_entry *def;
|
|
{
|
|
struct defect_entry *ptr, save;
|
|
struct defect_entry *end = list->list + list->header.count - 1;
|
|
struct dk_info dkinfo;
|
|
int prevdef, index, skew = 0;
|
|
int i, savpos;
|
|
int is_acb4000 = 0;
|
|
|
|
/*
|
|
* Only do this stuff for SMD and Adaptec controllers.
|
|
* Note sector skew for * 450/451 controllers.
|
|
*/
|
|
if (ioctl(cur_file, DKIOCINFO, &dkinfo) >= 0) {
|
|
switch (dkinfo.dki_ctype) {
|
|
case DKC_XY450:
|
|
skew = 1;
|
|
break;
|
|
case DKC_XD7053:
|
|
break;
|
|
case DKC_ACB4000:
|
|
is_acb4000 = 1;
|
|
break;
|
|
default:
|
|
return(0);
|
|
}
|
|
}
|
|
if (def != NULL ) {
|
|
/*
|
|
* Calculate the bfi for this defect. The caller will
|
|
* sort it and add it to the list.
|
|
*/
|
|
if (is_acb4000) {
|
|
if (acb_calc_bfi(def))
|
|
return(0);
|
|
}
|
|
else {
|
|
(void) calc_bfi(list, def, end, skew);
|
|
}
|
|
return(1);
|
|
}
|
|
/*
|
|
* if there is no defect list then return
|
|
*/
|
|
if (list->list == (struct defect_entry *) 0)
|
|
return(0);
|
|
/*
|
|
* Sort the defect list in place.
|
|
*/
|
|
for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
|
|
if (ptr->bfi != UNKNOWN)
|
|
continue;
|
|
/*
|
|
* Got a non-bfi defect. Look through the list up to this
|
|
* point for a previous defects on this track. If the FIRST
|
|
* defect on the track is closer to sector 0 than this one,
|
|
* we have to add the number of bytes/sector when we calculate
|
|
* the bfi, as we are "ahead of" the slipped sector.
|
|
* If the defect is numerically less than the first defect on
|
|
* the track, no addition is necessary.
|
|
*
|
|
* NOTE: all this assumes we only slip once per track and that
|
|
* logical defects always follow bfi defects in the defect list.
|
|
* If the list is all logical defects, we assume chronological
|
|
* ordering. Thus, sector 10 will be slipped if it is first in
|
|
* the list, even if followed by sector 5.
|
|
*/
|
|
|
|
if (is_acb4000) {
|
|
if (acb_calc_bfi(ptr))
|
|
return(0); /* something went wrong */
|
|
}
|
|
else {
|
|
(void) calc_bfi(list, ptr, ptr, skew);
|
|
}
|
|
|
|
}
|
|
/*
|
|
* Sort the list. We take the expedient route and remove the defect from
|
|
* the list and use add_def to put it back in. Not pretty, but it works.
|
|
*/
|
|
for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
|
|
bcopy((char *)ptr, (char *)&save, sizeof(struct defect_entry));
|
|
savpos = ptr - list->list;
|
|
for (i = savpos; i < list->header.count; i++)
|
|
*(list->list + i) = *(list->list + i + 1);
|
|
--list->header.count;
|
|
index = sort_defect(&save, list);
|
|
add_def(&save, list, index);
|
|
}
|
|
/*
|
|
* Make sure to redo the checksum.
|
|
*/
|
|
(void) checkdefsum (list,CK_MAKESUM);
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* Calculate the bytes from index for this defect. If the first defect
|
|
* on this track (determined by its position in the defect list) was
|
|
* closer to logical sector 0, then we add the number of bytes/sector,
|
|
* since this one is "ahead" of the slipped sector. We mark the length
|
|
* of the defect in bits as being the length of a sector to differentiate
|
|
* it from manufacturers and bfi defects added by the user.
|
|
*/
|
|
calc_bfi(list, def, end, skew)
|
|
struct defect_list *list;
|
|
struct defect_entry *def;
|
|
struct defect_entry *end;
|
|
int skew;
|
|
|
|
{
|
|
struct defect_entry *tmp;
|
|
int bps = cur_dtype->dtype_bps;
|
|
int a, b;
|
|
|
|
def->bfi = bps * ((def->sect + def->head * skew) % (nsect + 1));
|
|
def->nbits = SECSIZE;
|
|
|
|
if (list->list == (struct defect_entry *) 0)
|
|
return;
|
|
for( tmp = list->list; tmp < end; tmp++) {
|
|
if( tmp->cyl != def->cyl || tmp->head != def->head )
|
|
continue;
|
|
/*
|
|
* Found another defect on this track. See if it's closer
|
|
* numerically to logical sector 0. If it is, bump our defect's
|
|
* bfi by the number of bytes per sector.
|
|
*/
|
|
a = tmp->bfi;
|
|
b = def->bfi;
|
|
if (tmp->bfi < tmp->head * bps)
|
|
a = tmp->bfi + ((nsect + 1 - tmp->head) * bps);
|
|
if (def->bfi < def->head * bps)
|
|
b = def->bfi + ((nsect + 1 - def->head) * bps);
|
|
if (a <= b) {
|
|
def->bfi += bps;
|
|
return;
|
|
} else
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Add the logical sector information for each bfi entry. This is
|
|
* done to be compatible with the diag program
|
|
* This has to be done anytime that the defect list can be written to
|
|
* the disk or a file
|
|
* A disk on a 450 without a spare and with a runt does not work properly.
|
|
* This bug is left in because it would mean that the disk might have to
|
|
* be formatted to determine what the track headers look like. I didn't
|
|
* do that because the code to figure out if the disk needs to be formatted
|
|
* to determine the header geometry is error prone (and destructive).
|
|
*/
|
|
int
|
|
makelsect(list)
|
|
struct defect_list *list;
|
|
{
|
|
struct defect_entry *ptr,*tmp;
|
|
struct dk_info dkinfo;
|
|
struct dk_type type;
|
|
int skew = 0;
|
|
int runt = 1;
|
|
int hsect = nsect;
|
|
|
|
/*
|
|
* Only do for SMD disks which for now are 7053 & 451/450
|
|
*/
|
|
if (ioctl(cur_file, DKIOCINFO, &dkinfo) >= 0) {
|
|
switch (dkinfo.dki_ctype) {
|
|
case DKC_XY450:
|
|
skew = 1;
|
|
if (!ioctl(cur_file, DKIOCGTYPE, &type))
|
|
if ((hsect = type.dkt_hsect) == (nsect + 1))
|
|
runt = 0;
|
|
break;
|
|
case DKC_XD7053:
|
|
break;
|
|
default:
|
|
return(0);
|
|
}
|
|
}
|
|
/*
|
|
* if there is no defect list then return
|
|
*/
|
|
if (list->list == (struct defect_entry *) 0)
|
|
return(0);
|
|
|
|
for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
|
|
if (ptr->bfi == UNKNOWN)
|
|
continue;
|
|
ptr->sect = ptr->bfi/cur_dtype->dtype_bps - ptr->head * skew ;
|
|
if (ptr->sect < 0)
|
|
ptr->sect += runt ? (hsect - 1) : hsect;
|
|
/*
|
|
* correct the logical sector for track that has more than
|
|
* one defect that are on seperate physical sectors
|
|
*/
|
|
if (ptr != list->list) { /* wait until a defect to compare to */
|
|
tmp = &ptr[-1];
|
|
if (ptr->cyl == tmp->cyl && ptr->head == tmp->head &&
|
|
ptr->sect != tmp->sect && tmp->bfi != ptr->bfi)
|
|
ptr->sect -= 1;
|
|
}
|
|
}
|
|
/*
|
|
* Make sure to redo the checksum.
|
|
*/
|
|
(void) checkdefsum (list,CK_MAKESUM);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* This routine either checks or calculates the checksum for a defect
|
|
* list, depending on the mode parameter. In check mode, it returns
|
|
* whether or not the checksum is correct.
|
|
*/
|
|
int
|
|
checkdefsum(list, mode)
|
|
struct defect_list *list;
|
|
int mode;
|
|
{
|
|
register int *lp, i, sum = 0;
|
|
|
|
/*
|
|
* Perform the rolling xor to get what the checksum should be.
|
|
*/
|
|
lp = (int *)list->list;
|
|
for (i = 0; i < (list->header.count * sizeof (struct defect_entry) /
|
|
sizeof (int)); i++)
|
|
sum ^= *(lp + i);
|
|
/*
|
|
* If in check mode, return whether header checksum was correct.
|
|
*/
|
|
if (mode == CK_CHECKSUM)
|
|
return (sum != list->header.cksum);
|
|
/*
|
|
* If in create mode, set the header checksum.
|
|
*/
|
|
else {
|
|
list->header.cksum = sum;
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This routine prints a single defect to stdout in a readable format.
|
|
*/
|
|
pr_defect(def, num)
|
|
struct defect_entry *def;
|
|
int num;
|
|
{
|
|
|
|
/*
|
|
* Make defect numbering look 1 relative.
|
|
*/
|
|
++num;
|
|
/*
|
|
* Print out common values.
|
|
*/
|
|
printf("%4d%8d%7d", num, def->cyl, def->head);
|
|
/*
|
|
* The rest of the values may be unknown. If they are, just
|
|
* print blanks instead. Also, only print length only if bfi is
|
|
* known, and assume that a known bfi implies an unknown sect.
|
|
*/
|
|
if (def->bfi != UNKNOWN) {
|
|
printf("%8d", def->bfi);
|
|
if (def->nbits != UNKNOWN)
|
|
printf("%8d", def->nbits);
|
|
} else {
|
|
printf(" ");
|
|
printf("%8d", def->sect);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* This routine calculates where in a defect list a given defect should
|
|
* be sorted. It returns the index that the defect should become. The
|
|
* algorithm used sorts all bfi based defects by cylinder/head/bfi, and
|
|
* adds all logical sector defects to the end of the list. This is necessary
|
|
* because the ordering of logical sector defects is significant when
|
|
* sector slipping is employed.
|
|
*/
|
|
int
|
|
sort_defect(def, list)
|
|
struct defect_entry *def;
|
|
struct defect_list *list;
|
|
{
|
|
struct defect_entry *ptr;
|
|
|
|
/*
|
|
* If it's a logical sector defect, return the entry at the end
|
|
* of the list.
|
|
*/
|
|
if (def->bfi == UNKNOWN)
|
|
return (list->header.count);
|
|
/*
|
|
* It's a bfi defect. Loop through the defect list.
|
|
*/
|
|
for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
|
|
/*
|
|
* If we get to a logical sector defect, put this defect
|
|
* right before it.
|
|
*/
|
|
if (ptr->bfi == UNKNOWN)
|
|
goto found;
|
|
/*
|
|
* If we get to a defect that is past this one in
|
|
* cylinder/head/bfi, put this defect right before it.
|
|
*/
|
|
if (def->cyl < ptr->cyl)
|
|
goto found;
|
|
if (def->cyl != ptr->cyl)
|
|
continue;
|
|
if (def->head < ptr->head)
|
|
goto found;
|
|
if (def->head != ptr->head)
|
|
continue;
|
|
if (def->bfi < ptr->bfi)
|
|
goto found;
|
|
}
|
|
found:
|
|
/*
|
|
* Return the index to put the defect at.
|
|
*/
|
|
return (ptr - list->list);
|
|
}
|
|
|
|
/*
|
|
* This routine writes the defect list on the back on the disk. It also
|
|
* writes the bad block table to disk if bad-144 mapping applies to the
|
|
* current disk.
|
|
*/
|
|
write_deflist(list)
|
|
struct defect_list *list;
|
|
{
|
|
int size, status, head, sec;
|
|
caddr_t bad_ptr = (caddr_t)&badmap;
|
|
|
|
|
|
/* Panther's working list is maintained by the controller */
|
|
if (cur_ctype->ctype_flags & CF_WLIST) {
|
|
if ((status = (*cur_ops->wr_cur)(list) == 0)) {
|
|
return(0);
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* If the list is null, there is nothing to write.
|
|
*/
|
|
if (list->list != NULL) {
|
|
/*
|
|
* Make sure that the defect list is compatible with diag
|
|
*/
|
|
(void) makelsect(list);
|
|
|
|
/*
|
|
* calculate how many sectors the defect list will occupy.
|
|
*/
|
|
size = LISTSIZE(list->header.count);
|
|
/*
|
|
* Loop for each copy of the list to be written. Write out
|
|
* the header of the list followed by the data.
|
|
*/
|
|
for (head = 0; head < LISTCOUNT; head++) {
|
|
status = (*cur_ops->rdwr)(DIR_WRITE, cur_file,
|
|
chs2bn(ncyl + 1, head, 0), 1,
|
|
(char *)&list->header, F_NORMAL);
|
|
if (status) {
|
|
eprint("Warning: error saving defect list.\n");
|
|
continue;
|
|
}
|
|
status = (*cur_ops->rdwr)(DIR_WRITE, cur_file,
|
|
chs2bn(ncyl + 1, head, 1), size,
|
|
(char *)list->list, F_NORMAL);
|
|
if (status)
|
|
eprint("Warning: error saving defect list.\n");
|
|
}
|
|
}
|
|
if (!(cur_ctlr->ctlr_flags & DKI_BAD144))
|
|
return;
|
|
|
|
assert(!EMBEDDED_SCSI);
|
|
/*
|
|
* Current disk uses bad-144 mapping. Loop for each copy of the
|
|
* bad block table to be written and write it out.
|
|
*/
|
|
for (sec = 0; sec < BAD_LISTCNT * 2; sec += 2) {
|
|
status = (*cur_ops->rdwr)(DIR_WRITE, cur_file,
|
|
chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
|
|
&badmap, F_NORMAL);
|
|
if (status) {
|
|
eprint("Warning: error saving bad block map table.\n");
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
* Execute an ioctl to tell unix about the new bad block table.
|
|
*/
|
|
if (ioctl(cur_file, DKIOCSBAD, &bad_ptr))
|
|
eprint("Warning: error telling SunOS bad block map table.\n");
|
|
}
|
|
|
|
/*
|
|
* This routine adds a logical sector to the given defect list.
|
|
*/
|
|
add_ldef(blkno, list)
|
|
daddr_t blkno;
|
|
struct defect_list *list;
|
|
{
|
|
struct defect_entry def;
|
|
int index;
|
|
|
|
/*
|
|
* Calculate the fields for the defect struct.
|
|
*/
|
|
def.cyl = bn2c(blkno);
|
|
def.head = bn2h(blkno);
|
|
def.sect = bn2s(blkno);
|
|
/*
|
|
* Initialize the unknown fields.
|
|
*/
|
|
def.bfi = def.nbits = UNKNOWN;
|
|
/*
|
|
* Make this a bfi defect.
|
|
*/
|
|
(void) makebfi(list,&def);
|
|
/*
|
|
* Calculate the index into the list that the defect belongs at.
|
|
*/
|
|
index = sort_defect(&def, list);
|
|
/*
|
|
* Add the defect to the list.
|
|
*/
|
|
add_def(&def, list, index);
|
|
}
|
|
|
|
/*
|
|
* This routine adds the given defect struct to the defect list at
|
|
* a precalculated index.
|
|
*/
|
|
add_def(def, list, index)
|
|
struct defect_entry *def;
|
|
struct defect_list *list;
|
|
int index;
|
|
{
|
|
int count, i;
|
|
|
|
/*
|
|
* If adding this defect makes the list overflow into another
|
|
* sector, allocate the necessary space.
|
|
*/
|
|
count = list->header.count;
|
|
if (LISTSIZE(count + 1) > LISTSIZE(count))
|
|
list->list = (struct defect_entry *)rezalloc((char *)list->list,
|
|
LISTSIZE(count + 1) * SECSIZE);
|
|
/*
|
|
* Slip all the defects after this one down one slot in the list.
|
|
*/
|
|
for (i = count; i > index; i--)
|
|
*(list->list + i) = *(list->list + i - 1);
|
|
/*
|
|
* Fill in the created hole with this defect.
|
|
*/
|
|
*(list->list + i) = *def;
|
|
/*
|
|
* Increment the count and calculate a new checksum.
|
|
*/
|
|
list->header.count++;
|
|
(void)checkdefsum(list, CK_MAKESUM);
|
|
}
|
|
|
|
/*
|
|
* This routine sets the given defect list back to null.
|
|
*/
|
|
kill_deflist(list)
|
|
struct defect_list *list;
|
|
{
|
|
|
|
/*
|
|
* If it's already null, we're done.
|
|
*/
|
|
if (list->list == NULL)
|
|
return;
|
|
/*
|
|
* Free the malloc'd space it's using.
|
|
*/
|
|
destroy_data((char *)list->list);
|
|
/*
|
|
* Mark it as null.
|
|
*/
|
|
list->list = NULL;
|
|
list->flags = 0;
|
|
}
|
|
|