xref: /illumos-gate/usr/src/uts/sun4u/opl/io/dr_mem.c (revision d3d50737)
125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2125cf1a30Sjl /*
22*d3d50737SRafael Vanoni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2325cf1a30Sjl  * Use is subject to license terms.
2425cf1a30Sjl  */
2525cf1a30Sjl 
2625cf1a30Sjl /*
2725cf1a30Sjl  * DR memory support routines.
2825cf1a30Sjl  */
2925cf1a30Sjl 
3025cf1a30Sjl #include <sys/note.h>
3125cf1a30Sjl #include <sys/debug.h>
3225cf1a30Sjl #include <sys/types.h>
3325cf1a30Sjl #include <sys/errno.h>
3425cf1a30Sjl #include <sys/param.h>
3525cf1a30Sjl #include <sys/dditypes.h>
3625cf1a30Sjl #include <sys/kmem.h>
3725cf1a30Sjl #include <sys/conf.h>
3825cf1a30Sjl #include <sys/ddi.h>
3925cf1a30Sjl #include <sys/sunddi.h>
4025cf1a30Sjl #include <sys/sunndi.h>
4125cf1a30Sjl #include <sys/ddi_impldefs.h>
4225cf1a30Sjl #include <sys/ndi_impldefs.h>
4325cf1a30Sjl #include <sys/sysmacros.h>
4425cf1a30Sjl #include <sys/machsystm.h>
4525cf1a30Sjl #include <sys/spitregs.h>
4625cf1a30Sjl #include <sys/cpuvar.h>
4725cf1a30Sjl #include <sys/promif.h>
4825cf1a30Sjl #include <vm/seg_kmem.h>
4925cf1a30Sjl #include <sys/lgrp.h>
5025cf1a30Sjl #include <sys/platform_module.h>
5125cf1a30Sjl 
5225cf1a30Sjl #include <vm/page.h>
5325cf1a30Sjl 
5425cf1a30Sjl #include <sys/dr.h>
5525cf1a30Sjl #include <sys/dr_util.h>
5625cf1a30Sjl #include <sys/drmach.h>
5768ac2337Sjl #include <sys/kobj.h>
5825cf1a30Sjl 
5925cf1a30Sjl extern struct memlist	*phys_install;
6068ac2337Sjl extern vnode_t		*retired_pages;
6125cf1a30Sjl 
6225cf1a30Sjl /* TODO: push this reference below drmach line */
6325cf1a30Sjl extern int		kcage_on;
6425cf1a30Sjl 
6525cf1a30Sjl /* for the DR*INTERNAL_ERROR macros.  see sys/dr.h. */
6668ac2337Sjl static char *dr_ie_fmt = "dr_mem.c %d";
6725cf1a30Sjl 
6825cf1a30Sjl typedef enum {
6925cf1a30Sjl 	DR_TP_INVALID = -1,
7025cf1a30Sjl 	DR_TP_SAME,
7125cf1a30Sjl 	DR_TP_LARGE,
7225cf1a30Sjl 	DR_TP_NONRELOC,
7325cf1a30Sjl 	DR_TP_FLOATING
7425cf1a30Sjl } dr_target_pref_t;
7525cf1a30Sjl 
7625cf1a30Sjl static int		dr_post_detach_mem_unit(dr_mem_unit_t *mp);
7725cf1a30Sjl static int		dr_reserve_mem_spans(memhandle_t *mhp,
7825cf1a30Sjl 				struct memlist *mlist);
7925cf1a30Sjl static int		dr_select_mem_target(dr_handle_t *hp,
8025cf1a30Sjl 				dr_mem_unit_t *mp, struct memlist *ml);
8125cf1a30Sjl static void		dr_init_mem_unit_data(dr_mem_unit_t *mp);
8225cf1a30Sjl static struct memlist	*dr_memlist_del_retired_pages(struct memlist *ml);
8325cf1a30Sjl static dr_target_pref_t	dr_get_target_preference(dr_handle_t *hp,
8425cf1a30Sjl 				dr_mem_unit_t *t_mp, dr_mem_unit_t *s_mp,
8525cf1a30Sjl 				struct memlist *s_ml, struct memlist *x_ml,
8625cf1a30Sjl 				struct memlist *b_ml);
8725cf1a30Sjl 
8825cf1a30Sjl static int		memlist_canfit(struct memlist *s_mlist,
8925cf1a30Sjl 				struct memlist *t_mlist);
9025cf1a30Sjl static int		dr_del_mlist_query(struct memlist *mlist,
9125cf1a30Sjl 				memquery_t *mp);
9225cf1a30Sjl static struct memlist	*dr_get_copy_mlist(struct memlist *s_ml,
9325cf1a30Sjl 				struct memlist *t_ml, dr_mem_unit_t *s_mp,
9425cf1a30Sjl 				dr_mem_unit_t *t_mp);
9525cf1a30Sjl static struct memlist	*dr_get_nonreloc_mlist(struct memlist *s_ml,
9625cf1a30Sjl 				dr_mem_unit_t *s_mp);
9725cf1a30Sjl static int		dr_memlist_canfit(struct memlist *s_mlist,
9825cf1a30Sjl 				struct memlist *t_mlist, dr_mem_unit_t *s_mp,
9925cf1a30Sjl 				dr_mem_unit_t *t_mp);
10025cf1a30Sjl 
10125cf1a30Sjl /*
10225cf1a30Sjl  * dr_mem_unit_t.sbm_flags
10325cf1a30Sjl  */
10425cf1a30Sjl #define	DR_MFLAG_RESERVED	0x01	/* mem unit reserved for delete */
10525cf1a30Sjl #define	DR_MFLAG_SOURCE		0x02	/* source brd of copy/rename op */
10625cf1a30Sjl #define	DR_MFLAG_TARGET		0x04	/* target brd of copy/rename op */
10725cf1a30Sjl #define	DR_MFLAG_RELOWNER	0x20	/* memory release (delete) owner */
10825cf1a30Sjl #define	DR_MFLAG_RELDONE	0x40	/* memory release (delete) done */
10925cf1a30Sjl 
11025cf1a30Sjl /* helper macros */
11125cf1a30Sjl #define	_ptob64(p) ((uint64_t)(p) << PAGESHIFT)
11225cf1a30Sjl #define	_b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
11325cf1a30Sjl 
11425cf1a30Sjl static struct memlist *
11525cf1a30Sjl dr_get_memlist(dr_mem_unit_t *mp)
11625cf1a30Sjl {
11725cf1a30Sjl 	struct memlist	*mlist = NULL;
11825cf1a30Sjl 	sbd_error_t	*err;
11925cf1a30Sjl 	static fn_t	f = "dr_get_memlist";
12025cf1a30Sjl 
12125cf1a30Sjl 	PR_MEM("%s for %s...\n", f, mp->sbm_cm.sbdev_path);
12225cf1a30Sjl 
12325cf1a30Sjl 	/*
12425cf1a30Sjl 	 * Return cached memlist, if present.
12525cf1a30Sjl 	 * This memlist will be present following an
12625cf1a30Sjl 	 * unconfigure (a.k.a: detach) of this memunit.
12725cf1a30Sjl 	 * It should only be used in the case were a configure
12825cf1a30Sjl 	 * is bringing this memunit back in without going
12925cf1a30Sjl 	 * through the disconnect and connect states.
13025cf1a30Sjl 	 */
13125cf1a30Sjl 	if (mp->sbm_mlist) {
13225cf1a30Sjl 		PR_MEM("%s: found cached memlist\n", f);
13325cf1a30Sjl 
13425cf1a30Sjl 		mlist = memlist_dup(mp->sbm_mlist);
13525cf1a30Sjl 	} else {
13625cf1a30Sjl 		uint64_t basepa = _ptob64(mp->sbm_basepfn);
13725cf1a30Sjl 
13825cf1a30Sjl 		/* attempt to construct a memlist using phys_install */
13925cf1a30Sjl 
14025cf1a30Sjl 		/* round down to slice base address */
14125cf1a30Sjl 		basepa &= ~(mp->sbm_slice_size - 1);
14225cf1a30Sjl 
14325cf1a30Sjl 		/* get a copy of phys_install to edit */
14425cf1a30Sjl 		memlist_read_lock();
14525cf1a30Sjl 		mlist = memlist_dup(phys_install);
14625cf1a30Sjl 		memlist_read_unlock();
14725cf1a30Sjl 
14825cf1a30Sjl 		/* trim lower irrelevant span */
14925cf1a30Sjl 		if (mlist)
15025cf1a30Sjl 			mlist = memlist_del_span(mlist, 0ull, basepa);
15125cf1a30Sjl 
15225cf1a30Sjl 		/* trim upper irrelevant span */
15325cf1a30Sjl 		if (mlist) {
15425cf1a30Sjl 			uint64_t endpa;
15525cf1a30Sjl 
15625cf1a30Sjl 			basepa += mp->sbm_slice_size;
15725cf1a30Sjl 			endpa = _ptob64(physmax + 1);
15825cf1a30Sjl 			if (endpa > basepa)
15925cf1a30Sjl 				mlist = memlist_del_span(
1603103d4ceSjesusm 				    mlist, basepa,
1613103d4ceSjesusm 				    endpa - basepa);
16225cf1a30Sjl 		}
16325cf1a30Sjl 
16425cf1a30Sjl 		if (mlist) {
16525cf1a30Sjl 			/* successfully built a memlist */
16625cf1a30Sjl 			PR_MEM("%s: derived memlist from phys_install\n", f);
16725cf1a30Sjl 		}
16825cf1a30Sjl 
16925cf1a30Sjl 		/* if no mlist yet, try platform layer */
17025cf1a30Sjl 		if (!mlist) {
17125cf1a30Sjl 			err = drmach_mem_get_memlist(
1723103d4ceSjesusm 			    mp->sbm_cm.sbdev_id, &mlist);
17325cf1a30Sjl 			if (err) {
17425cf1a30Sjl 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
17525cf1a30Sjl 				mlist = NULL; /* paranoia */
17625cf1a30Sjl 			}
17725cf1a30Sjl 		}
17825cf1a30Sjl 	}
17925cf1a30Sjl 
18025cf1a30Sjl 	PR_MEM("%s: memlist for %s\n", f, mp->sbm_cm.sbdev_path);
18125cf1a30Sjl 	PR_MEMLIST_DUMP(mlist);
18225cf1a30Sjl 
18325cf1a30Sjl 	return (mlist);
18425cf1a30Sjl }
18525cf1a30Sjl 
18625cf1a30Sjl typedef struct {
18725cf1a30Sjl 	kcondvar_t cond;
18825cf1a30Sjl 	kmutex_t lock;
18925cf1a30Sjl 	int error;
19025cf1a30Sjl 	int done;
19125cf1a30Sjl } dr_release_mem_sync_t;
19225cf1a30Sjl 
19325cf1a30Sjl /*
19425cf1a30Sjl  * Memory has been logically removed by the time this routine is called.
19525cf1a30Sjl  */
19625cf1a30Sjl static void
19725cf1a30Sjl dr_mem_del_done(void *arg, int error)
19825cf1a30Sjl {
19925cf1a30Sjl 	dr_release_mem_sync_t *ds = arg;
20025cf1a30Sjl 
20125cf1a30Sjl 	mutex_enter(&ds->lock);
20225cf1a30Sjl 	ds->error = error;
20325cf1a30Sjl 	ds->done = 1;
20425cf1a30Sjl 	cv_signal(&ds->cond);
20525cf1a30Sjl 	mutex_exit(&ds->lock);
20625cf1a30Sjl }
20725cf1a30Sjl 
20825cf1a30Sjl /*
20925cf1a30Sjl  * When we reach here the memory being drained should have
21025cf1a30Sjl  * already been reserved in dr_pre_release_mem().
21125cf1a30Sjl  * Our only task here is to kick off the "drain" and wait
21225cf1a30Sjl  * for it to finish.
21325cf1a30Sjl  */
21425cf1a30Sjl void
21525cf1a30Sjl dr_release_mem(dr_common_unit_t *cp)
21625cf1a30Sjl {
21725cf1a30Sjl 	dr_mem_unit_t	*mp = (dr_mem_unit_t *)cp;
21825cf1a30Sjl 	int		err;
21925cf1a30Sjl 	dr_release_mem_sync_t rms;
22025cf1a30Sjl 	static fn_t	f = "dr_release_mem";
22125cf1a30Sjl 
22225cf1a30Sjl 	/* check that this memory unit has been reserved */
22325cf1a30Sjl 	if (!(mp->sbm_flags & DR_MFLAG_RELOWNER)) {
22425cf1a30Sjl 		DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
22525cf1a30Sjl 		return;
22625cf1a30Sjl 	}
22725cf1a30Sjl 
22825cf1a30Sjl 	bzero((void *) &rms, sizeof (rms));
22925cf1a30Sjl 
23025cf1a30Sjl 	mutex_init(&rms.lock, NULL, MUTEX_DRIVER, NULL);
23125cf1a30Sjl 	cv_init(&rms.cond, NULL, CV_DRIVER, NULL);
23225cf1a30Sjl 
23325cf1a30Sjl 	mutex_enter(&rms.lock);
23425cf1a30Sjl 	err = kphysm_del_start(mp->sbm_memhandle,
2353103d4ceSjesusm 	    dr_mem_del_done, (void *) &rms);
23625cf1a30Sjl 	if (err == KPHYSM_OK) {
23725cf1a30Sjl 		/* wait for completion or interrupt */
23825cf1a30Sjl 		while (!rms.done) {
23925cf1a30Sjl 			if (cv_wait_sig(&rms.cond, &rms.lock) == 0) {
24025cf1a30Sjl 				/* then there is a pending UNIX signal */
24125cf1a30Sjl 				(void) kphysm_del_cancel(mp->sbm_memhandle);
24225cf1a30Sjl 
24325cf1a30Sjl 				/* wait for completion */
24425cf1a30Sjl 				while (!rms.done)
24525cf1a30Sjl 					cv_wait(&rms.cond, &rms.lock);
24625cf1a30Sjl 			}
24725cf1a30Sjl 		}
24825cf1a30Sjl 		/* get the result of the memory delete operation */
24925cf1a30Sjl 		err = rms.error;
25025cf1a30Sjl 	}
25125cf1a30Sjl 	mutex_exit(&rms.lock);
25225cf1a30Sjl 
25325cf1a30Sjl 	cv_destroy(&rms.cond);
25425cf1a30Sjl 	mutex_destroy(&rms.lock);
25525cf1a30Sjl 
25625cf1a30Sjl 	if (err != KPHYSM_OK) {
25725cf1a30Sjl 		int e_code;
25825cf1a30Sjl 
25925cf1a30Sjl 		switch (err) {
26025cf1a30Sjl 			case KPHYSM_ENOWORK:
26125cf1a30Sjl 				e_code = ESBD_NOERROR;
26225cf1a30Sjl 				break;
26325cf1a30Sjl 
26425cf1a30Sjl 			case KPHYSM_EHANDLE:
26525cf1a30Sjl 			case KPHYSM_ESEQUENCE:
26625cf1a30Sjl 				e_code = ESBD_INTERNAL;
26725cf1a30Sjl 				break;
26825cf1a30Sjl 
26925cf1a30Sjl 			case KPHYSM_ENOTVIABLE:
27025cf1a30Sjl 				e_code = ESBD_MEM_NOTVIABLE;
27125cf1a30Sjl 				break;
27225cf1a30Sjl 
27325cf1a30Sjl 			case KPHYSM_EREFUSED:
27425cf1a30Sjl 				e_code = ESBD_MEM_REFUSED;
27525cf1a30Sjl 				break;
27625cf1a30Sjl 
27725cf1a30Sjl 			case KPHYSM_ENONRELOC:
27825cf1a30Sjl 				e_code = ESBD_MEM_NONRELOC;
27925cf1a30Sjl 				break;
28025cf1a30Sjl 
28125cf1a30Sjl 			case KPHYSM_ECANCELLED:
28225cf1a30Sjl 				e_code = ESBD_MEM_CANCELLED;
28325cf1a30Sjl 				break;
28425cf1a30Sjl 
28525cf1a30Sjl 			case KPHYSM_ERESOURCE:
28625cf1a30Sjl 				e_code = ESBD_MEMFAIL;
28725cf1a30Sjl 				break;
28825cf1a30Sjl 
28925cf1a30Sjl 			default:
29025cf1a30Sjl 				cmn_err(CE_WARN,
2913103d4ceSjesusm 				    "%s: unexpected kphysm error code %d,"
2923103d4ceSjesusm 				    " id 0x%p",
2933103d4ceSjesusm 				    f, err, mp->sbm_cm.sbdev_id);
29425cf1a30Sjl 
29525cf1a30Sjl 				e_code = ESBD_IO;
29625cf1a30Sjl 				break;
29725cf1a30Sjl 		}
29825cf1a30Sjl 
29925cf1a30Sjl 		if (e_code != ESBD_NOERROR) {
300b307f191Sbm 			dr_dev_err(CE_WARN, &mp->sbm_cm, e_code);
30125cf1a30Sjl 		}
30225cf1a30Sjl 	}
30325cf1a30Sjl }
30425cf1a30Sjl 
30525cf1a30Sjl void
30625cf1a30Sjl dr_attach_mem(dr_handle_t *hp, dr_common_unit_t *cp)
30725cf1a30Sjl {
30825cf1a30Sjl 	_NOTE(ARGUNUSED(hp))
30925cf1a30Sjl 
31025cf1a30Sjl 	dr_mem_unit_t	*mp = (dr_mem_unit_t *)cp;
31125cf1a30Sjl 	struct memlist	*ml, *mc;
31225cf1a30Sjl 	sbd_error_t	*err;
31325cf1a30Sjl 	static fn_t	f = "dr_attach_mem";
31425cf1a30Sjl 
31525cf1a30Sjl 	PR_MEM("%s...\n", f);
31625cf1a30Sjl 
31725cf1a30Sjl 	dr_lock_status(hp->h_bd);
31825cf1a30Sjl 	err = drmach_configure(cp->sbdev_id, 0);
31925cf1a30Sjl 	dr_unlock_status(hp->h_bd);
32025cf1a30Sjl 	if (err) {
32125cf1a30Sjl 		DRERR_SET_C(&cp->sbdev_error, &err);
32225cf1a30Sjl 		return;
32325cf1a30Sjl 	}
32425cf1a30Sjl 
32525cf1a30Sjl 	ml = dr_get_memlist(mp);
32625cf1a30Sjl 	for (mc = ml; mc; mc = mc->next) {
32725cf1a30Sjl 		int		 rv;
32825cf1a30Sjl 		sbd_error_t	*err;
32925cf1a30Sjl 
33025cf1a30Sjl 		rv = kphysm_add_memory_dynamic(
3313103d4ceSjesusm 		    (pfn_t)(mc->address >> PAGESHIFT),
3323103d4ceSjesusm 		    (pgcnt_t)(mc->size >> PAGESHIFT));
33325cf1a30Sjl 		if (rv != KPHYSM_OK) {
33425cf1a30Sjl 			/*
33525cf1a30Sjl 			 * translate kphysm error and
33625cf1a30Sjl 			 * store in devlist error
33725cf1a30Sjl 			 */
33825cf1a30Sjl 			switch (rv) {
33925cf1a30Sjl 			case KPHYSM_ERESOURCE:
34025cf1a30Sjl 				rv = ESBD_NOMEM;
34125cf1a30Sjl 				break;
34225cf1a30Sjl 
34325cf1a30Sjl 			case KPHYSM_EFAULT:
34425cf1a30Sjl 				rv = ESBD_FAULT;
34525cf1a30Sjl 				break;
34625cf1a30Sjl 
34725cf1a30Sjl 			default:
34825cf1a30Sjl 				rv = ESBD_INTERNAL;
34925cf1a30Sjl 				break;
35025cf1a30Sjl 			}
35125cf1a30Sjl 
35225cf1a30Sjl 			if (rv == ESBD_INTERNAL) {
35325cf1a30Sjl 				DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
35425cf1a30Sjl 			} else
35525cf1a30Sjl 				dr_dev_err(CE_WARN, &mp->sbm_cm, rv);
35625cf1a30Sjl 			break;
35725cf1a30Sjl 		}
35825cf1a30Sjl 
35925cf1a30Sjl 		err = drmach_mem_add_span(
3603103d4ceSjesusm 		    mp->sbm_cm.sbdev_id, mc->address, mc->size);
36125cf1a30Sjl 		if (err) {
36225cf1a30Sjl 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
36325cf1a30Sjl 			break;
36425cf1a30Sjl 		}
36525cf1a30Sjl 	}
36625cf1a30Sjl 
36725cf1a30Sjl 	memlist_delete(ml);
36825cf1a30Sjl 
36925cf1a30Sjl 	/* back out if configure failed */
37025cf1a30Sjl 	if (mp->sbm_cm.sbdev_error != NULL) {
37125cf1a30Sjl 		dr_lock_status(hp->h_bd);
37225cf1a30Sjl 		err = drmach_unconfigure(cp->sbdev_id, 0);
37325cf1a30Sjl 		if (err)
37425cf1a30Sjl 			sbd_err_clear(&err);
37525cf1a30Sjl 		dr_unlock_status(hp->h_bd);
37625cf1a30Sjl 	}
37725cf1a30Sjl }
37825cf1a30Sjl 
37925cf1a30Sjl static struct memlist *
38025cf1a30Sjl dr_memlist_del_retired_pages(struct memlist *mlist)
38125cf1a30Sjl {
38225cf1a30Sjl 	page_t		*pp;
38325cf1a30Sjl 	pfn_t		pfn;
38425cf1a30Sjl 	kmutex_t	*vphm;
38568ac2337Sjl 	vnode_t		*vp = retired_pages;
38625cf1a30Sjl 	static fn_t	f = "dr_memlist_del_retired_pages";
38725cf1a30Sjl 
38825cf1a30Sjl 	vphm = page_vnode_mutex(vp);
38925cf1a30Sjl 	mutex_enter(vphm);
39025cf1a30Sjl 
39125cf1a30Sjl 	PR_MEM("%s\n", f);
39225cf1a30Sjl 
39325cf1a30Sjl 	if ((pp = vp->v_pages) == NULL) {
39425cf1a30Sjl 		mutex_exit(vphm);
39525cf1a30Sjl 		return (mlist);
39625cf1a30Sjl 	}
39725cf1a30Sjl 
39825cf1a30Sjl 	do {
39925cf1a30Sjl 		ASSERT(pp != NULL);
40068ac2337Sjl 		ASSERT(pp->p_vnode == retired_pages);
40125cf1a30Sjl 
4020c48eb93Sbm 		if (!page_try_reclaim_lock(pp, SE_SHARED, SE_RETIRED))
40325cf1a30Sjl 			continue;
40425cf1a30Sjl 
40525cf1a30Sjl 		pfn = page_pptonum(pp);
40625cf1a30Sjl 
40725cf1a30Sjl 		/*
40825cf1a30Sjl 		 * Page retirement currently breaks large pages into PAGESIZE
40925cf1a30Sjl 		 * pages. If this changes, need to remove the assert and deal
41025cf1a30Sjl 		 * with different page sizes.
41125cf1a30Sjl 		 */
41225cf1a30Sjl 		ASSERT(pp->p_szc == 0);
41325cf1a30Sjl 
41425cf1a30Sjl 		if (address_in_memlist(mlist, ptob(pfn), PAGESIZE)) {
41525cf1a30Sjl 			mlist = memlist_del_span(mlist, ptob(pfn), PAGESIZE);
41625cf1a30Sjl 			PR_MEM("deleted retired page 0x%lx (pfn 0x%lx) "
41725cf1a30Sjl 			    "from memlist\n", ptob(pfn), pfn);
41825cf1a30Sjl 		}
41925cf1a30Sjl 
42025cf1a30Sjl 		page_unlock(pp);
42125cf1a30Sjl 	} while ((pp = pp->p_vpnext) != vp->v_pages);
42225cf1a30Sjl 
42325cf1a30Sjl 	mutex_exit(vphm);
42425cf1a30Sjl 
42525cf1a30Sjl 	return (mlist);
42625cf1a30Sjl }
42725cf1a30Sjl 
42825cf1a30Sjl static int
42925cf1a30Sjl dr_move_memory(dr_handle_t *hp, dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp)
43025cf1a30Sjl {
43125cf1a30Sjl 	int		rv = -1;
43225cf1a30Sjl 	time_t		 copytime;
43325cf1a30Sjl 	drmachid_t	 cr_id;
43425cf1a30Sjl 	dr_sr_handle_t	*srhp = NULL;
43525cf1a30Sjl 	dr_board_t	*t_bp, *s_bp;
43625cf1a30Sjl 	struct memlist	*c_ml, *d_ml;
43725cf1a30Sjl 	sbd_error_t	*err;
43825cf1a30Sjl 	static fn_t	 f = "dr_move_memory";
43925cf1a30Sjl 
44025cf1a30Sjl 	PR_MEM("%s: (INLINE) moving memory from %s to %s\n",
4413103d4ceSjesusm 	    f,
4423103d4ceSjesusm 	    s_mp->sbm_cm.sbdev_path,
4433103d4ceSjesusm 	    t_mp->sbm_cm.sbdev_path);
44425cf1a30Sjl 
44525cf1a30Sjl 	ASSERT(s_mp->sbm_flags & DR_MFLAG_SOURCE);
44625cf1a30Sjl 	ASSERT(s_mp->sbm_peer == t_mp);
44725cf1a30Sjl 	ASSERT(s_mp->sbm_mlist);
44825cf1a30Sjl 
44925cf1a30Sjl 	ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
45025cf1a30Sjl 	ASSERT(t_mp->sbm_peer == s_mp);
45125cf1a30Sjl 
45225cf1a30Sjl 	/*
45325cf1a30Sjl 	 * create a memlist of spans to copy by removing
45425cf1a30Sjl 	 * the spans that have been deleted, if any, from
45525cf1a30Sjl 	 * the full source board memlist.  s_mp->sbm_del_mlist
45625cf1a30Sjl 	 * will be NULL if there were no spans deleted from
45725cf1a30Sjl 	 * the source board.
45825cf1a30Sjl 	 */
45925cf1a30Sjl 	c_ml = memlist_dup(s_mp->sbm_mlist);
46025cf1a30Sjl 	d_ml = s_mp->sbm_del_mlist;
46125cf1a30Sjl 	while (d_ml != NULL) {
46225cf1a30Sjl 		c_ml = memlist_del_span(c_ml, d_ml->address, d_ml->size);
46325cf1a30Sjl 		d_ml = d_ml->next;
46425cf1a30Sjl 	}
46525cf1a30Sjl 
46625cf1a30Sjl 	/*
46725cf1a30Sjl 	 * Remove retired pages from the copy list. The page content
46825cf1a30Sjl 	 * need not be copied since the pages are no longer in use.
46925cf1a30Sjl 	 */
47025cf1a30Sjl 	PR_MEM("%s: copy list before removing retired pages (if any):\n", f);
47125cf1a30Sjl 	PR_MEMLIST_DUMP(c_ml);
47225cf1a30Sjl 
47325cf1a30Sjl 	c_ml = dr_memlist_del_retired_pages(c_ml);
47425cf1a30Sjl 
47525cf1a30Sjl 	PR_MEM("%s: copy list after removing retired pages:\n", f);
47625cf1a30Sjl 	PR_MEMLIST_DUMP(c_ml);
47725cf1a30Sjl 
47825cf1a30Sjl 	/*
47925cf1a30Sjl 	 * With parallel copy, it shouldn't make a difference which
48025cf1a30Sjl 	 * CPU is the actual master during copy-rename since all
48125cf1a30Sjl 	 * CPUs participate in the parallel copy anyway.
48225cf1a30Sjl 	 */
48325cf1a30Sjl 	affinity_set(CPU_CURRENT);
48425cf1a30Sjl 
48525cf1a30Sjl 	err = drmach_copy_rename_init(
4863103d4ceSjesusm 	    t_mp->sbm_cm.sbdev_id, s_mp->sbm_cm.sbdev_id, c_ml, &cr_id);
48725cf1a30Sjl 	if (err) {
48825cf1a30Sjl 		DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
48925cf1a30Sjl 		affinity_clear();
49025cf1a30Sjl 		memlist_delete(c_ml);
49125cf1a30Sjl 		return (-1);
49225cf1a30Sjl 	}
49325cf1a30Sjl 
49425cf1a30Sjl 	srhp = dr_get_sr_handle(hp);
49525cf1a30Sjl 	ASSERT(srhp);
49625cf1a30Sjl 
497*d3d50737SRafael Vanoni 	copytime = ddi_get_lbolt();
49825cf1a30Sjl 
49925cf1a30Sjl 	/* Quiesce the OS.  */
50025cf1a30Sjl 	if (dr_suspend(srhp)) {
50125cf1a30Sjl 		cmn_err(CE_WARN, "%s: failed to quiesce OS"
5023103d4ceSjesusm 		    " for copy-rename", f);
50325cf1a30Sjl 
50425cf1a30Sjl 		err = drmach_copy_rename_fini(cr_id);
50525cf1a30Sjl 		if (err) {
50625cf1a30Sjl 			/*
50725cf1a30Sjl 			 * no error is expected since the program has
50825cf1a30Sjl 			 * not yet run.
50925cf1a30Sjl 			 */
51025cf1a30Sjl 
51125cf1a30Sjl 			/* catch this in debug kernels */
51225cf1a30Sjl 			ASSERT(0);
51325cf1a30Sjl 
51425cf1a30Sjl 			sbd_err_clear(&err);
51525cf1a30Sjl 		}
51625cf1a30Sjl 
51725cf1a30Sjl 		/* suspend error reached via hp */
51825cf1a30Sjl 		s_mp->sbm_cm.sbdev_error = hp->h_err;
51925cf1a30Sjl 		hp->h_err = NULL;
52025cf1a30Sjl 		goto done;
52125cf1a30Sjl 	}
52225cf1a30Sjl 
52325cf1a30Sjl 	drmach_copy_rename(cr_id);
52425cf1a30Sjl 
52525cf1a30Sjl 	/* Resume the OS.  */
52625cf1a30Sjl 	dr_resume(srhp);
52725cf1a30Sjl 
528*d3d50737SRafael Vanoni 	copytime = ddi_get_lbolt() - copytime;
52925cf1a30Sjl 
53025cf1a30Sjl 	if (err = drmach_copy_rename_fini(cr_id))
53125cf1a30Sjl 		goto done;
53225cf1a30Sjl 
53325cf1a30Sjl 	/*
53425cf1a30Sjl 	 * Rename memory for lgroup.
53525cf1a30Sjl 	 * Source and target board numbers are packaged in arg.
53625cf1a30Sjl 	 */
53725cf1a30Sjl 	s_bp = s_mp->sbm_cm.sbdev_bp;
53825cf1a30Sjl 	t_bp = t_mp->sbm_cm.sbdev_bp;
53925cf1a30Sjl 
54025cf1a30Sjl 	lgrp_plat_config(LGRP_CONFIG_MEM_RENAME,
5413103d4ceSjesusm 	    (uintptr_t)(s_bp->b_num | (t_bp->b_num << 16)));
54225cf1a30Sjl 
54325cf1a30Sjl 
54425cf1a30Sjl 	PR_MEM("%s: copy-rename elapsed time = %ld ticks (%ld secs)\n",
5453103d4ceSjesusm 	    f, copytime, copytime / hz);
54625cf1a30Sjl 
54725cf1a30Sjl 	rv = 0;
54825cf1a30Sjl done:
54925cf1a30Sjl 	if (srhp)
55025cf1a30Sjl 		dr_release_sr_handle(srhp);
55125cf1a30Sjl 	if (err)
55225cf1a30Sjl 		DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
55325cf1a30Sjl 	affinity_clear();
55425cf1a30Sjl 
55525cf1a30Sjl 	return (rv);
55625cf1a30Sjl }
55725cf1a30Sjl 
55825cf1a30Sjl /*
55925cf1a30Sjl  * If detaching node contains memory that is "non-permanent"
56025cf1a30Sjl  * then the memory adr's are simply cleared.  If the memory
56125cf1a30Sjl  * is non-relocatable, then do a copy-rename.
56225cf1a30Sjl  */
56325cf1a30Sjl void
56425cf1a30Sjl dr_detach_mem(dr_handle_t *hp, dr_common_unit_t *cp)
56525cf1a30Sjl {
56625cf1a30Sjl 	int			rv = 0;
56725cf1a30Sjl 	dr_mem_unit_t		*s_mp = (dr_mem_unit_t *)cp;
56825cf1a30Sjl 	dr_mem_unit_t		*t_mp;
56925cf1a30Sjl 	dr_state_t		state;
57025cf1a30Sjl 	static fn_t		f = "dr_detach_mem";
57125cf1a30Sjl 
57225cf1a30Sjl 	PR_MEM("%s...\n", f);
57325cf1a30Sjl 
57425cf1a30Sjl 	/* lookup target mem unit and target board structure, if any */
57525cf1a30Sjl 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
57625cf1a30Sjl 		t_mp = s_mp->sbm_peer;
57725cf1a30Sjl 		ASSERT(t_mp != NULL);
57825cf1a30Sjl 		ASSERT(t_mp->sbm_peer == s_mp);
57925cf1a30Sjl 	} else {
58025cf1a30Sjl 		t_mp = NULL;
58125cf1a30Sjl 	}
58225cf1a30Sjl 
58325cf1a30Sjl 	/* verify mem unit's state is UNREFERENCED */
58425cf1a30Sjl 	state = s_mp->sbm_cm.sbdev_state;
58525cf1a30Sjl 	if (state != DR_STATE_UNREFERENCED) {
58625cf1a30Sjl 		dr_dev_err(CE_IGNORE, &s_mp->sbm_cm, ESBD_STATE);
58725cf1a30Sjl 		return;
58825cf1a30Sjl 	}
58925cf1a30Sjl 
59025cf1a30Sjl 	/* verify target mem unit's state is UNREFERENCED, if any */
59125cf1a30Sjl 	if (t_mp != NULL) {
59225cf1a30Sjl 		state = t_mp->sbm_cm.sbdev_state;
59325cf1a30Sjl 		if (state != DR_STATE_UNREFERENCED) {
59425cf1a30Sjl 			dr_dev_err(CE_IGNORE, &t_mp->sbm_cm, ESBD_STATE);
59525cf1a30Sjl 			return;
59625cf1a30Sjl 		}
59725cf1a30Sjl 	}
59825cf1a30Sjl 
59925cf1a30Sjl 	/*
60025cf1a30Sjl 	 * If there is no target board (no copy/rename was needed), then
60125cf1a30Sjl 	 * we're done!
60225cf1a30Sjl 	 */
60325cf1a30Sjl 	if (t_mp == NULL) {
60425cf1a30Sjl 		sbd_error_t *err;
60525cf1a30Sjl 		/*
60625cf1a30Sjl 		 * Reprogram interconnect hardware and disable
60725cf1a30Sjl 		 * memory controllers for memory node that's going away.
60825cf1a30Sjl 		 */
60925cf1a30Sjl 
61025cf1a30Sjl 		err = drmach_mem_disable(s_mp->sbm_cm.sbdev_id);
61125cf1a30Sjl 		if (err) {
61225cf1a30Sjl 			DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
61325cf1a30Sjl 			rv = -1;
61425cf1a30Sjl 		}
61525cf1a30Sjl 	} else {
61625cf1a30Sjl 		rv = dr_move_memory(hp, s_mp, t_mp);
61725cf1a30Sjl 		PR_MEM("%s: %s memory COPY-RENAME (board %d -> %d)\n",
6183103d4ceSjesusm 		    f,
6193103d4ceSjesusm 		    rv ? "FAILED" : "COMPLETED",
6203103d4ceSjesusm 		    s_mp->sbm_cm.sbdev_bp->b_num,
6213103d4ceSjesusm 		    t_mp->sbm_cm.sbdev_bp->b_num);
62225cf1a30Sjl 
62325cf1a30Sjl 		if (rv != 0)
62425cf1a30Sjl 			(void) dr_cancel_mem(s_mp);
62525cf1a30Sjl 	}
62625cf1a30Sjl 
62725cf1a30Sjl 	if (rv == 0) {
62825cf1a30Sjl 		sbd_error_t *err;
62925cf1a30Sjl 
63025cf1a30Sjl 		dr_lock_status(hp->h_bd);
63125cf1a30Sjl 		err = drmach_unconfigure(s_mp->sbm_cm.sbdev_id, 0);
63225cf1a30Sjl 		dr_unlock_status(hp->h_bd);
63325cf1a30Sjl 		if (err)
63425cf1a30Sjl 			sbd_err_clear(&err);
63525cf1a30Sjl 	}
63625cf1a30Sjl }
63725cf1a30Sjl 
63825cf1a30Sjl /*
63925cf1a30Sjl  * This routine acts as a wrapper for kphysm_del_span_query in order to
64025cf1a30Sjl  * support potential memory holes in a board's physical address space.
64125cf1a30Sjl  * It calls kphysm_del_span_query for each node in a memlist and accumulates
64225cf1a30Sjl  * the results in *mp.
64325cf1a30Sjl  */
64425cf1a30Sjl static int
64525cf1a30Sjl dr_del_mlist_query(struct memlist *mlist, memquery_t *mp)
64625cf1a30Sjl {
64725cf1a30Sjl 	struct memlist	*ml;
64825cf1a30Sjl 	int		 rv = 0;
64925cf1a30Sjl 
65025cf1a30Sjl 
65125cf1a30Sjl 	if (mlist == NULL)
65225cf1a30Sjl 		cmn_err(CE_WARN, "dr_del_mlist_query: mlist=NULL\n");
65325cf1a30Sjl 
65425cf1a30Sjl 	mp->phys_pages = 0;
65525cf1a30Sjl 	mp->managed = 0;
65625cf1a30Sjl 	mp->nonrelocatable = 0;
65725cf1a30Sjl 	mp->first_nonrelocatable = (pfn_t)-1;	/* XXX */
65825cf1a30Sjl 	mp->last_nonrelocatable = 0;
65925cf1a30Sjl 
66025cf1a30Sjl 	for (ml = mlist; ml; ml = ml->next) {
66125cf1a30Sjl 		memquery_t mq;
66225cf1a30Sjl 
66325cf1a30Sjl 		rv = kphysm_del_span_query(
6643103d4ceSjesusm 		    _b64top(ml->address), _b64top(ml->size), &mq);
66525cf1a30Sjl 		if (rv)
66625cf1a30Sjl 			break;
66725cf1a30Sjl 
66825cf1a30Sjl 		mp->phys_pages += mq.phys_pages;
66925cf1a30Sjl 		mp->managed += mq.managed;
67025cf1a30Sjl 		mp->nonrelocatable += mq.nonrelocatable;
67125cf1a30Sjl 
67225cf1a30Sjl 		if (mq.nonrelocatable != 0) {
67325cf1a30Sjl 			if (mq.first_nonrelocatable < mp->first_nonrelocatable)
67425cf1a30Sjl 				mp->first_nonrelocatable =
6753103d4ceSjesusm 				    mq.first_nonrelocatable;
67625cf1a30Sjl 			if (mq.last_nonrelocatable > mp->last_nonrelocatable)
67725cf1a30Sjl 				mp->last_nonrelocatable =
6783103d4ceSjesusm 				    mq.last_nonrelocatable;
67925cf1a30Sjl 		}
68025cf1a30Sjl 	}
68125cf1a30Sjl 
68225cf1a30Sjl 	if (mp->nonrelocatable == 0)
68325cf1a30Sjl 		mp->first_nonrelocatable = 0;	/* XXX */
68425cf1a30Sjl 
68525cf1a30Sjl 	return (rv);
68625cf1a30Sjl }
68725cf1a30Sjl 
68825cf1a30Sjl /*
68925cf1a30Sjl  * NOTE: This routine is only partially smart about multiple
69025cf1a30Sjl  *	 mem-units.  Need to make mem-status structure smart
69125cf1a30Sjl  *	 about them also.
69225cf1a30Sjl  */
69325cf1a30Sjl int
69425cf1a30Sjl dr_mem_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp)
69525cf1a30Sjl {
69625cf1a30Sjl 	int		m, mix;
69725cf1a30Sjl 	memdelstat_t	mdst;
69825cf1a30Sjl 	memquery_t	mq;
69925cf1a30Sjl 	dr_board_t	*bp;
70025cf1a30Sjl 	dr_mem_unit_t	*mp;
70125cf1a30Sjl 	sbd_mem_stat_t	*msp;
70225cf1a30Sjl 	static fn_t	f = "dr_mem_status";
70325cf1a30Sjl 
70425cf1a30Sjl 	bp = hp->h_bd;
70525cf1a30Sjl 	devset &= DR_DEVS_PRESENT(bp);
70625cf1a30Sjl 
70725cf1a30Sjl 	for (m = mix = 0; m < MAX_MEM_UNITS_PER_BOARD; m++) {
70825cf1a30Sjl 		int		rv;
70925cf1a30Sjl 		sbd_error_t	*err;
71025cf1a30Sjl 		drmach_status_t	 pstat;
71125cf1a30Sjl 		dr_mem_unit_t	*p_mp;
71225cf1a30Sjl 
71325cf1a30Sjl 		if (DEVSET_IN_SET(devset, SBD_COMP_MEM, m) == 0)
71425cf1a30Sjl 			continue;
71525cf1a30Sjl 
71625cf1a30Sjl 		mp = dr_get_mem_unit(bp, m);
71725cf1a30Sjl 
71825cf1a30Sjl 		if (mp->sbm_cm.sbdev_state == DR_STATE_EMPTY) {
71925cf1a30Sjl 			/* present, but not fully initialized */
72025cf1a30Sjl 			continue;
72125cf1a30Sjl 		}
72225cf1a30Sjl 
72325cf1a30Sjl 		if (mp->sbm_cm.sbdev_id == (drmachid_t)0)
72425cf1a30Sjl 			continue;
72525cf1a30Sjl 
72625cf1a30Sjl 		/* fetch platform status */
72725cf1a30Sjl 		err = drmach_status(mp->sbm_cm.sbdev_id, &pstat);
72825cf1a30Sjl 		if (err) {
72925cf1a30Sjl 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
73025cf1a30Sjl 			continue;
73125cf1a30Sjl 		}
73225cf1a30Sjl 
73325cf1a30Sjl 		msp = &dsp->d_mem;
73425cf1a30Sjl 		bzero((caddr_t)msp, sizeof (*msp));
73525cf1a30Sjl 
73625cf1a30Sjl 		strncpy(msp->ms_cm.c_id.c_name, pstat.type,
7373103d4ceSjesusm 		    sizeof (msp->ms_cm.c_id.c_name));
73825cf1a30Sjl 		msp->ms_cm.c_id.c_type = mp->sbm_cm.sbdev_type;
73925cf1a30Sjl 		msp->ms_cm.c_id.c_unit = SBD_NULL_UNIT;
74025cf1a30Sjl 		msp->ms_cm.c_cond = mp->sbm_cm.sbdev_cond;
74125cf1a30Sjl 		msp->ms_cm.c_busy = mp->sbm_cm.sbdev_busy | pstat.busy;
74225cf1a30Sjl 		msp->ms_cm.c_time = mp->sbm_cm.sbdev_time;
74325cf1a30Sjl 		msp->ms_cm.c_ostate = mp->sbm_cm.sbdev_ostate;
74425cf1a30Sjl 
74525cf1a30Sjl 		msp->ms_totpages = mp->sbm_npages;
74625cf1a30Sjl 		msp->ms_basepfn = mp->sbm_basepfn;
74725cf1a30Sjl 		msp->ms_pageslost = mp->sbm_pageslost;
74825cf1a30Sjl 		msp->ms_cage_enabled = kcage_on;
74925cf1a30Sjl 
75025cf1a30Sjl 		if (mp->sbm_flags & DR_MFLAG_RESERVED)
75125cf1a30Sjl 			p_mp = mp->sbm_peer;
75225cf1a30Sjl 		else
75325cf1a30Sjl 			p_mp = NULL;
75425cf1a30Sjl 
75525cf1a30Sjl 		if (p_mp == NULL) {
75625cf1a30Sjl 			msp->ms_peer_is_target = 0;
75725cf1a30Sjl 			msp->ms_peer_ap_id[0] = '\0';
75825cf1a30Sjl 		} else if (p_mp->sbm_flags & DR_MFLAG_RESERVED) {
75925cf1a30Sjl 			char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
76025cf1a30Sjl 			char *minor;
76125cf1a30Sjl 
76225cf1a30Sjl 			/*
76325cf1a30Sjl 			 * b_dip doesn't have to be held for ddi_pathname()
76425cf1a30Sjl 			 * because the board struct (dr_board_t) will be
76525cf1a30Sjl 			 * destroyed before b_dip detaches.
76625cf1a30Sjl 			 */
76725cf1a30Sjl 			(void) ddi_pathname(bp->b_dip, path);
76825cf1a30Sjl 			minor = strchr(p_mp->sbm_cm.sbdev_path, ':');
76925cf1a30Sjl 
77025cf1a30Sjl 			snprintf(msp->ms_peer_ap_id,
77125cf1a30Sjl 			    sizeof (msp->ms_peer_ap_id), "%s%s",
77225cf1a30Sjl 			    path, (minor == NULL) ? "" : minor);
77325cf1a30Sjl 
77425cf1a30Sjl 			kmem_free(path, MAXPATHLEN);
77525cf1a30Sjl 
77625cf1a30Sjl 			if (p_mp->sbm_flags & DR_MFLAG_TARGET)
77725cf1a30Sjl 				msp->ms_peer_is_target = 1;
77825cf1a30Sjl 		}
77925cf1a30Sjl 
78025cf1a30Sjl 		if (mp->sbm_flags & DR_MFLAG_RELOWNER)
78125cf1a30Sjl 			rv = kphysm_del_status(mp->sbm_memhandle, &mdst);
78225cf1a30Sjl 		else
78325cf1a30Sjl 			rv = KPHYSM_EHANDLE;	/* force 'if' to fail */
78425cf1a30Sjl 
78525cf1a30Sjl 		if (rv == KPHYSM_OK) {
78625cf1a30Sjl 			/*
78725cf1a30Sjl 			 * Any pages above managed is "free",
78825cf1a30Sjl 			 * i.e. it's collected.
78925cf1a30Sjl 			 */
79025cf1a30Sjl 			msp->ms_detpages += (uint_t)(mdst.collected +
79125cf1a30Sjl 			    mdst.phys_pages - mdst.managed);
79225cf1a30Sjl 		} else {
79325cf1a30Sjl 			/*
79425cf1a30Sjl 			 * If we're UNREFERENCED or UNCONFIGURED,
79525cf1a30Sjl 			 * then the number of detached pages is
79625cf1a30Sjl 			 * however many pages are on the board.
79725cf1a30Sjl 			 * I.e. detached = not in use by OS.
79825cf1a30Sjl 			 */
79925cf1a30Sjl 			switch (msp->ms_cm.c_ostate) {
80025cf1a30Sjl 			/*
80125cf1a30Sjl 			 * changed to use cfgadm states
80225cf1a30Sjl 			 *
80325cf1a30Sjl 			 * was:
80425cf1a30Sjl 			 *	case DR_STATE_UNREFERENCED:
80525cf1a30Sjl 			 *	case DR_STATE_UNCONFIGURED:
80625cf1a30Sjl 			 */
80725cf1a30Sjl 			case SBD_STAT_UNCONFIGURED:
80825cf1a30Sjl 				msp->ms_detpages = msp->ms_totpages;
80925cf1a30Sjl 				break;
81025cf1a30Sjl 
81125cf1a30Sjl 			default:
81225cf1a30Sjl 				break;
81325cf1a30Sjl 			}
81425cf1a30Sjl 		}
81525cf1a30Sjl 
81625cf1a30Sjl 		/*
81725cf1a30Sjl 		 * kphysm_del_span_query can report non-reloc pages = total
81825cf1a30Sjl 		 * pages for memory that is not yet configured
81925cf1a30Sjl 		 */
82025cf1a30Sjl 		if (mp->sbm_cm.sbdev_state != DR_STATE_UNCONFIGURED) {
82125cf1a30Sjl 			struct memlist *ml;
82225cf1a30Sjl 
82325cf1a30Sjl 			ml = dr_get_memlist(mp);
82425cf1a30Sjl 			rv = ml ? dr_del_mlist_query(ml, &mq) : -1;
82525cf1a30Sjl 			memlist_delete(ml);
82625cf1a30Sjl 
82725cf1a30Sjl 			if (rv == KPHYSM_OK) {
82825cf1a30Sjl 				msp->ms_managed_pages = mq.managed;
82925cf1a30Sjl 				msp->ms_noreloc_pages = mq.nonrelocatable;
83025cf1a30Sjl 				msp->ms_noreloc_first =
83125cf1a30Sjl 				    mq.first_nonrelocatable;
83225cf1a30Sjl 				msp->ms_noreloc_last =
83325cf1a30Sjl 				    mq.last_nonrelocatable;
83425cf1a30Sjl 				msp->ms_cm.c_sflags = 0;
83525cf1a30Sjl 				if (mq.nonrelocatable) {
83625cf1a30Sjl 					SBD_SET_SUSPEND(SBD_CMD_UNCONFIGURE,
83725cf1a30Sjl 					    msp->ms_cm.c_sflags);
83825cf1a30Sjl 				}
83925cf1a30Sjl 			} else {
84025cf1a30Sjl 				PR_MEM("%s: kphysm_del_span_query() = %d\n",
84125cf1a30Sjl 				    f, rv);
84225cf1a30Sjl 			}
84325cf1a30Sjl 		}
84425cf1a30Sjl 
84525cf1a30Sjl 		/*
84625cf1a30Sjl 		 * Check source unit state during copy-rename
84725cf1a30Sjl 		 */
84825cf1a30Sjl 		if ((mp->sbm_flags & DR_MFLAG_SOURCE) &&
84925cf1a30Sjl 		    (mp->sbm_cm.sbdev_state == DR_STATE_UNREFERENCED ||
85025cf1a30Sjl 		    mp->sbm_cm.sbdev_state == DR_STATE_RELEASE))
85125cf1a30Sjl 			msp->ms_cm.c_ostate = SBD_STAT_CONFIGURED;
85225cf1a30Sjl 
85325cf1a30Sjl 		mix++;
85425cf1a30Sjl 		dsp++;
85525cf1a30Sjl 	}
85625cf1a30Sjl 
85725cf1a30Sjl 	return (mix);
85825cf1a30Sjl }
85925cf1a30Sjl 
86025cf1a30Sjl int
86125cf1a30Sjl dr_pre_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
86225cf1a30Sjl {
86325cf1a30Sjl 	_NOTE(ARGUNUSED(hp))
86425cf1a30Sjl 
86525cf1a30Sjl 	int		err_flag = 0;
86625cf1a30Sjl 	int		d;
86725cf1a30Sjl 	sbd_error_t	*err;
86825cf1a30Sjl 	static fn_t	f = "dr_pre_attach_mem";
86925cf1a30Sjl 
87025cf1a30Sjl 	PR_MEM("%s...\n", f);
87125cf1a30Sjl 
87225cf1a30Sjl 	for (d = 0; d < devnum; d++) {
87325cf1a30Sjl 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
87425cf1a30Sjl 		dr_state_t	state;
87525cf1a30Sjl 
87625cf1a30Sjl 		cmn_err(CE_CONT, "OS configure %s", mp->sbm_cm.sbdev_path);
87725cf1a30Sjl 
87825cf1a30Sjl 		state = mp->sbm_cm.sbdev_state;
87925cf1a30Sjl 		switch (state) {
88025cf1a30Sjl 		case DR_STATE_UNCONFIGURED:
88125cf1a30Sjl 			PR_MEM("%s: recovering from UNCONFIG for %s\n",
8823103d4ceSjesusm 			    f,
8833103d4ceSjesusm 			    mp->sbm_cm.sbdev_path);
88425cf1a30Sjl 
88525cf1a30Sjl 			/* use memlist cached by dr_post_detach_mem_unit */
88625cf1a30Sjl 			ASSERT(mp->sbm_mlist != NULL);
88725cf1a30Sjl 			PR_MEM("%s: re-configuring cached memlist for %s:\n",
8883103d4ceSjesusm 			    f, mp->sbm_cm.sbdev_path);
88925cf1a30Sjl 			PR_MEMLIST_DUMP(mp->sbm_mlist);
89025cf1a30Sjl 
89125cf1a30Sjl 			/* kphysm del handle should be have been freed */
89225cf1a30Sjl 			ASSERT((mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
89325cf1a30Sjl 
89425cf1a30Sjl 			/*FALLTHROUGH*/
89525cf1a30Sjl 
89625cf1a30Sjl 		case DR_STATE_CONNECTED:
89725cf1a30Sjl 			PR_MEM("%s: reprogramming mem hardware on %s\n",
8983103d4ceSjesusm 			    f, mp->sbm_cm.sbdev_bp->b_path);
89925cf1a30Sjl 
90025cf1a30Sjl 			PR_MEM("%s: enabling %s\n",
9013103d4ceSjesusm 			    f, mp->sbm_cm.sbdev_path);
90225cf1a30Sjl 
90325cf1a30Sjl 			err = drmach_mem_enable(mp->sbm_cm.sbdev_id);
90425cf1a30Sjl 			if (err) {
90525cf1a30Sjl 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
90625cf1a30Sjl 				err_flag = 1;
90725cf1a30Sjl 			}
90825cf1a30Sjl 			break;
90925cf1a30Sjl 
91025cf1a30Sjl 		default:
91125cf1a30Sjl 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_STATE);
91225cf1a30Sjl 			err_flag = 1;
91325cf1a30Sjl 			break;
91425cf1a30Sjl 		}
91525cf1a30Sjl 
91625cf1a30Sjl 		/* exit for loop if error encountered */
91725cf1a30Sjl 		if (err_flag)
91825cf1a30Sjl 			break;
91925cf1a30Sjl 	}
92025cf1a30Sjl 
92125cf1a30Sjl 	return (err_flag ? -1 : 0);
92225cf1a30Sjl }
92325cf1a30Sjl 
92468ac2337Sjl static void
92568ac2337Sjl dr_update_mc_memory()
92668ac2337Sjl {
92768ac2337Sjl 	void		(*mc_update_mlist)(void);
92868ac2337Sjl 
92968ac2337Sjl 	/*
93068ac2337Sjl 	 * mc-opl is configured during drmach_mem_new but the memory
93168ac2337Sjl 	 * has not been added to phys_install at that time.
93268ac2337Sjl 	 * we must inform mc-opl to update the mlist after we
93368ac2337Sjl 	 * attach or detach a system board.
93468ac2337Sjl 	 */
93568ac2337Sjl 
93668ac2337Sjl 	mc_update_mlist = (void (*)(void))
93768ac2337Sjl 	    modgetsymvalue("opl_mc_update_mlist", 0);
93868ac2337Sjl 
93968ac2337Sjl 	if (mc_update_mlist != NULL) {
94068ac2337Sjl 		(*mc_update_mlist)();
94168ac2337Sjl 	}
94268ac2337Sjl }
94368ac2337Sjl 
94425cf1a30Sjl int
94525cf1a30Sjl dr_post_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
94625cf1a30Sjl {
94725cf1a30Sjl 	_NOTE(ARGUNUSED(hp))
94825cf1a30Sjl 
94925cf1a30Sjl 	int		d;
95025cf1a30Sjl 	static fn_t	f = "dr_post_attach_mem";
95125cf1a30Sjl 
95225cf1a30Sjl 	PR_MEM("%s...\n", f);
95325cf1a30Sjl 
95425cf1a30Sjl 	for (d = 0; d < devnum; d++) {
95525cf1a30Sjl 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
95625cf1a30Sjl 		struct memlist	*mlist, *ml;
95725cf1a30Sjl 
95825cf1a30Sjl 		mlist = dr_get_memlist(mp);
95925cf1a30Sjl 		if (mlist == NULL) {
96068ac2337Sjl 			/* OPL supports memoryless board */
96125cf1a30Sjl 			continue;
96225cf1a30Sjl 		}
96325cf1a30Sjl 
96425cf1a30Sjl 		/*
96525cf1a30Sjl 		 * Verify the memory really did successfully attach
96625cf1a30Sjl 		 * by checking for its existence in phys_install.
96725cf1a30Sjl 		 */
96825cf1a30Sjl 		memlist_read_lock();
96925cf1a30Sjl 		if (memlist_intersect(phys_install, mlist) == 0) {
97025cf1a30Sjl 			memlist_read_unlock();
97125cf1a30Sjl 
97225cf1a30Sjl 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
97325cf1a30Sjl 
97425cf1a30Sjl 			PR_MEM("%s: %s memlist not in phys_install",
9753103d4ceSjesusm 			    f, mp->sbm_cm.sbdev_path);
97625cf1a30Sjl 
97725cf1a30Sjl 			memlist_delete(mlist);
97825cf1a30Sjl 			continue;
97925cf1a30Sjl 		}
98025cf1a30Sjl 		memlist_read_unlock();
98125cf1a30Sjl 
98225cf1a30Sjl 		for (ml = mlist; ml != NULL; ml = ml->next) {
98325cf1a30Sjl 			sbd_error_t *err;
98425cf1a30Sjl 
98525cf1a30Sjl 			err = drmach_mem_add_span(
9863103d4ceSjesusm 			    mp->sbm_cm.sbdev_id,
9873103d4ceSjesusm 			    ml->address,
9883103d4ceSjesusm 			    ml->size);
98925cf1a30Sjl 			if (err)
99025cf1a30Sjl 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
99125cf1a30Sjl 		}
99225cf1a30Sjl 
99325cf1a30Sjl 		memlist_delete(mlist);
99425cf1a30Sjl 
99525cf1a30Sjl 		/*
99625cf1a30Sjl 		 * Destroy cached memlist, if any.
99725cf1a30Sjl 		 * There will be a cached memlist in sbm_mlist if
99825cf1a30Sjl 		 * this board is being configured directly after
99925cf1a30Sjl 		 * an unconfigure.
100025cf1a30Sjl 		 * To support this transition, dr_post_detach_mem
100125cf1a30Sjl 		 * left a copy of the last known memlist in sbm_mlist.
100225cf1a30Sjl 		 * This memlist could differ from any derived from
100325cf1a30Sjl 		 * hardware if while this memunit was last configured
100425cf1a30Sjl 		 * the system detected and deleted bad pages from
100525cf1a30Sjl 		 * phys_install.  The location of those bad pages
100625cf1a30Sjl 		 * will be reflected in the cached memlist.
100725cf1a30Sjl 		 */
100825cf1a30Sjl 		if (mp->sbm_mlist) {
100925cf1a30Sjl 			memlist_delete(mp->sbm_mlist);
101025cf1a30Sjl 			mp->sbm_mlist = NULL;
101125cf1a30Sjl 		}
101225cf1a30Sjl 	}
101325cf1a30Sjl 
101468ac2337Sjl 	dr_update_mc_memory();
101568ac2337Sjl 
101625cf1a30Sjl 	return (0);
101725cf1a30Sjl }
101825cf1a30Sjl 
101925cf1a30Sjl int
102025cf1a30Sjl dr_pre_detach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
102125cf1a30Sjl {
102225cf1a30Sjl 	_NOTE(ARGUNUSED(hp))
102325cf1a30Sjl 
102425cf1a30Sjl 	int d;
102525cf1a30Sjl 
102625cf1a30Sjl 	for (d = 0; d < devnum; d++) {
102725cf1a30Sjl 		dr_mem_unit_t *mp = (dr_mem_unit_t *)devlist[d];
102825cf1a30Sjl 
102925cf1a30Sjl 		cmn_err(CE_CONT, "OS unconfigure %s", mp->sbm_cm.sbdev_path);
103025cf1a30Sjl 	}
103125cf1a30Sjl 
103225cf1a30Sjl 	return (0);
103325cf1a30Sjl }
103425cf1a30Sjl 
103525cf1a30Sjl int
103625cf1a30Sjl dr_post_detach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
103725cf1a30Sjl {
103825cf1a30Sjl 	_NOTE(ARGUNUSED(hp))
103925cf1a30Sjl 
104025cf1a30Sjl 	int		d, rv;
104125cf1a30Sjl 	static fn_t	f = "dr_post_detach_mem";
104225cf1a30Sjl 
104325cf1a30Sjl 	PR_MEM("%s...\n", f);
104425cf1a30Sjl 
104525cf1a30Sjl 	rv = 0;
104625cf1a30Sjl 	for (d = 0; d < devnum; d++) {
104725cf1a30Sjl 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
104825cf1a30Sjl 
104925cf1a30Sjl 		ASSERT(mp->sbm_cm.sbdev_bp == hp->h_bd);
105025cf1a30Sjl 
105125cf1a30Sjl 		if (dr_post_detach_mem_unit(mp))
105225cf1a30Sjl 			rv = -1;
105325cf1a30Sjl 	}
105468ac2337Sjl 	dr_update_mc_memory();
105525cf1a30Sjl 
105625cf1a30Sjl 	return (rv);
105725cf1a30Sjl }
105825cf1a30Sjl 
105925cf1a30Sjl static void
106025cf1a30Sjl dr_add_memory_spans(dr_mem_unit_t *mp, struct memlist *ml)
106125cf1a30Sjl {
106225cf1a30Sjl 	static fn_t	f = "dr_add_memory_spans";
106325cf1a30Sjl 
106425cf1a30Sjl 	PR_MEM("%s...", f);
106525cf1a30Sjl 	PR_MEMLIST_DUMP(ml);
106625cf1a30Sjl 
106725cf1a30Sjl #ifdef DEBUG
106825cf1a30Sjl 	memlist_read_lock();
106925cf1a30Sjl 	if (memlist_intersect(phys_install, ml)) {
107025cf1a30Sjl 		PR_MEM("%s:WARNING: memlist intersects with phys_install\n", f);
107125cf1a30Sjl 	}
107225cf1a30Sjl 	memlist_read_unlock();
107325cf1a30Sjl #endif
107425cf1a30Sjl 
107525cf1a30Sjl 	for (; ml; ml = ml->next) {
107625cf1a30Sjl 		pfn_t		 base;
107725cf1a30Sjl 		pgcnt_t		 npgs;
107825cf1a30Sjl 		int		 rv;
107925cf1a30Sjl 		sbd_error_t	*err;
108025cf1a30Sjl 
108125cf1a30Sjl 		base = _b64top(ml->address);
108225cf1a30Sjl 		npgs = _b64top(ml->size);
108325cf1a30Sjl 
108425cf1a30Sjl 		rv = kphysm_add_memory_dynamic(base, npgs);
108525cf1a30Sjl 
108625cf1a30Sjl 		err = drmach_mem_add_span(
10873103d4ceSjesusm 		    mp->sbm_cm.sbdev_id,
10883103d4ceSjesusm 		    ml->address,
10893103d4ceSjesusm 		    ml->size);
109025cf1a30Sjl 
109125cf1a30Sjl 		if (err)
109225cf1a30Sjl 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
109325cf1a30Sjl 
109425cf1a30Sjl 		if (rv != KPHYSM_OK) {
109525cf1a30Sjl 			cmn_err(CE_WARN, "%s:"
10963103d4ceSjesusm 			    " unexpected kphysm_add_memory_dynamic"
10973103d4ceSjesusm 			    " return value %d;"
10983103d4ceSjesusm 			    " basepfn=0x%lx, npages=%ld\n",
10993103d4ceSjesusm 			    f, rv, base, npgs);
110025cf1a30Sjl 
110125cf1a30Sjl 			continue;
110225cf1a30Sjl 		}
110325cf1a30Sjl 	}
110425cf1a30Sjl }
110525cf1a30Sjl 
110668ac2337Sjl static int
110768ac2337Sjl memlist_touch(struct memlist *ml, uint64_t add)
110868ac2337Sjl {
110968ac2337Sjl 	while (ml != NULL) {
111068ac2337Sjl 		if ((add == ml->address) ||
11113103d4ceSjesusm 		    (add == (ml->address + ml->size)))
111268ac2337Sjl 			return (1);
111368ac2337Sjl 		ml = ml->next;
111468ac2337Sjl 	}
111568ac2337Sjl 	return (0);
111668ac2337Sjl }
111768ac2337Sjl 
111868ac2337Sjl static sbd_error_t *
111968ac2337Sjl dr_process_excess_mlist(dr_mem_unit_t *s_mp,
112068ac2337Sjl 	dr_mem_unit_t *t_mp, struct memlist *t_excess_mlist)
112168ac2337Sjl {
112268ac2337Sjl 	struct memlist	*ml;
112368ac2337Sjl 	sbd_error_t	*err;
112468ac2337Sjl 	static fn_t	f = "dr_process_excess_mlist";
112568ac2337Sjl 	uint64_t	new_pa, nbytes;
112668ac2337Sjl 	int rv;
112768ac2337Sjl 
112868ac2337Sjl 	err = NULL;
112968ac2337Sjl 
113068ac2337Sjl 	/*
113168ac2337Sjl 	 * After the small <-> big copy-rename,
113268ac2337Sjl 	 * the original address space for the
113368ac2337Sjl 	 * source board may have excess to be
113468ac2337Sjl 	 * deleted. This is a case different
113568ac2337Sjl 	 * from the big->small excess source
113668ac2337Sjl 	 * memory case listed below.
113768ac2337Sjl 	 * Remove s_mp->sbm_del_mlist from
113868ac2337Sjl 	 * the kernel cage glist.
113968ac2337Sjl 	 */
114068ac2337Sjl 	for (ml = s_mp->sbm_del_mlist; ml;
11413103d4ceSjesusm 	    ml = ml->next) {
114268ac2337Sjl 		PR_MEM("%s: delete small<->big copy-"
114368ac2337Sjl 		    "rename source excess memory", f);
114468ac2337Sjl 		PR_MEMLIST_DUMP(ml);
114568ac2337Sjl 
114668ac2337Sjl 		err = drmach_mem_del_span(
11473103d4ceSjesusm 		    s_mp->sbm_cm.sbdev_id,
11483103d4ceSjesusm 		    ml->address, ml->size);
114968ac2337Sjl 		if (err)
115068ac2337Sjl 			DRERR_SET_C(&s_mp->
115168ac2337Sjl 			    sbm_cm.sbdev_error, &err);
115268ac2337Sjl 		ASSERT(err == NULL);
115368ac2337Sjl 	}
115468ac2337Sjl 
115568ac2337Sjl 	PR_MEM("%s: adding back remaining portion"
11563103d4ceSjesusm 	    " of %s, memlist:\n",
11573103d4ceSjesusm 	    f, t_mp->sbm_cm.sbdev_path);
115868ac2337Sjl 	PR_MEMLIST_DUMP(t_excess_mlist);
115968ac2337Sjl 
116068ac2337Sjl 	for (ml = t_excess_mlist; ml; ml = ml->next) {
11613103d4ceSjesusm 		struct memlist ml0;
116268ac2337Sjl 
11633103d4ceSjesusm 		ml0.address = ml->address;
11643103d4ceSjesusm 		ml0.size = ml->size;
11653103d4ceSjesusm 		ml0.next = ml0.prev = NULL;
116668ac2337Sjl 
11673103d4ceSjesusm 		/*
11683103d4ceSjesusm 		 * If the memory object is 256 MB aligned (max page size
11693103d4ceSjesusm 		 * on OPL, it will not be coalesced to the adjacent memory
11703103d4ceSjesusm 		 * chunks.  The coalesce logic assumes contiguous page
11713103d4ceSjesusm 		 * structures for contiguous memory and we hit panic.
11723103d4ceSjesusm 		 * For anything less than 256 MB alignment, we have
11733103d4ceSjesusm 		 * to make sure that it is not adjacent to anything.
11743103d4ceSjesusm 		 * If the new chunk is adjacent to phys_install, we
11753103d4ceSjesusm 		 * truncate it to 4MB boundary.  4 MB is somewhat
11763103d4ceSjesusm 		 * arbitrary.  However we do not want to create
11773103d4ceSjesusm 		 * very small segments because they can cause problem.
11783103d4ceSjesusm 		 * The extreme case of 8K segment will fail
11793103d4ceSjesusm 		 * kphysm_add_memory_dynamic(), e.g.
11803103d4ceSjesusm 		 */
11813103d4ceSjesusm 		if ((ml->address & (MH_MPSS_ALIGNMENT - 1)) ||
11823103d4ceSjesusm 		    (ml->size & (MH_MPSS_ALIGNMENT - 1))) {
118368ac2337Sjl 
118468ac2337Sjl 		memlist_read_lock();
118568ac2337Sjl 		rv = memlist_touch(phys_install, ml0.address);
118668ac2337Sjl 		memlist_read_unlock();
118768ac2337Sjl 
118868ac2337Sjl 		if (rv) {
11893103d4ceSjesusm 			new_pa = roundup(ml0.address + 1, MH_MIN_ALIGNMENT);
11903103d4ceSjesusm 			nbytes = (new_pa -  ml0.address);
11913103d4ceSjesusm 			if (nbytes >= ml0.size) {
11923103d4ceSjesusm 				t_mp->sbm_dyn_segs =
11933103d4ceSjesusm 				    memlist_del_span(t_mp->sbm_dyn_segs,
11943103d4ceSjesusm 				    ml0.address, ml0.size);
11953103d4ceSjesusm 				continue;
11963103d4ceSjesusm 			}
119768ac2337Sjl 			t_mp->sbm_dyn_segs =
119868ac2337Sjl 			    memlist_del_span(t_mp->sbm_dyn_segs,
119968ac2337Sjl 			    ml0.address, nbytes);
12003103d4ceSjesusm 			ml0.size -= nbytes;
12013103d4ceSjesusm 			ml0.address = new_pa;
120268ac2337Sjl 		}
120368ac2337Sjl 
120468ac2337Sjl 		if (ml0.size == 0) {
12053103d4ceSjesusm 			continue;
120668ac2337Sjl 		}
120768ac2337Sjl 
120868ac2337Sjl 		memlist_read_lock();
120968ac2337Sjl 		rv = memlist_touch(phys_install, ml0.address + ml0.size);
121068ac2337Sjl 		memlist_read_unlock();
121168ac2337Sjl 
121268ac2337Sjl 		if (rv) {
12133103d4ceSjesusm 			new_pa = rounddown(ml0.address + ml0.size - 1,
12143103d4ceSjesusm 			    MH_MIN_ALIGNMENT);
12153103d4ceSjesusm 			nbytes = (ml0.address + ml0.size - new_pa);
12163103d4ceSjesusm 			if (nbytes >= ml0.size) {
12173103d4ceSjesusm 				t_mp->sbm_dyn_segs =
12183103d4ceSjesusm 				    memlist_del_span(t_mp->sbm_dyn_segs,
12193103d4ceSjesusm 				    ml0.address, ml0.size);
12203103d4ceSjesusm 				continue;
12213103d4ceSjesusm 			}
122268ac2337Sjl 			t_mp->sbm_dyn_segs =
122368ac2337Sjl 			    memlist_del_span(t_mp->sbm_dyn_segs,
122468ac2337Sjl 			    new_pa, nbytes);
12253103d4ceSjesusm 			ml0.size -= nbytes;
122668ac2337Sjl 		}
122768ac2337Sjl 
122868ac2337Sjl 		if (ml0.size > 0) {
12293103d4ceSjesusm 			dr_add_memory_spans(s_mp, &ml0);
12303103d4ceSjesusm 		}
12313103d4ceSjesusm 		} else if (ml0.size > 0) {
12323103d4ceSjesusm 			dr_add_memory_spans(s_mp, &ml0);
123368ac2337Sjl 		}
123468ac2337Sjl 	}
123568ac2337Sjl 	memlist_delete(t_excess_mlist);
123668ac2337Sjl 	return (err);
123768ac2337Sjl }
123868ac2337Sjl 
123925cf1a30Sjl static int
124025cf1a30Sjl dr_post_detach_mem_unit(dr_mem_unit_t *s_mp)
124125cf1a30Sjl {
124225cf1a30Sjl 	uint64_t	sz = s_mp->sbm_slice_size;
124325cf1a30Sjl 	uint64_t	sm = sz - 1;
124425cf1a30Sjl 	/* old and new below refer to PAs before and after copy-rename */
124525cf1a30Sjl 	uint64_t	s_old_basepa, s_new_basepa;
124625cf1a30Sjl 	uint64_t	t_old_basepa, t_new_basepa;
124725cf1a30Sjl 	dr_mem_unit_t	*t_mp, *x_mp;
124825cf1a30Sjl 	drmach_mem_info_t	minfo;
124925cf1a30Sjl 	struct memlist	*ml;
125025cf1a30Sjl 	struct memlist	*t_excess_mlist;
125125cf1a30Sjl 	int		rv;
125225cf1a30Sjl 	int		s_excess_mem_deleted = 0;
125325cf1a30Sjl 	sbd_error_t	*err;
125425cf1a30Sjl 	static fn_t	f = "dr_post_detach_mem_unit";
125525cf1a30Sjl 
125625cf1a30Sjl 	PR_MEM("%s...\n", f);
125725cf1a30Sjl 
125825cf1a30Sjl 	/* s_mp->sbm_del_mlist could be NULL, meaning no deleted spans */
125925cf1a30Sjl 	PR_MEM("%s: %s: deleted memlist (EMPTY maybe okay):\n",
12603103d4ceSjesusm 	    f, s_mp->sbm_cm.sbdev_path);
126125cf1a30Sjl 	PR_MEMLIST_DUMP(s_mp->sbm_del_mlist);
126225cf1a30Sjl 
126325cf1a30Sjl 	/* sanity check */
126425cf1a30Sjl 	ASSERT(s_mp->sbm_del_mlist == NULL ||
12653103d4ceSjesusm 	    (s_mp->sbm_flags & DR_MFLAG_RELDONE) != 0);
126625cf1a30Sjl 
126725cf1a30Sjl 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
126825cf1a30Sjl 		t_mp = s_mp->sbm_peer;
126925cf1a30Sjl 		ASSERT(t_mp != NULL);
127025cf1a30Sjl 		ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
127125cf1a30Sjl 		ASSERT(t_mp->sbm_peer == s_mp);
127225cf1a30Sjl 
127325cf1a30Sjl 		ASSERT(t_mp->sbm_flags & DR_MFLAG_RELDONE);
127425cf1a30Sjl 		ASSERT(t_mp->sbm_del_mlist);
127525cf1a30Sjl 
127625cf1a30Sjl 		PR_MEM("%s: target %s: deleted memlist:\n",
12773103d4ceSjesusm 		    f, t_mp->sbm_cm.sbdev_path);
127825cf1a30Sjl 		PR_MEMLIST_DUMP(t_mp->sbm_del_mlist);
127925cf1a30Sjl 	} else {
128025cf1a30Sjl 		/* this is no target unit */
128125cf1a30Sjl 		t_mp = NULL;
128225cf1a30Sjl 	}
128325cf1a30Sjl 
128425cf1a30Sjl 	/*
128525cf1a30Sjl 	 * Verify the memory really did successfully detach
128625cf1a30Sjl 	 * by checking for its non-existence in phys_install.
128725cf1a30Sjl 	 */
128825cf1a30Sjl 	rv = 0;
128925cf1a30Sjl 	memlist_read_lock();
129025cf1a30Sjl 	if (s_mp->sbm_flags & DR_MFLAG_RELDONE) {
129125cf1a30Sjl 		x_mp = s_mp;
129225cf1a30Sjl 		rv = memlist_intersect(phys_install, x_mp->sbm_del_mlist);
129325cf1a30Sjl 	}
129425cf1a30Sjl 	if (rv == 0 && t_mp && (t_mp->sbm_flags & DR_MFLAG_RELDONE)) {
129525cf1a30Sjl 		x_mp = t_mp;
129625cf1a30Sjl 		rv = memlist_intersect(phys_install, x_mp->sbm_del_mlist);
129725cf1a30Sjl 	}
129825cf1a30Sjl 	memlist_read_unlock();
129925cf1a30Sjl 
130025cf1a30Sjl 	if (rv) {
130125cf1a30Sjl 		/* error: memlist still in phys_install */
130225cf1a30Sjl 		DR_DEV_INTERNAL_ERROR(&x_mp->sbm_cm);
130325cf1a30Sjl 	}
130425cf1a30Sjl 
130525cf1a30Sjl 	/*
130625cf1a30Sjl 	 * clean mem unit state and bail out if an error has been recorded.
130725cf1a30Sjl 	 */
130825cf1a30Sjl 	rv = 0;
130925cf1a30Sjl 	if (s_mp->sbm_cm.sbdev_error) {
131025cf1a30Sjl 		PR_MEM("%s: %s flags=%x", f,
13113103d4ceSjesusm 		    s_mp->sbm_cm.sbdev_path, s_mp->sbm_flags);
131225cf1a30Sjl 		DR_DEV_CLR_UNREFERENCED(&s_mp->sbm_cm);
131325cf1a30Sjl 		DR_DEV_CLR_RELEASED(&s_mp->sbm_cm);
131425cf1a30Sjl 		dr_device_transition(&s_mp->sbm_cm, DR_STATE_CONFIGURED);
131525cf1a30Sjl 		rv = -1;
131625cf1a30Sjl 	}
131725cf1a30Sjl 	if (t_mp != NULL && t_mp->sbm_cm.sbdev_error != NULL) {
131825cf1a30Sjl 		PR_MEM("%s: %s flags=%x", f,
13193103d4ceSjesusm 		    s_mp->sbm_cm.sbdev_path, s_mp->sbm_flags);
132025cf1a30Sjl 		DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
132125cf1a30Sjl 		DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
132225cf1a30Sjl 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_CONFIGURED);
132325cf1a30Sjl 		rv = -1;
132425cf1a30Sjl 	}
132525cf1a30Sjl 	if (rv)
132625cf1a30Sjl 		goto cleanup;
132725cf1a30Sjl 
132825cf1a30Sjl 	s_old_basepa = _ptob64(s_mp->sbm_basepfn);
132925cf1a30Sjl 	err = drmach_mem_get_info(s_mp->sbm_cm.sbdev_id, &minfo);
133025cf1a30Sjl 	ASSERT(err == NULL);
133125cf1a30Sjl 	s_new_basepa = minfo.mi_basepa;
133225cf1a30Sjl 
133325cf1a30Sjl 	PR_MEM("%s:s_old_basepa: 0x%lx\n", f, s_old_basepa);
133425cf1a30Sjl 	PR_MEM("%s:s_new_basepa: 0x%lx\n", f, s_new_basepa);
133525cf1a30Sjl 
133625cf1a30Sjl 	if (t_mp != NULL) {
133725cf1a30Sjl 		struct memlist *s_copy_mlist;
133825cf1a30Sjl 
133925cf1a30Sjl 		t_old_basepa = _ptob64(t_mp->sbm_basepfn);
134025cf1a30Sjl 		err = drmach_mem_get_info(t_mp->sbm_cm.sbdev_id, &minfo);
134125cf1a30Sjl 		ASSERT(err == NULL);
134225cf1a30Sjl 		t_new_basepa = minfo.mi_basepa;
134325cf1a30Sjl 
134425cf1a30Sjl 		PR_MEM("%s:t_old_basepa: 0x%lx\n", f, t_old_basepa);
134525cf1a30Sjl 		PR_MEM("%s:t_new_basepa: 0x%lx\n", f, t_new_basepa);
134625cf1a30Sjl 
134725cf1a30Sjl 		/*
134825cf1a30Sjl 		 * Construct copy list with original source addresses.
134925cf1a30Sjl 		 * Used to add back excess target mem.
135025cf1a30Sjl 		 */
135125cf1a30Sjl 		s_copy_mlist = memlist_dup(s_mp->sbm_mlist);
135225cf1a30Sjl 		for (ml = s_mp->sbm_del_mlist; ml; ml = ml->next) {
135325cf1a30Sjl 			s_copy_mlist = memlist_del_span(s_copy_mlist,
135425cf1a30Sjl 			    ml->address, ml->size);
135525cf1a30Sjl 		}
135625cf1a30Sjl 
135725cf1a30Sjl 		PR_MEM("%s: source copy list:\n:", f);
135825cf1a30Sjl 		PR_MEMLIST_DUMP(s_copy_mlist);
135925cf1a30Sjl 
136025cf1a30Sjl 		/*
136125cf1a30Sjl 		 * We had to swap mem-units, so update
136225cf1a30Sjl 		 * memlists accordingly with new base
136325cf1a30Sjl 		 * addresses.
136425cf1a30Sjl 		 */
136525cf1a30Sjl 		for (ml = t_mp->sbm_mlist; ml; ml = ml->next) {
136625cf1a30Sjl 			ml->address -= t_old_basepa;
136725cf1a30Sjl 			ml->address += t_new_basepa;
136825cf1a30Sjl 		}
136925cf1a30Sjl 
137025cf1a30Sjl 		/*
137125cf1a30Sjl 		 * There is no need to explicitly rename the target delete
137225cf1a30Sjl 		 * memlist, because sbm_del_mlist and sbm_mlist always
137325cf1a30Sjl 		 * point to the same memlist for a copy/rename operation.
137425cf1a30Sjl 		 */
137525cf1a30Sjl 		ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
137625cf1a30Sjl 
137725cf1a30Sjl 		PR_MEM("%s: renamed target memlist and delete memlist:\n", f);
137825cf1a30Sjl 		PR_MEMLIST_DUMP(t_mp->sbm_mlist);
137925cf1a30Sjl 
138025cf1a30Sjl 		for (ml = s_mp->sbm_mlist; ml; ml = ml->next) {
138125cf1a30Sjl 			ml->address -= s_old_basepa;
138225cf1a30Sjl 			ml->address += s_new_basepa;
138325cf1a30Sjl 		}
138425cf1a30Sjl 
138525cf1a30Sjl 		PR_MEM("%s: renamed source memlist:\n", f);
138625cf1a30Sjl 		PR_MEMLIST_DUMP(s_mp->sbm_mlist);
138768ac2337Sjl 		PR_MEM("%s: source dyn seg memlist:\n", f);
138868ac2337Sjl 		PR_MEMLIST_DUMP(s_mp->sbm_dyn_segs);
138925cf1a30Sjl 
139025cf1a30Sjl 		/*
139125cf1a30Sjl 		 * Keep track of dynamically added segments
139225cf1a30Sjl 		 * since they cannot be split if we need to delete
139325cf1a30Sjl 		 * excess source memory later for this board.
139425cf1a30Sjl 		 */
139525cf1a30Sjl 		if (t_mp->sbm_dyn_segs)
139625cf1a30Sjl 			memlist_delete(t_mp->sbm_dyn_segs);
139725cf1a30Sjl 		t_mp->sbm_dyn_segs = s_mp->sbm_dyn_segs;
139825cf1a30Sjl 		s_mp->sbm_dyn_segs = NULL;
139925cf1a30Sjl 
140025cf1a30Sjl 		/*
140125cf1a30Sjl 		 * Add back excess target memory.
140225cf1a30Sjl 		 * Subtract out the portion of the target memory
140325cf1a30Sjl 		 * node that was taken over by the source memory
140425cf1a30Sjl 		 * node.
140525cf1a30Sjl 		 */
140625cf1a30Sjl 		t_excess_mlist = memlist_dup(t_mp->sbm_mlist);
140725cf1a30Sjl 		for (ml = s_copy_mlist; ml; ml = ml->next) {
140825cf1a30Sjl 			t_excess_mlist =
140925cf1a30Sjl 			    memlist_del_span(t_excess_mlist,
141025cf1a30Sjl 			    ml->address, ml->size);
141125cf1a30Sjl 		}
141268ac2337Sjl 		PR_MEM("%s: excess memlist:\n", f);
141368ac2337Sjl 		PR_MEMLIST_DUMP(t_excess_mlist);
141425cf1a30Sjl 
141525cf1a30Sjl 		/*
141625cf1a30Sjl 		 * Update dynamically added segs
141725cf1a30Sjl 		 */
141825cf1a30Sjl 		for (ml = s_mp->sbm_del_mlist; ml; ml = ml->next) {
141925cf1a30Sjl 			t_mp->sbm_dyn_segs =
142025cf1a30Sjl 			    memlist_del_span(t_mp->sbm_dyn_segs,
142125cf1a30Sjl 			    ml->address, ml->size);
142225cf1a30Sjl 		}
142325cf1a30Sjl 		for (ml = t_excess_mlist; ml; ml = ml->next) {
142425cf1a30Sjl 			t_mp->sbm_dyn_segs =
142525cf1a30Sjl 			    memlist_cat_span(t_mp->sbm_dyn_segs,
142625cf1a30Sjl 			    ml->address, ml->size);
142725cf1a30Sjl 		}
142825cf1a30Sjl 		PR_MEM("%s: %s: updated dynamic seg list:\n",
142925cf1a30Sjl 		    f, t_mp->sbm_cm.sbdev_path);
143025cf1a30Sjl 		PR_MEMLIST_DUMP(t_mp->sbm_dyn_segs);
143125cf1a30Sjl 
143225cf1a30Sjl 		if (t_excess_mlist != NULL) {
143368ac2337Sjl 			err = dr_process_excess_mlist(s_mp, t_mp,
14343103d4ceSjesusm 			    t_excess_mlist);
143525cf1a30Sjl 			s_excess_mem_deleted = 1;
143625cf1a30Sjl 		}
143768ac2337Sjl 
143825cf1a30Sjl 		memlist_delete(s_copy_mlist);
143925cf1a30Sjl 
144025cf1a30Sjl #ifdef DEBUG
144125cf1a30Sjl 		/*
144225cf1a30Sjl 		 * s_mp->sbm_del_mlist may still needed
144325cf1a30Sjl 		 */
144425cf1a30Sjl 		PR_MEM("%s: source delete memeory flag %d",
144525cf1a30Sjl 		    f, s_excess_mem_deleted);
144625cf1a30Sjl 		PR_MEM("%s: source delete memlist", f);
144725cf1a30Sjl 		PR_MEMLIST_DUMP(s_mp->sbm_del_mlist);
144825cf1a30Sjl #endif
144925cf1a30Sjl 
145025cf1a30Sjl 	}
145125cf1a30Sjl 
145225cf1a30Sjl 	if (t_mp != NULL) {
145325cf1a30Sjl 		/* delete target's entire address space */
145425cf1a30Sjl 		err = drmach_mem_del_span(
14553103d4ceSjesusm 		    t_mp->sbm_cm.sbdev_id, t_old_basepa & ~ sm, sz);
145625cf1a30Sjl 		if (err)
145725cf1a30Sjl 			DRERR_SET_C(&t_mp->sbm_cm.sbdev_error, &err);
145825cf1a30Sjl 		ASSERT(err == NULL);
145925cf1a30Sjl 
146025cf1a30Sjl 		/*
146125cf1a30Sjl 		 * After the copy/rename, the original address space
146225cf1a30Sjl 		 * for the source board (which is now located on the
146325cf1a30Sjl 		 * target board) may now have some excess to be deleted.
146425cf1a30Sjl 		 * Those excess memory on the source board are kept in
146525cf1a30Sjl 		 * source board's sbm_del_mlist
146625cf1a30Sjl 		 */
146725cf1a30Sjl 		for (ml = s_mp->sbm_del_mlist; !s_excess_mem_deleted && ml;
14683103d4ceSjesusm 		    ml = ml->next) {
146925cf1a30Sjl 			PR_MEM("%s: delete source excess memory", f);
147025cf1a30Sjl 			PR_MEMLIST_DUMP(ml);
147125cf1a30Sjl 
147225cf1a30Sjl 			err = drmach_mem_del_span(s_mp->sbm_cm.sbdev_id,
14733103d4ceSjesusm 			    ml->address, ml->size);
147425cf1a30Sjl 			if (err)
147525cf1a30Sjl 				DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
147625cf1a30Sjl 			ASSERT(err == NULL);
147725cf1a30Sjl 		}
147825cf1a30Sjl 
147925cf1a30Sjl 	} else {
148025cf1a30Sjl 		/* delete board's entire address space */
148125cf1a30Sjl 		err = drmach_mem_del_span(s_mp->sbm_cm.sbdev_id,
14823103d4ceSjesusm 		    s_old_basepa & ~ sm, sz);
148325cf1a30Sjl 		if (err)
148425cf1a30Sjl 			DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
148525cf1a30Sjl 		ASSERT(err == NULL);
148625cf1a30Sjl 	}
148725cf1a30Sjl 
148825cf1a30Sjl cleanup:
148925cf1a30Sjl 	/* clean up target mem unit */
149025cf1a30Sjl 	if (t_mp != NULL) {
149125cf1a30Sjl 		memlist_delete(t_mp->sbm_del_mlist);
149225cf1a30Sjl 		/* no need to delete sbm_mlist, it shares sbm_del_mlist */
149325cf1a30Sjl 
149425cf1a30Sjl 		t_mp->sbm_del_mlist = NULL;
149525cf1a30Sjl 		t_mp->sbm_mlist = NULL;
149625cf1a30Sjl 		t_mp->sbm_peer = NULL;
149725cf1a30Sjl 		t_mp->sbm_flags = 0;
149825cf1a30Sjl 		t_mp->sbm_cm.sbdev_busy = 0;
149925cf1a30Sjl 		dr_init_mem_unit_data(t_mp);
150025cf1a30Sjl 
150125cf1a30Sjl 	}
150225cf1a30Sjl 	if (t_mp != NULL && t_mp->sbm_cm.sbdev_error == NULL) {
150325cf1a30Sjl 		/*
150425cf1a30Sjl 		 * now that copy/rename has completed, undo this
150525cf1a30Sjl 		 * work that was done in dr_release_mem_done.
150625cf1a30Sjl 		 */
150725cf1a30Sjl 		DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
150825cf1a30Sjl 		DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
150925cf1a30Sjl 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_CONFIGURED);
151025cf1a30Sjl 	}
151125cf1a30Sjl 
151225cf1a30Sjl 	/*
151325cf1a30Sjl 	 * clean up (source) board's mem unit structure.
151425cf1a30Sjl 	 * NOTE: sbm_mlist is retained if no error has been record (in other
151525cf1a30Sjl 	 * words, when s_mp->sbm_cm.sbdev_error is NULL). This memlist is
151625cf1a30Sjl 	 * referred to elsewhere as the cached memlist.  The cached memlist
151725cf1a30Sjl 	 * is used to re-attach (configure back in) this memunit from the
151825cf1a30Sjl 	 * unconfigured state.  The memlist is retained because it may
151925cf1a30Sjl 	 * represent bad pages that were detected while the memory was
152025cf1a30Sjl 	 * configured into the OS.  The OS deletes bad pages from phys_install.
152125cf1a30Sjl 	 * Those deletes, if any, will be represented in the cached mlist.
152225cf1a30Sjl 	 */
152325cf1a30Sjl 	if (s_mp->sbm_del_mlist && s_mp->sbm_del_mlist != s_mp->sbm_mlist)
152425cf1a30Sjl 		memlist_delete(s_mp->sbm_del_mlist);
152525cf1a30Sjl 
152625cf1a30Sjl 	if (s_mp->sbm_cm.sbdev_error && s_mp->sbm_mlist) {
152725cf1a30Sjl 		memlist_delete(s_mp->sbm_mlist);
152825cf1a30Sjl 		s_mp->sbm_mlist = NULL;
152925cf1a30Sjl 	}
153025cf1a30Sjl 
153125cf1a30Sjl 	if (s_mp->sbm_dyn_segs != NULL && s_mp->sbm_cm.sbdev_error == 0) {
153225cf1a30Sjl 		memlist_delete(s_mp->sbm_dyn_segs);
153325cf1a30Sjl 		s_mp->sbm_dyn_segs = NULL;
153425cf1a30Sjl 	}
153525cf1a30Sjl 
153625cf1a30Sjl 	s_mp->sbm_del_mlist = NULL;
153725cf1a30Sjl 	s_mp->sbm_peer = NULL;
153825cf1a30Sjl 	s_mp->sbm_flags = 0;
153925cf1a30Sjl 	s_mp->sbm_cm.sbdev_busy = 0;
154025cf1a30Sjl 	dr_init_mem_unit_data(s_mp);
154125cf1a30Sjl 
154225cf1a30Sjl 	PR_MEM("%s: cached memlist for %s:", f, s_mp->sbm_cm.sbdev_path);
154325cf1a30Sjl 	PR_MEMLIST_DUMP(s_mp->sbm_mlist);
154425cf1a30Sjl 
154525cf1a30Sjl 	return (0);
154625cf1a30Sjl }
154725cf1a30Sjl 
154825cf1a30Sjl /*
154925cf1a30Sjl  * Successful return from this function will have the memory
155025cf1a30Sjl  * handle in bp->b_dev[..mem-unit...].sbm_memhandle allocated
155125cf1a30Sjl  * and waiting.  This routine's job is to select the memory that
155225cf1a30Sjl  * actually has to be released (detached) which may not necessarily
155325cf1a30Sjl  * be the same memory node that came in in devlist[],
155425cf1a30Sjl  * i.e. a copy-rename is needed.
155525cf1a30Sjl  */
155625cf1a30Sjl int
155725cf1a30Sjl dr_pre_release_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
155825cf1a30Sjl {
155925cf1a30Sjl 	int		d;
156025cf1a30Sjl 	int		err_flag = 0;
156125cf1a30Sjl 	static fn_t	f = "dr_pre_release_mem";
156225cf1a30Sjl 
156325cf1a30Sjl 	PR_MEM("%s...\n", f);
156425cf1a30Sjl 
156525cf1a30Sjl 	for (d = 0; d < devnum; d++) {
156625cf1a30Sjl 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
156725cf1a30Sjl 		int		rv;
156825cf1a30Sjl 		memquery_t	mq;
156925cf1a30Sjl 		struct memlist	*ml;
157025cf1a30Sjl 
157125cf1a30Sjl 		if (mp->sbm_cm.sbdev_error) {
157225cf1a30Sjl 			err_flag = 1;
157325cf1a30Sjl 			continue;
157425cf1a30Sjl 		} else if (!kcage_on) {
157525cf1a30Sjl 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_KCAGE_OFF);
157625cf1a30Sjl 			err_flag = 1;
157725cf1a30Sjl 			continue;
157825cf1a30Sjl 		}
157925cf1a30Sjl 
158025cf1a30Sjl 		if (mp->sbm_flags & DR_MFLAG_RESERVED) {
158125cf1a30Sjl 			/*
158225cf1a30Sjl 			 * Board is currently involved in a delete
158325cf1a30Sjl 			 * memory operation. Can't detach this guy until
158425cf1a30Sjl 			 * that operation completes.
158525cf1a30Sjl 			 */
158625cf1a30Sjl 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_INVAL);
158725cf1a30Sjl 			err_flag = 1;
158825cf1a30Sjl 			break;
158925cf1a30Sjl 		}
159025cf1a30Sjl 
159125cf1a30Sjl 		/* flags should be clean at this time */
159225cf1a30Sjl 		ASSERT(mp->sbm_flags == 0);
159325cf1a30Sjl 
159425cf1a30Sjl 		ASSERT(mp->sbm_mlist == NULL);
159525cf1a30Sjl 		ASSERT(mp->sbm_del_mlist == NULL);
159625cf1a30Sjl 		if (mp->sbm_mlist != NULL) {
159725cf1a30Sjl 			memlist_delete(mp->sbm_mlist);
159825cf1a30Sjl 			mp->sbm_mlist = NULL;
159925cf1a30Sjl 		}
160025cf1a30Sjl 
160125cf1a30Sjl 		ml = dr_get_memlist(mp);
160225cf1a30Sjl 		if (ml == NULL) {
160325cf1a30Sjl 			err_flag = 1;
160425cf1a30Sjl 			PR_MEM("%s: no memlist found for %s\n",
160525cf1a30Sjl 			    f, mp->sbm_cm.sbdev_path);
160625cf1a30Sjl 			continue;
160725cf1a30Sjl 		}
160825cf1a30Sjl 
160925cf1a30Sjl 		/*
161025cf1a30Sjl 		 * Check whether the detaching memory requires a
161125cf1a30Sjl 		 * copy-rename.
161225cf1a30Sjl 		 */
161325cf1a30Sjl 		ASSERT(mp->sbm_npages != 0);
161468ac2337Sjl 
161525cf1a30Sjl 		rv = dr_del_mlist_query(ml, &mq);
161625cf1a30Sjl 		if (rv != KPHYSM_OK) {
161725cf1a30Sjl 			memlist_delete(ml);
161825cf1a30Sjl 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
161925cf1a30Sjl 			err_flag = 1;
162025cf1a30Sjl 			break;
162125cf1a30Sjl 		}
162225cf1a30Sjl 
162325cf1a30Sjl 		if (mq.nonrelocatable != 0) {
162425cf1a30Sjl 			if (!(dr_cmd_flags(hp) &
16253103d4ceSjesusm 			    (SBD_FLAG_FORCE | SBD_FLAG_QUIESCE_OKAY))) {
162625cf1a30Sjl 				memlist_delete(ml);
162725cf1a30Sjl 				/* caller wasn't prompted for a suspend */
162825cf1a30Sjl 				dr_dev_err(CE_WARN, &mp->sbm_cm,
16293103d4ceSjesusm 				    ESBD_QUIESCE_REQD);
163025cf1a30Sjl 				err_flag = 1;
163125cf1a30Sjl 				break;
163225cf1a30Sjl 			}
163325cf1a30Sjl 		}
163425cf1a30Sjl 
163525cf1a30Sjl 		/* allocate a kphysm handle */
163625cf1a30Sjl 		rv = kphysm_del_gethandle(&mp->sbm_memhandle);
163725cf1a30Sjl 		if (rv != KPHYSM_OK) {
163825cf1a30Sjl 			memlist_delete(ml);
163925cf1a30Sjl 
164025cf1a30Sjl 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
164125cf1a30Sjl 			err_flag = 1;
164225cf1a30Sjl 			break;
164325cf1a30Sjl 		}
164425cf1a30Sjl 		mp->sbm_flags |= DR_MFLAG_RELOWNER;
164525cf1a30Sjl 
164625cf1a30Sjl 		if ((mq.nonrelocatable != 0) ||
16473103d4ceSjesusm 		    dr_reserve_mem_spans(&mp->sbm_memhandle, ml)) {
164825cf1a30Sjl 			/*
164925cf1a30Sjl 			 * Either the detaching memory node contains
165025cf1a30Sjl 			 * non-reloc memory or we failed to reserve the
165125cf1a30Sjl 			 * detaching memory node (which did _not_ have
165225cf1a30Sjl 			 * any non-reloc memory, i.e. some non-reloc mem
165325cf1a30Sjl 			 * got onboard).
165425cf1a30Sjl 			 */
165525cf1a30Sjl 
165625cf1a30Sjl 			if (dr_select_mem_target(hp, mp, ml)) {
165725cf1a30Sjl 				int rv;
165825cf1a30Sjl 
165925cf1a30Sjl 				/*
166025cf1a30Sjl 				 * We had no luck locating a target
166125cf1a30Sjl 				 * memory node to be the recipient of
166225cf1a30Sjl 				 * the non-reloc memory on the node
166325cf1a30Sjl 				 * we're trying to detach.
166425cf1a30Sjl 				 * Clean up be disposing the mem handle
166525cf1a30Sjl 				 * and the mem list.
166625cf1a30Sjl 				 */
166725cf1a30Sjl 				rv = kphysm_del_release(mp->sbm_memhandle);
166825cf1a30Sjl 				if (rv != KPHYSM_OK) {
166925cf1a30Sjl 					/*
167025cf1a30Sjl 					 * can do nothing but complain
167125cf1a30Sjl 					 * and hope helpful for debug
167225cf1a30Sjl 					 */
167325cf1a30Sjl 					cmn_err(CE_WARN, "%s: unexpected"
16743103d4ceSjesusm 					    " kphysm_del_release return"
16753103d4ceSjesusm 					    " value %d",
16763103d4ceSjesusm 					    f, rv);
167725cf1a30Sjl 				}
167825cf1a30Sjl 				mp->sbm_flags &= ~DR_MFLAG_RELOWNER;
167925cf1a30Sjl 
168025cf1a30Sjl 				memlist_delete(ml);
168125cf1a30Sjl 
168225cf1a30Sjl 				/* make sure sbm_flags is clean */
168325cf1a30Sjl 				ASSERT(mp->sbm_flags == 0);
168425cf1a30Sjl 
168525cf1a30Sjl 				dr_dev_err(CE_WARN,
16863103d4ceSjesusm 				    &mp->sbm_cm, ESBD_NO_TARGET);
168725cf1a30Sjl 
168825cf1a30Sjl 				err_flag = 1;
168925cf1a30Sjl 				break;
169025cf1a30Sjl 			}
169125cf1a30Sjl 
169225cf1a30Sjl 			/*
169325cf1a30Sjl 			 * ml is not memlist_delete'd here because
169425cf1a30Sjl 			 * it has been assigned to mp->sbm_mlist
169525cf1a30Sjl 			 * by dr_select_mem_target.
169625cf1a30Sjl 			 */
169725cf1a30Sjl 		} else {
169825cf1a30Sjl 			/* no target needed to detach this board */
169925cf1a30Sjl 			mp->sbm_flags |= DR_MFLAG_RESERVED;
170025cf1a30Sjl 			mp->sbm_peer = NULL;
170125cf1a30Sjl 			mp->sbm_del_mlist = ml;
170225cf1a30Sjl 			mp->sbm_mlist = ml;
170325cf1a30Sjl 			mp->sbm_cm.sbdev_busy = 1;
170425cf1a30Sjl 		}
170525cf1a30Sjl #ifdef DEBUG
170625cf1a30Sjl 		ASSERT(mp->sbm_mlist != NULL);
170725cf1a30Sjl 
170825cf1a30Sjl 		if (mp->sbm_flags & DR_MFLAG_SOURCE) {
170925cf1a30Sjl 			PR_MEM("%s: release of %s requires copy/rename;"
17103103d4ceSjesusm 			    " selected target board %s\n",
17113103d4ceSjesusm 			    f,
17123103d4ceSjesusm 			    mp->sbm_cm.sbdev_path,
17133103d4ceSjesusm 			    mp->sbm_peer->sbm_cm.sbdev_path);
171425cf1a30Sjl 		} else {
171525cf1a30Sjl 			PR_MEM("%s: copy/rename not required to release %s\n",
17163103d4ceSjesusm 			    f, mp->sbm_cm.sbdev_path);
171725cf1a30Sjl 		}
171825cf1a30Sjl 
171925cf1a30Sjl 		ASSERT(mp->sbm_flags & DR_MFLAG_RELOWNER);
172025cf1a30Sjl 		ASSERT(mp->sbm_flags & DR_MFLAG_RESERVED);
172125cf1a30Sjl #endif
172225cf1a30Sjl 	}
172325cf1a30Sjl 
172425cf1a30Sjl 	return (err_flag ? -1 : 0);
172525cf1a30Sjl }
172625cf1a30Sjl 
172725cf1a30Sjl void
172825cf1a30Sjl dr_release_mem_done(dr_common_unit_t *cp)
172925cf1a30Sjl {
173025cf1a30Sjl 	dr_mem_unit_t	*s_mp = (dr_mem_unit_t *)cp;
173125cf1a30Sjl 	dr_mem_unit_t *t_mp, *mp;
173225cf1a30Sjl 	int		rv;
173325cf1a30Sjl 	static fn_t	f = "dr_release_mem_done";
173425cf1a30Sjl 
173525cf1a30Sjl 	/*
173625cf1a30Sjl 	 * This unit will be flagged with DR_MFLAG_SOURCE, if it
173725cf1a30Sjl 	 * has a target unit.
173825cf1a30Sjl 	 */
173925cf1a30Sjl 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
174025cf1a30Sjl 		t_mp = s_mp->sbm_peer;
174125cf1a30Sjl 		ASSERT(t_mp != NULL);
174225cf1a30Sjl 		ASSERT(t_mp->sbm_peer == s_mp);
174325cf1a30Sjl 		ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
174425cf1a30Sjl 		ASSERT(t_mp->sbm_flags & DR_MFLAG_RESERVED);
174525cf1a30Sjl 	} else {
174625cf1a30Sjl 		/* this is no target unit */
174725cf1a30Sjl 		t_mp = NULL;
174825cf1a30Sjl 	}
174925cf1a30Sjl 
175025cf1a30Sjl 	/* free delete handle */
175125cf1a30Sjl 	ASSERT(s_mp->sbm_flags & DR_MFLAG_RELOWNER);
175225cf1a30Sjl 	ASSERT(s_mp->sbm_flags & DR_MFLAG_RESERVED);
175325cf1a30Sjl 	rv = kphysm_del_release(s_mp->sbm_memhandle);
175425cf1a30Sjl 	if (rv != KPHYSM_OK) {
175525cf1a30Sjl 		/*
175625cf1a30Sjl 		 * can do nothing but complain
175725cf1a30Sjl 		 * and hope helpful for debug
175825cf1a30Sjl 		 */
175925cf1a30Sjl 		cmn_err(CE_WARN, "%s: unexpected kphysm_del_release"
17603103d4ceSjesusm 		    " return value %d", f, rv);
176125cf1a30Sjl 	}
176225cf1a30Sjl 	s_mp->sbm_flags &= ~DR_MFLAG_RELOWNER;
176325cf1a30Sjl 
176425cf1a30Sjl 	/*
176525cf1a30Sjl 	 * If an error was encountered during release, clean up
176625cf1a30Sjl 	 * the source (and target, if present) unit data.
176725cf1a30Sjl 	 */
176825cf1a30Sjl /* XXX Can we know that sbdev_error was encountered during release? */
176925cf1a30Sjl 	if (s_mp->sbm_cm.sbdev_error != NULL) {
177025cf1a30Sjl 
177125cf1a30Sjl 		if (t_mp != NULL) {
177225cf1a30Sjl 			ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
177325cf1a30Sjl 			t_mp->sbm_del_mlist = NULL;
177425cf1a30Sjl 
177525cf1a30Sjl 			if (t_mp->sbm_mlist != NULL) {
177625cf1a30Sjl 				memlist_delete(t_mp->sbm_mlist);
177725cf1a30Sjl 				t_mp->sbm_mlist = NULL;
177825cf1a30Sjl 			}
177925cf1a30Sjl 
178025cf1a30Sjl 			t_mp->sbm_peer = NULL;
178125cf1a30Sjl 			t_mp->sbm_flags = 0;
178225cf1a30Sjl 			t_mp->sbm_cm.sbdev_busy = 0;
178325cf1a30Sjl 		}
178425cf1a30Sjl 
178525cf1a30Sjl 		if (s_mp->sbm_del_mlist != s_mp->sbm_mlist)
178625cf1a30Sjl 			memlist_delete(s_mp->sbm_del_mlist);
178725cf1a30Sjl 		s_mp->sbm_del_mlist = NULL;
178825cf1a30Sjl 
178925cf1a30Sjl 		if (s_mp->sbm_mlist != NULL) {
179025cf1a30Sjl 			memlist_delete(s_mp->sbm_mlist);
179125cf1a30Sjl 			s_mp->sbm_mlist = NULL;
179225cf1a30Sjl 		}
179325cf1a30Sjl 
179425cf1a30Sjl 		s_mp->sbm_peer = NULL;
179525cf1a30Sjl 		s_mp->sbm_flags = 0;
179625cf1a30Sjl 		s_mp->sbm_cm.sbdev_busy = 0;
179725cf1a30Sjl 
179825cf1a30Sjl 		/* bail out */
179925cf1a30Sjl 		return;
180025cf1a30Sjl 	}
180125cf1a30Sjl 
180225cf1a30Sjl 	DR_DEV_SET_RELEASED(&s_mp->sbm_cm);
180325cf1a30Sjl 	dr_device_transition(&s_mp->sbm_cm, DR_STATE_RELEASE);
180425cf1a30Sjl 
180525cf1a30Sjl 	if (t_mp != NULL) {
180625cf1a30Sjl 		/*
180725cf1a30Sjl 		 * the kphysm delete operation that drained the source
180825cf1a30Sjl 		 * board also drained this target board.  Since the source
180925cf1a30Sjl 		 * board drain is now known to have succeeded, we know this
181025cf1a30Sjl 		 * target board is drained too.
181125cf1a30Sjl 		 *
181225cf1a30Sjl 		 * because DR_DEV_SET_RELEASED and dr_device_transition
181325cf1a30Sjl 		 * is done here, the dr_release_dev_done should not
181425cf1a30Sjl 		 * fail.
181525cf1a30Sjl 		 */
181625cf1a30Sjl 		DR_DEV_SET_RELEASED(&t_mp->sbm_cm);
181725cf1a30Sjl 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_RELEASE);
181825cf1a30Sjl 
181925cf1a30Sjl 		/*
182025cf1a30Sjl 		 * NOTE: do not transition target's board state,
182125cf1a30Sjl 		 * even if the mem-unit was the last configure
182225cf1a30Sjl 		 * unit of the board.  When copy/rename completes
182325cf1a30Sjl 		 * this mem-unit will transitioned back to
182425cf1a30Sjl 		 * the configured state.  In the meantime, the
182525cf1a30Sjl 		 * board's must remain as is.
182625cf1a30Sjl 		 */
182725cf1a30Sjl 	}
182825cf1a30Sjl 
182925cf1a30Sjl 	/* if board(s) had deleted memory, verify it is gone */
183025cf1a30Sjl 	rv = 0;
183125cf1a30Sjl 	memlist_read_lock();
183225cf1a30Sjl 	if (s_mp->sbm_del_mlist != NULL) {
183325cf1a30Sjl 		mp = s_mp;
183425cf1a30Sjl 		rv = memlist_intersect(phys_install, mp->sbm_del_mlist);
183525cf1a30Sjl 	}
183625cf1a30Sjl 	if (rv == 0 && t_mp && t_mp->sbm_del_mlist != NULL) {
183725cf1a30Sjl 		mp = t_mp;
183825cf1a30Sjl 		rv = memlist_intersect(phys_install, mp->sbm_del_mlist);
183925cf1a30Sjl 	}
184025cf1a30Sjl 	memlist_read_unlock();
184125cf1a30Sjl 	if (rv) {
184225cf1a30Sjl 		cmn_err(CE_WARN, "%s: %smem-unit (%d.%d): "
18433103d4ceSjesusm 		    "deleted memory still found in phys_install",
18443103d4ceSjesusm 		    f,
18453103d4ceSjesusm 		    (mp == t_mp ? "target " : ""),
18463103d4ceSjesusm 		    mp->sbm_cm.sbdev_bp->b_num,
18473103d4ceSjesusm 		    mp->sbm_cm.sbdev_unum);
184825cf1a30Sjl 
184925cf1a30Sjl 		DR_DEV_INTERNAL_ERROR(&s_mp->sbm_cm);
185025cf1a30Sjl 		return;
185125cf1a30Sjl 	}
185225cf1a30Sjl 
185325cf1a30Sjl 	s_mp->sbm_flags |= DR_MFLAG_RELDONE;
185425cf1a30Sjl 	if (t_mp != NULL)
185525cf1a30Sjl 		t_mp->sbm_flags |= DR_MFLAG_RELDONE;
185625cf1a30Sjl 
185725cf1a30Sjl 	/* this should not fail */
185825cf1a30Sjl 	if (dr_release_dev_done(&s_mp->sbm_cm) != 0) {
185925cf1a30Sjl 		/* catch this in debug kernels */
186025cf1a30Sjl 		ASSERT(0);
186125cf1a30Sjl 		return;
186225cf1a30Sjl 	}
186325cf1a30Sjl 
186425cf1a30Sjl 	PR_MEM("%s: marking %s release DONE\n",
18653103d4ceSjesusm 	    f, s_mp->sbm_cm.sbdev_path);
186625cf1a30Sjl 
186725cf1a30Sjl 	s_mp->sbm_cm.sbdev_ostate = SBD_STAT_UNCONFIGURED;
186825cf1a30Sjl 
186925cf1a30Sjl 	if (t_mp != NULL) {
187025cf1a30Sjl 		/* should not fail */
187125cf1a30Sjl 		rv = dr_release_dev_done(&t_mp->sbm_cm);
187225cf1a30Sjl 		if (rv != 0) {
187325cf1a30Sjl 			/* catch this in debug kernels */
187425cf1a30Sjl 			ASSERT(0);
187525cf1a30Sjl 			return;
187625cf1a30Sjl 		}
187725cf1a30Sjl 
187825cf1a30Sjl 		PR_MEM("%s: marking %s release DONE\n",
18793103d4ceSjesusm 		    f, t_mp->sbm_cm.sbdev_path);
188025cf1a30Sjl 
188125cf1a30Sjl 		t_mp->sbm_cm.sbdev_ostate = SBD_STAT_UNCONFIGURED;
188225cf1a30Sjl 	}
188325cf1a30Sjl }
188425cf1a30Sjl 
188525cf1a30Sjl /*ARGSUSED*/
188625cf1a30Sjl int
188725cf1a30Sjl dr_disconnect_mem(dr_mem_unit_t *mp)
188825cf1a30Sjl {
188925cf1a30Sjl 	static fn_t	f = "dr_disconnect_mem";
189025cf1a30Sjl 	update_membounds_t umb;
189125cf1a30Sjl 
189225cf1a30Sjl #ifdef DEBUG
189325cf1a30Sjl 	int state = mp->sbm_cm.sbdev_state;
189425cf1a30Sjl 	ASSERT(state == DR_STATE_CONNECTED ||
18953103d4ceSjesusm 	    state == DR_STATE_UNCONFIGURED);
189625cf1a30Sjl #endif
189725cf1a30Sjl 
189825cf1a30Sjl 	PR_MEM("%s...\n", f);
189925cf1a30Sjl 
190025cf1a30Sjl 	if (mp->sbm_del_mlist && mp->sbm_del_mlist != mp->sbm_mlist)
190125cf1a30Sjl 		memlist_delete(mp->sbm_del_mlist);
190225cf1a30Sjl 	mp->sbm_del_mlist = NULL;
190325cf1a30Sjl 
190425cf1a30Sjl 	if (mp->sbm_mlist) {
190525cf1a30Sjl 		memlist_delete(mp->sbm_mlist);
190625cf1a30Sjl 		mp->sbm_mlist = NULL;
190725cf1a30Sjl 	}
190825cf1a30Sjl 
190925cf1a30Sjl 	/*
191025cf1a30Sjl 	 * Remove memory from lgroup
191125cf1a30Sjl 	 * For now, only board info is required.
191225cf1a30Sjl 	 */
191325cf1a30Sjl 	umb.u_board = mp->sbm_cm.sbdev_bp->b_num;
191425cf1a30Sjl 	umb.u_base = (uint64_t)-1;
191525cf1a30Sjl 	umb.u_len = (uint64_t)-1;
191625cf1a30Sjl 
191725cf1a30Sjl 	lgrp_plat_config(LGRP_CONFIG_MEM_DEL, (uintptr_t)&umb);
191825cf1a30Sjl 
191925cf1a30Sjl 	return (0);
192025cf1a30Sjl }
192125cf1a30Sjl 
192225cf1a30Sjl int
192325cf1a30Sjl dr_cancel_mem(dr_mem_unit_t *s_mp)
192425cf1a30Sjl {
192525cf1a30Sjl 	dr_mem_unit_t	*t_mp;
192625cf1a30Sjl 	dr_state_t	state;
192725cf1a30Sjl 	static fn_t	f = "dr_cancel_mem";
192825cf1a30Sjl 
192925cf1a30Sjl 	state = s_mp->sbm_cm.sbdev_state;
193025cf1a30Sjl 
193125cf1a30Sjl 	if (s_mp->sbm_flags & DR_MFLAG_TARGET) {
193225cf1a30Sjl 		/* must cancel source board, not target board */
193325cf1a30Sjl 		/* TODO: set error */
193425cf1a30Sjl 		return (-1);
193525cf1a30Sjl 	} else if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
193625cf1a30Sjl 		t_mp = s_mp->sbm_peer;
193725cf1a30Sjl 		ASSERT(t_mp != NULL);
193825cf1a30Sjl 		ASSERT(t_mp->sbm_peer == s_mp);
193925cf1a30Sjl 
194025cf1a30Sjl 		/* must always match the source board's state */
194125cf1a30Sjl 		/* TODO: is this assertion correct? */
194225cf1a30Sjl 		ASSERT(t_mp->sbm_cm.sbdev_state == state);
194325cf1a30Sjl 	} else {
194425cf1a30Sjl 		/* this is no target unit */
194525cf1a30Sjl 		t_mp = NULL;
194625cf1a30Sjl 	}
194725cf1a30Sjl 
194825cf1a30Sjl 	switch (state) {
194925cf1a30Sjl 	case DR_STATE_UNREFERENCED:	/* state set by dr_release_dev_done */
195025cf1a30Sjl 		ASSERT((s_mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
195125cf1a30Sjl 
195225cf1a30Sjl 		if (t_mp != NULL && t_mp->sbm_del_mlist != NULL) {
195325cf1a30Sjl 			PR_MEM("%s: undoing target %s memory delete\n",
19543103d4ceSjesusm 			    f, t_mp->sbm_cm.sbdev_path);
195525cf1a30Sjl 			dr_add_memory_spans(t_mp, t_mp->sbm_del_mlist);
195625cf1a30Sjl 
195725cf1a30Sjl 			DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
195825cf1a30Sjl 		}
195925cf1a30Sjl 
196025cf1a30Sjl 		if (s_mp->sbm_del_mlist != NULL) {
196125cf1a30Sjl 			PR_MEM("%s: undoing %s memory delete\n",
19623103d4ceSjesusm 			    f, s_mp->sbm_cm.sbdev_path);
196325cf1a30Sjl 
196425cf1a30Sjl 			dr_add_memory_spans(s_mp, s_mp->sbm_del_mlist);
196525cf1a30Sjl 		}
196625cf1a30Sjl 
196725cf1a30Sjl 		/*FALLTHROUGH*/
196825cf1a30Sjl 
196925cf1a30Sjl /* TODO: should no longer be possible to see the release state here */
197025cf1a30Sjl 	case DR_STATE_RELEASE:	/* state set by dr_release_mem_done */
197125cf1a30Sjl 
197225cf1a30Sjl 		ASSERT((s_mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
197325cf1a30Sjl 
197425cf1a30Sjl 		if (t_mp != NULL) {
197525cf1a30Sjl 			ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
197625cf1a30Sjl 			t_mp->sbm_del_mlist = NULL;
197725cf1a30Sjl 
197825cf1a30Sjl 			if (t_mp->sbm_mlist != NULL) {
197925cf1a30Sjl 				memlist_delete(t_mp->sbm_mlist);
198025cf1a30Sjl 				t_mp->sbm_mlist = NULL;
198125cf1a30Sjl 			}
198225cf1a30Sjl 
198325cf1a30Sjl 			t_mp->sbm_peer = NULL;
198425cf1a30Sjl 			t_mp->sbm_flags = 0;
198525cf1a30Sjl 			t_mp->sbm_cm.sbdev_busy = 0;
198625cf1a30Sjl 			dr_init_mem_unit_data(t_mp);
198725cf1a30Sjl 
198825cf1a30Sjl 			DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
198925cf1a30Sjl 
199025cf1a30Sjl 			dr_device_transition(
19913103d4ceSjesusm 			    &t_mp->sbm_cm, DR_STATE_CONFIGURED);
199225cf1a30Sjl 		}
199325cf1a30Sjl 
199425cf1a30Sjl 		if (s_mp->sbm_del_mlist != s_mp->sbm_mlist)
199525cf1a30Sjl 			memlist_delete(s_mp->sbm_del_mlist);
199625cf1a30Sjl 		s_mp->sbm_del_mlist = NULL;
199725cf1a30Sjl 
199825cf1a30Sjl 		if (s_mp->sbm_mlist != NULL) {
199925cf1a30Sjl 			memlist_delete(s_mp->sbm_mlist);
200025cf1a30Sjl 			s_mp->sbm_mlist = NULL;
200125cf1a30Sjl 		}
200225cf1a30Sjl 
200325cf1a30Sjl 		s_mp->sbm_peer = NULL;
200425cf1a30Sjl 		s_mp->sbm_flags = 0;
200525cf1a30Sjl 		s_mp->sbm_cm.sbdev_busy = 0;
200625cf1a30Sjl 		dr_init_mem_unit_data(s_mp);
200725cf1a30Sjl 
200825cf1a30Sjl 		return (0);
200925cf1a30Sjl 
201025cf1a30Sjl 	default:
201125cf1a30Sjl 		PR_MEM("%s: WARNING unexpected state (%d) for %s\n",
20123103d4ceSjesusm 		    f, (int)state, s_mp->sbm_cm.sbdev_path);
201325cf1a30Sjl 
201425cf1a30Sjl 		return (-1);
201525cf1a30Sjl 	}
201625cf1a30Sjl 	/*NOTREACHED*/
201725cf1a30Sjl }
201825cf1a30Sjl 
201925cf1a30Sjl void
202025cf1a30Sjl dr_init_mem_unit(dr_mem_unit_t *mp)
202125cf1a30Sjl {
202225cf1a30Sjl 	dr_state_t	new_state;
202325cf1a30Sjl 
202425cf1a30Sjl 
202525cf1a30Sjl 	if (DR_DEV_IS_ATTACHED(&mp->sbm_cm)) {
202625cf1a30Sjl 		new_state = DR_STATE_CONFIGURED;
202725cf1a30Sjl 		mp->sbm_cm.sbdev_cond = SBD_COND_OK;
202825cf1a30Sjl 	} else if (DR_DEV_IS_PRESENT(&mp->sbm_cm)) {
202925cf1a30Sjl 		new_state = DR_STATE_CONNECTED;
203025cf1a30Sjl 		mp->sbm_cm.sbdev_cond = SBD_COND_OK;
203125cf1a30Sjl 	} else if (mp->sbm_cm.sbdev_id != (drmachid_t)0) {
203225cf1a30Sjl 		new_state = DR_STATE_OCCUPIED;
203325cf1a30Sjl 	} else {
203425cf1a30Sjl 		new_state = DR_STATE_EMPTY;
203525cf1a30Sjl 	}
203625cf1a30Sjl 
203725cf1a30Sjl 	if (DR_DEV_IS_PRESENT(&mp->sbm_cm))
203825cf1a30Sjl 		dr_init_mem_unit_data(mp);
203925cf1a30Sjl 
204025cf1a30Sjl 	/* delay transition until fully initialized */
204125cf1a30Sjl 	dr_device_transition(&mp->sbm_cm, new_state);
204225cf1a30Sjl }
204325cf1a30Sjl 
204425cf1a30Sjl static void
204525cf1a30Sjl dr_init_mem_unit_data(dr_mem_unit_t *mp)
204625cf1a30Sjl {
204725cf1a30Sjl 	drmachid_t	id = mp->sbm_cm.sbdev_id;
204825cf1a30Sjl 	drmach_mem_info_t	minfo;
204925cf1a30Sjl 	sbd_error_t	*err;
205025cf1a30Sjl 	static fn_t	f = "dr_init_mem_unit_data";
205125cf1a30Sjl 	update_membounds_t umb;
205225cf1a30Sjl 
205325cf1a30Sjl 	PR_MEM("%s...\n", f);
205425cf1a30Sjl 
205525cf1a30Sjl 	/* a little sanity checking */
205625cf1a30Sjl 	ASSERT(mp->sbm_peer == NULL);
205725cf1a30Sjl 	ASSERT(mp->sbm_flags == 0);
205825cf1a30Sjl 
205925cf1a30Sjl 	if (err = drmach_mem_get_info(id, &minfo)) {
206025cf1a30Sjl 		DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
206125cf1a30Sjl 		return;
206225cf1a30Sjl 	}
206325cf1a30Sjl 	mp->sbm_basepfn = _b64top(minfo.mi_basepa);
206425cf1a30Sjl 	mp->sbm_npages = _b64top(minfo.mi_size);
206525cf1a30Sjl 	mp->sbm_alignment_mask = _b64top(minfo.mi_alignment_mask);
206625cf1a30Sjl 	mp->sbm_slice_size = minfo.mi_slice_size;
206725cf1a30Sjl 
206825cf1a30Sjl 	/*
206925cf1a30Sjl 	 * Add memory to lgroup
207025cf1a30Sjl 	 */
207125cf1a30Sjl 	umb.u_board = mp->sbm_cm.sbdev_bp->b_num;
207225cf1a30Sjl 	umb.u_base = (uint64_t)mp->sbm_basepfn << MMU_PAGESHIFT;
207325cf1a30Sjl 	umb.u_len = (uint64_t)mp->sbm_npages << MMU_PAGESHIFT;
207425cf1a30Sjl 
207525cf1a30Sjl 	lgrp_plat_config(LGRP_CONFIG_MEM_ADD, (uintptr_t)&umb);
207625cf1a30Sjl 
207725cf1a30Sjl 	PR_MEM("%s: %s (basepfn = 0x%lx, npgs = %ld)\n",
20783103d4ceSjesusm 	    f, mp->sbm_cm.sbdev_path, mp->sbm_basepfn, mp->sbm_npages);
207925cf1a30Sjl }
208025cf1a30Sjl 
208125cf1a30Sjl static int
208225cf1a30Sjl dr_reserve_mem_spans(memhandle_t *mhp, struct memlist *ml)
208325cf1a30Sjl {
208425cf1a30Sjl 	int		err;
208525cf1a30Sjl 	pfn_t		base;
208625cf1a30Sjl 	pgcnt_t		npgs;
208725cf1a30Sjl 	struct memlist	*mc;
208825cf1a30Sjl 	static fn_t	f = "dr_reserve_mem_spans";
208925cf1a30Sjl 
209025cf1a30Sjl 	PR_MEM("%s...\n", f);
209125cf1a30Sjl 
209225cf1a30Sjl 	/*
209325cf1a30Sjl 	 * Walk the supplied memlist scheduling each span for removal
209425cf1a30Sjl 	 * with kphysm_del_span.  It is possible that a span may intersect
209525cf1a30Sjl 	 * an area occupied by the cage.
209625cf1a30Sjl 	 */
209725cf1a30Sjl 	for (mc = ml; mc != NULL; mc = mc->next) {
209825cf1a30Sjl 		base = _b64top(mc->address);
209925cf1a30Sjl 		npgs = _b64top(mc->size);
210025cf1a30Sjl 
210125cf1a30Sjl 		err = kphysm_del_span(*mhp, base, npgs);
210225cf1a30Sjl 		if (err != KPHYSM_OK) {
210325cf1a30Sjl 			cmn_err(CE_WARN, "%s memory reserve failed."
21043103d4ceSjesusm 			    " unexpected kphysm_del_span return value %d;"
21053103d4ceSjesusm 			    " basepfn=0x%lx npages=%ld",
21063103d4ceSjesusm 			    f, err, base, npgs);
210725cf1a30Sjl 
210825cf1a30Sjl 			return (-1);
210925cf1a30Sjl 		}
211025cf1a30Sjl 	}
211125cf1a30Sjl 
211225cf1a30Sjl 	return (0);
211325cf1a30Sjl }
211425cf1a30Sjl 
211525cf1a30Sjl #define	DR_SMT_NPREF_SETS	6
211625cf1a30Sjl #define	DR_SMT_NUNITS_PER_SET	MAX_BOARDS * MAX_MEM_UNITS_PER_BOARD
211725cf1a30Sjl 
211825cf1a30Sjl /* debug counters */
211925cf1a30Sjl int dr_smt_realigned;
212025cf1a30Sjl int dr_smt_preference[DR_SMT_NPREF_SETS];
212125cf1a30Sjl 
212225cf1a30Sjl #ifdef DEBUG
212325cf1a30Sjl uint_t dr_ignore_board; /* if bit[bnum-1] set, board won't be candidate */
212425cf1a30Sjl #endif
212525cf1a30Sjl 
212625cf1a30Sjl /*
212725cf1a30Sjl  * Find and reserve a copy/rename target board suitable for the
212825cf1a30Sjl  * given source board.
212925cf1a30Sjl  * All boards in the system are examined and categorized in relation to
213025cf1a30Sjl  * their memory size versus the source board's memory size.  Order of
213125cf1a30Sjl  * preference is:
213225cf1a30Sjl  *	1st copy all source, source/target same size
213325cf1a30Sjl  *	2nd copy all source, larger target
213425cf1a30Sjl  * 	3rd copy nonrelocatable source span
213525cf1a30Sjl  */
213625cf1a30Sjl static int
213725cf1a30Sjl dr_select_mem_target(dr_handle_t *hp,
213825cf1a30Sjl 	dr_mem_unit_t *s_mp, struct memlist *s_ml)
213925cf1a30Sjl {
214025cf1a30Sjl 	dr_target_pref_t preference; /* lower value is higher preference */
214125cf1a30Sjl 	int		idx;
214225cf1a30Sjl 	dr_mem_unit_t	**sets;
214325cf1a30Sjl 
214425cf1a30Sjl 	int		t_bd;
214525cf1a30Sjl 	int		t_unit;
214625cf1a30Sjl 	int		rv;
214725cf1a30Sjl 	dr_board_t	*s_bp, *t_bp;
214825cf1a30Sjl 	dr_mem_unit_t	*t_mp, *c_mp;
214925cf1a30Sjl 	struct memlist	*d_ml, *t_ml, *ml, *b_ml, *x_ml = NULL;
215025cf1a30Sjl 	memquery_t	s_mq = {0};
215125cf1a30Sjl 	static fn_t	f = "dr_select_mem_target";
215225cf1a30Sjl 
215325cf1a30Sjl 	PR_MEM("%s...\n", f);
215425cf1a30Sjl 
215525cf1a30Sjl 	ASSERT(s_ml != NULL);
215625cf1a30Sjl 
215725cf1a30Sjl 	sets = GETSTRUCT(dr_mem_unit_t *, DR_SMT_NUNITS_PER_SET *
215825cf1a30Sjl 	    DR_SMT_NPREF_SETS);
215925cf1a30Sjl 
216025cf1a30Sjl 	s_bp = hp->h_bd;
216125cf1a30Sjl 	/* calculate the offset into the slice of the last source board pfn */
216225cf1a30Sjl 	ASSERT(s_mp->sbm_npages != 0);
216325cf1a30Sjl 
216425cf1a30Sjl 	/*
216525cf1a30Sjl 	 * Find non-relocatable span on source board.
216625cf1a30Sjl 	 */
216725cf1a30Sjl 	rv = kphysm_del_span_query(s_mp->sbm_basepfn, s_mp->sbm_npages, &s_mq);
216825cf1a30Sjl 	if (rv != KPHYSM_OK) {
216925cf1a30Sjl 		PR_MEM("%s: %s: unexpected kphysm_del_span_query"
217025cf1a30Sjl 		    " return value %d; basepfn 0x%lx, npages %ld\n",
217125cf1a30Sjl 		    f, s_mp->sbm_cm.sbdev_path, rv, s_mp->sbm_basepfn,
217225cf1a30Sjl 		    s_mp->sbm_npages);
217325cf1a30Sjl 		return (-1);
217425cf1a30Sjl 	}
217525cf1a30Sjl 
217625cf1a30Sjl 	ASSERT(s_mq.phys_pages != 0);
217725cf1a30Sjl 	ASSERT(s_mq.nonrelocatable != 0);
217825cf1a30Sjl 
217925cf1a30Sjl 	PR_MEM("%s: %s: nonrelocatable span (0x%lx..0x%lx)\n", f,
218025cf1a30Sjl 	    s_mp->sbm_cm.sbdev_path, s_mq.first_nonrelocatable,
218125cf1a30Sjl 	    s_mq.last_nonrelocatable);
218225cf1a30Sjl 
218325cf1a30Sjl 	/* break down s_ml if it contains dynamic segments */
218425cf1a30Sjl 	b_ml = memlist_dup(s_ml);
218525cf1a30Sjl 
218625cf1a30Sjl 	for (ml = s_mp->sbm_dyn_segs; ml; ml = ml->next) {
218725cf1a30Sjl 		b_ml = memlist_del_span(b_ml, ml->address, ml->size);
218825cf1a30Sjl 		b_ml = memlist_cat_span(b_ml, ml->address, ml->size);
218925cf1a30Sjl 	}
219025cf1a30Sjl 
219125cf1a30Sjl 
219225cf1a30Sjl 	/*
219325cf1a30Sjl 	 * Make one pass through all memory units on all boards
219425cf1a30Sjl 	 * and categorize them with respect to the source board.
219525cf1a30Sjl 	 */
219625cf1a30Sjl 	for (t_bd = 0; t_bd < MAX_BOARDS; t_bd++) {
219725cf1a30Sjl 		/*
219825cf1a30Sjl 		 * The board structs are a contiguous array
219925cf1a30Sjl 		 * so we take advantage of that to find the
220025cf1a30Sjl 		 * correct board struct pointer for a given
220125cf1a30Sjl 		 * board number.
220225cf1a30Sjl 		 */
220325cf1a30Sjl 		t_bp = dr_lookup_board(t_bd);
220425cf1a30Sjl 
220525cf1a30Sjl 		/* source board can not be its own target */
220625cf1a30Sjl 		if (s_bp->b_num == t_bp->b_num)
220725cf1a30Sjl 			continue;
220825cf1a30Sjl 
220925cf1a30Sjl 		for (t_unit = 0; t_unit < MAX_MEM_UNITS_PER_BOARD; t_unit++) {
221025cf1a30Sjl 
221125cf1a30Sjl 			t_mp = dr_get_mem_unit(t_bp, t_unit);
221225cf1a30Sjl 
221325cf1a30Sjl 			/* this memory node must be attached */
221425cf1a30Sjl 			if (!DR_DEV_IS_ATTACHED(&t_mp->sbm_cm))
221525cf1a30Sjl 				continue;
221625cf1a30Sjl 
221725cf1a30Sjl 			/* source unit can not be its own target */
221825cf1a30Sjl 			if (s_mp == t_mp) {
221925cf1a30Sjl 				/* catch this is debug kernels */
222025cf1a30Sjl 				ASSERT(0);
222125cf1a30Sjl 				continue;
222225cf1a30Sjl 			}
222325cf1a30Sjl 
222425cf1a30Sjl 			/*
222525cf1a30Sjl 			 * this memory node must not already be reserved
222625cf1a30Sjl 			 * by some other memory delete operation.
222725cf1a30Sjl 			 */
222825cf1a30Sjl 			if (t_mp->sbm_flags & DR_MFLAG_RESERVED)
222925cf1a30Sjl 				continue;
223025cf1a30Sjl 
223125cf1a30Sjl 			/* get target board memlist */
223225cf1a30Sjl 			t_ml = dr_get_memlist(t_mp);
223325cf1a30Sjl 			if (t_ml == NULL) {
223425cf1a30Sjl 				cmn_err(CE_WARN, "%s: no memlist for"
223525cf1a30Sjl 				    " mem-unit %d, board %d", f,
223625cf1a30Sjl 				    t_mp->sbm_cm.sbdev_bp->b_num,
223725cf1a30Sjl 				    t_mp->sbm_cm.sbdev_unum);
223825cf1a30Sjl 				continue;
223925cf1a30Sjl 			}
224025cf1a30Sjl 
224125cf1a30Sjl 			preference = dr_get_target_preference(hp, t_mp, s_mp,
224225cf1a30Sjl 			    t_ml, s_ml, b_ml);
224325cf1a30Sjl 
22443103d4ceSjesusm 			memlist_delete(t_ml);
22453103d4ceSjesusm 
224625cf1a30Sjl 			if (preference == DR_TP_INVALID)
224725cf1a30Sjl 				continue;
224825cf1a30Sjl 
224925cf1a30Sjl 			dr_smt_preference[preference]++;
225025cf1a30Sjl 
225125cf1a30Sjl 			/* calculate index to start of preference set */
225225cf1a30Sjl 			idx  = DR_SMT_NUNITS_PER_SET * preference;
225325cf1a30Sjl 			/* calculate offset to respective element */
225425cf1a30Sjl 			idx += t_bd * MAX_MEM_UNITS_PER_BOARD + t_unit;
225525cf1a30Sjl 
225625cf1a30Sjl 			ASSERT(idx < DR_SMT_NUNITS_PER_SET * DR_SMT_NPREF_SETS);
225725cf1a30Sjl 			sets[idx] = t_mp;
225825cf1a30Sjl 		}
225925cf1a30Sjl 	}
226025cf1a30Sjl 
226125cf1a30Sjl 	if (b_ml != NULL)
226225cf1a30Sjl 		memlist_delete(b_ml);
226325cf1a30Sjl 
226425cf1a30Sjl 	/*
226525cf1a30Sjl 	 * NOTE: this would be a good place to sort each candidate
226625cf1a30Sjl 	 * set in to some desired order, e.g. memory size in ascending
226725cf1a30Sjl 	 * order.  Without an additional sorting step here, the order
226825cf1a30Sjl 	 * within a set is ascending board number order.
226925cf1a30Sjl 	 */
227025cf1a30Sjl 
227125cf1a30Sjl 	c_mp = NULL;
227225cf1a30Sjl 	x_ml = NULL;
227325cf1a30Sjl 	t_ml = NULL;
227425cf1a30Sjl 	for (idx = 0; idx < DR_SMT_NUNITS_PER_SET * DR_SMT_NPREF_SETS; idx++) {
227525cf1a30Sjl 		memquery_t mq;
227625cf1a30Sjl 
227725cf1a30Sjl 		preference = (dr_target_pref_t)(idx / DR_SMT_NUNITS_PER_SET);
227825cf1a30Sjl 
227925cf1a30Sjl 		ASSERT(preference != DR_TP_INVALID);
228025cf1a30Sjl 
228125cf1a30Sjl 		/* cleanup t_ml after previous pass */
228225cf1a30Sjl 		if (t_ml != NULL) {
228325cf1a30Sjl 			memlist_delete(t_ml);
228425cf1a30Sjl 			t_ml = NULL;
228525cf1a30Sjl 		}
228625cf1a30Sjl 
228725cf1a30Sjl 		/* get candidate target board mem unit */
228825cf1a30Sjl 		t_mp = sets[idx];
228925cf1a30Sjl 		if (t_mp == NULL)
229025cf1a30Sjl 			continue;
229125cf1a30Sjl 
229225cf1a30Sjl 		/* get target board memlist */
229325cf1a30Sjl 		t_ml = dr_get_memlist(t_mp);
229425cf1a30Sjl 		if (t_ml == NULL) {
229525cf1a30Sjl 			cmn_err(CE_WARN, "%s: no memlist for"
22963103d4ceSjesusm 			    " mem-unit %d, board %d",
22973103d4ceSjesusm 			    f,
22983103d4ceSjesusm 			    t_mp->sbm_cm.sbdev_bp->b_num,
22993103d4ceSjesusm 			    t_mp->sbm_cm.sbdev_unum);
230025cf1a30Sjl 
230125cf1a30Sjl 			continue;
230225cf1a30Sjl 		}
230325cf1a30Sjl 
230425cf1a30Sjl 		PR_MEM("%s: checking for no-reloc in %s, "
23053103d4ceSjesusm 		    " basepfn=0x%lx, npages=%ld\n",
23063103d4ceSjesusm 		    f,
23073103d4ceSjesusm 		    t_mp->sbm_cm.sbdev_path,
23083103d4ceSjesusm 		    t_mp->sbm_basepfn,
23093103d4ceSjesusm 		    t_mp->sbm_npages);
231025cf1a30Sjl 
231125cf1a30Sjl 		rv = dr_del_mlist_query(t_ml, &mq);
231225cf1a30Sjl 		if (rv != KPHYSM_OK) {
231325cf1a30Sjl 			PR_MEM("%s: kphysm_del_span_query:"
23143103d4ceSjesusm 			    " unexpected return value %d\n", f, rv);
231525cf1a30Sjl 
231625cf1a30Sjl 			continue;
231725cf1a30Sjl 		}
231825cf1a30Sjl 
231925cf1a30Sjl 		if (mq.nonrelocatable != 0) {
232025cf1a30Sjl 			PR_MEM("%s: candidate %s has"
23213103d4ceSjesusm 			    " nonrelocatable span [0x%lx..0x%lx]\n",
23223103d4ceSjesusm 			    f,
23233103d4ceSjesusm 			    t_mp->sbm_cm.sbdev_path,
23243103d4ceSjesusm 			    mq.first_nonrelocatable,
23253103d4ceSjesusm 			    mq.last_nonrelocatable);
232625cf1a30Sjl 
232725cf1a30Sjl 			continue;
232825cf1a30Sjl 		}
232925cf1a30Sjl 
233025cf1a30Sjl #ifdef DEBUG
233125cf1a30Sjl 		/*
233225cf1a30Sjl 		 * This is a debug tool for excluding certain boards
233325cf1a30Sjl 		 * from being selected as a target board candidate.
233425cf1a30Sjl 		 * dr_ignore_board is only tested by this driver.
233525cf1a30Sjl 		 * It must be set with adb, obp, /etc/system or your
233625cf1a30Sjl 		 * favorite debugger.
233725cf1a30Sjl 		 */
233825cf1a30Sjl 		if (dr_ignore_board &
23393103d4ceSjesusm 		    (1 << (t_mp->sbm_cm.sbdev_bp->b_num - 1))) {
234025cf1a30Sjl 			PR_MEM("%s: dr_ignore_board flag set,"
23413103d4ceSjesusm 			    " ignoring %s as candidate\n",
23423103d4ceSjesusm 			    f, t_mp->sbm_cm.sbdev_path);
234325cf1a30Sjl 			continue;
234425cf1a30Sjl 		}
234525cf1a30Sjl #endif
234625cf1a30Sjl 
234725cf1a30Sjl 		/*
234825cf1a30Sjl 		 * Reserve excess source board memory, if any.
234925cf1a30Sjl 		 *
235025cf1a30Sjl 		 * Only the nonrelocatable source span will be copied
235125cf1a30Sjl 		 * so schedule the rest of the source mem to be deleted.
235225cf1a30Sjl 		 */
235325cf1a30Sjl 		switch (preference) {
235425cf1a30Sjl 		case DR_TP_NONRELOC:
235525cf1a30Sjl 			/*
235625cf1a30Sjl 			 * Get source copy memlist and use it to construct
235725cf1a30Sjl 			 * delete memlist.
235825cf1a30Sjl 			 */
235925cf1a30Sjl 			d_ml = memlist_dup(s_ml);
236025cf1a30Sjl 			x_ml = dr_get_copy_mlist(s_ml, t_ml, s_mp, t_mp);
236125cf1a30Sjl 
236225cf1a30Sjl 			/* XXX */
236325cf1a30Sjl 			ASSERT(d_ml != NULL);
236425cf1a30Sjl 			ASSERT(x_ml != NULL);
236525cf1a30Sjl 
236625cf1a30Sjl 			for (ml = x_ml; ml != NULL; ml = ml->next) {
236725cf1a30Sjl 				d_ml = memlist_del_span(d_ml, ml->address,
236825cf1a30Sjl 				    ml->size);
236925cf1a30Sjl 			}
237025cf1a30Sjl 
237125cf1a30Sjl 			PR_MEM("%s: %s: reserving src brd memlist:\n", f,
237225cf1a30Sjl 			    s_mp->sbm_cm.sbdev_path);
237325cf1a30Sjl 			PR_MEMLIST_DUMP(d_ml);
237425cf1a30Sjl 
237525cf1a30Sjl 			/* reserve excess spans */
237625cf1a30Sjl 			if (dr_reserve_mem_spans(&s_mp->sbm_memhandle,
237725cf1a30Sjl 			    d_ml) != 0) {
237825cf1a30Sjl 				/* likely more non-reloc pages appeared */
237925cf1a30Sjl 				/* TODO: restart from top? */
238025cf1a30Sjl 				continue;
238125cf1a30Sjl 			}
238225cf1a30Sjl 			break;
238325cf1a30Sjl 		default:
238425cf1a30Sjl 			d_ml = NULL;
238525cf1a30Sjl 			break;
238625cf1a30Sjl 		}
238725cf1a30Sjl 
238825cf1a30Sjl 		s_mp->sbm_flags |= DR_MFLAG_RESERVED;
238925cf1a30Sjl 
239025cf1a30Sjl 		/*
239125cf1a30Sjl 		 * reserve all memory on target board.
239225cf1a30Sjl 		 * NOTE: source board's memhandle is used.
239325cf1a30Sjl 		 *
239425cf1a30Sjl 		 * If this succeeds (eq 0), then target selection is
239525cf1a30Sjl 		 * complete and all unwanted memory spans, both source and
239625cf1a30Sjl 		 * target, have been reserved.  Loop is terminated.
239725cf1a30Sjl 		 */
239825cf1a30Sjl 		if (dr_reserve_mem_spans(&s_mp->sbm_memhandle, t_ml) == 0) {
239925cf1a30Sjl 			PR_MEM("%s: %s: target board memory reserved\n",
24003103d4ceSjesusm 			    f, t_mp->sbm_cm.sbdev_path);
240125cf1a30Sjl 
240225cf1a30Sjl 			/* a candidate target board is now reserved */
240325cf1a30Sjl 			t_mp->sbm_flags |= DR_MFLAG_RESERVED;
240425cf1a30Sjl 			c_mp = t_mp;
240525cf1a30Sjl 
240625cf1a30Sjl 			/* *** EXITING LOOP *** */
240725cf1a30Sjl 			break;
240825cf1a30Sjl 		}
240925cf1a30Sjl 
241025cf1a30Sjl 		/* did not successfully reserve the target board. */
241125cf1a30Sjl 		PR_MEM("%s: could not reserve target %s\n",
24123103d4ceSjesusm 		    f, t_mp->sbm_cm.sbdev_path);
241325cf1a30Sjl 
241425cf1a30Sjl 		/*
241525cf1a30Sjl 		 * NOTE: an undo of the dr_reserve_mem_span work
241625cf1a30Sjl 		 * will happen automatically when the memhandle
241725cf1a30Sjl 		 * (s_mp->sbm_memhandle) is kphysm_del_release'd.
241825cf1a30Sjl 		 */
241925cf1a30Sjl 
242025cf1a30Sjl 		s_mp->sbm_flags &= ~DR_MFLAG_RESERVED;
242125cf1a30Sjl 	}
242225cf1a30Sjl 
242325cf1a30Sjl 	/* clean up after memlist editing logic */
242425cf1a30Sjl 	if (x_ml != NULL)
242525cf1a30Sjl 		memlist_delete(x_ml);
242625cf1a30Sjl 
242725cf1a30Sjl 	FREESTRUCT(sets, dr_mem_unit_t *, DR_SMT_NUNITS_PER_SET *
242825cf1a30Sjl 	    DR_SMT_NPREF_SETS);
242925cf1a30Sjl 
243025cf1a30Sjl 	/*
243125cf1a30Sjl 	 * c_mp will be NULL when the entire sets[] array
243225cf1a30Sjl 	 * has been searched without reserving a target board.
243325cf1a30Sjl 	 */
243425cf1a30Sjl 	if (c_mp == NULL) {
243525cf1a30Sjl 		PR_MEM("%s: %s: target selection failed.\n",
24363103d4ceSjesusm 		    f, s_mp->sbm_cm.sbdev_path);
243725cf1a30Sjl 
243825cf1a30Sjl 		if (t_ml != NULL)
243925cf1a30Sjl 			memlist_delete(t_ml);
244025cf1a30Sjl 
244125cf1a30Sjl 		return (-1);
244225cf1a30Sjl 	}
244325cf1a30Sjl 
244425cf1a30Sjl 	PR_MEM("%s: found target %s for source %s\n",
24453103d4ceSjesusm 	    f,
24463103d4ceSjesusm 	    c_mp->sbm_cm.sbdev_path,
24473103d4ceSjesusm 	    s_mp->sbm_cm.sbdev_path);
244825cf1a30Sjl 
244925cf1a30Sjl 	s_mp->sbm_peer = c_mp;
245025cf1a30Sjl 	s_mp->sbm_flags |= DR_MFLAG_SOURCE;
245125cf1a30Sjl 	s_mp->sbm_del_mlist = d_ml;	/* spans to be deleted, if any */
245225cf1a30Sjl 	s_mp->sbm_mlist = s_ml;
245325cf1a30Sjl 	s_mp->sbm_cm.sbdev_busy = 1;
245425cf1a30Sjl 
245525cf1a30Sjl 	c_mp->sbm_peer = s_mp;
245625cf1a30Sjl 	c_mp->sbm_flags |= DR_MFLAG_TARGET;
245725cf1a30Sjl 	c_mp->sbm_del_mlist = t_ml;	/* spans to be deleted */
245825cf1a30Sjl 	c_mp->sbm_mlist = t_ml;
245925cf1a30Sjl 	c_mp->sbm_cm.sbdev_busy = 1;
246025cf1a30Sjl 
246125cf1a30Sjl 	return (0);
246225cf1a30Sjl }
246325cf1a30Sjl 
246425cf1a30Sjl /*
246525cf1a30Sjl  * Returns target preference rank:
246625cf1a30Sjl  *     -1 not a valid copy-rename target board
246725cf1a30Sjl  *	0 copy all source, source/target same size
246825cf1a30Sjl  *	1 copy all source, larger target
246925cf1a30Sjl  * 	2 copy nonrelocatable source span
247025cf1a30Sjl  */
247125cf1a30Sjl static dr_target_pref_t
247225cf1a30Sjl dr_get_target_preference(dr_handle_t *hp,
247325cf1a30Sjl     dr_mem_unit_t *t_mp, dr_mem_unit_t *s_mp,
247425cf1a30Sjl     struct memlist *t_ml, struct memlist *s_ml,
247525cf1a30Sjl     struct memlist *b_ml)
247625cf1a30Sjl {
247725cf1a30Sjl 	dr_target_pref_t preference;
247825cf1a30Sjl 	struct memlist *s_nonreloc_ml = NULL;
247925cf1a30Sjl 	drmachid_t t_id;
248025cf1a30Sjl 	static fn_t	f = "dr_get_target_preference";
248125cf1a30Sjl 
248225cf1a30Sjl 	t_id = t_mp->sbm_cm.sbdev_bp->b_id;
248325cf1a30Sjl 
248425cf1a30Sjl 	/*
248525cf1a30Sjl 	 * Can the entire source board be copied?
248625cf1a30Sjl 	 */
248725cf1a30Sjl 	if (dr_memlist_canfit(s_ml, t_ml, s_mp, t_mp)) {
248825cf1a30Sjl 		if (s_mp->sbm_npages == t_mp->sbm_npages)
248925cf1a30Sjl 			preference = DR_TP_SAME;	/* same size */
249025cf1a30Sjl 		else
249125cf1a30Sjl 			preference = DR_TP_LARGE;	/* larger target */
249225cf1a30Sjl 	} else {
249325cf1a30Sjl 		/*
249425cf1a30Sjl 		 * Entire source won't fit so try non-relocatable memory only
249525cf1a30Sjl 		 * (target aligned).
249625cf1a30Sjl 		 */
249725cf1a30Sjl 		s_nonreloc_ml = dr_get_nonreloc_mlist(b_ml, s_mp);
249825cf1a30Sjl 		if (s_nonreloc_ml == NULL) {
249925cf1a30Sjl 			PR_MEM("%s: dr_get_nonreloc_mlist failed\n", f);
250025cf1a30Sjl 			preference = DR_TP_INVALID;
250125cf1a30Sjl 		}
250225cf1a30Sjl 		if (dr_memlist_canfit(s_nonreloc_ml, t_ml, s_mp, t_mp))
250325cf1a30Sjl 			preference = DR_TP_NONRELOC;
250425cf1a30Sjl 		else
250525cf1a30Sjl 			preference = DR_TP_INVALID;
250625cf1a30Sjl 	}
250725cf1a30Sjl 
250825cf1a30Sjl 	if (s_nonreloc_ml != NULL)
250925cf1a30Sjl 		memlist_delete(s_nonreloc_ml);
251025cf1a30Sjl 
251125cf1a30Sjl 	/*
251225cf1a30Sjl 	 * Force floating board preference lower than all other boards
251325cf1a30Sjl 	 * if the force flag is present; otherwise disallow the board.
251425cf1a30Sjl 	 */
251525cf1a30Sjl 	if ((preference != DR_TP_INVALID) && drmach_board_is_floating(t_id)) {
251625cf1a30Sjl 		if (dr_cmd_flags(hp) & SBD_FLAG_FORCE)
251725cf1a30Sjl 			preference += DR_TP_FLOATING;
251825cf1a30Sjl 		else
251925cf1a30Sjl 			preference = DR_TP_INVALID;
252025cf1a30Sjl 	}
252125cf1a30Sjl 
252225cf1a30Sjl 	PR_MEM("%s: %s preference=%d\n", f, t_mp->sbm_cm.sbdev_path,
252325cf1a30Sjl 	    preference);
252425cf1a30Sjl 
252525cf1a30Sjl 	return (preference);
252625cf1a30Sjl }
252725cf1a30Sjl 
252825cf1a30Sjl /*
252925cf1a30Sjl  * Create a memlist representing the source memory that will be copied to
253025cf1a30Sjl  * the target board.  The memory to be copied is the maximum amount that
253125cf1a30Sjl  * will fit on the target board.
253225cf1a30Sjl  */
253325cf1a30Sjl static struct memlist *
253425cf1a30Sjl dr_get_copy_mlist(struct memlist *s_mlist, struct memlist *t_mlist,
253525cf1a30Sjl     dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp)
253625cf1a30Sjl {
253725cf1a30Sjl 	struct memlist	*t_ml, *s_copy_ml, *s_del_ml, *ml, *x_ml;
253825cf1a30Sjl 	uint64_t	s_slice_mask, s_slice_base;
253925cf1a30Sjl 	uint64_t	t_slice_mask, t_slice_base;
254025cf1a30Sjl 	static fn_t	f = "dr_get_copy_mlist";
254125cf1a30Sjl 
254225cf1a30Sjl 	ASSERT(s_mlist != NULL);
254325cf1a30Sjl 	ASSERT(t_mlist != NULL);
254425cf1a30Sjl 	ASSERT(t_mp->sbm_slice_size == s_mp->sbm_slice_size);
254525cf1a30Sjl 
254625cf1a30Sjl 	s_slice_mask = s_mp->sbm_slice_size - 1;
254725cf1a30Sjl 	s_slice_base = s_mlist->address & ~s_slice_mask;
254825cf1a30Sjl 
254925cf1a30Sjl 	t_slice_mask = t_mp->sbm_slice_size - 1;
255025cf1a30Sjl 	t_slice_base = t_mlist->address & ~t_slice_mask;
255125cf1a30Sjl 
255225cf1a30Sjl 	t_ml = memlist_dup(t_mlist);
255325cf1a30Sjl 	s_del_ml = memlist_dup(s_mlist);
255425cf1a30Sjl 	s_copy_ml = memlist_dup(s_mlist);
255525cf1a30Sjl 
255625cf1a30Sjl 	/* XXX */
255725cf1a30Sjl 	ASSERT(t_ml != NULL);
255825cf1a30Sjl 	ASSERT(s_del_ml != NULL);
255925cf1a30Sjl 	ASSERT(s_copy_ml != NULL);
256025cf1a30Sjl 
256125cf1a30Sjl 	/*
256225cf1a30Sjl 	 * To construct the source copy memlist:
256325cf1a30Sjl 	 *
256425cf1a30Sjl 	 * The target memlist is converted to the post-rename
256525cf1a30Sjl 	 * source addresses.  This is the physical address range
256625cf1a30Sjl 	 * the target will have after the copy-rename.  Overlaying
256725cf1a30Sjl 	 * and deleting this from the current source memlist will
256825cf1a30Sjl 	 * give the source delete memlist.  The copy memlist is
256925cf1a30Sjl 	 * the reciprocal of the source delete memlist.
257025cf1a30Sjl 	 */
257125cf1a30Sjl 	for (ml = t_ml; ml != NULL; ml = ml->next) {
257225cf1a30Sjl 		/*
257325cf1a30Sjl 		 * Normalize relative to target slice base PA
257425cf1a30Sjl 		 * in order to preseve slice offsets.
257525cf1a30Sjl 		 */
257625cf1a30Sjl 		ml->address -= t_slice_base;
257725cf1a30Sjl 		/*
257825cf1a30Sjl 		 * Convert to source slice PA address.
257925cf1a30Sjl 		 */
258025cf1a30Sjl 		ml->address += s_slice_base;
258125cf1a30Sjl 	}
258225cf1a30Sjl 
258325cf1a30Sjl 	for (ml = t_ml; ml != NULL; ml = ml->next) {
258425cf1a30Sjl 		s_del_ml = memlist_del_span(s_del_ml, ml->address, ml->size);
258525cf1a30Sjl 	}
258625cf1a30Sjl 
258725cf1a30Sjl 	/*
258825cf1a30Sjl 	 * Expand the delete mlist to fully include any dynamic segments
258925cf1a30Sjl 	 * it intersects with.
259025cf1a30Sjl 	 */
259125cf1a30Sjl 	for (x_ml = NULL, ml = s_del_ml; ml != NULL; ml = ml->next) {
259225cf1a30Sjl 		uint64_t del_base = ml->address;
259325cf1a30Sjl 		uint64_t del_end = ml->address + ml->size;
259425cf1a30Sjl 		struct memlist *dyn;
259525cf1a30Sjl 
259625cf1a30Sjl 		for (dyn = s_mp->sbm_dyn_segs; dyn != NULL; dyn = dyn->next) {
259725cf1a30Sjl 			uint64_t dyn_base = dyn->address;
259825cf1a30Sjl 			uint64_t dyn_end = dyn->address + dyn->size;
259925cf1a30Sjl 
260025cf1a30Sjl 			if (del_base > dyn_base && del_base < dyn_end)
260125cf1a30Sjl 				del_base = dyn_base;
260225cf1a30Sjl 
260325cf1a30Sjl 			if (del_end > dyn_base && del_end < dyn_end)
260425cf1a30Sjl 				del_end = dyn_end;
260525cf1a30Sjl 		}
260625cf1a30Sjl 
260725cf1a30Sjl 		x_ml = memlist_cat_span(x_ml, del_base, del_end - del_base);
260825cf1a30Sjl 	}
260925cf1a30Sjl 
261025cf1a30Sjl 	memlist_delete(s_del_ml);
261125cf1a30Sjl 	s_del_ml = x_ml;
261225cf1a30Sjl 
261325cf1a30Sjl 	for (ml = s_del_ml; ml != NULL; ml = ml->next) {
261425cf1a30Sjl 		s_copy_ml = memlist_del_span(s_copy_ml, ml->address, ml->size);
261525cf1a30Sjl 	}
261625cf1a30Sjl 
261725cf1a30Sjl 	PR_MEM("%s: source delete mlist\n", f);
261825cf1a30Sjl 	PR_MEMLIST_DUMP(s_del_ml);
261925cf1a30Sjl 
262025cf1a30Sjl 	PR_MEM("%s: source copy mlist\n", f);
262125cf1a30Sjl 	PR_MEMLIST_DUMP(s_copy_ml);
262225cf1a30Sjl 
262325cf1a30Sjl 	memlist_delete(t_ml);
262425cf1a30Sjl 	memlist_delete(s_del_ml);
262525cf1a30Sjl 
262625cf1a30Sjl 	return (s_copy_ml);
262725cf1a30Sjl }
262825cf1a30Sjl 
262925cf1a30Sjl /*
263025cf1a30Sjl  * Scan the non-relocatable spans on the source memory
263125cf1a30Sjl  * and construct a minimum mlist that includes all non-reloc
263225cf1a30Sjl  * memory subject to target alignment, and dynamic segment
263325cf1a30Sjl  * constraints where only whole dynamic segments may be deleted.
263425cf1a30Sjl  */
263525cf1a30Sjl static struct memlist *
263625cf1a30Sjl dr_get_nonreloc_mlist(struct memlist *s_ml, dr_mem_unit_t *s_mp)
263725cf1a30Sjl {
263825cf1a30Sjl 	struct memlist	*x_ml = NULL;
263925cf1a30Sjl 	struct memlist	*ml;
264025cf1a30Sjl 	static fn_t	f = "dr_get_nonreloc_mlist";
264125cf1a30Sjl 
264225cf1a30Sjl 	PR_MEM("%s: checking for split of dyn seg list:\n", f);
264325cf1a30Sjl 	PR_MEMLIST_DUMP(s_mp->sbm_dyn_segs);
264425cf1a30Sjl 
264525cf1a30Sjl 	for (ml = s_ml; ml; ml = ml->next) {
264625cf1a30Sjl 		int rv;
264725cf1a30Sjl 		uint64_t nr_base, nr_end;
264825cf1a30Sjl 		memquery_t mq;
264925cf1a30Sjl 		struct memlist *dyn;
265025cf1a30Sjl 
265125cf1a30Sjl 		rv = kphysm_del_span_query(
26523103d4ceSjesusm 		    _b64top(ml->address), _b64top(ml->size), &mq);
265325cf1a30Sjl 		if (rv) {
265425cf1a30Sjl 			memlist_delete(x_ml);
265525cf1a30Sjl 			return (NULL);
265625cf1a30Sjl 		}
265725cf1a30Sjl 
265825cf1a30Sjl 		if (mq.nonrelocatable == 0)
265925cf1a30Sjl 			continue;
266025cf1a30Sjl 
266125cf1a30Sjl 		PR_MEM("%s: non-reloc span: 0x%lx, 0x%lx (%lx, %lx)\n", f,
26623103d4ceSjesusm 		    _ptob64(mq.first_nonrelocatable),
26633103d4ceSjesusm 		    _ptob64(mq.last_nonrelocatable),
26643103d4ceSjesusm 		    mq.first_nonrelocatable,
26653103d4ceSjesusm 		    mq.last_nonrelocatable);
266625cf1a30Sjl 
266725cf1a30Sjl 		/*
266825cf1a30Sjl 		 * Align the span at both ends to allow for possible
266925cf1a30Sjl 		 * cage expansion.
267025cf1a30Sjl 		 */
267125cf1a30Sjl 		nr_base = _ptob64(mq.first_nonrelocatable);
267225cf1a30Sjl 		nr_end = _ptob64(mq.last_nonrelocatable + 1);
267325cf1a30Sjl 
267425cf1a30Sjl 		PR_MEM("%s: adjusted non-reloc span: 0x%lx, 0x%lx\n",
26753103d4ceSjesusm 		    f, nr_base, nr_end);
267625cf1a30Sjl 
267725cf1a30Sjl 		/*
267825cf1a30Sjl 		 * Expand the non-reloc span to fully include any
267925cf1a30Sjl 		 * dynamic segments it intersects with.
268025cf1a30Sjl 		 */
268125cf1a30Sjl 		for (dyn = s_mp->sbm_dyn_segs; dyn != NULL; dyn = dyn->next) {
268225cf1a30Sjl 			uint64_t dyn_base = dyn->address;
268325cf1a30Sjl 			uint64_t dyn_end = dyn->address + dyn->size;
268425cf1a30Sjl 
268525cf1a30Sjl 			if (nr_base > dyn_base && nr_base < dyn_end)
268625cf1a30Sjl 				nr_base = dyn_base;
268725cf1a30Sjl 
268825cf1a30Sjl 			if (nr_end > dyn_base && nr_end < dyn_end)
268925cf1a30Sjl 				nr_end = dyn_end;
269025cf1a30Sjl 		}
269125cf1a30Sjl 
269225cf1a30Sjl 		x_ml = memlist_cat_span(x_ml, nr_base, nr_end - nr_base);
269325cf1a30Sjl 	}
269425cf1a30Sjl 
269525cf1a30Sjl 	if (x_ml == NULL) {
269625cf1a30Sjl 		PR_MEM("%s: source didn't have any non-reloc pages!\n", f);
269725cf1a30Sjl 		return (NULL);
269825cf1a30Sjl 	}
269925cf1a30Sjl 
270025cf1a30Sjl 	PR_MEM("%s: %s: edited source memlist:\n", f, s_mp->sbm_cm.sbdev_path);
270125cf1a30Sjl 	PR_MEMLIST_DUMP(x_ml);
270225cf1a30Sjl 
270325cf1a30Sjl 	return (x_ml);
270425cf1a30Sjl }
270525cf1a30Sjl 
270625cf1a30Sjl /*
270725cf1a30Sjl  * Check if source memlist can fit in target memlist while maintaining
270825cf1a30Sjl  * relative offsets within board.
270925cf1a30Sjl  */
271025cf1a30Sjl static int
271125cf1a30Sjl dr_memlist_canfit(struct memlist *s_mlist, struct memlist *t_mlist,
271225cf1a30Sjl     dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp)
271325cf1a30Sjl {
271425cf1a30Sjl 	int		canfit = 0;
271525cf1a30Sjl 	struct memlist	*s_ml, *t_ml, *ml;
271625cf1a30Sjl 	uint64_t	s_slice_mask, t_slice_mask;
271725cf1a30Sjl 	static fn_t	f = "dr_mlist_canfit";
271825cf1a30Sjl 
271925cf1a30Sjl 	s_ml = memlist_dup(s_mlist);
272025cf1a30Sjl 	t_ml = memlist_dup(t_mlist);
272125cf1a30Sjl 
272225cf1a30Sjl 	if (s_ml == NULL || t_ml == NULL) {
272325cf1a30Sjl 		cmn_err(CE_WARN, "%s: memlist_dup failed\n", f);
272425cf1a30Sjl 		goto done;
272525cf1a30Sjl 	}
272625cf1a30Sjl 
272725cf1a30Sjl 	s_slice_mask = s_mp->sbm_slice_size - 1;
272825cf1a30Sjl 	t_slice_mask = t_mp->sbm_slice_size - 1;
272925cf1a30Sjl 
273025cf1a30Sjl 	/*
273125cf1a30Sjl 	 * Normalize to slice relative offsets.
273225cf1a30Sjl 	 */
273325cf1a30Sjl 	for (ml = s_ml; ml; ml = ml->next)
273425cf1a30Sjl 		ml->address &= s_slice_mask;
273525cf1a30Sjl 
273625cf1a30Sjl 	for (ml = t_ml; ml; ml = ml->next)
273725cf1a30Sjl 		ml->address &= t_slice_mask;
273825cf1a30Sjl 
273925cf1a30Sjl 	canfit = memlist_canfit(s_ml, t_ml);
274025cf1a30Sjl done:
274125cf1a30Sjl 	memlist_delete(s_ml);
274225cf1a30Sjl 	memlist_delete(t_ml);
274325cf1a30Sjl 
274425cf1a30Sjl 	return (canfit);
274525cf1a30Sjl }
274625cf1a30Sjl 
274725cf1a30Sjl /*
274825cf1a30Sjl  * Memlist support.
274925cf1a30Sjl  */
275025cf1a30Sjl 
275125cf1a30Sjl /*
275225cf1a30Sjl  * Determine whether the source memlist (s_mlist) will
275325cf1a30Sjl  * fit into the target memlist (t_mlist) in terms of
275425cf1a30Sjl  * size and holes.  Assumes the caller has normalized the
275525cf1a30Sjl  * memlist physical addresses for comparison.
275625cf1a30Sjl  */
275725cf1a30Sjl static int
275825cf1a30Sjl memlist_canfit(struct memlist *s_mlist, struct memlist *t_mlist)
275925cf1a30Sjl {
276025cf1a30Sjl 	int		rv = 0;
276125cf1a30Sjl 	struct memlist	*s_ml, *t_ml;
276225cf1a30Sjl 
276325cf1a30Sjl 	if ((s_mlist == NULL) || (t_mlist == NULL))
276425cf1a30Sjl 		return (0);
276525cf1a30Sjl 
276625cf1a30Sjl 	s_ml = s_mlist;
276725cf1a30Sjl 	for (t_ml = t_mlist; t_ml && s_ml; t_ml = t_ml->next) {
276825cf1a30Sjl 		uint64_t	s_start, s_end;
276925cf1a30Sjl 		uint64_t	t_start, t_end;
277025cf1a30Sjl 
277125cf1a30Sjl 		t_start = t_ml->address;
277225cf1a30Sjl 		t_end = t_start + t_ml->size;
277325cf1a30Sjl 
277425cf1a30Sjl 		for (; s_ml; s_ml = s_ml->next) {
277525cf1a30Sjl 			s_start = s_ml->address;
277625cf1a30Sjl 			s_end = s_start + s_ml->size;
277725cf1a30Sjl 
277825cf1a30Sjl 			if ((s_start < t_start) || (s_end > t_end))
277925cf1a30Sjl 				break;
278025cf1a30Sjl 		}
278125cf1a30Sjl 	}
278225cf1a30Sjl 
278325cf1a30Sjl 	/*
278425cf1a30Sjl 	 * If we ran out of source memlist chunks that mean
278525cf1a30Sjl 	 * we found a home for all of them.
278625cf1a30Sjl 	 */
278725cf1a30Sjl 	if (s_ml == NULL)
278825cf1a30Sjl 		rv = 1;
278925cf1a30Sjl 
279025cf1a30Sjl 	return (rv);
279125cf1a30Sjl }
2792