/*
 * NAME:	resync_comp
 *
 * DESCRIPTION: Resync the component.  Iterate through the raid unit a line at
 *		a time, read from the good device(s) and write the resync
 *		device.
 *
 * PARAMETERS:	minor_t	   mnum - minor number identity of metadevice
 *		md_raidcs_t *cs - child save struct
 *
 * RETURN:	 0 - successfull
 *		 1 - failed
 *		-1 - aborted
 *
 * LOCKS:	Expects Unit Reader Lock to be held across call.  Acquires and
 *		releases Line Reader Lock for per-line I/O.
 */
static void
resync_comp(
	minor_t		mnum,
	md_raidcs_t	*cs
)
{
	mdi_unit_t	*ui;
	mr_unit_t	*un;
	mddb_recid_t	recids[2];
	rcs_state_t	state;
	md_dev64_t	dev_to_write;
	diskaddr_t	write_pwstart;
	diskaddr_t	write_devstart;
	md_dev64_t	dev;
	int		resync;
	int		i;
	int		single_read = 0;
	int		err;
	int		err_cnt;
	int		last_err;
	diskaddr_t	line;
	diskaddr_t	segsincolumn;
	size_t		bsize;
	uint_t		line_count;

	/*
	 * hs_state is the state of the hotspare on the column being resynced
	 * dev_state is the state of the resync target
	 */
	hs_cmds_t	hs_state;
	int		err_col = -1;
	diskaddr_t	resync_end_pos;

	ui = MDI_UNIT(mnum);
	ASSERT(ui != NULL);

	un = cs->cs_un;

	md_unit_readerexit(ui);
	un = (mr_unit_t *)md_io_writerlock(ui);
	un = (mr_unit_t *)md_unit_writerlock(ui);
	resync = un->un_resync_index;
	state = un->un_column[resync].un_devstate;
	line_count = un->un_maxio / un->un_segsize;
	if (line_count == 0) { /* handle the case of segsize > maxio */
		line_count = 1;
		bsize = un->un_maxio;
	} else
		bsize = line_count * un->un_segsize;

	un->un_resync_copysize = (uint_t)bsize;

	ASSERT(un->c.un_status & MD_UN_RESYNC_ACTIVE);
	ASSERT(un->un_column[resync].un_devflags &
	    (MD_RAID_COPY_RESYNC | MD_RAID_REGEN_RESYNC));

	/*
	 * if the column is not in resync then just bail out.
	 */
	if (! (un->un_column[resync].un_devstate & RCS_RESYNC)) {
		md_unit_writerexit(ui);
		md_io_writerexit(ui);
		un = (mr_unit_t *)md_unit_readerlock(ui);
		return;
	}
	SE_NOTIFY(EC_SVM_STATE, ESC_SVM_RESYNC_START, SVM_TAG_METADEVICE,
	    MD_UN2SET(un), MD_SID(un));

	/* identify device to write and its start block */

	if (un->un_column[resync].un_alt_dev != NODEV64) {
		if (raid_open_alt(un, resync)) {
			raid_set_state(un, resync, state, 0);
			md_unit_writerexit(ui);
			md_io_writerexit(ui);
			un = (mr_unit_t *)md_unit_readerlock(ui);
			cmn_err(CE_WARN, "md: %s: %s open failed replace "
				"terminated", md_shortname(MD_SID(un)),
				md_devname(MD_UN2SET(un),
					un->un_column[resync].un_alt_dev,
					NULL, 0));
			SE_NOTIFY(EC_SVM_STATE, ESC_SVM_RESYNC_FAILED,
			    SVM_TAG_METADEVICE, MD_UN2SET(un), MD_SID(un));
			return;
		}
		ASSERT(un->un_column[resync].un_devflags & MD_RAID_COPY_RESYNC);
		dev_to_write = un->un_column[resync].un_alt_dev;
		write_devstart = un->un_column[resync].un_alt_devstart;
		write_pwstart = un->un_column[resync].un_alt_pwstart;
		if (un->un_column[resync].un_devflags & MD_RAID_DEV_ERRED) {
			single_read = 0;
			hs_state = HS_BAD;
		} else {
			hs_state = HS_FREE;
			single_read = 1;
		}
		un->un_column[resync].un_devflags |= MD_RAID_WRITE_ALT;
	} else {
		dev_to_write = un->un_column[resync].un_dev;
		write_devstart = un->un_column[resync].un_devstart;
		write_pwstart = un->un_column[resync].un_pwstart;
		single_read = 0;
		hs_state = HS_FREE;
		ASSERT(un->un_column[resync].un_devflags &
		    MD_RAID_REGEN_RESYNC);
	}

	alloc_bufs(cs, dbtob(bsize));
	/* initialize pre-write area */
	if (init_pw_area(un, dev_to_write, write_pwstart, resync)) {
		un->un_column[resync].un_devflags &= ~MD_RAID_WRITE_ALT;
		if (un->un_column[resync].un_alt_dev != NODEV64) {
			raid_close_alt(un, resync);
		}
		md_unit_writerexit(ui);
		md_io_writerexit(ui);
		if (dev_to_write == un->un_column[resync].un_dev)
			hs_state = HS_BAD;
		err = RAID_RESYNC_WRERROR;
		goto resync_comp_error;
	}

	un->c.un_status &= ~MD_UN_RESYNC_CANCEL;
	segsincolumn = un->un_segsincolumn;
	err_cnt = raid_state_cnt(un, RCS_ERRED | RCS_LAST_ERRED);

	/* commit the record */

	md_unit_writerexit(ui);
	md_io_writerexit(ui);


	/* resync each line of the unit */
	for (line = 0; line <  segsincolumn; line += line_count) {
		/*
		 * Update address range in child struct and lock the line.
		 *
		 * The reader version of the line lock is used since only
		 * resync will use data beyond un_resync_line_index on the
		 * resync device.
		 */
		un = (mr_unit_t *)md_io_readerlock(ui);
		if (line + line_count > segsincolumn)
			line_count = segsincolumn - line;
		resync_end_pos = raid_resync_fillin_cs(line, line_count, cs);
		(void) md_unit_readerlock(ui);
		ASSERT(un->un_resync_line_index == resync_end_pos);
		err = raid_resync_region(cs, line, (int)line_count,
		    &single_read, &hs_state, &err_col, dev_to_write,
		    write_devstart);

		/*
		 * if the column failed to resync then stop writing directly
		 * to the column.
		 */
		if (err)
			un->un_resync_line_index = 0;

		md_unit_readerexit(ui);
		raid_line_exit(cs);
		md_io_readerexit(ui);

		if (err)
			break;

		un = (mr_unit_t *)md_unit_writerlock(ui);

		if (raid_state_cnt(un, RCS_ERRED | RCS_LAST_ERRED) != err_cnt) {
			err = RAID_RESYNC_STATE;
			md_unit_writerexit(ui);
			break;
		}
		md_unit_writerexit(ui);
	} /* for */

resync_comp_error:
	un = (mr_unit_t *)md_io_writerlock(ui);
	(void) md_unit_writerlock(ui);
	un->un_column[resync].un_devflags &= ~MD_RAID_WRITE_ALT;

	recids[0] = 0;
	recids[1] = 0;
	switch (err) {
		/*
		 * successful resync
		 */
	    case RAID_RESYNC_OKAY:
		/* initialize pre-write area */
		if ((un->un_column[resync].un_orig_dev != NODEV64) &&
		    (un->un_column[resync].un_orig_dev ==
		    un->un_column[resync].un_alt_dev)) {
			/*
			 * replacing a hot spare
			 * release the hot spare, which will close the hotspare
			 * and mark it closed.
			 */
			raid_hs_release(hs_state, un, &recids[0], resync);
			/*
			 * make the resync target the main device and
			 * mark open
			 */
			un->un_column[resync].un_hs_id = 0;
			un->un_column[resync].un_dev =
			    un->un_column[resync].un_orig_dev;
			un->un_column[resync].un_devstart =
			    un->un_column[resync].un_orig_devstart;
			un->un_column[resync].un_pwstart =
			    un->un_column[resync].un_orig_pwstart;
			un->un_column[resync].un_devflags |= MD_RAID_DEV_ISOPEN;
			/* alt becomes the device so don't close it */
			un->un_column[resync].un_devflags &= ~MD_RAID_WRITE_ALT;
			un->un_column[resync].un_devflags &=
			    ~MD_RAID_ALT_ISOPEN;
			un->un_column[resync].un_alt_dev = NODEV64;
		}
		raid_set_state(un, resync, RCS_OKAY, 0);
		break;

	    case RAID_RESYNC_WRERROR:
		if (HOTSPARED(un, resync) && single_read &&
		    (un->un_column[resync].un_devflags & MD_RAID_COPY_RESYNC)) {
			/*
			 * this is the case where the resync target is
			 * bad but there is a good hotspare.  In this
			 * case keep the hotspare, and go back to okay.
			 */
			raid_set_state(un, resync, RCS_OKAY, 0);
			cmn_err(CE_WARN, "md: %s: %s write error, replace "
				"terminated", md_shortname(MD_SID(un)),
				md_devname(MD_UN2SET(un),
					un->un_column[resync].un_orig_dev,
					NULL, 0));
			break;
		}
		if (HOTSPARED(un, resync)) {
			raid_hs_release(hs_state, un, &recids[0], resync);
			un->un_column[resync].un_dev =
			    un->un_column[resync].un_orig_dev;
			un->un_column[resync].un_devstart =
			    un->un_column[resync].un_orig_devstart;
			un->un_column[resync].un_pwstart =
			    un->un_column[resync].un_orig_pwstart;
		}
		raid_set_state(un, resync, RCS_ERRED, 0);
		if (un->un_column[resync].un_devflags & MD_RAID_REGEN_RESYNC)
			dev = un->un_column[resync].un_dev;
		else
			dev = un->un_column[resync].un_alt_dev;
		cmn_err(CE_WARN, "md: %s: %s write error replace terminated",
		    md_shortname(MD_SID(un)), md_devname(MD_UN2SET(un), dev,
		    NULL, 0));
		break;

	    case RAID_RESYNC_STATE:
		if (HOTSPARED(un, resync) && single_read &&
		    (un->un_column[resync].un_devflags & MD_RAID_COPY_RESYNC)) {
			/*
			 * this is the case where the resync target is
			 * bad but there is a good hotspare.  In this
			 * case keep the hotspare, and go back to okay.
			 */
			raid_set_state(un, resync, RCS_OKAY, 0);
			cmn_err(CE_WARN, "md: %s: needs maintenance, replace "
			    "terminated", md_shortname(MD_SID(un)));
			break;
		}
		if (HOTSPARED(un, resync)) {
			raid_hs_release(hs_state, un, &recids[0], resync);
			un->un_column[resync].un_dev =
			    un->un_column[resync].un_orig_dev;
			un->un_column[resync].un_devstart =
			    un->un_column[resync].un_orig_devstart;
			un->un_column[resync].un_pwstart =
			    un->un_column[resync].un_orig_pwstart;
		}
		break;
	    case RAID_RESYNC_RDERROR:
		if (HOTSPARED(un, resync)) {
			raid_hs_release(hs_state, un, &recids[0], resync);
			un->un_column[resync].un_dev =
			    un->un_column[resync].un_orig_dev;
			un->un_column[resync].un_devstart =
			    un->un_column[resync].un_orig_devstart;
			un->un_column[resync].un_pwstart =
			    un->un_column[resync].un_orig_pwstart;
		}

		if ((resync != err_col) && (err_col != NOCOLUMN))
			raid_set_state(un, err_col, RCS_ERRED, 0);
		break;

	    default:
		ASSERT(0);
	}
	if (un->un_column[resync].un_alt_dev != NODEV64) {
		raid_close_alt(un, resync);
	}

	/*
	 * an io operation may have gotten an error and placed a
	 * column in erred state.  This will abort the resync, which
	 * will end up in last erred.  This is ugly so go through
	 * the columns and do cleanup
	 */
	err_cnt = 0;
	last_err = 0;
	for (i = 0; i < un->un_totalcolumncnt; i++) {
		if (un->un_column[i].un_devstate & RCS_OKAY)
			continue;
		if (i == resync) {
			raid_set_state(un, i, RCS_ERRED, 1);
			err_cnt++;
		} else if (err == RAID_RESYNC_OKAY) {
			err_cnt++;
		} else {
			raid_set_state(un, i, RCS_LAST_ERRED, 1);
			last_err++;
		}
	}
	if ((err_cnt == 0) && (last_err == 0))
		un->un_state = RUS_OKAY;
	else if (last_err == 0) {
		un->un_state = RUS_ERRED;
		ASSERT(err_cnt == 1);
	} else if (last_err > 0) {
		un->un_state = RUS_LAST_ERRED;
	}

	uniqtime32(&un->un_column[resync].un_devtimestamp);
	un->un_resync_copysize = 0;
	un->un_column[resync].un_devflags &=
	    ~(MD_RAID_REGEN_RESYNC | MD_RAID_COPY_RESYNC);
	raid_commit(un, recids);
	/* release unit writer lock and acquire unit reader lock */
	md_unit_writerexit(ui);
	md_io_writerexit(ui);
	(void) md_unit_readerlock(ui);
	if (err == RAID_RESYNC_OKAY) {
		SE_NOTIFY(EC_SVM_STATE, ESC_SVM_RESYNC_DONE,
		    SVM_TAG_METADEVICE, MD_UN2SET(un), MD_SID(un));
	} else {
		SE_NOTIFY(EC_SVM_STATE, ESC_SVM_RESYNC_FAILED,
		    SVM_TAG_METADEVICE, MD_UN2SET(un), MD_SID(un));
		if (raid_state_cnt(un, RCS_ERRED |
			RCS_LAST_ERRED) > 1) {
			SE_NOTIFY(EC_SVM_STATE, ESC_SVM_LASTERRED,
			    SVM_TAG_METADEVICE, MD_UN2SET(un), MD_SID(un));
		} else {
			SE_NOTIFY(EC_SVM_STATE, ESC_SVM_ERRED,
			    SVM_TAG_METADEVICE, MD_UN2SET(un), MD_SID(un));
		}
	}

	free_bufs(dbtob(bsize), cs);
}
示例#2
0
/*
 * NAME:	check_comp_4_hs
 *
 * DESCRIPTION: Check whether the input component has an error and can be
 *		backed with a hot spare (RCS_ERRED state), and initiate
 *		a resync if so.
 *
 * PARAMETERS:	mr_unit_t *un - raid unit
 *		int hs_index	- component to check
 *
 * LOCKS:	Expects Unit Writer Lock to be held upon entrance.  Releases
 *		the lock prior to calling raid_resync_unit, then reacquires
 *		it before returning.
 */
