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