OpenSolaris_b135/cmd/format/defect.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This file contains routines that manipulate the defect list.
 */
#include "global.h"
#include <sys/types.h>
#include <sys/param.h>

#if defined(sparc)
#include <sys/hdio.h>
#endif		/* defined(sparc) */

#include <sys/buf.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/fcntl.h>
#include <string.h>
#include <unistd.h>
#include <memory.h>

#if defined(sparc)
#include <sys/dkbad.h>
#endif		/* defined(sparc) */

#include "misc.h"
#include "param.h"


#if defined(sparc)
/*
 * This structure is the bad block table for the current disk if
 * the disk uses bad-144 defect mapping.
 */
struct	dkbad badmap;
#endif		/* defined(sparc) */

/*
 * 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.
 */
void
read_list(struct defect_list *list)
{
	int	size, head;

#if defined(sparc)
	int sec, status;
	struct	bt_bad *bt;
#endif	/* defined(sparc) */

	assert(!EMBEDDED_SCSI);

	/*
	 * This flags has been introduced only for Sparc ATA IDE.
	 * This indicates that no list manipulation is done in this controller
	 * and hence return without any other checking.
	 */
	if (cur_ctype->ctype_flags & CF_NOWLIST) {
		return;
	}

	/*
	 * Panther's working list is maintained by the controller
	 */
	if (cur_ctype->ctype_flags & CF_WLIST) {
		if (*cur_ops->op_ex_cur != NULL &&
		    ((*cur_ops->op_ex_cur)(list)) == 0) {
			if (list->header.magicno != DEFECT_MAGIC) {
				fmt_print("Defect list BAD\n");
			} else {
				fmt_print("Controller working list found\n");
			}
			return;
		}

		if (*cur_ops->op_ex_man != NULL &&
		    ((*cur_ops->op_ex_man)(list)) == 0) {
			if (list->header.magicno != DEFECT_MAGIC) {
				fmt_print("Defect list BAD\n");
			} else {
				fmt_print("MANUFACTURER's list found\n");
			}
			return;
		}
		fmt_print("No defect list found\n");
		return;
	}

	/*
	 * 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->op_rdwr)(DIR_READ, cur_file,
		    (diskaddr_t)chs2bn(ncyl + 1, head, 0), 1,
		    (char *)&list->header, NULL), 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 = deflist_size(cur_blksz, list->header.count);
		list->list = (struct defect_entry *)zalloc(size * cur_blksz);
		/*
		 * 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->op_rdwr)(DIR_READ, cur_file,
		    (diskaddr_t)chs2bn(ncyl + 1, head, 1), size,
		    (char *)list->list, F_NORMAL, NULL) ||
		    checkdefsum(list, CK_CHECKSUM)) {
			/*
			 * Destroy the list and go on.
			 */
			kill_deflist(list);
			continue;
		}
		/*
		 * Got a good copy, stop searching.
		 */
		break;
	}
#if defined(sparc)
	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 < nsect)); sec += 2) {
		status = (*cur_ops->op_rdwr)(DIR_READ, cur_file,
		    (diskaddr_t)chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
		    &badmap, F_NORMAL, NULL);
		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;
#endif		/* defined(sparc) */
}

/*
 * 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(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.
 */
void
pr_defect(struct defect_entry *def, int num)
{

	/*
	 * Make defect numbering look 1 relative.
	 */
	++num;
	/*
	 * Print out common values.
	 */
	fmt_print("%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) {
		fmt_print("%8d", def->bfi);
		if (def->nbits != UNKNOWN)
			fmt_print("%8d", def->nbits);
	} else {
		fmt_print("                ");
		fmt_print("%8d", def->sect);
		fmt_print("%8llu", chs2bn(def->cyl, def->head, def->sect));
	}
	fmt_print("\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(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.
 */
void
write_deflist(struct defect_list *list)
{
	int	size, head, status;

#if defined(sparc)
	int sec;
	caddr_t	bad_ptr = (caddr_t)&badmap;
#endif			/* defined(sparc) */

	assert(!EMBEDDED_SCSI);

	/*
	 * Sparc ATA IDE.
	 * This indicates that no list manipulation is done in this controller
	 * and hence return without any other checking.
	 */
	if (cur_ctype->ctype_flags & CF_NOWLIST) {
		return;
	}

	/*
	 * Panther's working list is maintained by the controller
	 */
	if (cur_ctype->ctype_flags & CF_WLIST) {
		(*cur_ops->op_wr_cur)(list);
		return;
	}

	/*
	 * If the list is null, there is nothing to write.
	 */
	if (list->list != NULL) {
		/*
		 * calculate how many sectors the defect list will occupy.
		 */
		size = deflist_size(cur_blksz, 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->op_rdwr)(DIR_WRITE, cur_file,
			    (diskaddr_t)chs2bn(ncyl + 1, head, 0), 1,
			    (char *)&list->header, F_NORMAL, NULL);
			if (status) {
				err_print(
"Warning: error saving defect list.\n");
				continue;
			}
			status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
			    (diskaddr_t)chs2bn(ncyl + 1, head, 1), size,
			    (char *)list->list, F_NORMAL, NULL);
			if (status)
				err_print(
"Warning: error saving defect list.\n");
		}
	}
	if (!(cur_ctlr->ctlr_flags & DKI_BAD144))
		return;
#if defined(sparc)
	/*
	 * 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 < nsect)); sec += 2) {
		status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
		    (diskaddr_t)chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
		    &badmap, F_NORMAL, NULL);
		if (status) {
			err_print(
"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, HDKIOCSBAD, &bad_ptr))
		err_print(
"Warning: error telling SunOS bad block map table.\n");
#endif		/* defined(sparc) */
}

/*
 * This routine adds a logical sector to the given defect list.
 */
void
add_ldef(diskaddr_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;
	/*
	 * 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.
 */
void
add_def(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 (deflist_size(cur_blksz, count + 1) > deflist_size(cur_blksz, count))
		list->list = (struct defect_entry *)rezalloc((void *)list->list,
		    deflist_size(cur_blksz, count + 1) * cur_blksz);
	/*
	 * 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.
 */
void
kill_deflist(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, and clear any flags.
	 */
	list->list = NULL;
	list->flags = 0;
}

/*
 * This routine returns the defect list size
 * according to the sector size.
 */
int
deflist_size(int secsz, int sz)
{
	int rval;

	if (secsz == 0) {
		secsz = SECSIZE;
	}

	rval = sz ? ((sz * sizeof (struct defect_entry) +
	    secsz - 1) / secsz) : 1;

	return (rval);
}