static void
check_comp_4_hs(
	mr_unit_t *un,
	int hs_index
)
{
	mddb_recid_t	recids[3];
	minor_t		mnum = MD_SID(un);
	mdi_unit_t	*ui;
	rcs_state_t	state;
	diskaddr_t	size;
	int		err;
	mr_column_t	*col;
	md_error_t	mde = mdnullerror;
	char		devname[MD_MAX_CTDLEN];
	char		hs_devname[MD_MAX_CTDLEN];
	set_t		setno;
	md_dev64_t	tmpdev;
	diskaddr_t	tmpdaddr;


	/* initialize */
	setno = MD_UN2SET(un);
	ui = MDI_UNIT(mnum);
	md_unit_readerexit(ui);
	(void) md_io_writerlock(ui);
	un = (mr_unit_t *)md_unit_writerlock(ui);
	col = &un->un_column[hs_index];

	/*
	 * add a hotspare for erred column only if not resyncing
	 */
	if ((!(COLUMN_STATE(un, hs_index) & RCS_ERRED)) ||
	    (raid_state_cnt(un, (RCS_ERRED | RCS_LAST_ERRED)) != 1) ||
	    (raid_state_cnt(un, RCS_RESYNC) > 0)) {
		goto errout;
	}

	recids[0] = 0;
	recids[1] = 0;
	/* if there is already a hotspare then just return */
	if (HOTSPARED(un, hs_index) && (col->un_devstate & RCS_ERRED)) {
		raid_hs_release(HS_BAD, un, &recids[0], hs_index);
		cmn_err(CE_WARN, "md: %s: %s hotspare errored and released",
		    md_shortname(mnum),
		    md_devname(MD_MIN2SET(mnum), col->un_dev, NULL, 0));
		col->un_dev = col->un_orig_dev;
		col->un_pwstart = col->un_orig_pwstart;
		col->un_devstart = col->un_orig_devstart;
		raid_commit(un, recids);

		SE_NOTIFY(EC_SVM_STATE, ESC_SVM_HS_FREED, SVM_TAG_METADEVICE,
		    setno, MD_SID(un));
	}
	ASSERT(!HOTSPARED(un, hs_index));

	state = col->un_devstate;
	size = col->un_pwstart + un->un_pwsize +
	    (un->un_segsize * un->un_segsincolumn);

again:
	/* quit if resync is already active */
	col->un_devflags |= MD_RAID_REGEN_RESYNC;
	if (resync_request(mnum, hs_index, 0, NULL))
		goto errout;

	recids[0] = 0;
	recids[1] = 0;

	tmpdev = col->un_dev;
	tmpdaddr = col->un_hs_pwstart;

	/* get a hotspare */
	if (md_hot_spare_ifc(HS_GET, un->un_hsp_id, size,
	    ((col->un_orig_pwstart >= 1) &&
	    (col->un_orig_pwstart != MD_DISKADDR_ERROR)),
	    &col->un_hs_id, &col->un_hs_key, &tmpdev, &tmpdaddr) != 0) {
		col->un_dev = tmpdev;
		col->un_hs_pwstart = tmpdaddr;
		release_resync_request(mnum);
		raid_set_state(un, hs_index, state, 1);
		goto errout;
	}

	col->un_hs_pwstart = tmpdaddr;

	/*
	 * record id is filled in by raid_commit, recids[0] filled in by
	 * md_hot_spare_ifc if needed
	 */
	recids[0] = col->un_hs_id;
	recids[1] = 0;

	/*
	 * close the device and open the hot spare.  The device should
	 * never be a hotspare here.
	 */
	if (col->un_devflags & MD_RAID_DEV_ISOPEN) {
		md_layered_close(col->un_orig_dev, MD_OFLG_NULL);
		col->un_devflags &= ~MD_RAID_DEV_ISOPEN;
	}
	/*
	 * Try open by device id
	 */
	tmpdev = md_resolve_bydevid(mnum, tmpdev, col->un_hs_key);
	if (md_layered_open(mnum, &tmpdev, MD_OFLG_NULL)) {
		md_dev64_t hs_dev = tmpdev;
		/* cannot open return to orig */
		raid_hs_release(HS_BAD, un, &recids[0], hs_index);
		release_resync_request(mnum);
		raid_set_state(un, hs_index, state, 1);
		col->un_dev = col->un_orig_dev;
		col->un_devstart = col->un_orig_devstart;
		col->un_pwstart = col->un_orig_pwstart;
		col->un_devflags &= ~MD_RAID_DEV_ISOPEN;
		raid_commit(un, recids);
		cmn_err(CE_WARN, "md: %s: open error of hotspare %s",
		    md_shortname(mnum),
		    md_devname(MD_MIN2SET(mnum), hs_dev, NULL, 0));
		SE_NOTIFY(EC_SVM_STATE, ESC_SVM_HS_FREED, SVM_TAG_HS, setno,
		    MD_SID(un));
		goto again;
	}

	col->un_dev = tmpdev;

	col->un_devflags |= MD_RAID_DEV_ISOPEN;

	/*
	 * move the values into the device fields.  Since in some cases
	 * the pwstart is not zero this must be added into the start of
	 * the hotspare to avoid over writting the label
	 */
	col->un_hs_pwstart += col->un_orig_pwstart;
	col->un_pwstart = col->un_hs_pwstart;
	col->un_hs_devstart = col->un_hs_pwstart + un->un_pwsize;
	col->un_devstart = col->un_hs_devstart;

	/* commit unit and hotspare records and release lock */
	raid_commit(un, recids);
	md_unit_writerexit(ui);
	md_io_writerexit(ui);

	err = raid_resync_unit(mnum, &mde);

	/* if resync fails, transition back to erred state and reset */
	if (err) {
		/* reaquire unit writerr lock */
		un = (mr_unit_t *)md_unit_writerlock(ui);

		raid_set_state(un, hs_index, RCS_ERRED, 0);

		/*
		 * close the hotspare and return it.  Then restore the
		 * original device back to the original state
		 */
		raid_hs_release(HS_FREE, un, &recids[0], hs_index);
		col->un_dev = col->un_orig_dev;
		col->un_devstart = col->un_orig_devstart;
		col->un_pwstart = col->un_orig_pwstart;
		raid_commit(un, recids);
		md_unit_writerexit(ui);
		un = (mr_unit_t *)md_unit_readerlock(ui);
		return;
	}

	setno = MD_MIN2SET(mnum);

	(void) md_devname(setno, col->un_orig_dev, devname,
		sizeof (devname));
	(void) md_devname(setno, col->un_dev, hs_devname,
		sizeof (hs_devname));

	cmn_err(CE_NOTE, "md: %s: hotspared device %s with %s",
	    md_shortname(mnum), devname, hs_devname);
	SE_NOTIFY(EC_SVM_STATE, ESC_SVM_HOTSPARED, SVM_TAG_HS, setno,
	    MD_SID(un));
	(void) md_unit_readerlock(ui);
	return;

errout:
	md_unit_writerexit(ui);
	md_io_writerexit(ui);
	un = (mr_unit_t *)md_unit_readerlock(ui);
}