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