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_WARN, &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 1774 if (t_mp != NULL) { 1775 ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist); 1776 t_mp->sbm_del_mlist = NULL; 1777 1778 if (t_mp->sbm_mlist != NULL) { 1779 memlist_delete(t_mp->sbm_mlist); 1780 t_mp->sbm_mlist = NULL; 1781 } 1782 1783 t_mp->sbm_peer = NULL; 1784 t_mp->sbm_flags = 0; 1785 t_mp->sbm_cm.sbdev_busy = 0; 1786 } 1787 1788 if (s_mp->sbm_del_mlist != s_mp->sbm_mlist) 1789 memlist_delete(s_mp->sbm_del_mlist); 1790 s_mp->sbm_del_mlist = NULL; 1791 1792 if (s_mp->sbm_mlist != NULL) { 1793 memlist_delete(s_mp->sbm_mlist); 1794 s_mp->sbm_mlist = NULL; 1795 } 1796 1797 s_mp->sbm_peer = NULL; 1798 s_mp->sbm_flags = 0; 1799 s_mp->sbm_cm.sbdev_busy = 0; 1800 1801 /* bail out */ 1802 return; 1803 } 1804 1805 DR_DEV_SET_RELEASED(&s_mp->sbm_cm); 1806 dr_device_transition(&s_mp->sbm_cm, DR_STATE_RELEASE); 1807 1808 if (t_mp != NULL) { 1809 /* 1810 * the kphysm delete operation that drained the source 1811 * board also drained this target board. Since the source 1812 * board drain is now known to have succeeded, we know this 1813 * target board is drained too. 1814 * 1815 * because DR_DEV_SET_RELEASED and dr_device_transition 1816 * is done here, the dr_release_dev_done should not 1817 * fail. 1818 */ 1819 DR_DEV_SET_RELEASED(&t_mp->sbm_cm); 1820 dr_device_transition(&t_mp->sbm_cm, DR_STATE_RELEASE); 1821 1822 /* 1823 * NOTE: do not transition target's board state, 1824 * even if the mem-unit was the last configure 1825 * unit of the board. When copy/rename completes 1826 * this mem-unit will transitioned back to 1827 * the configured state. In the meantime, the 1828 * board's must remain as is. 1829 */ 1830 } 1831 1832 /* if board(s) had deleted memory, verify it is gone */ 1833 rv = 0; 1834 memlist_read_lock(); 1835 if (s_mp->sbm_del_mlist != NULL) { 1836 mp = s_mp; 1837 rv = memlist_intersect(phys_install, mp->sbm_del_mlist); 1838 } 1839 if (rv == 0 && t_mp && t_mp->sbm_del_mlist != NULL) { 1840 mp = t_mp; 1841 rv = memlist_intersect(phys_install, mp->sbm_del_mlist); 1842 } 1843 memlist_read_unlock(); 1844 if (rv) { 1845 cmn_err(CE_WARN, "%s: %smem-unit (%d.%d): " 1846 "deleted memory still found in phys_install", 1847 f, 1848 (mp == t_mp ? "target " : ""), 1849 mp->sbm_cm.sbdev_bp->b_num, 1850 mp->sbm_cm.sbdev_unum); 1851 1852 DR_DEV_INTERNAL_ERROR(&s_mp->sbm_cm); 1853 return; 1854 } 1855 1856 s_mp->sbm_flags |= DR_MFLAG_RELDONE; 1857 if (t_mp != NULL) 1858 t_mp->sbm_flags |= DR_MFLAG_RELDONE; 1859 1860 /* this should not fail */ 1861 if (dr_release_dev_done(&s_mp->sbm_cm) != 0) { 1862 /* catch this in debug kernels */ 1863 ASSERT(0); 1864 return; 1865 } 1866 1867 PR_MEM("%s: marking %s release DONE\n", 1868 f, s_mp->sbm_cm.sbdev_path); 1869 1870 s_mp->sbm_cm.sbdev_ostate = SBD_STAT_UNCONFIGURED; 1871 1872 if (t_mp != NULL) { 1873 /* should not fail */ 1874 rv = dr_release_dev_done(&t_mp->sbm_cm); 1875 if (rv != 0) { 1876 /* catch this in debug kernels */ 1877 ASSERT(0); 1878 return; 1879 } 1880 1881 PR_MEM("%s: marking %s release DONE\n", 1882 f, t_mp->sbm_cm.sbdev_path); 1883 1884 t_mp->sbm_cm.sbdev_ostate = SBD_STAT_UNCONFIGURED; 1885 } 1886 } 1887 1888 /*ARGSUSED*/ 1889 int 1890 dr_disconnect_mem(dr_mem_unit_t *mp) 1891 { 1892 static fn_t f = "dr_disconnect_mem"; 1893 update_membounds_t umb; 1894 1895 #ifdef DEBUG 1896 int state = mp->sbm_cm.sbdev_state; 1897 ASSERT(state == DR_STATE_CONNECTED || 1898 state == DR_STATE_UNCONFIGURED); 1899 #endif 1900 1901 PR_MEM("%s...\n", f); 1902 1903 if (mp->sbm_del_mlist && mp->sbm_del_mlist != mp->sbm_mlist) 1904 memlist_delete(mp->sbm_del_mlist); 1905 mp->sbm_del_mlist = NULL; 1906 1907 if (mp->sbm_mlist) { 1908 memlist_delete(mp->sbm_mlist); 1909 mp->sbm_mlist = NULL; 1910 } 1911 1912 /* 1913 * Remove memory from lgroup 1914 * For now, only board info is required. 1915 */ 1916 umb.u_board = mp->sbm_cm.sbdev_bp->b_num; 1917 umb.u_base = (uint64_t)-1; 1918 umb.u_len = (uint64_t)-1; 1919 1920 lgrp_plat_config(LGRP_CONFIG_MEM_DEL, (uintptr_t)&umb); 1921 1922 return (0); 1923 } 1924 1925 int 1926 dr_cancel_mem(dr_mem_unit_t *s_mp) 1927 { 1928 dr_mem_unit_t *t_mp; 1929 dr_state_t state; 1930 static fn_t f = "dr_cancel_mem"; 1931 1932 state = s_mp->sbm_cm.sbdev_state; 1933 1934 if (s_mp->sbm_flags & DR_MFLAG_TARGET) { 1935 /* must cancel source board, not target board */ 1936 /* TODO: set error */ 1937 return (-1); 1938 } else if (s_mp->sbm_flags & DR_MFLAG_SOURCE) { 1939 t_mp = s_mp->sbm_peer; 1940 ASSERT(t_mp != NULL); 1941 ASSERT(t_mp->sbm_peer == s_mp); 1942 1943 /* must always match the source board's state */ 1944 /* TODO: is this assertion correct? */ 1945 ASSERT(t_mp->sbm_cm.sbdev_state == state); 1946 } else { 1947 /* this is no target unit */ 1948 t_mp = NULL; 1949 } 1950 1951 switch (state) { 1952 case DR_STATE_UNREFERENCED: /* state set by dr_release_dev_done */ 1953 ASSERT((s_mp->sbm_flags & DR_MFLAG_RELOWNER) == 0); 1954 1955 if (t_mp != NULL && t_mp->sbm_del_mlist != NULL) { 1956 PR_MEM("%s: undoing target %s memory delete\n", 1957 f, t_mp->sbm_cm.sbdev_path); 1958 dr_add_memory_spans(t_mp, t_mp->sbm_del_mlist); 1959 1960 DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm); 1961 } 1962 1963 if (s_mp->sbm_del_mlist != NULL) { 1964 PR_MEM("%s: undoing %s memory delete\n", 1965 f, s_mp->sbm_cm.sbdev_path); 1966 1967 dr_add_memory_spans(s_mp, s_mp->sbm_del_mlist); 1968 } 1969 1970 /*FALLTHROUGH*/ 1971 1972 /* TODO: should no longer be possible to see the release state here */ 1973 case DR_STATE_RELEASE: /* state set by dr_release_mem_done */ 1974 1975 ASSERT((s_mp->sbm_flags & DR_MFLAG_RELOWNER) == 0); 1976 1977 if (t_mp != NULL) { 1978 ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist); 1979 t_mp->sbm_del_mlist = NULL; 1980 1981 if (t_mp->sbm_mlist != NULL) { 1982 memlist_delete(t_mp->sbm_mlist); 1983 t_mp->sbm_mlist = NULL; 1984 } 1985 1986 t_mp->sbm_peer = NULL; 1987 t_mp->sbm_flags = 0; 1988 t_mp->sbm_cm.sbdev_busy = 0; 1989 dr_init_mem_unit_data(t_mp); 1990 1991 DR_DEV_CLR_RELEASED(&t_mp->sbm_cm); 1992 1993 dr_device_transition( 1994 &t_mp->sbm_cm, DR_STATE_CONFIGURED); 1995 } 1996 1997 if (s_mp->sbm_del_mlist != s_mp->sbm_mlist) 1998 memlist_delete(s_mp->sbm_del_mlist); 1999 s_mp->sbm_del_mlist = NULL; 2000 2001 if (s_mp->sbm_mlist != NULL) { 2002 memlist_delete(s_mp->sbm_mlist); 2003 s_mp->sbm_mlist = NULL; 2004 } 2005 2006 s_mp->sbm_peer = NULL; 2007 s_mp->sbm_flags = 0; 2008 s_mp->sbm_cm.sbdev_busy = 0; 2009 dr_init_mem_unit_data(s_mp); 2010 2011 return (0); 2012 2013 default: 2014 PR_MEM("%s: WARNING unexpected state (%d) for %s\n", 2015 f, (int)state, s_mp->sbm_cm.sbdev_path); 2016 2017 return (-1); 2018 } 2019 /*NOTREACHED*/ 2020 } 2021 2022 void 2023 dr_init_mem_unit(dr_mem_unit_t *mp) 2024 { 2025 dr_state_t new_state; 2026 2027 2028 if (DR_DEV_IS_ATTACHED(&mp->sbm_cm)) { 2029 new_state = DR_STATE_CONFIGURED; 2030 mp->sbm_cm.sbdev_cond = SBD_COND_OK; 2031 } else if (DR_DEV_IS_PRESENT(&mp->sbm_cm)) { 2032 new_state = DR_STATE_CONNECTED; 2033 mp->sbm_cm.sbdev_cond = SBD_COND_OK; 2034 } else if (mp->sbm_cm.sbdev_id != (drmachid_t)0) { 2035 new_state = DR_STATE_OCCUPIED; 2036 } else { 2037 new_state = DR_STATE_EMPTY; 2038 } 2039 2040 if (DR_DEV_IS_PRESENT(&mp->sbm_cm)) 2041 dr_init_mem_unit_data(mp); 2042 2043 /* delay transition until fully initialized */ 2044 dr_device_transition(&mp->sbm_cm, new_state); 2045 } 2046 2047 static void 2048 dr_init_mem_unit_data(dr_mem_unit_t *mp) 2049 { 2050 drmachid_t id = mp->sbm_cm.sbdev_id; 2051 drmach_mem_info_t minfo; 2052 sbd_error_t *err; 2053 static fn_t f = "dr_init_mem_unit_data"; 2054 update_membounds_t umb; 2055 2056 PR_MEM("%s...\n", f); 2057 2058 /* a little sanity checking */ 2059 ASSERT(mp->sbm_peer == NULL); 2060 ASSERT(mp->sbm_flags == 0); 2061 2062 if (err = drmach_mem_get_info(id, &minfo)) { 2063 DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err); 2064 return; 2065 } 2066 mp->sbm_basepfn = _b64top(minfo.mi_basepa); 2067 mp->sbm_npages = _b64top(minfo.mi_size); 2068 mp->sbm_alignment_mask = _b64top(minfo.mi_alignment_mask); 2069 mp->sbm_slice_size = minfo.mi_slice_size; 2070 2071 /* 2072 * Add memory to lgroup 2073 */ 2074 umb.u_board = mp->sbm_cm.sbdev_bp->b_num; 2075 umb.u_base = (uint64_t)mp->sbm_basepfn << MMU_PAGESHIFT; 2076 umb.u_len = (uint64_t)mp->sbm_npages << MMU_PAGESHIFT; 2077 2078 lgrp_plat_config(LGRP_CONFIG_MEM_ADD, (uintptr_t)&umb); 2079 2080 PR_MEM("%s: %s (basepfn = 0x%lx, npgs = %ld)\n", 2081 f, mp->sbm_cm.sbdev_path, mp->sbm_basepfn, mp->sbm_npages); 2082 } 2083 2084 static int 2085 dr_reserve_mem_spans(memhandle_t *mhp, struct memlist *ml) 2086 { 2087 int err; 2088 pfn_t base; 2089 pgcnt_t npgs; 2090 struct memlist *mc; 2091 static fn_t f = "dr_reserve_mem_spans"; 2092 2093 PR_MEM("%s...\n", f); 2094 2095 /* 2096 * Walk the supplied memlist scheduling each span for removal 2097 * with kphysm_del_span. It is possible that a span may intersect 2098 * an area occupied by the cage. 2099 */ 2100 for (mc = ml; mc != NULL; mc = mc->next) { 2101 base = _b64top(mc->address); 2102 npgs = _b64top(mc->size); 2103 2104 err = kphysm_del_span(*mhp, base, npgs); 2105 if (err != KPHYSM_OK) { 2106 cmn_err(CE_WARN, "%s memory reserve failed." 2107 " unexpected kphysm_del_span return value %d;" 2108 " basepfn=0x%lx npages=%ld", 2109 f, err, base, npgs); 2110 2111 return (-1); 2112 } 2113 } 2114 2115 return (0); 2116 } 2117 2118 #define DR_SMT_NPREF_SETS 6 2119 #define DR_SMT_NUNITS_PER_SET MAX_BOARDS * MAX_MEM_UNITS_PER_BOARD 2120 2121 /* debug counters */ 2122 int dr_smt_realigned; 2123 int dr_smt_preference[DR_SMT_NPREF_SETS]; 2124 2125 #ifdef DEBUG 2126 uint_t dr_ignore_board; /* if bit[bnum-1] set, board won't be candidate */ 2127 #endif 2128 2129 /* 2130 * Find and reserve a copy/rename target board suitable for the 2131 * given source board. 2132 * All boards in the system are examined and categorized in relation to 2133 * their memory size versus the source board's memory size. Order of 2134 * preference is: 2135 * 1st copy all source, source/target same size 2136 * 2nd copy all source, larger target 2137 * 3rd copy nonrelocatable source span 2138 */ 2139 static int 2140 dr_select_mem_target(dr_handle_t *hp, 2141 dr_mem_unit_t *s_mp, struct memlist *s_ml) 2142 { 2143 dr_target_pref_t preference; /* lower value is higher preference */ 2144 int idx; 2145 dr_mem_unit_t **sets; 2146 2147 int t_bd; 2148 int t_unit; 2149 int rv; 2150 dr_board_t *s_bp, *t_bp; 2151 dr_mem_unit_t *t_mp, *c_mp; 2152 struct memlist *d_ml, *t_ml, *ml, *b_ml, *x_ml = NULL; 2153 memquery_t s_mq = {0}; 2154 static fn_t f = "dr_select_mem_target"; 2155 2156 PR_MEM("%s...\n", f); 2157 2158 ASSERT(s_ml != NULL); 2159 2160 sets = GETSTRUCT(dr_mem_unit_t *, DR_SMT_NUNITS_PER_SET * 2161 DR_SMT_NPREF_SETS); 2162 2163 s_bp = hp->h_bd; 2164 /* calculate the offset into the slice of the last source board pfn */ 2165 ASSERT(s_mp->sbm_npages != 0); 2166 2167 /* 2168 * Find non-relocatable span on source board. 2169 */ 2170 rv = kphysm_del_span_query(s_mp->sbm_basepfn, s_mp->sbm_npages, &s_mq); 2171 if (rv != KPHYSM_OK) { 2172 PR_MEM("%s: %s: unexpected kphysm_del_span_query" 2173 " return value %d; basepfn 0x%lx, npages %ld\n", 2174 f, s_mp->sbm_cm.sbdev_path, rv, s_mp->sbm_basepfn, 2175 s_mp->sbm_npages); 2176 return (-1); 2177 } 2178 2179 ASSERT(s_mq.phys_pages != 0); 2180 ASSERT(s_mq.nonrelocatable != 0); 2181 2182 PR_MEM("%s: %s: nonrelocatable span (0x%lx..0x%lx)\n", f, 2183 s_mp->sbm_cm.sbdev_path, s_mq.first_nonrelocatable, 2184 s_mq.last_nonrelocatable); 2185 2186 /* break down s_ml if it contains dynamic segments */ 2187 b_ml = memlist_dup(s_ml); 2188 2189 for (ml = s_mp->sbm_dyn_segs; ml; ml = ml->next) { 2190 b_ml = memlist_del_span(b_ml, ml->address, ml->size); 2191 b_ml = memlist_cat_span(b_ml, ml->address, ml->size); 2192 } 2193 2194 2195 /* 2196 * Make one pass through all memory units on all boards 2197 * and categorize them with respect to the source board. 2198 */ 2199 for (t_bd = 0; t_bd < MAX_BOARDS; t_bd++) { 2200 /* 2201 * The board structs are a contiguous array 2202 * so we take advantage of that to find the 2203 * correct board struct pointer for a given 2204 * board number. 2205 */ 2206 t_bp = dr_lookup_board(t_bd); 2207 2208 /* source board can not be its own target */ 2209 if (s_bp->b_num == t_bp->b_num) 2210 continue; 2211 2212 for (t_unit = 0; t_unit < MAX_MEM_UNITS_PER_BOARD; t_unit++) { 2213 2214 t_mp = dr_get_mem_unit(t_bp, t_unit); 2215 2216 /* this memory node must be attached */ 2217 if (!DR_DEV_IS_ATTACHED(&t_mp->sbm_cm)) 2218 continue; 2219 2220 /* source unit can not be its own target */ 2221 if (s_mp == t_mp) { 2222 /* catch this is debug kernels */ 2223 ASSERT(0); 2224 continue; 2225 } 2226 2227 /* 2228 * this memory node must not already be reserved 2229 * by some other memory delete operation. 2230 */ 2231 if (t_mp->sbm_flags & DR_MFLAG_RESERVED) 2232 continue; 2233 2234 /* get target board memlist */ 2235 t_ml = dr_get_memlist(t_mp); 2236 if (t_ml == NULL) { 2237 cmn_err(CE_WARN, "%s: no memlist for" 2238 " mem-unit %d, board %d", f, 2239 t_mp->sbm_cm.sbdev_bp->b_num, 2240 t_mp->sbm_cm.sbdev_unum); 2241 continue; 2242 } 2243 2244 preference = dr_get_target_preference(hp, t_mp, s_mp, 2245 t_ml, s_ml, b_ml); 2246 2247 if (preference == DR_TP_INVALID) 2248 continue; 2249 2250 dr_smt_preference[preference]++; 2251 2252 /* calculate index to start of preference set */ 2253 idx = DR_SMT_NUNITS_PER_SET * preference; 2254 /* calculate offset to respective element */ 2255 idx += t_bd * MAX_MEM_UNITS_PER_BOARD + t_unit; 2256 2257 ASSERT(idx < DR_SMT_NUNITS_PER_SET * DR_SMT_NPREF_SETS); 2258 sets[idx] = t_mp; 2259 } 2260 } 2261 2262 if (b_ml != NULL) 2263 memlist_delete(b_ml); 2264 2265 /* 2266 * NOTE: this would be a good place to sort each candidate 2267 * set in to some desired order, e.g. memory size in ascending 2268 * order. Without an additional sorting step here, the order 2269 * within a set is ascending board number order. 2270 */ 2271 2272 c_mp = NULL; 2273 x_ml = NULL; 2274 t_ml = NULL; 2275 for (idx = 0; idx < DR_SMT_NUNITS_PER_SET * DR_SMT_NPREF_SETS; idx++) { 2276 memquery_t mq; 2277 2278 preference = (dr_target_pref_t)(idx / DR_SMT_NUNITS_PER_SET); 2279 2280 ASSERT(preference != DR_TP_INVALID); 2281 2282 /* cleanup t_ml after previous pass */ 2283 if (t_ml != NULL) { 2284 memlist_delete(t_ml); 2285 t_ml = NULL; 2286 } 2287 2288 /* get candidate target board mem unit */ 2289 t_mp = sets[idx]; 2290 if (t_mp == NULL) 2291 continue; 2292 2293 /* get target board memlist */ 2294 t_ml = dr_get_memlist(t_mp); 2295 if (t_ml == NULL) { 2296 cmn_err(CE_WARN, "%s: no memlist for" 2297 " mem-unit %d, board %d", 2298 f, 2299 t_mp->sbm_cm.sbdev_bp->b_num, 2300 t_mp->sbm_cm.sbdev_unum); 2301 2302 continue; 2303 } 2304 2305 PR_MEM("%s: checking for no-reloc in %s, " 2306 " basepfn=0x%lx, npages=%ld\n", 2307 f, 2308 t_mp->sbm_cm.sbdev_path, 2309 t_mp->sbm_basepfn, 2310 t_mp->sbm_npages); 2311 2312 rv = dr_del_mlist_query(t_ml, &mq); 2313 if (rv != KPHYSM_OK) { 2314 PR_MEM("%s: kphysm_del_span_query:" 2315 " unexpected return value %d\n", f, rv); 2316 2317 continue; 2318 } 2319 2320 if (mq.nonrelocatable != 0) { 2321 PR_MEM("%s: candidate %s has" 2322 " nonrelocatable span [0x%lx..0x%lx]\n", 2323 f, 2324 t_mp->sbm_cm.sbdev_path, 2325 mq.first_nonrelocatable, 2326 mq.last_nonrelocatable); 2327 2328 continue; 2329 } 2330 2331 #ifdef DEBUG 2332 /* 2333 * This is a debug tool for excluding certain boards 2334 * from being selected as a target board candidate. 2335 * dr_ignore_board is only tested by this driver. 2336 * It must be set with adb, obp, /etc/system or your 2337 * favorite debugger. 2338 */ 2339 if (dr_ignore_board & 2340 (1 << (t_mp->sbm_cm.sbdev_bp->b_num - 1))) { 2341 PR_MEM("%s: dr_ignore_board flag set," 2342 " ignoring %s as candidate\n", 2343 f, t_mp->sbm_cm.sbdev_path); 2344 continue; 2345 } 2346 #endif 2347 2348 /* 2349 * Reserve excess source board memory, if any. 2350 * 2351 * Only the nonrelocatable source span will be copied 2352 * so schedule the rest of the source mem to be deleted. 2353 */ 2354 switch (preference) { 2355 case DR_TP_NONRELOC: 2356 /* 2357 * Get source copy memlist and use it to construct 2358 * delete memlist. 2359 */ 2360 d_ml = memlist_dup(s_ml); 2361 x_ml = dr_get_copy_mlist(s_ml, t_ml, s_mp, t_mp); 2362 2363 /* XXX */ 2364 ASSERT(d_ml != NULL); 2365 ASSERT(x_ml != NULL); 2366 2367 for (ml = x_ml; ml != NULL; ml = ml->next) { 2368 d_ml = memlist_del_span(d_ml, ml->address, 2369 ml->size); 2370 } 2371 2372 PR_MEM("%s: %s: reserving src brd memlist:\n", f, 2373 s_mp->sbm_cm.sbdev_path); 2374 PR_MEMLIST_DUMP(d_ml); 2375 2376 /* reserve excess spans */ 2377 if (dr_reserve_mem_spans(&s_mp->sbm_memhandle, 2378 d_ml) != 0) { 2379 /* likely more non-reloc pages appeared */ 2380 /* TODO: restart from top? */ 2381 continue; 2382 } 2383 break; 2384 default: 2385 d_ml = NULL; 2386 break; 2387 } 2388 2389 s_mp->sbm_flags |= DR_MFLAG_RESERVED; 2390 2391 /* 2392 * reserve all memory on target board. 2393 * NOTE: source board's memhandle is used. 2394 * 2395 * If this succeeds (eq 0), then target selection is 2396 * complete and all unwanted memory spans, both source and 2397 * target, have been reserved. Loop is terminated. 2398 */ 2399 if (dr_reserve_mem_spans(&s_mp->sbm_memhandle, t_ml) == 0) { 2400 PR_MEM("%s: %s: target board memory reserved\n", 2401 f, t_mp->sbm_cm.sbdev_path); 2402 2403 /* a candidate target board is now reserved */ 2404 t_mp->sbm_flags |= DR_MFLAG_RESERVED; 2405 c_mp = t_mp; 2406 2407 /* *** EXITING LOOP *** */ 2408 break; 2409 } 2410 2411 /* did not successfully reserve the target board. */ 2412 PR_MEM("%s: could not reserve target %s\n", 2413 f, t_mp->sbm_cm.sbdev_path); 2414 2415 /* 2416 * NOTE: an undo of the dr_reserve_mem_span work 2417 * will happen automatically when the memhandle 2418 * (s_mp->sbm_memhandle) is kphysm_del_release'd. 2419 */ 2420 2421 s_mp->sbm_flags &= ~DR_MFLAG_RESERVED; 2422 } 2423 2424 /* clean up after memlist editing logic */ 2425 if (x_ml != NULL) 2426 memlist_delete(x_ml); 2427 2428 FREESTRUCT(sets, dr_mem_unit_t *, DR_SMT_NUNITS_PER_SET * 2429 DR_SMT_NPREF_SETS); 2430 2431 /* 2432 * c_mp will be NULL when the entire sets[] array 2433 * has been searched without reserving a target board. 2434 */ 2435 if (c_mp == NULL) { 2436 PR_MEM("%s: %s: target selection failed.\n", 2437 f, s_mp->sbm_cm.sbdev_path); 2438 2439 if (t_ml != NULL) 2440 memlist_delete(t_ml); 2441 2442 return (-1); 2443 } 2444 2445 PR_MEM("%s: found target %s for source %s\n", 2446 f, 2447 c_mp->sbm_cm.sbdev_path, 2448 s_mp->sbm_cm.sbdev_path); 2449 2450 s_mp->sbm_peer = c_mp; 2451 s_mp->sbm_flags |= DR_MFLAG_SOURCE; 2452 s_mp->sbm_del_mlist = d_ml; /* spans to be deleted, if any */ 2453 s_mp->sbm_mlist = s_ml; 2454 s_mp->sbm_cm.sbdev_busy = 1; 2455 2456 c_mp->sbm_peer = s_mp; 2457 c_mp->sbm_flags |= DR_MFLAG_TARGET; 2458 c_mp->sbm_del_mlist = t_ml; /* spans to be deleted */ 2459 c_mp->sbm_mlist = t_ml; 2460 c_mp->sbm_cm.sbdev_busy = 1; 2461 2462 return (0); 2463 } 2464 2465 /* 2466 * Returns target preference rank: 2467 * -1 not a valid copy-rename target board 2468 * 0 copy all source, source/target same size 2469 * 1 copy all source, larger target 2470 * 2 copy nonrelocatable source span 2471 */ 2472 static dr_target_pref_t 2473 dr_get_target_preference(dr_handle_t *hp, 2474 dr_mem_unit_t *t_mp, dr_mem_unit_t *s_mp, 2475 struct memlist *t_ml, struct memlist *s_ml, 2476 struct memlist *b_ml) 2477 { 2478 dr_target_pref_t preference; 2479 struct memlist *s_nonreloc_ml = NULL; 2480 drmachid_t t_id; 2481 static fn_t f = "dr_get_target_preference"; 2482 2483 t_id = t_mp->sbm_cm.sbdev_bp->b_id; 2484 2485 /* 2486 * Can the entire source board be copied? 2487 */ 2488 if (dr_memlist_canfit(s_ml, t_ml, s_mp, t_mp)) { 2489 if (s_mp->sbm_npages == t_mp->sbm_npages) 2490 preference = DR_TP_SAME; /* same size */ 2491 else 2492 preference = DR_TP_LARGE; /* larger target */ 2493 } else { 2494 /* 2495 * Entire source won't fit so try non-relocatable memory only 2496 * (target aligned). 2497 */ 2498 s_nonreloc_ml = dr_get_nonreloc_mlist(b_ml, s_mp); 2499 if (s_nonreloc_ml == NULL) { 2500 PR_MEM("%s: dr_get_nonreloc_mlist failed\n", f); 2501 preference = DR_TP_INVALID; 2502 } 2503 if (dr_memlist_canfit(s_nonreloc_ml, t_ml, s_mp, t_mp)) 2504 preference = DR_TP_NONRELOC; 2505 else 2506 preference = DR_TP_INVALID; 2507 } 2508 2509 if (s_nonreloc_ml != NULL) 2510 memlist_delete(s_nonreloc_ml); 2511 2512 /* 2513 * Force floating board preference lower than all other boards 2514 * if the force flag is present; otherwise disallow the board. 2515 */ 2516 if ((preference != DR_TP_INVALID) && drmach_board_is_floating(t_id)) { 2517 if (dr_cmd_flags(hp) & SBD_FLAG_FORCE) 2518 preference += DR_TP_FLOATING; 2519 else 2520 preference = DR_TP_INVALID; 2521 } 2522 2523 PR_MEM("%s: %s preference=%d\n", f, t_mp->sbm_cm.sbdev_path, 2524 preference); 2525 2526 return (preference); 2527 } 2528 2529 /* 2530 * Create a memlist representing the source memory that will be copied to 2531 * the target board. The memory to be copied is the maximum amount that 2532 * will fit on the target board. 2533 */ 2534 static struct memlist * 2535 dr_get_copy_mlist(struct memlist *s_mlist, struct memlist *t_mlist, 2536 dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp) 2537 { 2538 struct memlist *t_ml, *s_copy_ml, *s_del_ml, *ml, *x_ml; 2539 uint64_t s_slice_mask, s_slice_base; 2540 uint64_t t_slice_mask, t_slice_base; 2541 static fn_t f = "dr_get_copy_mlist"; 2542 2543 ASSERT(s_mlist != NULL); 2544 ASSERT(t_mlist != NULL); 2545 ASSERT(t_mp->sbm_slice_size == s_mp->sbm_slice_size); 2546 2547 s_slice_mask = s_mp->sbm_slice_size - 1; 2548 s_slice_base = s_mlist->address & ~s_slice_mask; 2549 2550 t_slice_mask = t_mp->sbm_slice_size - 1; 2551 t_slice_base = t_mlist->address & ~t_slice_mask; 2552 2553 t_ml = memlist_dup(t_mlist); 2554 s_del_ml = memlist_dup(s_mlist); 2555 s_copy_ml = memlist_dup(s_mlist); 2556 2557 /* XXX */ 2558 ASSERT(t_ml != NULL); 2559 ASSERT(s_del_ml != NULL); 2560 ASSERT(s_copy_ml != NULL); 2561 2562 /* 2563 * To construct the source copy memlist: 2564 * 2565 * The target memlist is converted to the post-rename 2566 * source addresses. This is the physical address range 2567 * the target will have after the copy-rename. Overlaying 2568 * and deleting this from the current source memlist will 2569 * give the source delete memlist. The copy memlist is 2570 * the reciprocal of the source delete memlist. 2571 */ 2572 for (ml = t_ml; ml != NULL; ml = ml->next) { 2573 /* 2574 * Normalize relative to target slice base PA 2575 * in order to preseve slice offsets. 2576 */ 2577 ml->address -= t_slice_base; 2578 /* 2579 * Convert to source slice PA address. 2580 */ 2581 ml->address += s_slice_base; 2582 } 2583 2584 for (ml = t_ml; ml != NULL; ml = ml->next) { 2585 s_del_ml = memlist_del_span(s_del_ml, ml->address, ml->size); 2586 } 2587 2588 /* 2589 * Expand the delete mlist to fully include any dynamic segments 2590 * it intersects with. 2591 */ 2592 for (x_ml = NULL, ml = s_del_ml; ml != NULL; ml = ml->next) { 2593 uint64_t del_base = ml->address; 2594 uint64_t del_end = ml->address + ml->size; 2595 struct memlist *dyn; 2596 2597 for (dyn = s_mp->sbm_dyn_segs; dyn != NULL; dyn = dyn->next) { 2598 uint64_t dyn_base = dyn->address; 2599 uint64_t dyn_end = dyn->address + dyn->size; 2600 2601 if (del_base > dyn_base && del_base < dyn_end) 2602 del_base = dyn_base; 2603 2604 if (del_end > dyn_base && del_end < dyn_end) 2605 del_end = dyn_end; 2606 } 2607 2608 x_ml = memlist_cat_span(x_ml, del_base, del_end - del_base); 2609 } 2610 2611 memlist_delete(s_del_ml); 2612 s_del_ml = x_ml; 2613 2614 for (ml = s_del_ml; ml != NULL; ml = ml->next) { 2615 s_copy_ml = memlist_del_span(s_copy_ml, ml->address, ml->size); 2616 } 2617 2618 PR_MEM("%s: source delete mlist\n", f); 2619 PR_MEMLIST_DUMP(s_del_ml); 2620 2621 PR_MEM("%s: source copy mlist\n", f); 2622 PR_MEMLIST_DUMP(s_copy_ml); 2623 2624 memlist_delete(t_ml); 2625 memlist_delete(s_del_ml); 2626 2627 return (s_copy_ml); 2628 } 2629 2630 /* 2631 * Scan the non-relocatable spans on the source memory 2632 * and construct a minimum mlist that includes all non-reloc 2633 * memory subject to target alignment, and dynamic segment 2634 * constraints where only whole dynamic segments may be deleted. 2635 */ 2636 static struct memlist * 2637 dr_get_nonreloc_mlist(struct memlist *s_ml, dr_mem_unit_t *s_mp) 2638 { 2639 struct memlist *x_ml = NULL; 2640 struct memlist *ml; 2641 static fn_t f = "dr_get_nonreloc_mlist"; 2642 2643 PR_MEM("%s: checking for split of dyn seg list:\n", f); 2644 PR_MEMLIST_DUMP(s_mp->sbm_dyn_segs); 2645 2646 for (ml = s_ml; ml; ml = ml->next) { 2647 int rv; 2648 uint64_t nr_base, nr_end; 2649 memquery_t mq; 2650 struct memlist *dyn; 2651 2652 rv = kphysm_del_span_query( 2653 _b64top(ml->address), _b64top(ml->size), &mq); 2654 if (rv) { 2655 memlist_delete(x_ml); 2656 return (NULL); 2657 } 2658 2659 if (mq.nonrelocatable == 0) 2660 continue; 2661 2662 PR_MEM("%s: non-reloc span: 0x%lx, 0x%lx (%lx, %lx)\n", f, 2663 _ptob64(mq.first_nonrelocatable), 2664 _ptob64(mq.last_nonrelocatable), 2665 mq.first_nonrelocatable, 2666 mq.last_nonrelocatable); 2667 2668 /* 2669 * Align the span at both ends to allow for possible 2670 * cage expansion. 2671 */ 2672 nr_base = _ptob64(mq.first_nonrelocatable); 2673 nr_end = _ptob64(mq.last_nonrelocatable + 1); 2674 2675 PR_MEM("%s: adjusted non-reloc span: 0x%lx, 0x%lx\n", 2676 f, nr_base, nr_end); 2677 2678 /* 2679 * Expand the non-reloc span to fully include any 2680 * dynamic segments it intersects with. 2681 */ 2682 for (dyn = s_mp->sbm_dyn_segs; dyn != NULL; dyn = dyn->next) { 2683 uint64_t dyn_base = dyn->address; 2684 uint64_t dyn_end = dyn->address + dyn->size; 2685 2686 if (nr_base > dyn_base && nr_base < dyn_end) 2687 nr_base = dyn_base; 2688 2689 if (nr_end > dyn_base && nr_end < dyn_end) 2690 nr_end = dyn_end; 2691 } 2692 2693 x_ml = memlist_cat_span(x_ml, nr_base, nr_end - nr_base); 2694 } 2695 2696 if (x_ml == NULL) { 2697 PR_MEM("%s: source didn't have any non-reloc pages!\n", f); 2698 return (NULL); 2699 } 2700 2701 PR_MEM("%s: %s: edited source memlist:\n", f, s_mp->sbm_cm.sbdev_path); 2702 PR_MEMLIST_DUMP(x_ml); 2703 2704 return (x_ml); 2705 } 2706 2707 /* 2708 * Check if source memlist can fit in target memlist while maintaining 2709 * relative offsets within board. 2710 */ 2711 static int 2712 dr_memlist_canfit(struct memlist *s_mlist, struct memlist *t_mlist, 2713 dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp) 2714 { 2715 int canfit = 0; 2716 struct memlist *s_ml, *t_ml, *ml; 2717 uint64_t s_slice_mask, t_slice_mask; 2718 static fn_t f = "dr_mlist_canfit"; 2719 2720 s_ml = memlist_dup(s_mlist); 2721 t_ml = memlist_dup(t_mlist); 2722 2723 if (s_ml == NULL || t_ml == NULL) { 2724 cmn_err(CE_WARN, "%s: memlist_dup failed\n", f); 2725 goto done; 2726 } 2727 2728 s_slice_mask = s_mp->sbm_slice_size - 1; 2729 t_slice_mask = t_mp->sbm_slice_size - 1; 2730 2731 /* 2732 * Normalize to slice relative offsets. 2733 */ 2734 for (ml = s_ml; ml; ml = ml->next) 2735 ml->address &= s_slice_mask; 2736 2737 for (ml = t_ml; ml; ml = ml->next) 2738 ml->address &= t_slice_mask; 2739 2740 canfit = memlist_canfit(s_ml, t_ml); 2741 done: 2742 memlist_delete(s_ml); 2743 memlist_delete(t_ml); 2744 2745 return (canfit); 2746 } 2747 2748 /* 2749 * Memlist support. 2750 */ 2751 2752 /* 2753 * Determine whether the source memlist (s_mlist) will 2754 * fit into the target memlist (t_mlist) in terms of 2755 * size and holes. Assumes the caller has normalized the 2756 * memlist physical addresses for comparison. 2757 */ 2758 static int 2759 memlist_canfit(struct memlist *s_mlist, struct memlist *t_mlist) 2760 { 2761 int rv = 0; 2762 struct memlist *s_ml, *t_ml; 2763 2764 if ((s_mlist == NULL) || (t_mlist == NULL)) 2765 return (0); 2766 2767 s_ml = s_mlist; 2768 for (t_ml = t_mlist; t_ml && s_ml; t_ml = t_ml->next) { 2769 uint64_t s_start, s_end; 2770 uint64_t t_start, t_end; 2771 2772 t_start = t_ml->address; 2773 t_end = t_start + t_ml->size; 2774 2775 for (; s_ml; s_ml = s_ml->next) { 2776 s_start = s_ml->address; 2777 s_end = s_start + s_ml->size; 2778 2779 if ((s_start < t_start) || (s_end > t_end)) 2780 break; 2781 } 2782 } 2783 2784 /* 2785 * If we ran out of source memlist chunks that mean 2786 * we found a home for all of them. 2787 */ 2788 if (s_ml == NULL) 2789 rv = 1; 2790 2791 return (rv); 2792 } 2793