xref: /illumos-gate/usr/src/uts/sun4u/opl/io/dr_mem.c (revision 56f33205)
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*56f33205SJonathan Adams  * Copyright 2010 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 *
dr_get_memlist(dr_mem_unit_t * mp)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
dr_mem_del_done(void * arg,int error)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
dr_release_mem(dr_common_unit_t * cp)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
dr_attach_mem(dr_handle_t * hp,dr_common_unit_t * cp)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);
326*56f33205SJonathan Adams 	for (mc = ml; mc; mc = mc->ml_next) {
32725cf1a30Sjl 		int		 rv;
32825cf1a30Sjl 		sbd_error_t	*err;
32925cf1a30Sjl 
33025cf1a30Sjl 		rv = kphysm_add_memory_dynamic(
331*56f33205SJonathan Adams 		    (pfn_t)(mc->ml_address >> PAGESHIFT),
332*56f33205SJonathan Adams 		    (pgcnt_t)(mc->ml_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(
360*56f33205SJonathan Adams 		    mp->sbm_cm.sbdev_id, mc->ml_address, mc->ml_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 *
dr_memlist_del_retired_pages(struct memlist * mlist)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
dr_move_memory(dr_handle_t * hp,dr_mem_unit_t * s_mp,dr_mem_unit_t * t_mp)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) {
462*56f33205SJonathan Adams 		c_ml = memlist_del_span(c_ml, d_ml->ml_address, d_ml->ml_size);
463*56f33205SJonathan Adams 		d_ml = d_ml->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 
497d3d50737SRafael 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 
528d3d50737SRafael 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
dr_detach_mem(dr_handle_t * hp,dr_common_unit_t * cp)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
dr_del_mlist_query(struct memlist * mlist,memquery_t * mp)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 
660*56f33205SJonathan Adams 	for (ml = mlist; ml; ml = ml->ml_next) {
66125cf1a30Sjl 		memquery_t mq;
66225cf1a30Sjl 
66325cf1a30Sjl 		rv = kphysm_del_span_query(
664*56f33205SJonathan Adams 		    _b64top(ml->ml_address), _b64top(ml->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
dr_mem_status(dr_handle_t * hp,dr_devset_t devset,sbd_dev_stat_t * dsp)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 
73607d06da5SSurya Prakki 		(void) 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 
77007d06da5SSurya Prakki 			(void) 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
dr_pre_attach_mem(dr_handle_t * hp,dr_common_unit_t ** devlist,int devnum)86125cf1a30Sjl dr_pre_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
86225cf1a30Sjl {
863