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