xref: /titanic_52/usr/src/uts/sun4u/opl/io/dr_mem.c (revision 99ebb4ca412cb0a19d77a3899a87c055b9c30fa8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * DR memory support routines.
30  */
31 
32 #include <sys/note.h>
33 #include <sys/debug.h>
34 #include <sys/types.h>
35 #include <sys/errno.h>
36 #include <sys/param.h>
37 #include <sys/dditypes.h>
38 #include <sys/kmem.h>
39 #include <sys/conf.h>
40 #include <sys/ddi.h>
41 #include <sys/sunddi.h>
42 #include <sys/sunndi.h>
43 #include <sys/ddi_impldefs.h>
44 #include <sys/ndi_impldefs.h>
45 #include <sys/sysmacros.h>
46 #include <sys/machsystm.h>
47 #include <sys/spitregs.h>
48 #include <sys/cpuvar.h>
49 #include <sys/promif.h>
50 #include <vm/seg_kmem.h>
51 #include <sys/lgrp.h>
52 #include <sys/platform_module.h>
53 
54 #include <vm/page.h>
55 
56 #include <sys/dr.h>
57 #include <sys/dr_util.h>
58 #include <sys/drmach.h>
59 
60 extern struct memlist	*phys_install;
61 extern vnode_t		retired_pages;
62 
63 /* TODO: push this reference below drmach line */
64 extern int		kcage_on;
65 
66 /* for the DR*INTERNAL_ERROR macros.  see sys/dr.h. */
67 static char *dr_ie_fmt = "%M% %d";
68 
69 typedef enum {
70 	DR_TP_INVALID = -1,
71 	DR_TP_SAME,
72 	DR_TP_LARGE,
73 	DR_TP_NONRELOC,
74 	DR_TP_FLOATING
75 } dr_target_pref_t;
76 
77 static int		dr_post_detach_mem_unit(dr_mem_unit_t *mp);
78 static int		dr_reserve_mem_spans(memhandle_t *mhp,
79 				struct memlist *mlist);
80 static int		dr_select_mem_target(dr_handle_t *hp,
81 				dr_mem_unit_t *mp, struct memlist *ml);
82 static void		dr_init_mem_unit_data(dr_mem_unit_t *mp);
83 static struct memlist	*dr_memlist_del_retired_pages(struct memlist *ml);
84 static dr_target_pref_t	dr_get_target_preference(dr_handle_t *hp,
85 				dr_mem_unit_t *t_mp, dr_mem_unit_t *s_mp,
86 				struct memlist *s_ml, struct memlist *x_ml,
87 				struct memlist *b_ml);
88 
89 static int		memlist_canfit(struct memlist *s_mlist,
90 				struct memlist *t_mlist);
91 static int		dr_del_mlist_query(struct memlist *mlist,
92 				memquery_t *mp);
93 static struct memlist	*dr_get_copy_mlist(struct memlist *s_ml,
94 				struct memlist *t_ml, dr_mem_unit_t *s_mp,
95 				dr_mem_unit_t *t_mp);
96 static struct memlist	*dr_get_nonreloc_mlist(struct memlist *s_ml,
97 				dr_mem_unit_t *s_mp);
98 static int		dr_memlist_canfit(struct memlist *s_mlist,
99 				struct memlist *t_mlist, dr_mem_unit_t *s_mp,
100 				dr_mem_unit_t *t_mp);
101 
102 extern void		page_unretire_pages(void);
103 
104 /*
105  * dr_mem_unit_t.sbm_flags
106  */
107 #define	DR_MFLAG_RESERVED	0x01	/* mem unit reserved for delete */
108 #define	DR_MFLAG_SOURCE		0x02	/* source brd of copy/rename op */
109 #define	DR_MFLAG_TARGET		0x04	/* target brd of copy/rename op */
110 #define	DR_MFLAG_RELOWNER	0x20	/* memory release (delete) owner */
111 #define	DR_MFLAG_RELDONE	0x40	/* memory release (delete) done */
112 
113 /* helper macros */
114 #define	_ptob64(p) ((uint64_t)(p) << PAGESHIFT)
115 #define	_b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
116 
117 static struct memlist *
118 dr_get_memlist(dr_mem_unit_t *mp)
119 {
120 	struct memlist	*mlist = NULL;
121 	sbd_error_t	*err;
122 	static fn_t	f = "dr_get_memlist";
123 
124 	PR_MEM("%s for %s...\n", f, mp->sbm_cm.sbdev_path);
125 
126 	/*
127 	 * Return cached memlist, if present.
128 	 * This memlist will be present following an
129 	 * unconfigure (a.k.a: detach) of this memunit.
130 	 * It should only be used in the case were a configure
131 	 * is bringing this memunit back in without going
132 	 * through the disconnect and connect states.
133 	 */
134 	if (mp->sbm_mlist) {
135 		PR_MEM("%s: found cached memlist\n", f);
136 
137 		mlist = memlist_dup(mp->sbm_mlist);
138 	} else {
139 		uint64_t basepa = _ptob64(mp->sbm_basepfn);
140 
141 		/* attempt to construct a memlist using phys_install */
142 
143 		/* round down to slice base address */
144 		basepa &= ~(mp->sbm_slice_size - 1);
145 
146 		/* get a copy of phys_install to edit */
147 		memlist_read_lock();
148 		mlist = memlist_dup(phys_install);
149 		memlist_read_unlock();
150 
151 		/* trim lower irrelevant span */
152 		if (mlist)
153 			mlist = memlist_del_span(mlist, 0ull, basepa);
154 
155 		/* trim upper irrelevant span */
156 		if (mlist) {
157 			uint64_t endpa;
158 
159 			basepa += mp->sbm_slice_size;
160 			endpa = _ptob64(physmax + 1);
161 			if (endpa > basepa)
162 				mlist = memlist_del_span(
163 					mlist, basepa,
164 					endpa - basepa);
165 		}
166 
167 		if (mlist) {
168 			/* successfully built a memlist */
169 			PR_MEM("%s: derived memlist from phys_install\n", f);
170 		}
171 
172 		/* if no mlist yet, try platform layer */
173 		if (!mlist) {
174 			err = drmach_mem_get_memlist(
175 				mp->sbm_cm.sbdev_id, &mlist);
176 			if (err) {
177 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
178 				mlist = NULL; /* paranoia */
179 			}
180 		}
181 	}
182 
183 	PR_MEM("%s: memlist for %s\n", f, mp->sbm_cm.sbdev_path);
184 	PR_MEMLIST_DUMP(mlist);
185 
186 	return (mlist);
187 }
188 
189 typedef struct {
190 	kcondvar_t cond;
191 	kmutex_t lock;
192 	int error;
193 	int done;
194 } dr_release_mem_sync_t;
195 
196 /*
197  * Memory has been logically removed by the time this routine is called.
198  */
199 static void
200 dr_mem_del_done(void *arg, int error)
201 {
202 	dr_release_mem_sync_t *ds = arg;
203 
204 	mutex_enter(&ds->lock);
205 	ds->error = error;
206 	ds->done = 1;
207 	cv_signal(&ds->cond);
208 	mutex_exit(&ds->lock);
209 }
210 
211 /*
212  * When we reach here the memory being drained should have
213  * already been reserved in dr_pre_release_mem().
214  * Our only task here is to kick off the "drain" and wait
215  * for it to finish.
216  */
217 void
218 dr_release_mem(dr_common_unit_t *cp)
219 {
220 	dr_mem_unit_t	*mp = (dr_mem_unit_t *)cp;
221 	int		err;
222 	dr_release_mem_sync_t rms;
223 	static fn_t	f = "dr_release_mem";
224 
225 	/* check that this memory unit has been reserved */
226 	if (!(mp->sbm_flags & DR_MFLAG_RELOWNER)) {
227 		DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
228 		return;
229 	}
230 
231 	bzero((void *) &rms, sizeof (rms));
232 
233 	mutex_init(&rms.lock, NULL, MUTEX_DRIVER, NULL);
234 	cv_init(&rms.cond, NULL, CV_DRIVER, NULL);
235 
236 	mutex_enter(&rms.lock);
237 	err = kphysm_del_start(mp->sbm_memhandle,
238 		dr_mem_del_done, (void *) &rms);
239 	if (err == KPHYSM_OK) {
240 		/* wait for completion or interrupt */
241 		while (!rms.done) {
242 			if (cv_wait_sig(&rms.cond, &rms.lock) == 0) {
243 				/* then there is a pending UNIX signal */
244 				(void) kphysm_del_cancel(mp->sbm_memhandle);
245 
246 				/* wait for completion */
247 				while (!rms.done)
248 					cv_wait(&rms.cond, &rms.lock);
249 			}
250 		}
251 		/* get the result of the memory delete operation */
252 		err = rms.error;
253 	}
254 	mutex_exit(&rms.lock);
255 
256 	cv_destroy(&rms.cond);
257 	mutex_destroy(&rms.lock);
258 
259 	if (err != KPHYSM_OK) {
260 		int e_code;
261 
262 		switch (err) {
263 			case KPHYSM_ENOWORK:
264 				e_code = ESBD_NOERROR;
265 				break;
266 
267 			case KPHYSM_EHANDLE:
268 			case KPHYSM_ESEQUENCE:
269 				e_code = ESBD_INTERNAL;
270 				break;
271 
272 			case KPHYSM_ENOTVIABLE:
273 				e_code = ESBD_MEM_NOTVIABLE;
274 				break;
275 
276 			case KPHYSM_EREFUSED:
277 				e_code = ESBD_MEM_REFUSED;
278 				break;
279 
280 			case KPHYSM_ENONRELOC:
281 				e_code = ESBD_MEM_NONRELOC;
282 				break;
283 
284 			case KPHYSM_ECANCELLED:
285 				e_code = ESBD_MEM_CANCELLED;
286 				break;
287 
288 			case KPHYSM_ERESOURCE:
289 				e_code = ESBD_MEMFAIL;
290 				break;
291 
292 			default:
293 				cmn_err(CE_WARN,
294 					"%s: unexpected kphysm error code %d,"
295 					" id 0x%p",
296 					f, err, mp->sbm_cm.sbdev_id);
297 
298 				e_code = ESBD_IO;
299 				break;
300 		}
301 
302 		if (e_code != ESBD_NOERROR) {
303 			dr_dev_err(CE_IGNORE, &mp->sbm_cm, e_code);
304 		}
305 	}
306 }
307 
308 void
309 dr_attach_mem(dr_handle_t *hp, dr_common_unit_t *cp)
310 {
311 	_NOTE(ARGUNUSED(hp))
312 
313 	dr_mem_unit_t	*mp = (dr_mem_unit_t *)cp;
314 	struct memlist	*ml, *mc;
315 	sbd_error_t	*err;
316 	static fn_t	f = "dr_attach_mem";
317 
318 	PR_MEM("%s...\n", f);
319 
320 	dr_lock_status(hp->h_bd);
321 	err = drmach_configure(cp->sbdev_id, 0);
322 	dr_unlock_status(hp->h_bd);
323 	if (err) {
324 		DRERR_SET_C(&cp->sbdev_error, &err);
325 		return;
326 	}
327 
328 	ml = dr_get_memlist(mp);
329 	for (mc = ml; mc; mc = mc->next) {
330 		int		 rv;
331 		sbd_error_t	*err;
332 
333 		rv = kphysm_add_memory_dynamic(
334 			(pfn_t)(mc->address >> PAGESHIFT),
335 			(pgcnt_t)(mc->size >> PAGESHIFT));
336 		if (rv != KPHYSM_OK) {
337 			/*
338 			 * translate kphysm error and
339 			 * store in devlist error
340 			 */
341 			switch (rv) {
342 			case KPHYSM_ERESOURCE:
343 				rv = ESBD_NOMEM;
344 				break;
345 
346 			case KPHYSM_EFAULT:
347 				rv = ESBD_FAULT;
348 				break;
349 
350 			default:
351 				rv = ESBD_INTERNAL;
352 				break;
353 			}
354 
355 			if (rv == ESBD_INTERNAL) {
356 				DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
357 			} else
358 				dr_dev_err(CE_WARN, &mp->sbm_cm, rv);
359 			break;
360 		}
361 
362 		err = drmach_mem_add_span(
363 			mp->sbm_cm.sbdev_id, mc->address, mc->size);
364 		if (err) {
365 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
366 			break;
367 		}
368 	}
369 
370 	memlist_delete(ml);
371 
372 	/* back out if configure failed */
373 	if (mp->sbm_cm.sbdev_error != NULL) {
374 		dr_lock_status(hp->h_bd);
375 		err = drmach_unconfigure(cp->sbdev_id, 0);
376 		if (err)
377 			sbd_err_clear(&err);
378 		dr_unlock_status(hp->h_bd);
379 	}
380 }
381 
382 static struct memlist *
383 dr_memlist_del_retired_pages(struct memlist *mlist)
384 {
385 	page_t		*pp;
386 	pfn_t		pfn;
387 	kmutex_t	*vphm;
388 	vnode_t		*vp = &retired_pages;
389 	static fn_t	f = "dr_memlist_del_retired_pages";
390 
391 	vphm = page_vnode_mutex(vp);
392 	mutex_enter(vphm);
393 
394 	PR_MEM("%s\n", f);
395 
396 	if ((pp = vp->v_pages) == NULL) {
397 		mutex_exit(vphm);
398 		return (mlist);
399 	}
400 
401 	do {
402 		ASSERT(pp != NULL);
403 		/*
404 		 * page_downgrade happens after page_hashin, so we
405 		 * can't assert PAGE_SE. Just assert locked to catch
406 		 * changes to the retired vnode locking scheme.
407 		 */
408 		ASSERT(PAGE_LOCKED(pp));
409 		ASSERT(pp->p_vnode == &retired_pages);
410 
411 		if (!page_trylock(pp, SE_SHARED))
412 			continue;
413 
414 		pfn = page_pptonum(pp);
415 
416 		ASSERT((pp->p_offset >> PAGESHIFT) == pfn);
417 		/*
418 		 * Page retirement currently breaks large pages into PAGESIZE
419 		 * pages. If this changes, need to remove the assert and deal
420 		 * with different page sizes.
421 		 */
422 		ASSERT(pp->p_szc == 0);
423 
424 		if (address_in_memlist(mlist, ptob(pfn), PAGESIZE)) {
425 			mlist = memlist_del_span(mlist, ptob(pfn), PAGESIZE);
426 			PR_MEM("deleted retired page 0x%lx (pfn 0x%lx) "
427 			    "from memlist\n", ptob(pfn), pfn);
428 		}
429 
430 		page_unlock(pp);
431 	} while ((pp = pp->p_vpnext) != vp->v_pages);
432 
433 	mutex_exit(vphm);
434 
435 	return (mlist);
436 }
437 
438 #ifdef	DEBUG
439 int dbg_retirecnt = 10;
440 
441 static void
442 dbg_page_retire(struct memlist *r_ml)
443 {
444 	struct memlist	*t_ml;
445 	page_t		*pp, *epp;
446 	pfn_t		pfn, epfn;
447 	struct memseg	*seg;
448 
449 	int dbg_retired = 0;
450 	int dbg_skip = 10;
451 	int dbg_seq = 1;
452 
453 	if (r_ml == NULL)
454 		return;
455 
456 	for (t_ml = r_ml; (t_ml != NULL); t_ml = t_ml->next) {
457 		pfn = _b64top(t_ml->address);
458 		epfn = _b64top(t_ml->address + t_ml->size);
459 
460 		for (seg = memsegs; seg != NULL; seg = seg->next) {
461 			int retire = 0;
462 			int skip = 0;
463 			if (pfn >= seg->pages_end || epfn < seg->pages_base)
464 				continue;
465 
466 			pp = seg->pages;
467 			if (pfn > seg->pages_base)
468 				pp += pfn - seg->pages_base;
469 
470 			epp = seg->epages;
471 			if (epfn < seg->pages_end)
472 				epp -= seg->pages_end - epfn;
473 
474 			ASSERT(pp < epp);
475 #if 0
476 			while (pp < epp) {
477 				if (PP_ISFREE(pp) && !page_isfaulty(pp)) {
478 					if (retire++ < dbg_seq) {
479 						page_settoxic(pp,
480 							PAGE_IS_FAULTY);
481 						page_retire(pp,
482 							PAGE_IS_FAILING);
483 						if (++dbg_retired >=
484 							dbg_retirecnt)
485 							return;
486 					} else if (skip++ >= dbg_skip) {
487 						skip = 0;
488 						retire = 0;
489 						dbg_seq++;
490 					}
491 				}
492 				pp++;
493 			}
494 #endif /* 0 */
495 			while (pp < epp) {
496 				if (PP_ISFREE(pp)) {
497 					if (retire++ < dbg_seq) {
498 						page_retire(t_ml->address,
499 						    PR_OK);
500 						if (++dbg_retired >=
501 							dbg_retirecnt)
502 							return;
503 					} else if (skip++ >= dbg_skip) {
504 						skip = 0;
505 						retire = 0;
506 						dbg_seq++;
507 					}
508 				}
509 				pp++;
510 			}
511 		}
512 	}
513 }
514 #endif
515 
516 static int
517 dr_move_memory(dr_handle_t *hp, dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp)
518 {
519 	int		rv = -1;
520 	time_t		 copytime;
521 	drmachid_t	 cr_id;
522 	dr_sr_handle_t	*srhp = NULL;
523 	dr_board_t	*t_bp, *s_bp;
524 	struct memlist	*c_ml, *d_ml;
525 	sbd_error_t	*err;
526 	static fn_t	 f = "dr_move_memory";
527 
528 	PR_MEM("%s: (INLINE) moving memory from %s to %s\n",
529 		f,
530 		s_mp->sbm_cm.sbdev_path,
531 		t_mp->sbm_cm.sbdev_path);
532 
533 	ASSERT(s_mp->sbm_flags & DR_MFLAG_SOURCE);
534 	ASSERT(s_mp->sbm_peer == t_mp);
535 	ASSERT(s_mp->sbm_mlist);
536 
537 	ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
538 	ASSERT(t_mp->sbm_peer == s_mp);
539 
540 #ifdef	DEBUG
541 	if (dbg_retirecnt)
542 		dbg_page_retire(s_mp->sbm_mlist);
543 #endif
544 
545 	/*
546 	 * create a memlist of spans to copy by removing
547 	 * the spans that have been deleted, if any, from
548 	 * the full source board memlist.  s_mp->sbm_del_mlist
549 	 * will be NULL if there were no spans deleted from
550 	 * the source board.
551 	 */
552 	c_ml = memlist_dup(s_mp->sbm_mlist);
553 	d_ml = s_mp->sbm_del_mlist;
554 	while (d_ml != NULL) {
555 		c_ml = memlist_del_span(c_ml, d_ml->address, d_ml->size);
556 		d_ml = d_ml->next;
557 	}
558 
559 	/*
560 	 * Remove retired pages from the copy list. The page content
561 	 * need not be copied since the pages are no longer in use.
562 	 */
563 	PR_MEM("%s: copy list before removing retired pages (if any):\n", f);
564 	PR_MEMLIST_DUMP(c_ml);
565 
566 	c_ml = dr_memlist_del_retired_pages(c_ml);
567 
568 	PR_MEM("%s: copy list after removing retired pages:\n", f);
569 	PR_MEMLIST_DUMP(c_ml);
570 
571 	/*
572 	 * With parallel copy, it shouldn't make a difference which
573 	 * CPU is the actual master during copy-rename since all
574 	 * CPUs participate in the parallel copy anyway.
575 	 */
576 	affinity_set(CPU_CURRENT);
577 
578 	err = drmach_copy_rename_init(
579 		t_mp->sbm_cm.sbdev_id, s_mp->sbm_cm.sbdev_id, c_ml, &cr_id);
580 	if (err) {
581 		DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
582 		affinity_clear();
583 		memlist_delete(c_ml);
584 		return (-1);
585 	}
586 
587 	srhp = dr_get_sr_handle(hp);
588 	ASSERT(srhp);
589 
590 	copytime = lbolt;
591 
592 	/* Quiesce the OS.  */
593 	if (dr_suspend(srhp)) {
594 		cmn_err(CE_WARN, "%s: failed to quiesce OS"
595 			" for copy-rename", f);
596 
597 		err = drmach_copy_rename_fini(cr_id);
598 		if (err) {
599 			/*
600 			 * no error is expected since the program has
601 			 * not yet run.
602 			 */
603 
604 			/* catch this in debug kernels */
605 			ASSERT(0);
606 
607 			sbd_err_clear(&err);
608 		}
609 
610 		/* suspend error reached via hp */
611 		s_mp->sbm_cm.sbdev_error = hp->h_err;
612 		hp->h_err = NULL;
613 		goto done;
614 	}
615 
616 	drmach_copy_rename(cr_id);
617 
618 	/* Resume the OS.  */
619 	dr_resume(srhp);
620 
621 	copytime = lbolt - copytime;
622 
623 	if (err = drmach_copy_rename_fini(cr_id))
624 		goto done;
625 
626 	/*
627 	 * Rename memory for lgroup.
628 	 * Source and target board numbers are packaged in arg.
629 	 */
630 	s_bp = s_mp->sbm_cm.sbdev_bp;
631 	t_bp = t_mp->sbm_cm.sbdev_bp;
632 
633 	lgrp_plat_config(LGRP_CONFIG_MEM_RENAME,
634 		(uintptr_t)(s_bp->b_num | (t_bp->b_num << 16)));
635 
636 
637 	PR_MEM("%s: copy-rename elapsed time = %ld ticks (%ld secs)\n",
638 		f, copytime, copytime / hz);
639 
640 	rv = 0;
641 done:
642 	if (srhp)
643 		dr_release_sr_handle(srhp);
644 	if (err)
645 		DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
646 	affinity_clear();
647 
648 	return (rv);
649 }
650 
651 /*
652  * If detaching node contains memory that is "non-permanent"
653  * then the memory adr's are simply cleared.  If the memory
654  * is non-relocatable, then do a copy-rename.
655  */
656 void
657 dr_detach_mem(dr_handle_t *hp, dr_common_unit_t *cp)
658 {
659 	int			rv = 0;
660 	dr_mem_unit_t		*s_mp = (dr_mem_unit_t *)cp;
661 	dr_mem_unit_t		*t_mp;
662 	dr_state_t		state;
663 	static fn_t		f = "dr_detach_mem";
664 
665 	PR_MEM("%s...\n", f);
666 
667 	/* lookup target mem unit and target board structure, if any */
668 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
669 		t_mp = s_mp->sbm_peer;
670 		ASSERT(t_mp != NULL);
671 		ASSERT(t_mp->sbm_peer == s_mp);
672 	} else {
673 		t_mp = NULL;
674 	}
675 
676 	/* verify mem unit's state is UNREFERENCED */
677 	state = s_mp->sbm_cm.sbdev_state;
678 	if (state != DR_STATE_UNREFERENCED) {
679 		dr_dev_err(CE_IGNORE, &s_mp->sbm_cm, ESBD_STATE);
680 		return;
681 	}
682 
683 	/* verify target mem unit's state is UNREFERENCED, if any */
684 	if (t_mp != NULL) {
685 		state = t_mp->sbm_cm.sbdev_state;
686 		if (state != DR_STATE_UNREFERENCED) {
687 			dr_dev_err(CE_IGNORE, &t_mp->sbm_cm, ESBD_STATE);
688 			return;
689 		}
690 	}
691 
692 	/*
693 	 * If there is no target board (no copy/rename was needed), then
694 	 * we're done!
695 	 */
696 	if (t_mp == NULL) {
697 		sbd_error_t *err;
698 		/*
699 		 * Reprogram interconnect hardware and disable
700 		 * memory controllers for memory node that's going away.
701 		 */
702 
703 		err = drmach_mem_disable(s_mp->sbm_cm.sbdev_id);
704 		if (err) {
705 			DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
706 			rv = -1;
707 		}
708 	} else {
709 		rv = dr_move_memory(hp, s_mp, t_mp);
710 		PR_MEM("%s: %s memory COPY-RENAME (board %d -> %d)\n",
711 			f,
712 			rv ? "FAILED" : "COMPLETED",
713 			s_mp->sbm_cm.sbdev_bp->b_num,
714 			t_mp->sbm_cm.sbdev_bp->b_num);
715 
716 		if (rv != 0)
717 			(void) dr_cancel_mem(s_mp);
718 	}
719 
720 	if (rv == 0) {
721 		sbd_error_t *err;
722 
723 		dr_lock_status(hp->h_bd);
724 		err = drmach_unconfigure(s_mp->sbm_cm.sbdev_id, 0);
725 		dr_unlock_status(hp->h_bd);
726 		if (err)
727 			sbd_err_clear(&err);
728 	}
729 }
730 
731 /*
732  * This routine acts as a wrapper for kphysm_del_span_query in order to
733  * support potential memory holes in a board's physical address space.
734  * It calls kphysm_del_span_query for each node in a memlist and accumulates
735  * the results in *mp.
736  */
737 static int
738 dr_del_mlist_query(struct memlist *mlist, memquery_t *mp)
739 {
740 	struct memlist	*ml;
741 	int		 rv = 0;
742 
743 
744 	if (mlist == NULL)
745 		cmn_err(CE_WARN, "dr_del_mlist_query: mlist=NULL\n");
746 
747 	mp->phys_pages = 0;
748 	mp->managed = 0;
749 	mp->nonrelocatable = 0;
750 	mp->first_nonrelocatable = (pfn_t)-1;	/* XXX */
751 	mp->last_nonrelocatable = 0;
752 
753 	for (ml = mlist; ml; ml = ml->next) {
754 		memquery_t mq;
755 
756 		rv = kphysm_del_span_query(
757 			_b64top(ml->address), _b64top(ml->size), &mq);
758 		if (rv)
759 			break;
760 
761 		mp->phys_pages += mq.phys_pages;
762 		mp->managed += mq.managed;
763 		mp->nonrelocatable += mq.nonrelocatable;
764 
765 		if (mq.nonrelocatable != 0) {
766 			if (mq.first_nonrelocatable < mp->first_nonrelocatable)
767 				mp->first_nonrelocatable =
768 					mq.first_nonrelocatable;
769 			if (mq.last_nonrelocatable > mp->last_nonrelocatable)
770 				mp->last_nonrelocatable =
771 					mq.last_nonrelocatable;
772 		}
773 	}
774 
775 	if (mp->nonrelocatable == 0)
776 		mp->first_nonrelocatable = 0;	/* XXX */
777 
778 	return (rv);
779 }
780 
781 /*
782  * NOTE: This routine is only partially smart about multiple
783  *	 mem-units.  Need to make mem-status structure smart
784  *	 about them also.
785  */
786 int
787 dr_mem_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp)
788 {
789 	int		m, mix;
790 	memdelstat_t	mdst;
791 	memquery_t	mq;
792 	dr_board_t	*bp;
793 	dr_mem_unit_t	*mp;
794 	sbd_mem_stat_t	*msp;
795 	static fn_t	f = "dr_mem_status";
796 
797 	bp = hp->h_bd;
798 	devset &= DR_DEVS_PRESENT(bp);
799 
800 	for (m = mix = 0; m < MAX_MEM_UNITS_PER_BOARD; m++) {
801 		int		rv;
802 		sbd_error_t	*err;
803 		drmach_status_t	 pstat;
804 		dr_mem_unit_t	*p_mp;
805 
806 		if (DEVSET_IN_SET(devset, SBD_COMP_MEM, m) == 0)
807 			continue;
808 
809 		mp = dr_get_mem_unit(bp, m);
810 
811 		if (mp->sbm_cm.sbdev_state == DR_STATE_EMPTY) {
812 			/* present, but not fully initialized */
813 			continue;
814 		}
815 
816 		if (mp->sbm_cm.sbdev_id == (drmachid_t)0)
817 			continue;
818 
819 		/* fetch platform status */
820 		err = drmach_status(mp->sbm_cm.sbdev_id, &pstat);
821 		if (err) {
822 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
823 			continue;
824 		}
825 
826 		msp = &dsp->d_mem;
827 		bzero((caddr_t)msp, sizeof (*msp));
828 
829 		strncpy(msp->ms_cm.c_id.c_name, pstat.type,
830 			sizeof (msp->ms_cm.c_id.c_name));
831 		msp->ms_cm.c_id.c_type = mp->sbm_cm.sbdev_type;
832 		msp->ms_cm.c_id.c_unit = SBD_NULL_UNIT;
833 		msp->ms_cm.c_cond = mp->sbm_cm.sbdev_cond;
834 		msp->ms_cm.c_busy = mp->sbm_cm.sbdev_busy | pstat.busy;
835 		msp->ms_cm.c_time = mp->sbm_cm.sbdev_time;
836 		msp->ms_cm.c_ostate = mp->sbm_cm.sbdev_ostate;
837 
838 		msp->ms_totpages = mp->sbm_npages;
839 		msp->ms_basepfn = mp->sbm_basepfn;
840 		msp->ms_pageslost = mp->sbm_pageslost;
841 		msp->ms_cage_enabled = kcage_on;
842 
843 		if (mp->sbm_flags & DR_MFLAG_RESERVED)
844 			p_mp = mp->sbm_peer;
845 		else
846 			p_mp = NULL;
847 
848 		if (p_mp == NULL) {
849 			msp->ms_peer_is_target = 0;
850 			msp->ms_peer_ap_id[0] = '\0';
851 		} else if (p_mp->sbm_flags & DR_MFLAG_RESERVED) {
852 			char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
853 			char *minor;
854 
855 			/*
856 			 * b_dip doesn't have to be held for ddi_pathname()
857 			 * because the board struct (dr_board_t) will be
858 			 * destroyed before b_dip detaches.
859 			 */
860 			(void) ddi_pathname(bp->b_dip, path);
861 			minor = strchr(p_mp->sbm_cm.sbdev_path, ':');
862 
863 			snprintf(msp->ms_peer_ap_id,
864 			    sizeof (msp->ms_peer_ap_id), "%s%s",
865 			    path, (minor == NULL) ? "" : minor);
866 
867 			kmem_free(path, MAXPATHLEN);
868 
869 			if (p_mp->sbm_flags & DR_MFLAG_TARGET)
870 				msp->ms_peer_is_target = 1;
871 		}
872 
873 		if (mp->sbm_flags & DR_MFLAG_RELOWNER)
874 			rv = kphysm_del_status(mp->sbm_memhandle, &mdst);
875 		else
876 			rv = KPHYSM_EHANDLE;	/* force 'if' to fail */
877 
878 		if (rv == KPHYSM_OK) {
879 			/*
880 			 * Any pages above managed is "free",
881 			 * i.e. it's collected.
882 			 */
883 			msp->ms_detpages += (uint_t)(mdst.collected +
884 			    mdst.phys_pages - mdst.managed);
885 		} else {
886 			/*
887 			 * If we're UNREFERENCED or UNCONFIGURED,
888 			 * then the number of detached pages is
889 			 * however many pages are on the board.
890 			 * I.e. detached = not in use by OS.
891 			 */
892 			switch (msp->ms_cm.c_ostate) {
893 			/*
894 			 * changed to use cfgadm states
895 			 *
896 			 * was:
897 			 *	case DR_STATE_UNREFERENCED:
898 			 *	case DR_STATE_UNCONFIGURED:
899 			 */
900 			case SBD_STAT_UNCONFIGURED:
901 				msp->ms_detpages = msp->ms_totpages;
902 				break;
903 
904 			default:
905 				break;
906 			}
907 		}
908 
909 		/*
910 		 * kphysm_del_span_query can report non-reloc pages = total
911 		 * pages for memory that is not yet configured
912 		 */
913 		if (mp->sbm_cm.sbdev_state != DR_STATE_UNCONFIGURED) {
914 			struct memlist *ml;
915 
916 			ml = dr_get_memlist(mp);
917 			rv = ml ? dr_del_mlist_query(ml, &mq) : -1;
918 			memlist_delete(ml);
919 
920 			if (rv == KPHYSM_OK) {
921 				msp->ms_managed_pages = mq.managed;
922 				msp->ms_noreloc_pages = mq.nonrelocatable;
923 				msp->ms_noreloc_first =
924 				    mq.first_nonrelocatable;
925 				msp->ms_noreloc_last =
926 				    mq.last_nonrelocatable;
927 				msp->ms_cm.c_sflags = 0;
928 				if (mq.nonrelocatable) {
929 					SBD_SET_SUSPEND(SBD_CMD_UNCONFIGURE,
930 					    msp->ms_cm.c_sflags);
931 				}
932 			} else {
933 				PR_MEM("%s: kphysm_del_span_query() = %d\n",
934 				    f, rv);
935 			}
936 		}
937 
938 		/*
939 		 * Check source unit state during copy-rename
940 		 */
941 		if ((mp->sbm_flags & DR_MFLAG_SOURCE) &&
942 		    (mp->sbm_cm.sbdev_state == DR_STATE_UNREFERENCED ||
943 		    mp->sbm_cm.sbdev_state == DR_STATE_RELEASE))
944 			msp->ms_cm.c_ostate = SBD_STAT_CONFIGURED;
945 
946 		mix++;
947 		dsp++;
948 	}
949 
950 	return (mix);
951 }
952 
953 int
954 dr_pre_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
955 {
956 	_NOTE(ARGUNUSED(hp))
957 
958 	int		err_flag = 0;
959 	int		d;
960 	sbd_error_t	*err;
961 	static fn_t	f = "dr_pre_attach_mem";
962 
963 	PR_MEM("%s...\n", f);
964 
965 	for (d = 0; d < devnum; d++) {
966 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
967 		dr_state_t	state;
968 
969 		cmn_err(CE_CONT, "OS configure %s", mp->sbm_cm.sbdev_path);
970 
971 		state = mp->sbm_cm.sbdev_state;
972 		switch (state) {
973 		case DR_STATE_UNCONFIGURED:
974 			PR_MEM("%s: recovering from UNCONFIG for %s\n",
975 				f,
976 				mp->sbm_cm.sbdev_path);
977 
978 			/* use memlist cached by dr_post_detach_mem_unit */
979 			ASSERT(mp->sbm_mlist != NULL);
980 			PR_MEM("%s: re-configuring cached memlist for %s:\n",
981 				f, mp->sbm_cm.sbdev_path);
982 			PR_MEMLIST_DUMP(mp->sbm_mlist);
983 
984 			/* kphysm del handle should be have been freed */
985 			ASSERT((mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
986 
987 			/*FALLTHROUGH*/
988 
989 		case DR_STATE_CONNECTED:
990 			PR_MEM("%s: reprogramming mem hardware on %s\n",
991 				f, mp->sbm_cm.sbdev_bp->b_path);
992 
993 			PR_MEM("%s: enabling %s\n",
994 				f, mp->sbm_cm.sbdev_path);
995 
996 			err = drmach_mem_enable(mp->sbm_cm.sbdev_id);
997 			if (err) {
998 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
999 				err_flag = 1;
1000 			}
1001 			break;
1002 
1003 		default:
1004 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_STATE);
1005 			err_flag = 1;
1006 			break;
1007 		}
1008 
1009 		/* exit for loop if error encountered */
1010 		if (err_flag)
1011 			break;
1012 	}
1013 
1014 	return (err_flag ? -1 : 0);
1015 }
1016 
1017 int
1018 dr_post_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1019 {
1020 	_NOTE(ARGUNUSED(hp))
1021 
1022 	int		d;
1023 	static fn_t	f = "dr_post_attach_mem";
1024 
1025 	PR_MEM("%s...\n", f);
1026 
1027 	for (d = 0; d < devnum; d++) {
1028 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
1029 		struct memlist	*mlist, *ml;
1030 
1031 		mlist = dr_get_memlist(mp);
1032 		if (mlist == NULL) {
1033 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_MEMFAIL);
1034 			continue;
1035 		}
1036 
1037 		/*
1038 		 * Verify the memory really did successfully attach
1039 		 * by checking for its existence in phys_install.
1040 		 */
1041 		memlist_read_lock();
1042 		if (memlist_intersect(phys_install, mlist) == 0) {
1043 			memlist_read_unlock();
1044 
1045 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
1046 
1047 			PR_MEM("%s: %s memlist not in phys_install",
1048 				f, mp->sbm_cm.sbdev_path);
1049 
1050 			memlist_delete(mlist);
1051 			continue;
1052 		}
1053 		memlist_read_unlock();
1054 
1055 		for (ml = mlist; ml != NULL; ml = ml->next) {
1056 			sbd_error_t *err;
1057 
1058 			err = drmach_mem_add_span(
1059 				mp->sbm_cm.sbdev_id,
1060 				ml->address,
1061 				ml->size);
1062 			if (err)
1063 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
1064 		}
1065 
1066 		memlist_delete(mlist);
1067 
1068 		/*
1069 		 * Destroy cached memlist, if any.
1070 		 * There will be a cached memlist in sbm_mlist if
1071 		 * this board is being configured directly after
1072 		 * an unconfigure.
1073 		 * To support this transition, dr_post_detach_mem
1074 		 * left a copy of the last known memlist in sbm_mlist.
1075 		 * This memlist could differ from any derived from
1076 		 * hardware if while this memunit was last configured
1077 		 * the system detected and deleted bad pages from
1078 		 * phys_install.  The location of those bad pages
1079 		 * will be reflected in the cached memlist.
1080 		 */
1081 		if (mp->sbm_mlist) {
1082 			memlist_delete(mp->sbm_mlist);
1083 			mp->sbm_mlist = NULL;
1084 		}
1085 	}
1086 
1087 	return (0);
1088 }
1089 
1090 int
1091 dr_pre_detach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1092 {
1093 	_NOTE(ARGUNUSED(hp))
1094 
1095 	int d;
1096 
1097 	for (d = 0; d < devnum; d++) {
1098 		dr_mem_unit_t *mp = (dr_mem_unit_t *)devlist[d];
1099 
1100 		cmn_err(CE_CONT, "OS unconfigure %s", mp->sbm_cm.sbdev_path);
1101 	}
1102 
1103 	return (0);
1104 }
1105 
1106 int
1107 dr_post_detach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1108 {
1109 	_NOTE(ARGUNUSED(hp))
1110 
1111 	int		d, rv;
1112 	static fn_t	f = "dr_post_detach_mem";
1113 
1114 	PR_MEM("%s...\n", f);
1115 
1116 	rv = 0;
1117 	for (d = 0; d < devnum; d++) {
1118 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
1119 
1120 		ASSERT(mp->sbm_cm.sbdev_bp == hp->h_bd);
1121 
1122 		if (dr_post_detach_mem_unit(mp))
1123 			rv = -1;
1124 	}
1125 
1126 	return (rv);
1127 }
1128 
1129 static void
1130 dr_add_memory_spans(dr_mem_unit_t *mp, struct memlist *ml)
1131 {
1132 	static fn_t	f = "dr_add_memory_spans";
1133 
1134 	PR_MEM("%s...", f);
1135 	PR_MEMLIST_DUMP(ml);
1136 
1137 #ifdef DEBUG
1138 	memlist_read_lock();
1139 	if (memlist_intersect(phys_install, ml)) {
1140 		PR_MEM("%s:WARNING: memlist intersects with phys_install\n", f);
1141 	}
1142 	memlist_read_unlock();
1143 #endif
1144 
1145 	for (; ml; ml = ml->next) {
1146 		pfn_t		 base;
1147 		pgcnt_t		 npgs;
1148 		int		 rv;
1149 		sbd_error_t	*err;
1150 
1151 		base = _b64top(ml->address);
1152 		npgs = _b64top(ml->size);
1153 
1154 		rv = kphysm_add_memory_dynamic(base, npgs);
1155 
1156 		err = drmach_mem_add_span(
1157 			mp->sbm_cm.sbdev_id,
1158 			ml->address,
1159 			ml->size);
1160 
1161 		if (err)
1162 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
1163 
1164 		if (rv != KPHYSM_OK) {
1165 			cmn_err(CE_WARN, "%s:"
1166 				" unexpected kphysm_add_memory_dynamic"
1167 				" return value %d;"
1168 				" basepfn=0x%lx, npages=%ld\n",
1169 				f, rv, base, npgs);
1170 
1171 			continue;
1172 		}
1173 	}
1174 }
1175 
1176 static int
1177 dr_post_detach_mem_unit(dr_mem_unit_t *s_mp)
1178 {
1179 	uint64_t	sz = s_mp->sbm_slice_size;
1180 	uint64_t	sm = sz - 1;
1181 	/* old and new below refer to PAs before and after copy-rename */
1182 	uint64_t	s_old_basepa, s_new_basepa;
1183 	uint64_t	t_old_basepa, t_new_basepa;
1184 	dr_mem_unit_t	*t_mp, *x_mp;
1185 	drmach_mem_info_t	minfo;
1186 	struct memlist	*ml;
1187 	struct memlist	*t_excess_mlist;
1188 	int		rv;
1189 	int		s_excess_mem_deleted = 0;
1190 	sbd_error_t	*err;
1191 	static fn_t	f = "dr_post_detach_mem_unit";
1192 
1193 	PR_MEM("%s...\n", f);
1194 
1195 	/* s_mp->sbm_del_mlist could be NULL, meaning no deleted spans */
1196 	PR_MEM("%s: %s: deleted memlist (EMPTY maybe okay):\n",
1197 		f, s_mp->sbm_cm.sbdev_path);
1198 	PR_MEMLIST_DUMP(s_mp->sbm_del_mlist);
1199 
1200 	/* sanity check */
1201 	ASSERT(s_mp->sbm_del_mlist == NULL ||
1202 		(s_mp->sbm_flags & DR_MFLAG_RELDONE) != 0);
1203 
1204 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
1205 		t_mp = s_mp->sbm_peer;
1206 		ASSERT(t_mp != NULL);
1207 		ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
1208 		ASSERT(t_mp->sbm_peer == s_mp);
1209 
1210 		ASSERT(t_mp->sbm_flags & DR_MFLAG_RELDONE);
1211 		ASSERT(t_mp->sbm_del_mlist);
1212 
1213 		PR_MEM("%s: target %s: deleted memlist:\n",
1214 			f, t_mp->sbm_cm.sbdev_path);
1215 		PR_MEMLIST_DUMP(t_mp->sbm_del_mlist);
1216 	} else {
1217 		/* this is no target unit */
1218 		t_mp = NULL;
1219 	}
1220 
1221 	/*
1222 	 * Verify the memory really did successfully detach
1223 	 * by checking for its non-existence in phys_install.
1224 	 */
1225 	rv = 0;
1226 	memlist_read_lock();
1227 	if (s_mp->sbm_flags & DR_MFLAG_RELDONE) {
1228 		x_mp = s_mp;
1229 		rv = memlist_intersect(phys_install, x_mp->sbm_del_mlist);
1230 	}
1231 	if (rv == 0 && t_mp && (t_mp->sbm_flags & DR_MFLAG_RELDONE)) {
1232 		x_mp = t_mp;
1233 		rv = memlist_intersect(phys_install, x_mp->sbm_del_mlist);
1234 	}
1235 	memlist_read_unlock();
1236 
1237 	if (rv) {
1238 		/* error: memlist still in phys_install */
1239 		DR_DEV_INTERNAL_ERROR(&x_mp->sbm_cm);
1240 	}
1241 
1242 	/*
1243 	 * clean mem unit state and bail out if an error has been recorded.
1244 	 */
1245 	rv = 0;
1246 	if (s_mp->sbm_cm.sbdev_error) {
1247 		PR_MEM("%s: %s flags=%x", f,
1248 			s_mp->sbm_cm.sbdev_path, s_mp->sbm_flags);
1249 		DR_DEV_CLR_UNREFERENCED(&s_mp->sbm_cm);
1250 		DR_DEV_CLR_RELEASED(&s_mp->sbm_cm);
1251 		dr_device_transition(&s_mp->sbm_cm, DR_STATE_CONFIGURED);
1252 		rv = -1;
1253 	}
1254 	if (t_mp != NULL && t_mp->sbm_cm.sbdev_error != NULL) {
1255 		PR_MEM("%s: %s flags=%x", f,
1256 			s_mp->sbm_cm.sbdev_path, s_mp->sbm_flags);
1257 		DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
1258 		DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
1259 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_CONFIGURED);
1260 		rv = -1;
1261 	}
1262 	if (rv)
1263 		goto cleanup;
1264 
1265 	s_old_basepa = _ptob64(s_mp->sbm_basepfn);
1266 	err = drmach_mem_get_info(s_mp->sbm_cm.sbdev_id, &minfo);
1267 	ASSERT(err == NULL);
1268 	s_new_basepa = minfo.mi_basepa;
1269 
1270 	PR_MEM("%s:s_old_basepa: 0x%lx\n", f, s_old_basepa);
1271 	PR_MEM("%s:s_new_basepa: 0x%lx\n", f, s_new_basepa);
1272 
1273 	if (t_mp != NULL) {
1274 		struct memlist *s_copy_mlist;
1275 
1276 		t_old_basepa = _ptob64(t_mp->sbm_basepfn);
1277 		err = drmach_mem_get_info(t_mp->sbm_cm.sbdev_id, &minfo);
1278 		ASSERT(err == NULL);
1279 		t_new_basepa = minfo.mi_basepa;
1280 
1281 		PR_MEM("%s:t_old_basepa: 0x%lx\n", f, t_old_basepa);
1282 		PR_MEM("%s:t_new_basepa: 0x%lx\n", f, t_new_basepa);
1283 
1284 		/*
1285 		 * Construct copy list with original source addresses.
1286 		 * Used to add back excess target mem.
1287 		 */
1288 		s_copy_mlist = memlist_dup(s_mp->sbm_mlist);
1289 		for (ml = s_mp->sbm_del_mlist; ml; ml = ml->next) {
1290 			s_copy_mlist = memlist_del_span(s_copy_mlist,
1291 			    ml->address, ml->size);
1292 		}
1293 
1294 		PR_MEM("%s: source copy list:\n:", f);
1295 		PR_MEMLIST_DUMP(s_copy_mlist);
1296 
1297 		/*
1298 		 * We had to swap mem-units, so update
1299 		 * memlists accordingly with new base
1300 		 * addresses.
1301 		 */
1302 		for (ml = t_mp->sbm_mlist; ml; ml = ml->next) {
1303 			ml->address -= t_old_basepa;
1304 			ml->address += t_new_basepa;
1305 		}
1306 
1307 		/*
1308 		 * There is no need to explicitly rename the target delete
1309 		 * memlist, because sbm_del_mlist and sbm_mlist always
1310 		 * point to the same memlist for a copy/rename operation.
1311 		 */
1312 		ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
1313 
1314 		PR_MEM("%s: renamed target memlist and delete memlist:\n", f);
1315 		PR_MEMLIST_DUMP(t_mp->sbm_mlist);
1316 
1317 		for (ml = s_mp->sbm_mlist; ml; ml = ml->next) {
1318 			ml->address -= s_old_basepa;
1319 			ml->address += s_new_basepa;
1320 		}
1321 
1322 		PR_MEM("%s: renamed source memlist:\n", f);
1323 		PR_MEMLIST_DUMP(s_mp->sbm_mlist);
1324 
1325 		/*
1326 		 * Keep track of dynamically added segments
1327 		 * since they cannot be split if we need to delete
1328 		 * excess source memory later for this board.
1329 		 */
1330 		if (t_mp->sbm_dyn_segs)
1331 			memlist_delete(t_mp->sbm_dyn_segs);
1332 		t_mp->sbm_dyn_segs = s_mp->sbm_dyn_segs;
1333 		s_mp->sbm_dyn_segs = NULL;
1334 
1335 		/*
1336 		 * Add back excess target memory.
1337 		 * Subtract out the portion of the target memory
1338 		 * node that was taken over by the source memory
1339 		 * node.
1340 		 */
1341 		t_excess_mlist = memlist_dup(t_mp->sbm_mlist);
1342 		for (ml = s_copy_mlist; ml; ml = ml->next) {
1343 			t_excess_mlist =
1344 			    memlist_del_span(t_excess_mlist,
1345 			    ml->address, ml->size);
1346 		}
1347 
1348 		/*
1349 		 * Update dynamically added segs
1350 		 */
1351 		for (ml = s_mp->sbm_del_mlist; ml; ml = ml->next) {
1352 			t_mp->sbm_dyn_segs =
1353 			    memlist_del_span(t_mp->sbm_dyn_segs,
1354 			    ml->address, ml->size);
1355 		}
1356 		for (ml = t_excess_mlist; ml; ml = ml->next) {
1357 			t_mp->sbm_dyn_segs =
1358 			    memlist_cat_span(t_mp->sbm_dyn_segs,
1359 			    ml->address, ml->size);
1360 		}
1361 		PR_MEM("%s: %s: updated dynamic seg list:\n",
1362 		    f, t_mp->sbm_cm.sbdev_path);
1363 		PR_MEMLIST_DUMP(t_mp->sbm_dyn_segs);
1364 
1365 		if (t_excess_mlist != NULL) {
1366 			/*
1367 			 * After the small <-> big copy-rename,
1368 			 * the original address space for the
1369 			 * source board may have excess to be
1370 			 * deleted. This is a case different
1371 			 * from the big->small excess source
1372 			 * memory case listed below.
1373 			 * Remove s_mp->sbm_del_mlist from
1374 			 * the kernel cage glist.
1375 			 */
1376 			for (ml = s_mp->sbm_del_mlist; ml;
1377 				ml = ml->next) {
1378 				PR_MEM("%s: delete small<->big copy-"
1379 				    "rename source excess memory", f);
1380 				PR_MEMLIST_DUMP(ml);
1381 
1382 				err = drmach_mem_del_span(
1383 					s_mp->sbm_cm.sbdev_id,
1384 					    ml->address, ml->size);
1385 				if (err)
1386 					DRERR_SET_C(&s_mp->
1387 					    sbm_cm.sbdev_error, &err);
1388 				ASSERT(err == NULL);
1389 			}
1390 
1391 			/*
1392 			 * mark sbm_del_mlist as been deleted so that
1393 			 * we won't end up to delete it twice later
1394 			 * from the span list
1395 			 */
1396 			s_excess_mem_deleted = 1;
1397 
1398 			PR_MEM("%s: adding back remaining portion"
1399 				" of %s, memlist:\n",
1400 				f, t_mp->sbm_cm.sbdev_path);
1401 			PR_MEMLIST_DUMP(t_excess_mlist);
1402 
1403 			dr_add_memory_spans(s_mp, t_excess_mlist);
1404 			memlist_delete(t_excess_mlist);
1405 		}
1406 		memlist_delete(s_copy_mlist);
1407 
1408 #ifdef DEBUG
1409 		/*
1410 		 * s_mp->sbm_del_mlist may still needed
1411 		 */
1412 		PR_MEM("%s: source delete memeory flag %d",
1413 		    f, s_excess_mem_deleted);
1414 		PR_MEM("%s: source delete memlist", f);
1415 		PR_MEMLIST_DUMP(s_mp->sbm_del_mlist);
1416 #endif
1417 
1418 	}
1419 
1420 	if (t_mp != NULL) {
1421 		/* delete target's entire address space */
1422 		err = drmach_mem_del_span(
1423 			t_mp->sbm_cm.sbdev_id, t_old_basepa & ~ sm, sz);
1424 		if (err)
1425 			DRERR_SET_C(&t_mp->sbm_cm.sbdev_error, &err);
1426 		ASSERT(err == NULL);
1427 
1428 		/*
1429 		 * After the copy/rename, the original address space
1430 		 * for the source board (which is now located on the
1431 		 * target board) may now have some excess to be deleted.
1432 		 * Those excess memory on the source board are kept in
1433 		 * source board's sbm_del_mlist
1434 		 */
1435 		for (ml = s_mp->sbm_del_mlist; !s_excess_mem_deleted && ml;
1436 			ml = ml->next) {
1437 			PR_MEM("%s: delete source excess memory", f);
1438 			PR_MEMLIST_DUMP(ml);
1439 
1440 			err = drmach_mem_del_span(s_mp->sbm_cm.sbdev_id,
1441 				ml->address, ml->size);
1442 			if (err)
1443 				DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
1444 			ASSERT(err == NULL);
1445 		}
1446 
1447 	} else {
1448 		/* delete board's entire address space */
1449 		err = drmach_mem_del_span(s_mp->sbm_cm.sbdev_id,
1450 						s_old_basepa & ~ sm, sz);
1451 		if (err)
1452 			DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
1453 		ASSERT(err == NULL);
1454 	}
1455 
1456 cleanup:
1457 	/* clean up target mem unit */
1458 	if (t_mp != NULL) {
1459 		memlist_delete(t_mp->sbm_del_mlist);
1460 		/* no need to delete sbm_mlist, it shares sbm_del_mlist */
1461 
1462 		t_mp->sbm_del_mlist = NULL;
1463 		t_mp->sbm_mlist = NULL;
1464 		t_mp->sbm_peer = NULL;
1465 		t_mp->sbm_flags = 0;
1466 		t_mp->sbm_cm.sbdev_busy = 0;
1467 		dr_init_mem_unit_data(t_mp);
1468 
1469 	}
1470 	if (t_mp != NULL && t_mp->sbm_cm.sbdev_error == NULL) {
1471 		/*
1472 		 * now that copy/rename has completed, undo this
1473 		 * work that was done in dr_release_mem_done.
1474 		 */
1475 		DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
1476 		DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
1477 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_CONFIGURED);
1478 	}
1479 
1480 	/*
1481 	 * clean up (source) board's mem unit structure.
1482 	 * NOTE: sbm_mlist is retained if no error has been record (in other
1483 	 * words, when s_mp->sbm_cm.sbdev_error is NULL). This memlist is
1484 	 * referred to elsewhere as the cached memlist.  The cached memlist
1485 	 * is used to re-attach (configure back in) this memunit from the
1486 	 * unconfigured state.  The memlist is retained because it may
1487 	 * represent bad pages that were detected while the memory was
1488 	 * configured into the OS.  The OS deletes bad pages from phys_install.
1489 	 * Those deletes, if any, will be represented in the cached mlist.
1490 	 */
1491 	if (s_mp->sbm_del_mlist && s_mp->sbm_del_mlist != s_mp->sbm_mlist)
1492 		memlist_delete(s_mp->sbm_del_mlist);
1493 
1494 	if (s_mp->sbm_cm.sbdev_error && s_mp->sbm_mlist) {
1495 		memlist_delete(s_mp->sbm_mlist);
1496 		s_mp->sbm_mlist = NULL;
1497 	}
1498 
1499 	if (s_mp->sbm_dyn_segs != NULL && s_mp->sbm_cm.sbdev_error == 0) {
1500 		memlist_delete(s_mp->sbm_dyn_segs);
1501 		s_mp->sbm_dyn_segs = NULL;
1502 	}
1503 
1504 	s_mp->sbm_del_mlist = NULL;
1505 	s_mp->sbm_peer = NULL;
1506 	s_mp->sbm_flags = 0;
1507 	s_mp->sbm_cm.sbdev_busy = 0;
1508 	dr_init_mem_unit_data(s_mp);
1509 
1510 	PR_MEM("%s: cached memlist for %s:", f, s_mp->sbm_cm.sbdev_path);
1511 	PR_MEMLIST_DUMP(s_mp->sbm_mlist);
1512 
1513 	return (0);
1514 }
1515 
1516 /*
1517  * Successful return from this function will have the memory
1518  * handle in bp->b_dev[..mem-unit...].sbm_memhandle allocated
1519  * and waiting.  This routine's job is to select the memory that
1520  * actually has to be released (detached) which may not necessarily
1521  * be the same memory node that came in in devlist[],
1522  * i.e. a copy-rename is needed.
1523  */
1524 int
1525 dr_pre_release_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1526 {
1527 	int		d;
1528 	int		err_flag = 0;
1529 	static fn_t	f = "dr_pre_release_mem";
1530 
1531 	PR_MEM("%s...\n", f);
1532 
1533 	for (d = 0; d < devnum; d++) {
1534 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
1535 		int		rv;
1536 		memquery_t	mq;
1537 		struct memlist	*ml;
1538 
1539 		if (mp->sbm_cm.sbdev_error) {
1540 			err_flag = 1;
1541 			continue;
1542 		} else if (!kcage_on) {
1543 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_KCAGE_OFF);
1544 			err_flag = 1;
1545 			continue;
1546 		}
1547 
1548 		if (mp->sbm_flags & DR_MFLAG_RESERVED) {
1549 			/*
1550 			 * Board is currently involved in a delete
1551 			 * memory operation. Can't detach this guy until
1552 			 * that operation completes.
1553 			 */
1554 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_INVAL);
1555 			err_flag = 1;
1556 			break;
1557 		}
1558 
1559 		/* flags should be clean at this time */
1560 		ASSERT(mp->sbm_flags == 0);
1561 
1562 		ASSERT(mp->sbm_mlist == NULL);
1563 		ASSERT(mp->sbm_del_mlist == NULL);
1564 		if (mp->sbm_mlist != NULL) {
1565 			memlist_delete(mp->sbm_mlist);
1566 			mp->sbm_mlist = NULL;
1567 		}
1568 
1569 		ml = dr_get_memlist(mp);
1570 		if (ml == NULL) {
1571 			err_flag = 1;
1572 			PR_MEM("%s: no memlist found for %s\n",
1573 			    f, mp->sbm_cm.sbdev_path);
1574 			continue;
1575 		}
1576 
1577 		/*
1578 		 * Check whether the detaching memory requires a
1579 		 * copy-rename.
1580 		 */
1581 		ASSERT(mp->sbm_npages != 0);
1582 		rv = dr_del_mlist_query(ml, &mq);
1583 		if (rv != KPHYSM_OK) {
1584 			memlist_delete(ml);
1585 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
1586 			err_flag = 1;
1587 			break;
1588 		}
1589 
1590 		if (mq.nonrelocatable != 0) {
1591 			if (!(dr_cmd_flags(hp) &
1592 				(SBD_FLAG_FORCE | SBD_FLAG_QUIESCE_OKAY))) {
1593 				memlist_delete(ml);
1594 				/* caller wasn't prompted for a suspend */
1595 				dr_dev_err(CE_WARN, &mp->sbm_cm,
1596 					ESBD_QUIESCE_REQD);
1597 				err_flag = 1;
1598 				break;
1599 			}
1600 		}
1601 
1602 		/* allocate a kphysm handle */
1603 		rv = kphysm_del_gethandle(&mp->sbm_memhandle);
1604 		if (rv != KPHYSM_OK) {
1605 			memlist_delete(ml);
1606 
1607 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
1608 			err_flag = 1;
1609 			break;
1610 		}
1611 		mp->sbm_flags |= DR_MFLAG_RELOWNER;
1612 
1613 		if ((mq.nonrelocatable != 0) ||
1614 			dr_reserve_mem_spans(&mp->sbm_memhandle, ml)) {
1615 			/*
1616 			 * Either the detaching memory node contains
1617 			 * non-reloc memory or we failed to reserve the
1618 			 * detaching memory node (which did _not_ have
1619 			 * any non-reloc memory, i.e. some non-reloc mem
1620 			 * got onboard).
1621 			 */
1622 
1623 			if (dr_select_mem_target(hp, mp, ml)) {
1624 				int rv;
1625 
1626 				/*
1627 				 * We had no luck locating a target
1628 				 * memory node to be the recipient of
1629 				 * the non-reloc memory on the node
1630 				 * we're trying to detach.
1631 				 * Clean up be disposing the mem handle
1632 				 * and the mem list.
1633 				 */
1634 				rv = kphysm_del_release(mp->sbm_memhandle);
1635 				if (rv != KPHYSM_OK) {
1636 					/*
1637 					 * can do nothing but complain
1638 					 * and hope helpful for debug
1639 					 */
1640 					cmn_err(CE_WARN, "%s: unexpected"
1641 						" kphysm_del_release return"
1642 						" value %d",
1643 						f, rv);
1644 				}
1645 				mp->sbm_flags &= ~DR_MFLAG_RELOWNER;
1646 
1647 				memlist_delete(ml);
1648 
1649 				/* make sure sbm_flags is clean */
1650 				ASSERT(mp->sbm_flags == 0);
1651 
1652 				dr_dev_err(CE_WARN,
1653 					&mp->sbm_cm, ESBD_NO_TARGET);
1654 
1655 				err_flag = 1;
1656 				break;
1657 			}
1658 
1659 			/*
1660 			 * ml is not memlist_delete'd here because
1661 			 * it has been assigned to mp->sbm_mlist
1662 			 * by dr_select_mem_target.
1663 			 */
1664 		} else {
1665 			/* no target needed to detach this board */
1666 			mp->sbm_flags |= DR_MFLAG_RESERVED;
1667 			mp->sbm_peer = NULL;
1668 			mp->sbm_del_mlist = ml;
1669 			mp->sbm_mlist = ml;
1670 			mp->sbm_cm.sbdev_busy = 1;
1671 		}
1672 #ifdef DEBUG
1673 		ASSERT(mp->sbm_mlist != NULL);
1674 
1675 		if (mp->sbm_flags & DR_MFLAG_SOURCE) {
1676 			PR_MEM("%s: release of %s requires copy/rename;"
1677 				" selected target board %s\n",
1678 				f,
1679 				mp->sbm_cm.sbdev_path,
1680 				mp->sbm_peer->sbm_cm.sbdev_path);
1681 		} else {
1682 			PR_MEM("%s: copy/rename not required to release %s\n",
1683 				f, mp->sbm_cm.sbdev_path);
1684 		}
1685 
1686 		ASSERT(mp->sbm_flags & DR_MFLAG_RELOWNER);
1687 		ASSERT(mp->sbm_flags & DR_MFLAG_RESERVED);
1688 #endif
1689 	}
1690 
1691 	return (err_flag ? -1 : 0);
1692 }
1693 
1694 void
1695 dr_release_mem_done(dr_common_unit_t *cp)
1696 {
1697 	dr_mem_unit_t	*s_mp = (dr_mem_unit_t *)cp;
1698 	dr_mem_unit_t *t_mp, *mp;
1699 	int		rv;
1700 	static fn_t	f = "dr_release_mem_done";
1701 
1702 	/*
1703 	 * This unit will be flagged with DR_MFLAG_SOURCE, if it
1704 	 * has a target unit.
1705 	 */
1706 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
1707 		t_mp = s_mp->sbm_peer;
1708 		ASSERT(t_mp != NULL);
1709 		ASSERT(t_mp->sbm_peer == s_mp);
1710 		ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
1711 		ASSERT(t_mp->sbm_flags & DR_MFLAG_RESERVED);
1712 	} else {
1713 		/* this is no target unit */
1714 		t_mp = NULL;
1715 	}
1716 
1717 	/* free delete handle */
1718 	ASSERT(s_mp->sbm_flags & DR_MFLAG_RELOWNER);
1719 	ASSERT(s_mp->sbm_flags & DR_MFLAG_RESERVED);
1720 	rv = kphysm_del_release(s_mp->sbm_memhandle);
1721 	if (rv != KPHYSM_OK) {
1722 		/*
1723 		 * can do nothing but complain
1724 		 * and hope helpful for debug
1725 		 */
1726 		cmn_err(CE_WARN, "%s: unexpected kphysm_del_release"
1727 			" return value %d", f, rv);
1728 	}
1729 	s_mp->sbm_flags &= ~DR_MFLAG_RELOWNER;
1730 
1731 	/*
1732 	 * If an error was encountered during release, clean up
1733 	 * the source (and target, if present) unit data.
1734 	 */
1735 /* XXX Can we know that sbdev_error was encountered during release? */
1736 	if (s_mp->sbm_cm.sbdev_error != NULL) {
1737 		PR_MEM("%s: %s: error %d noted\n",
1738 			f,
1739 			s_mp->sbm_cm.sbdev_path,
1740 			s_mp->sbm_cm.sbdev_error->e_code);
1741 
1742 		if (t_mp != NULL) {
1743 			ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
1744 			t_mp->sbm_del_mlist = NULL;
1745 
1746 			if (t_mp->sbm_mlist != NULL) {
1747 				memlist_delete(t_mp->sbm_mlist);
1748 				t_mp->sbm_mlist = NULL;
1749 			}
1750 
1751 			t_mp->sbm_peer = NULL;
1752 			t_mp->sbm_flags = 0;
1753 			t_mp->sbm_cm.sbdev_busy = 0;
1754 		}
1755 
1756 		if (s_mp->sbm_del_mlist != s_mp->sbm_mlist)
1757 			memlist_delete(s_mp->sbm_del_mlist);
1758 		s_mp->sbm_del_mlist = NULL;
1759 
1760 		if (s_mp->sbm_mlist != NULL) {
1761 			memlist_delete(s_mp->sbm_mlist);
1762 			s_mp->sbm_mlist = NULL;
1763 		}
1764 
1765 		s_mp->sbm_peer = NULL;
1766 		s_mp->sbm_flags = 0;
1767 		s_mp->sbm_cm.sbdev_busy = 0;
1768 
1769 		/* bail out */
1770 		return;
1771 	}
1772 
1773 	DR_DEV_SET_RELEASED(&s_mp->sbm_cm);
1774 	dr_device_transition(&s_mp->sbm_cm, DR_STATE_RELEASE);
1775 
1776 	if (t_mp != NULL) {
1777 		/*
1778 		 * the kphysm delete operation that drained the source
1779 		 * board also drained this target board.  Since the source
1780 		 * board drain is now known to have succeeded, we know this
1781 		 * target board is drained too.
1782 		 *
1783 		 * because DR_DEV_SET_RELEASED and dr_device_transition
1784 		 * is done here, the dr_release_dev_done should not
1785 		 * fail.
1786 		 */
1787 		DR_DEV_SET_RELEASED(&t_mp->sbm_cm);
1788 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_RELEASE);
1789 
1790 		/*
1791 		 * NOTE: do not transition target's board state,
1792 		 * even if the mem-unit was the last configure
1793 		 * unit of the board.  When copy/rename completes
1794 		 * this mem-unit will transitioned back to
1795 		 * the configured state.  In the meantime, the
1796 		 * board's must remain as is.
1797 		 */
1798 	}
1799 
1800 	/* if board(s) had deleted memory, verify it is gone */
1801 	rv = 0;
1802 	memlist_read_lock();
1803 	if (s_mp->sbm_del_mlist != NULL) {
1804 		mp = s_mp;
1805 		rv = memlist_intersect(phys_install, mp->sbm_del_mlist);
1806 	}
1807 	if (rv == 0 && t_mp && t_mp->sbm_del_mlist != NULL) {
1808 		mp = t_mp;
1809 		rv = memlist_intersect(phys_install, mp->sbm_del_mlist);
1810 	}
1811 	memlist_read_unlock();
1812 	if (rv) {
1813 		cmn_err(CE_WARN, "%s: %smem-unit (%d.%d): "
1814 			"deleted memory still found in phys_install",
1815 			f,
1816 			(mp == t_mp ? "target " : ""),
1817 			mp->sbm_cm.sbdev_bp->b_num,
1818 			mp->sbm_cm.sbdev_unum);
1819 
1820 		DR_DEV_INTERNAL_ERROR(&s_mp->sbm_cm);
1821 		return;
1822 	}
1823 
1824 	s_mp->sbm_flags |= DR_MFLAG_RELDONE;
1825 	if (t_mp != NULL)
1826 		t_mp->sbm_flags |= DR_MFLAG_RELDONE;
1827 
1828 	/* this should not fail */
1829 	if (dr_release_dev_done(&s_mp->sbm_cm) != 0) {
1830 		/* catch this in debug kernels */
1831 		ASSERT(0);
1832 		return;
1833 	}
1834 
1835 	PR_MEM("%s: marking %s release DONE\n",
1836 		f, s_mp->sbm_cm.sbdev_path);
1837 
1838 	s_mp->sbm_cm.sbdev_ostate = SBD_STAT_UNCONFIGURED;
1839 
1840 	if (t_mp != NULL) {
1841 		/* should not fail */
1842 		rv = dr_release_dev_done(&t_mp->sbm_cm);
1843 		if (rv != 0) {
1844 			/* catch this in debug kernels */
1845 			ASSERT(0);
1846 			return;
1847 		}
1848 
1849 		PR_MEM("%s: marking %s release DONE\n",
1850 			f, t_mp->sbm_cm.sbdev_path);
1851 
1852 		t_mp->sbm_cm.sbdev_ostate = SBD_STAT_UNCONFIGURED;
1853 	}
1854 }
1855 
1856 /*ARGSUSED*/
1857 int
1858 dr_disconnect_mem(dr_mem_unit_t *mp)
1859 {
1860 	static fn_t	f = "dr_disconnect_mem";
1861 	update_membounds_t umb;
1862 
1863 #ifdef DEBUG
1864 	int state = mp->sbm_cm.sbdev_state;
1865 	ASSERT(state == DR_STATE_CONNECTED ||
1866 		state == DR_STATE_UNCONFIGURED);
1867 #endif
1868 
1869 	PR_MEM("%s...\n", f);
1870 
1871 	if (mp->sbm_del_mlist && mp->sbm_del_mlist != mp->sbm_mlist)
1872 		memlist_delete(mp->sbm_del_mlist);
1873 	mp->sbm_del_mlist = NULL;
1874 
1875 	if (mp->sbm_mlist) {
1876 		memlist_delete(mp->sbm_mlist);
1877 		mp->sbm_mlist = NULL;
1878 	}
1879 
1880 	/*
1881 	 * Remove memory from lgroup
1882 	 * For now, only board info is required.
1883 	 */
1884 	umb.u_board = mp->sbm_cm.sbdev_bp->b_num;
1885 	umb.u_base = (uint64_t)-1;
1886 	umb.u_len = (uint64_t)-1;
1887 
1888 	lgrp_plat_config(LGRP_CONFIG_MEM_DEL, (uintptr_t)&umb);
1889 
1890 	return (0);
1891 }
1892 
1893 int
1894 dr_cancel_mem(dr_mem_unit_t *s_mp)
1895 {
1896 	dr_mem_unit_t	*t_mp;
1897 	dr_state_t	state;
1898 	static fn_t	f = "dr_cancel_mem";
1899 
1900 	state = s_mp->sbm_cm.sbdev_state;
1901 
1902 	if (s_mp->sbm_flags & DR_MFLAG_TARGET) {
1903 		/* must cancel source board, not target board */
1904 		/* TODO: set error */
1905 		return (-1);
1906 	} else if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
1907 		t_mp = s_mp->sbm_peer;
1908 		ASSERT(t_mp != NULL);
1909 		ASSERT(t_mp->sbm_peer == s_mp);
1910 
1911 		/* must always match the source board's state */
1912 		/* TODO: is this assertion correct? */
1913 		ASSERT(t_mp->sbm_cm.sbdev_state == state);
1914 	} else {
1915 		/* this is no target unit */
1916 		t_mp = NULL;
1917 	}
1918 
1919 	switch (state) {
1920 	case DR_STATE_UNREFERENCED:	/* state set by dr_release_dev_done */
1921 		ASSERT((s_mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
1922 
1923 		if (t_mp != NULL && t_mp->sbm_del_mlist != NULL) {
1924 			PR_MEM("%s: undoing target %s memory delete\n",
1925 				f, t_mp->sbm_cm.sbdev_path);
1926 			dr_add_memory_spans(t_mp, t_mp->sbm_del_mlist);
1927 
1928 			DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
1929 		}
1930 
1931 		if (s_mp->sbm_del_mlist != NULL) {
1932 			PR_MEM("%s: undoing %s memory delete\n",
1933 				f, s_mp->sbm_cm.sbdev_path);
1934 
1935 			dr_add_memory_spans(s_mp, s_mp->sbm_del_mlist);
1936 		}
1937 
1938 		/*FALLTHROUGH*/
1939 
1940 /* TODO: should no longer be possible to see the release state here */
1941 	case DR_STATE_RELEASE:	/* state set by dr_release_mem_done */
1942 
1943 		ASSERT((s_mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
1944 
1945 		if (t_mp != NULL) {
1946 			ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
1947 			t_mp->sbm_del_mlist = NULL;
1948 
1949 			if (t_mp->sbm_mlist != NULL) {
1950 				memlist_delete(t_mp->sbm_mlist);
1951 				t_mp->sbm_mlist = NULL;
1952 			}
1953 
1954 			t_mp->sbm_peer = NULL;
1955 			t_mp->sbm_flags = 0;
1956 			t_mp->sbm_cm.sbdev_busy = 0;
1957 			dr_init_mem_unit_data(t_mp);
1958 
1959 			DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
1960 
1961 			dr_device_transition(
1962 				&t_mp->sbm_cm, DR_STATE_CONFIGURED);
1963 		}
1964 
1965 		if (s_mp->sbm_del_mlist != s_mp->sbm_mlist)
1966 			memlist_delete(s_mp->sbm_del_mlist);
1967 		s_mp->sbm_del_mlist = NULL;
1968 
1969 		if (s_mp->sbm_mlist != NULL) {
1970 			memlist_delete(s_mp->sbm_mlist);
1971 			s_mp->sbm_mlist = NULL;
1972 		}
1973 
1974 		s_mp->sbm_peer = NULL;
1975 		s_mp->sbm_flags = 0;
1976 		s_mp->sbm_cm.sbdev_busy = 0;
1977 		dr_init_mem_unit_data(s_mp);
1978 
1979 		return (0);
1980 
1981 	default:
1982 		PR_MEM("%s: WARNING unexpected state (%d) for %s\n",
1983 			f, (int)state, s_mp->sbm_cm.sbdev_path);
1984 
1985 		return (-1);
1986 	}
1987 	/*NOTREACHED*/
1988 }
1989 
1990 void
1991 dr_init_mem_unit(dr_mem_unit_t *mp)
1992 {
1993 	dr_state_t	new_state;
1994 
1995 
1996 	if (DR_DEV_IS_ATTACHED(&mp->sbm_cm)) {
1997 		new_state = DR_STATE_CONFIGURED;
1998 		mp->sbm_cm.sbdev_cond = SBD_COND_OK;
1999 	} else if (DR_DEV_IS_PRESENT(&mp->sbm_cm)) {
2000 		new_state = DR_STATE_CONNECTED;
2001 		mp->sbm_cm.sbdev_cond = SBD_COND_OK;
2002 	} else if (mp->sbm_cm.sbdev_id != (drmachid_t)0) {
2003 		new_state = DR_STATE_OCCUPIED;
2004 	} else {
2005 		new_state = DR_STATE_EMPTY;
2006 	}
2007 
2008 	if (DR_DEV_IS_PRESENT(&mp->sbm_cm))
2009 		dr_init_mem_unit_data(mp);
2010 
2011 	/* delay transition until fully initialized */
2012 	dr_device_transition(&mp->sbm_cm, new_state);
2013 }
2014 
2015 static void
2016 dr_init_mem_unit_data(dr_mem_unit_t *mp)
2017 {
2018 	drmachid_t	id = mp->sbm_cm.sbdev_id;
2019 	drmach_mem_info_t	minfo;
2020 	sbd_error_t	*err;
2021 	static fn_t	f = "dr_init_mem_unit_data";
2022 	update_membounds_t umb;
2023 
2024 	PR_MEM("%s...\n", f);
2025 
2026 	/* a little sanity checking */
2027 	ASSERT(mp->sbm_peer == NULL);
2028 	ASSERT(mp->sbm_flags == 0);
2029 
2030 	if (err = drmach_mem_get_info(id, &minfo)) {
2031 		DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
2032 		return;
2033 	}
2034 	mp->sbm_basepfn = _b64top(minfo.mi_basepa);
2035 	mp->sbm_npages = _b64top(minfo.mi_size);
2036 	mp->sbm_alignment_mask = _b64top(minfo.mi_alignment_mask);
2037 	mp->sbm_slice_size = minfo.mi_slice_size;
2038 
2039 	/*
2040 	 * Add memory to lgroup
2041 	 */
2042 	umb.u_board = mp->sbm_cm.sbdev_bp->b_num;
2043 	umb.u_base = (uint64_t)mp->sbm_basepfn << MMU_PAGESHIFT;
2044 	umb.u_len = (uint64_t)mp->sbm_npages << MMU_PAGESHIFT;
2045 
2046 	lgrp_plat_config(LGRP_CONFIG_MEM_ADD, (uintptr_t)&umb);
2047 
2048 	PR_MEM("%s: %s (basepfn = 0x%lx, npgs = %ld)\n",
2049 		f, mp->sbm_cm.sbdev_path, mp->sbm_basepfn, mp->sbm_npages);
2050 }
2051 
2052 static int
2053 dr_reserve_mem_spans(memhandle_t *mhp, struct memlist *ml)
2054 {
2055 	int		err;
2056 	pfn_t		base;
2057 	pgcnt_t		npgs;
2058 	struct memlist	*mc;
2059 	static fn_t	f = "dr_reserve_mem_spans";
2060 
2061 	PR_MEM("%s...\n", f);
2062 
2063 	/*
2064 	 * Walk the supplied memlist scheduling each span for removal
2065 	 * with kphysm_del_span.  It is possible that a span may intersect
2066 	 * an area occupied by the cage.
2067 	 */
2068 	for (mc = ml; mc != NULL; mc = mc->next) {
2069 		base = _b64top(mc->address);
2070 		npgs = _b64top(mc->size);
2071 
2072 		err = kphysm_del_span(*mhp, base, npgs);
2073 		if (err != KPHYSM_OK) {
2074 			cmn_err(CE_WARN, "%s memory reserve failed."
2075 				" unexpected kphysm_del_span return value %d;"
2076 				" basepfn=0x%lx npages=%ld",
2077 				f, err, base, npgs);
2078 
2079 			return (-1);
2080 		}
2081 	}
2082 
2083 	return (0);
2084 }
2085 
2086 #define	DR_SMT_NPREF_SETS	6
2087 #define	DR_SMT_NUNITS_PER_SET	MAX_BOARDS * MAX_MEM_UNITS_PER_BOARD
2088 
2089 /* debug counters */
2090 int dr_smt_realigned;
2091 int dr_smt_preference[DR_SMT_NPREF_SETS];
2092 
2093 #ifdef DEBUG
2094 uint_t dr_ignore_board; /* if bit[bnum-1] set, board won't be candidate */
2095 #endif
2096 
2097 /*
2098  * Find and reserve a copy/rename target board suitable for the
2099  * given source board.
2100  * All boards in the system are examined and categorized in relation to
2101  * their memory size versus the source board's memory size.  Order of
2102  * preference is:
2103  *	1st copy all source, source/target same size
2104  *	2nd copy all source, larger target
2105  * 	3rd copy nonrelocatable source span
2106  */
2107 static int
2108 dr_select_mem_target(dr_handle_t *hp,
2109 	dr_mem_unit_t *s_mp, struct memlist *s_ml)
2110 {
2111 	dr_target_pref_t preference; /* lower value is higher preference */
2112 	int		idx;
2113 	dr_mem_unit_t	**sets;
2114 
2115 	int		t_bd;
2116 	int		t_unit;
2117 	int		rv;
2118 	dr_board_t	*s_bp, *t_bp;
2119 	dr_mem_unit_t	*t_mp, *c_mp;
2120 	struct memlist	*d_ml, *t_ml, *ml, *b_ml, *x_ml = NULL;
2121 	memquery_t	s_mq = {0};
2122 	static fn_t	f = "dr_select_mem_target";
2123 
2124 	PR_MEM("%s...\n", f);
2125 
2126 	ASSERT(s_ml != NULL);
2127 
2128 	sets = GETSTRUCT(dr_mem_unit_t *, DR_SMT_NUNITS_PER_SET *
2129 	    DR_SMT_NPREF_SETS);
2130 
2131 	s_bp = hp->h_bd;
2132 	/* calculate the offset into the slice of the last source board pfn */
2133 	ASSERT(s_mp->sbm_npages != 0);
2134 
2135 	/*
2136 	 * Find non-relocatable span on source board.
2137 	 */
2138 	rv = kphysm_del_span_query(s_mp->sbm_basepfn, s_mp->sbm_npages, &s_mq);
2139 	if (rv != KPHYSM_OK) {
2140 		PR_MEM("%s: %s: unexpected kphysm_del_span_query"
2141 		    " return value %d; basepfn 0x%lx, npages %ld\n",
2142 		    f, s_mp->sbm_cm.sbdev_path, rv, s_mp->sbm_basepfn,
2143 		    s_mp->sbm_npages);
2144 		return (-1);
2145 	}
2146 
2147 	ASSERT(s_mq.phys_pages != 0);
2148 	ASSERT(s_mq.nonrelocatable != 0);
2149 
2150 	PR_MEM("%s: %s: nonrelocatable span (0x%lx..0x%lx)\n", f,
2151 	    s_mp->sbm_cm.sbdev_path, s_mq.first_nonrelocatable,
2152 	    s_mq.last_nonrelocatable);
2153 
2154 	/* break down s_ml if it contains dynamic segments */
2155 	b_ml = memlist_dup(s_ml);
2156 
2157 	for (ml = s_mp->sbm_dyn_segs; ml; ml = ml->next) {
2158 		b_ml = memlist_del_span(b_ml, ml->address, ml->size);
2159 		b_ml = memlist_cat_span(b_ml, ml->address, ml->size);
2160 	}
2161 
2162 
2163 	/*
2164 	 * Make one pass through all memory units on all boards
2165 	 * and categorize them with respect to the source board.
2166 	 */
2167 	for (t_bd = 0; t_bd < MAX_BOARDS; t_bd++) {
2168 		/*
2169 		 * The board structs are a contiguous array
2170 		 * so we take advantage of that to find the
2171 		 * correct board struct pointer for a given
2172 		 * board number.
2173 		 */
2174 		t_bp = dr_lookup_board(t_bd);
2175 
2176 		/* source board can not be its own target */
2177 		if (s_bp->b_num == t_bp->b_num)
2178 			continue;
2179 
2180 		for (t_unit = 0; t_unit < MAX_MEM_UNITS_PER_BOARD; t_unit++) {
2181 
2182 			t_mp = dr_get_mem_unit(t_bp, t_unit);
2183 
2184 			/* this memory node must be attached */
2185 			if (!DR_DEV_IS_ATTACHED(&t_mp->sbm_cm))
2186 				continue;
2187 
2188 			/* source unit can not be its own target */
2189 			if (s_mp == t_mp) {
2190 				/* catch this is debug kernels */
2191 				ASSERT(0);
2192 				continue;
2193 			}
2194 
2195 			/*
2196 			 * this memory node must not already be reserved
2197 			 * by some other memory delete operation.
2198 			 */
2199 			if (t_mp->sbm_flags & DR_MFLAG_RESERVED)
2200 				continue;
2201 
2202 			/* get target board memlist */
2203 			t_ml = dr_get_memlist(t_mp);
2204 			if (t_ml == NULL) {
2205 				cmn_err(CE_WARN, "%s: no memlist for"
2206 				    " mem-unit %d, board %d", f,
2207 				    t_mp->sbm_cm.sbdev_bp->b_num,
2208 				    t_mp->sbm_cm.sbdev_unum);
2209 				continue;
2210 			}
2211 
2212 			preference = dr_get_target_preference(hp, t_mp, s_mp,
2213 			    t_ml, s_ml, b_ml);
2214 
2215 			if (preference == DR_TP_INVALID)
2216 				continue;
2217 
2218 			dr_smt_preference[preference]++;
2219 
2220 			/* calculate index to start of preference set */
2221 			idx  = DR_SMT_NUNITS_PER_SET * preference;
2222 			/* calculate offset to respective element */
2223 			idx += t_bd * MAX_MEM_UNITS_PER_BOARD + t_unit;
2224 
2225 			ASSERT(idx < DR_SMT_NUNITS_PER_SET * DR_SMT_NPREF_SETS);
2226 			sets[idx] = t_mp;
2227 		}
2228 	}
2229 
2230 	if (b_ml != NULL)
2231 		memlist_delete(b_ml);
2232 
2233 	/*
2234 	 * NOTE: this would be a good place to sort each candidate
2235 	 * set in to some desired order, e.g. memory size in ascending
2236 	 * order.  Without an additional sorting step here, the order
2237 	 * within a set is ascending board number order.
2238 	 */
2239 
2240 	c_mp = NULL;
2241 	x_ml = NULL;
2242 	t_ml = NULL;
2243 	for (idx = 0; idx < DR_SMT_NUNITS_PER_SET * DR_SMT_NPREF_SETS; idx++) {
2244 		memquery_t mq;
2245 
2246 		preference = (dr_target_pref_t)(idx / DR_SMT_NUNITS_PER_SET);
2247 
2248 		ASSERT(preference != DR_TP_INVALID);
2249 
2250 		/* cleanup t_ml after previous pass */
2251 		if (t_ml != NULL) {
2252 			memlist_delete(t_ml);
2253 			t_ml = NULL;
2254 		}
2255 
2256 		/* get candidate target board mem unit */
2257 		t_mp = sets[idx];
2258 		if (t_mp == NULL)
2259 			continue;
2260 
2261 		/* get target board memlist */
2262 		t_ml = dr_get_memlist(t_mp);
2263 		if (t_ml == NULL) {
2264 			cmn_err(CE_WARN, "%s: no memlist for"
2265 				" mem-unit %d, board %d",
2266 				f,
2267 				t_mp->sbm_cm.sbdev_bp->b_num,
2268 				t_mp->sbm_cm.sbdev_unum);
2269 
2270 			continue;
2271 		}
2272 
2273 		PR_MEM("%s: checking for no-reloc in %s, "
2274 			" basepfn=0x%lx, npages=%ld\n",
2275 			f,
2276 			t_mp->sbm_cm.sbdev_path,
2277 			t_mp->sbm_basepfn,
2278 			t_mp->sbm_npages);
2279 
2280 		rv = dr_del_mlist_query(t_ml, &mq);
2281 		if (rv != KPHYSM_OK) {
2282 			PR_MEM("%s: kphysm_del_span_query:"
2283 				" unexpected return value %d\n", f, rv);
2284 
2285 			continue;
2286 		}
2287 
2288 		if (mq.nonrelocatable != 0) {
2289 			PR_MEM("%s: candidate %s has"
2290 				" nonrelocatable span [0x%lx..0x%lx]\n",
2291 				f,
2292 				t_mp->sbm_cm.sbdev_path,
2293 				mq.first_nonrelocatable,
2294 				mq.last_nonrelocatable);
2295 
2296 			continue;
2297 		}
2298 
2299 #ifdef DEBUG
2300 		/*
2301 		 * This is a debug tool for excluding certain boards
2302 		 * from being selected as a target board candidate.
2303 		 * dr_ignore_board is only tested by this driver.
2304 		 * It must be set with adb, obp, /etc/system or your
2305 		 * favorite debugger.
2306 		 */
2307 		if (dr_ignore_board &
2308 			(1 << (t_mp->sbm_cm.sbdev_bp->b_num - 1))) {
2309 			PR_MEM("%s: dr_ignore_board flag set,"
2310 				" ignoring %s as candidate\n",
2311 				f, t_mp->sbm_cm.sbdev_path);
2312 			continue;
2313 		}
2314 #endif
2315 
2316 		/*
2317 		 * Reserve excess source board memory, if any.
2318 		 *
2319 		 * Only the nonrelocatable source span will be copied
2320 		 * so schedule the rest of the source mem to be deleted.
2321 		 */
2322 		switch (preference) {
2323 		case DR_TP_NONRELOC:
2324 			/*
2325 			 * Get source copy memlist and use it to construct
2326 			 * delete memlist.
2327 			 */
2328 			d_ml = memlist_dup(s_ml);
2329 			x_ml = dr_get_copy_mlist(s_ml, t_ml, s_mp, t_mp);
2330 
2331 			/* XXX */
2332 			ASSERT(d_ml != NULL);
2333 			ASSERT(x_ml != NULL);
2334 
2335 			for (ml = x_ml; ml != NULL; ml = ml->next) {
2336 				d_ml = memlist_del_span(d_ml, ml->address,
2337 				    ml->size);
2338 			}
2339 
2340 			PR_MEM("%s: %s: reserving src brd memlist:\n", f,
2341 			    s_mp->sbm_cm.sbdev_path);
2342 			PR_MEMLIST_DUMP(d_ml);
2343 
2344 			/* reserve excess spans */
2345 			if (dr_reserve_mem_spans(&s_mp->sbm_memhandle,
2346 			    d_ml) != 0) {
2347 				/* likely more non-reloc pages appeared */
2348 				/* TODO: restart from top? */
2349 				continue;
2350 			}
2351 			break;
2352 		default:
2353 			d_ml = NULL;
2354 			break;
2355 		}
2356 
2357 		s_mp->sbm_flags |= DR_MFLAG_RESERVED;
2358 
2359 		/*
2360 		 * reserve all memory on target board.
2361 		 * NOTE: source board's memhandle is used.
2362 		 *
2363 		 * If this succeeds (eq 0), then target selection is
2364 		 * complete and all unwanted memory spans, both source and
2365 		 * target, have been reserved.  Loop is terminated.
2366 		 */
2367 		if (dr_reserve_mem_spans(&s_mp->sbm_memhandle, t_ml) == 0) {
2368 			PR_MEM("%s: %s: target board memory reserved\n",
2369 				f, t_mp->sbm_cm.sbdev_path);
2370 
2371 			/* a candidate target board is now reserved */
2372 			t_mp->sbm_flags |= DR_MFLAG_RESERVED;
2373 			c_mp = t_mp;
2374 
2375 			/* *** EXITING LOOP *** */
2376 			break;
2377 		}
2378 
2379 		/* did not successfully reserve the target board. */
2380 		PR_MEM("%s: could not reserve target %s\n",
2381 			f, t_mp->sbm_cm.sbdev_path);
2382 
2383 		/*
2384 		 * NOTE: an undo of the dr_reserve_mem_span work
2385 		 * will happen automatically when the memhandle
2386 		 * (s_mp->sbm_memhandle) is kphysm_del_release'd.
2387 		 */
2388 
2389 		s_mp->sbm_flags &= ~DR_MFLAG_RESERVED;
2390 	}
2391 
2392 	/* clean up after memlist editing logic */
2393 	if (x_ml != NULL)
2394 		memlist_delete(x_ml);
2395 
2396 	FREESTRUCT(sets, dr_mem_unit_t *, DR_SMT_NUNITS_PER_SET *
2397 	    DR_SMT_NPREF_SETS);
2398 
2399 	/*
2400 	 * c_mp will be NULL when the entire sets[] array
2401 	 * has been searched without reserving a target board.
2402 	 */
2403 	if (c_mp == NULL) {
2404 		PR_MEM("%s: %s: target selection failed.\n",
2405 			f, s_mp->sbm_cm.sbdev_path);
2406 
2407 		if (t_ml != NULL)
2408 			memlist_delete(t_ml);
2409 
2410 		return (-1);
2411 	}
2412 
2413 	PR_MEM("%s: found target %s for source %s\n",
2414 		f,
2415 		c_mp->sbm_cm.sbdev_path,
2416 		s_mp->sbm_cm.sbdev_path);
2417 
2418 	s_mp->sbm_peer = c_mp;
2419 	s_mp->sbm_flags |= DR_MFLAG_SOURCE;
2420 	s_mp->sbm_del_mlist = d_ml;	/* spans to be deleted, if any */
2421 	s_mp->sbm_mlist = s_ml;
2422 	s_mp->sbm_cm.sbdev_busy = 1;
2423 
2424 	c_mp->sbm_peer = s_mp;
2425 	c_mp->sbm_flags |= DR_MFLAG_TARGET;
2426 	c_mp->sbm_del_mlist = t_ml;	/* spans to be deleted */
2427 	c_mp->sbm_mlist = t_ml;
2428 	c_mp->sbm_cm.sbdev_busy = 1;
2429 
2430 	return (0);
2431 }
2432 
2433 /*
2434  * Returns target preference rank:
2435  *     -1 not a valid copy-rename target board
2436  *	0 copy all source, source/target same size
2437  *	1 copy all source, larger target
2438  * 	2 copy nonrelocatable source span
2439  */
2440 static dr_target_pref_t
2441 dr_get_target_preference(dr_handle_t *hp,
2442     dr_mem_unit_t *t_mp, dr_mem_unit_t *s_mp,
2443     struct memlist *t_ml, struct memlist *s_ml,
2444     struct memlist *b_ml)
2445 {
2446 	dr_target_pref_t preference;
2447 	struct memlist *s_nonreloc_ml = NULL;
2448 	drmachid_t t_id;
2449 	static fn_t	f = "dr_get_target_preference";
2450 
2451 	t_id = t_mp->sbm_cm.sbdev_bp->b_id;
2452 
2453 	/*
2454 	 * Can the entire source board be copied?
2455 	 */
2456 	if (dr_memlist_canfit(s_ml, t_ml, s_mp, t_mp)) {
2457 		if (s_mp->sbm_npages == t_mp->sbm_npages)
2458 			preference = DR_TP_SAME;	/* same size */
2459 		else
2460 			preference = DR_TP_LARGE;	/* larger target */
2461 	} else {
2462 		/*
2463 		 * Entire source won't fit so try non-relocatable memory only
2464 		 * (target aligned).
2465 		 */
2466 		s_nonreloc_ml = dr_get_nonreloc_mlist(b_ml, s_mp);
2467 		if (s_nonreloc_ml == NULL) {
2468 			PR_MEM("%s: dr_get_nonreloc_mlist failed\n", f);
2469 			preference = DR_TP_INVALID;
2470 		}
2471 		if (dr_memlist_canfit(s_nonreloc_ml, t_ml, s_mp, t_mp))
2472 			preference = DR_TP_NONRELOC;
2473 		else
2474 			preference = DR_TP_INVALID;
2475 	}
2476 
2477 	if (s_nonreloc_ml != NULL)
2478 		memlist_delete(s_nonreloc_ml);
2479 
2480 	/*
2481 	 * Force floating board preference lower than all other boards
2482 	 * if the force flag is present; otherwise disallow the board.
2483 	 */
2484 	if ((preference != DR_TP_INVALID) && drmach_board_is_floating(t_id)) {
2485 		if (dr_cmd_flags(hp) & SBD_FLAG_FORCE)
2486 			preference += DR_TP_FLOATING;
2487 		else
2488 			preference = DR_TP_INVALID;
2489 	}
2490 
2491 	PR_MEM("%s: %s preference=%d\n", f, t_mp->sbm_cm.sbdev_path,
2492 	    preference);
2493 
2494 	return (preference);
2495 }
2496 
2497 /*
2498  * Create a memlist representing the source memory that will be copied to
2499  * the target board.  The memory to be copied is the maximum amount that
2500  * will fit on the target board.
2501  */
2502 static struct memlist *
2503 dr_get_copy_mlist(struct memlist *s_mlist, struct memlist *t_mlist,
2504     dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp)
2505 {
2506 	struct memlist	*t_ml, *s_copy_ml, *s_del_ml, *ml, *x_ml;
2507 	uint64_t	s_slice_mask, s_slice_base;
2508 	uint64_t	t_slice_mask, t_slice_base;
2509 	static fn_t	f = "dr_get_copy_mlist";
2510 
2511 	ASSERT(s_mlist != NULL);
2512 	ASSERT(t_mlist != NULL);
2513 	ASSERT(t_mp->sbm_slice_size == s_mp->sbm_slice_size);
2514 
2515 	s_slice_mask = s_mp->sbm_slice_size - 1;
2516 	s_slice_base = s_mlist->address & ~s_slice_mask;
2517 
2518 	t_slice_mask = t_mp->sbm_slice_size - 1;
2519 	t_slice_base = t_mlist->address & ~t_slice_mask;
2520 
2521 	t_ml = memlist_dup(t_mlist);
2522 	s_del_ml = memlist_dup(s_mlist);
2523 	s_copy_ml = memlist_dup(s_mlist);
2524 
2525 	/* XXX */
2526 	ASSERT(t_ml != NULL);
2527 	ASSERT(s_del_ml != NULL);
2528 	ASSERT(s_copy_ml != NULL);
2529 
2530 	/*
2531 	 * To construct the source copy memlist:
2532 	 *
2533 	 * The target memlist is converted to the post-rename
2534 	 * source addresses.  This is the physical address range
2535 	 * the target will have after the copy-rename.  Overlaying
2536 	 * and deleting this from the current source memlist will
2537 	 * give the source delete memlist.  The copy memlist is
2538 	 * the reciprocal of the source delete memlist.
2539 	 */
2540 	for (ml = t_ml; ml != NULL; ml = ml->next) {
2541 		/*
2542 		 * Normalize relative to target slice base PA
2543 		 * in order to preseve slice offsets.
2544 		 */
2545 		ml->address -= t_slice_base;
2546 		/*
2547 		 * Convert to source slice PA address.
2548 		 */
2549 		ml->address += s_slice_base;
2550 	}
2551 
2552 	for (ml = t_ml; ml != NULL; ml = ml->next) {
2553 		s_del_ml = memlist_del_span(s_del_ml, ml->address, ml->size);
2554 	}
2555 
2556 	/*
2557 	 * Expand the delete mlist to fully include any dynamic segments
2558 	 * it intersects with.
2559 	 */
2560 	for (x_ml = NULL, ml = s_del_ml; ml != NULL; ml = ml->next) {
2561 		uint64_t del_base = ml->address;
2562 		uint64_t del_end = ml->address + ml->size;
2563 		struct memlist *dyn;
2564 
2565 		for (dyn = s_mp->sbm_dyn_segs; dyn != NULL; dyn = dyn->next) {
2566 			uint64_t dyn_base = dyn->address;
2567 			uint64_t dyn_end = dyn->address + dyn->size;
2568 
2569 			if (del_base > dyn_base && del_base < dyn_end)
2570 				del_base = dyn_base;
2571 
2572 			if (del_end > dyn_base && del_end < dyn_end)
2573 				del_end = dyn_end;
2574 		}
2575 
2576 		x_ml = memlist_cat_span(x_ml, del_base, del_end - del_base);
2577 	}
2578 
2579 	memlist_delete(s_del_ml);
2580 	s_del_ml = x_ml;
2581 
2582 	for (ml = s_del_ml; ml != NULL; ml = ml->next) {
2583 		s_copy_ml = memlist_del_span(s_copy_ml, ml->address, ml->size);
2584 	}
2585 
2586 	PR_MEM("%s: source delete mlist\n", f);
2587 	PR_MEMLIST_DUMP(s_del_ml);
2588 
2589 	PR_MEM("%s: source copy mlist\n", f);
2590 	PR_MEMLIST_DUMP(s_copy_ml);
2591 
2592 	memlist_delete(t_ml);
2593 	memlist_delete(s_del_ml);
2594 
2595 	return (s_copy_ml);
2596 }
2597 
2598 /*
2599  * Scan the non-relocatable spans on the source memory
2600  * and construct a minimum mlist that includes all non-reloc
2601  * memory subject to target alignment, and dynamic segment
2602  * constraints where only whole dynamic segments may be deleted.
2603  */
2604 static struct memlist *
2605 dr_get_nonreloc_mlist(struct memlist *s_ml, dr_mem_unit_t *s_mp)
2606 {
2607 	struct memlist	*x_ml = NULL;
2608 	struct memlist	*ml;
2609 	static fn_t	f = "dr_get_nonreloc_mlist";
2610 
2611 	PR_MEM("%s: checking for split of dyn seg list:\n", f);
2612 	PR_MEMLIST_DUMP(s_mp->sbm_dyn_segs);
2613 
2614 	for (ml = s_ml; ml; ml = ml->next) {
2615 		int rv;
2616 		uint64_t nr_base, nr_end;
2617 		memquery_t mq;
2618 		struct memlist *dyn;
2619 
2620 		rv = kphysm_del_span_query(
2621 			_b64top(ml->address), _b64top(ml->size), &mq);
2622 		if (rv) {
2623 			memlist_delete(x_ml);
2624 			return (NULL);
2625 		}
2626 
2627 		if (mq.nonrelocatable == 0)
2628 			continue;
2629 
2630 		PR_MEM("%s: non-reloc span: 0x%lx, 0x%lx (%lx, %lx)\n", f,
2631 			_ptob64(mq.first_nonrelocatable),
2632 			_ptob64(mq.last_nonrelocatable),
2633 			mq.first_nonrelocatable,
2634 			mq.last_nonrelocatable);
2635 
2636 		/*
2637 		 * Align the span at both ends to allow for possible
2638 		 * cage expansion.
2639 		 */
2640 		nr_base = _ptob64(mq.first_nonrelocatable);
2641 		nr_end = _ptob64(mq.last_nonrelocatable + 1);
2642 
2643 		PR_MEM("%s: adjusted non-reloc span: 0x%lx, 0x%lx\n",
2644 			f, nr_base, nr_end);
2645 
2646 		/*
2647 		 * Expand the non-reloc span to fully include any
2648 		 * dynamic segments it intersects with.
2649 		 */
2650 		for (dyn = s_mp->sbm_dyn_segs; dyn != NULL; dyn = dyn->next) {
2651 			uint64_t dyn_base = dyn->address;
2652 			uint64_t dyn_end = dyn->address + dyn->size;
2653 
2654 			if (nr_base > dyn_base && nr_base < dyn_end)
2655 				nr_base = dyn_base;
2656 
2657 			if (nr_end > dyn_base && nr_end < dyn_end)
2658 				nr_end = dyn_end;
2659 		}
2660 
2661 		x_ml = memlist_cat_span(x_ml, nr_base, nr_end - nr_base);
2662 	}
2663 
2664 	if (x_ml == NULL) {
2665 		PR_MEM("%s: source didn't have any non-reloc pages!\n", f);
2666 		return (NULL);
2667 	}
2668 
2669 	PR_MEM("%s: %s: edited source memlist:\n", f, s_mp->sbm_cm.sbdev_path);
2670 	PR_MEMLIST_DUMP(x_ml);
2671 
2672 	return (x_ml);
2673 }
2674 
2675 /*
2676  * Check if source memlist can fit in target memlist while maintaining
2677  * relative offsets within board.
2678  */
2679 static int
2680 dr_memlist_canfit(struct memlist *s_mlist, struct memlist *t_mlist,
2681     dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp)
2682 {
2683 	int		canfit = 0;
2684 	struct memlist	*s_ml, *t_ml, *ml;
2685 	uint64_t	s_slice_mask, t_slice_mask;
2686 	static fn_t	f = "dr_mlist_canfit";
2687 
2688 	s_ml = memlist_dup(s_mlist);
2689 	t_ml = memlist_dup(t_mlist);
2690 
2691 	if (s_ml == NULL || t_ml == NULL) {
2692 		cmn_err(CE_WARN, "%s: memlist_dup failed\n", f);
2693 		goto done;
2694 	}
2695 
2696 	s_slice_mask = s_mp->sbm_slice_size - 1;
2697 	t_slice_mask = t_mp->sbm_slice_size - 1;
2698 
2699 	/*
2700 	 * Normalize to slice relative offsets.
2701 	 */
2702 	for (ml = s_ml; ml; ml = ml->next)
2703 		ml->address &= s_slice_mask;
2704 
2705 	for (ml = t_ml; ml; ml = ml->next)
2706 		ml->address &= t_slice_mask;
2707 
2708 	canfit = memlist_canfit(s_ml, t_ml);
2709 done:
2710 	memlist_delete(s_ml);
2711 	memlist_delete(t_ml);
2712 
2713 	return (canfit);
2714 }
2715 
2716 /*
2717  * Memlist support.
2718  */
2719 
2720 /*
2721  * Determine whether the source memlist (s_mlist) will
2722  * fit into the target memlist (t_mlist) in terms of
2723  * size and holes.  Assumes the caller has normalized the
2724  * memlist physical addresses for comparison.
2725  */
2726 static int
2727 memlist_canfit(struct memlist *s_mlist, struct memlist *t_mlist)
2728 {
2729 	int		rv = 0;
2730 	struct memlist	*s_ml, *t_ml;
2731 
2732 	if ((s_mlist == NULL) || (t_mlist == NULL))
2733 		return (0);
2734 
2735 	s_ml = s_mlist;
2736 	for (t_ml = t_mlist; t_ml && s_ml; t_ml = t_ml->next) {
2737 		uint64_t	s_start, s_end;
2738 		uint64_t	t_start, t_end;
2739 
2740 		t_start = t_ml->address;
2741 		t_end = t_start + t_ml->size;
2742 
2743 		for (; s_ml; s_ml = s_ml->next) {
2744 			s_start = s_ml->address;
2745 			s_end = s_start + s_ml->size;
2746 
2747 			if ((s_start < t_start) || (s_end > t_end))
2748 				break;
2749 		}
2750 	}
2751 
2752 	/*
2753 	 * If we ran out of source memlist chunks that mean
2754 	 * we found a home for all of them.
2755 	 */
2756 	if (s_ml == NULL)
2757 		rv = 1;
2758 
2759 	return (rv);
2760 }
2761