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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * sun4u Memory Scrubbing 28 * 29 * On detection of a correctable memory ECC error, the sun4u kernel 30 * returns the corrected data to the requester and re-writes it 31 * to memory (DRAM). So if the correctable error was transient, 32 * the read has effectively been cleaned (scrubbed) from memory. 33 * 34 * Scrubbing thus reduces the likelyhood that multiple transient errors 35 * will occur in the same memory word, making uncorrectable errors due 36 * to transients less likely. 37 * 38 * Thus is born the desire that every memory location be periodically 39 * accessed. 40 * 41 * This file implements a memory scrubbing thread. This scrubber 42 * guarantees that all of physical memory is accessed periodically 43 * (memscrub_period_sec -- 12 hours). 44 * 45 * It attempts to do this as unobtrusively as possible. The thread 46 * schedules itself to wake up at an interval such that if it reads 47 * memscrub_span_pages (32MB) on each wakeup, it will read all of physical 48 * memory in in memscrub_period_sec (12 hours). 49 * 50 * The scrubber uses the block load and prefetch hardware to read memory 51 * @ 1300MB/s, so it reads spans of 32MB in 0.025 seconds. Unlike the 52 * original sun4d scrubber the sun4u scrubber does not read ahead if the 53 * system is idle because we can read memory very efficently. 54 * 55 * The scrubber maintains a private copy of the phys_install memory list 56 * to keep track of what memory should be scrubbed. 57 * 58 * The global routines memscrub_add_span() and memscrub_delete_span() are 59 * used to add and delete from this list. If hotplug memory is later 60 * supported these two routines can be used to notify the scrubber of 61 * memory configuration changes. 62 * 63 * The following parameters can be set via /etc/system 64 * 65 * memscrub_span_pages = MEMSCRUB_DFL_SPAN_PAGES (8MB) 66 * memscrub_period_sec = MEMSCRUB_DFL_PERIOD_SEC (12 hours) 67 * memscrub_thread_pri = MEMSCRUB_DFL_THREAD_PRI (MINCLSYSPRI) 68 * memscrub_delay_start_sec = (5 minutes) 69 * memscrub_verbose = (0) 70 * memscrub_override_ticks = (1 tick) 71 * disable_memscrub = (0) 72 * pause_memscrub = (0) 73 * read_all_memscrub = (0) 74 * 75 * The scrubber will print NOTICE messages of what it is doing if 76 * "memscrub_verbose" is set. 77 * 78 * If the scrubber's sleep time calculation drops to zero ticks, 79 * memscrub_override_ticks will be used as the sleep time instead. The 80 * sleep time should only drop to zero on a system with over 131.84 81 * terabytes of memory, or where the default scrubber parameters have 82 * been adjusted. For example, reducing memscrub_span_pages or 83 * memscrub_period_sec causes the sleep time to drop to zero with less 84 * memory. Note that since the sleep time is calculated in clock ticks, 85 * using hires clock ticks allows for more memory before the sleep time 86 * becomes zero. 87 * 88 * The scrubber will exit (or never be started) if it finds the variable 89 * "disable_memscrub" set. 90 * 91 * The scrubber will pause (not read memory) when "pause_memscrub" 92 * is set. It will check the state of pause_memscrub at each wakeup 93 * period. The scrubber will not make up for lost time. If you 94 * pause the scrubber for a prolonged period of time you can use 95 * the "read_all_memscrub" switch (see below) to catch up. In addition, 96 * pause_memscrub is used internally by the post memory DR callbacks. 97 * It is set for the small period of time during which the callbacks 98 * are executing. This ensures "memscrub_lock" will be released, 99 * allowing the callbacks to finish. 100 * 101 * The scrubber will read all memory if "read_all_memscrub" is set. 102 * The normal span read will also occur during the wakeup. 103 * 104 * MEMSCRUB_MIN_PAGES (32MB) is the minimum amount of memory a system 105 * must have before we'll start the scrubber. 106 * 107 * MEMSCRUB_DFL_SPAN_PAGES (32MB) is based on the guess that 0.025 sec 108 * is a "good" amount of minimum time for the thread to run at a time. 109 * 110 * MEMSCRUB_DFL_PERIOD_SEC (12 hours) is nearly a total guess -- 111 * twice the frequency the hardware folk estimated would be necessary. 112 * 113 * MEMSCRUB_DFL_THREAD_PRI (MINCLSYSPRI) is based on the assumption 114 * that the scurbber should get its fair share of time (since it 115 * is short). At a priority of 0 the scrubber will be starved. 116 */ 117 118 #include <sys/systm.h> /* timeout, types, t_lock */ 119 #include <sys/cmn_err.h> 120 #include <sys/sysmacros.h> /* MIN */ 121 #include <sys/memlist.h> /* memlist */ 122 #include <sys/mem_config.h> /* memory add/delete */ 123 #include <sys/kmem.h> /* KMEM_NOSLEEP */ 124 #include <sys/cpuvar.h> /* ncpus_online */ 125 #include <sys/debug.h> /* ASSERTs */ 126 #include <sys/machsystm.h> /* lddphys */ 127 #include <sys/cpu_module.h> /* vtag_flushpage */ 128 #include <sys/kstat.h> 129 #include <sys/atomic.h> /* atomic_add_32 */ 130 131 #include <vm/hat.h> 132 #include <vm/seg_kmem.h> 133 #include <vm/hat_sfmmu.h> /* XXX FIXME - delete */ 134 135 #include <sys/time.h> 136 #include <sys/callb.h> /* CPR callback */ 137 #include <sys/ontrap.h> 138 139 /* 140 * Should really have paddr_t defined, but it is broken. Use 141 * ms_paddr_t in the meantime to make the code cleaner 142 */ 143 typedef uint64_t ms_paddr_t; 144 145 /* 146 * Global Routines: 147 */ 148 int memscrub_add_span(pfn_t pfn, pgcnt_t pages); 149 int memscrub_delete_span(pfn_t pfn, pgcnt_t pages); 150 int memscrub_init(void); 151 void memscrub_induced_error(void); 152 153 /* 154 * Global Data: 155 */ 156 157 /* 158 * scrub if we have at least this many pages 159 */ 160 #define MEMSCRUB_MIN_PAGES (32 * 1024 * 1024 / PAGESIZE) 161 162 /* 163 * scan all of physical memory at least once every MEMSCRUB_PERIOD_SEC 164 */ 165 #define MEMSCRUB_DFL_PERIOD_SEC (12 * 60 * 60) /* 12 hours */ 166 167 /* 168 * scan at least MEMSCRUB_DFL_SPAN_PAGES each iteration 169 */ 170 #define MEMSCRUB_DFL_SPAN_PAGES ((32 * 1024 * 1024) / PAGESIZE) 171 172 /* 173 * almost anything is higher priority than scrubbing 174 */ 175 #define MEMSCRUB_DFL_THREAD_PRI MINCLSYSPRI 176 177 /* 178 * size used when scanning memory 179 */ 180 #define MEMSCRUB_BLOCK_SIZE 256 181 #define MEMSCRUB_BLOCK_SIZE_SHIFT 8 /* log2(MEMSCRUB_BLOCK_SIZE) */ 182 #define MEMSCRUB_BLOCKS_PER_PAGE (PAGESIZE >> MEMSCRUB_BLOCK_SIZE_SHIFT) 183 184 #define MEMSCRUB_BPP4M MMU_PAGESIZE4M >> MEMSCRUB_BLOCK_SIZE_SHIFT 185 #define MEMSCRUB_BPP512K MMU_PAGESIZE512K >> MEMSCRUB_BLOCK_SIZE_SHIFT 186 #define MEMSCRUB_BPP64K MMU_PAGESIZE64K >> MEMSCRUB_BLOCK_SIZE_SHIFT 187 #define MEMSCRUB_BPP MMU_PAGESIZE >> MEMSCRUB_BLOCK_SIZE_SHIFT 188 189 /* 190 * This message indicates that we have exceeded the limitations of 191 * the memscrubber. See the comments above regarding what would 192 * cause the sleep time to become zero. In DEBUG mode, this message 193 * is logged on the console and in the messages file. In non-DEBUG 194 * mode, it is only logged in the messages file. 195 */ 196 #ifdef DEBUG 197 #define MEMSCRUB_OVERRIDE_MSG "Memory scrubber sleep time is zero " \ 198 "seconds, consuming entire CPU." 199 #else 200 #define MEMSCRUB_OVERRIDE_MSG "!Memory scrubber sleep time is zero " \ 201 "seconds, consuming entire CPU." 202 #endif /* DEBUG */ 203 204 /* 205 * we can patch these defaults in /etc/system if necessary 206 */ 207 uint_t disable_memscrub = 0; 208 uint_t pause_memscrub = 0; 209 uint_t read_all_memscrub = 0; 210 uint_t memscrub_verbose = 0; 211 uint_t memscrub_all_idle = 0; 212 uint_t memscrub_span_pages = MEMSCRUB_DFL_SPAN_PAGES; 213 uint_t memscrub_period_sec = MEMSCRUB_DFL_PERIOD_SEC; 214 uint_t memscrub_thread_pri = MEMSCRUB_DFL_THREAD_PRI; 215 uint_t memscrub_delay_start_sec = 5 * 60; 216 uint_t memscrub_override_ticks = 1; 217 218 /* 219 * Static Routines 220 */ 221 static void memscrubber(void); 222 static void memscrub_cleanup(void); 223 static int memscrub_add_span_gen(pfn_t, pgcnt_t, struct memlist **, uint_t *); 224 static int memscrub_verify_span(ms_paddr_t *addrp, pgcnt_t *pagesp); 225 static void memscrub_scan(uint_t blks, ms_paddr_t src); 226 227 /* 228 * Static Data 229 */ 230 231 static struct memlist *memscrub_memlist; 232 static uint_t memscrub_phys_pages; 233 234 static kcondvar_t memscrub_cv; 235 static kmutex_t memscrub_lock; 236 /* 237 * memscrub_lock protects memscrub_memlist, interval_ticks, cprinfo, ... 238 */ 239 static void memscrub_init_mem_config(void); 240 static void memscrub_uninit_mem_config(void); 241 242 /* 243 * Linked list of memscrub aware spans having retired pages. 244 * Currently enabled only on sun4u USIII-based platforms. 245 */ 246 typedef struct memscrub_page_retire_span { 247 ms_paddr_t address; 248 struct memscrub_page_retire_span *next; 249 } memscrub_page_retire_span_t; 250 251 static memscrub_page_retire_span_t *memscrub_page_retire_span_list = NULL; 252 253 static void memscrub_page_retire_span_add(ms_paddr_t); 254 static void memscrub_page_retire_span_delete(ms_paddr_t); 255 static int memscrub_page_retire_span_search(ms_paddr_t); 256 static void memscrub_page_retire_span_list_update(void); 257 258 /* 259 * add_to_page_retire_list: Set by cpu_async_log_err() routine 260 * by calling memscrub_induced_error() when CE/UE occurs on a retired 261 * page due to memscrub reading. Cleared by memscrub after updating 262 * global page retire span list. Piggybacking on protection of 263 * memscrub_lock, which is held during set and clear. 264 * Note: When cpu_async_log_err() calls memscrub_induced_error(), it is running 265 * on softint context, which gets fired on a cpu memscrub thread currently 266 * running. Memscrub thread has affinity set during memscrub_read(), hence 267 * migration to new cpu not expected. 268 */ 269 static int add_to_page_retire_list = 0; 270 271 /* 272 * Keep track of some interesting statistics 273 */ 274 static struct memscrub_kstats { 275 kstat_named_t done_early; /* ahead of schedule */ 276 kstat_named_t early_sec; /* by cumulative num secs */ 277 kstat_named_t done_late; /* behind schedule */ 278 kstat_named_t late_sec; /* by cumulative num secs */ 279 kstat_named_t interval_ticks; /* num ticks between intervals */ 280 kstat_named_t force_run; /* forced to run, non-timeout */ 281 kstat_named_t errors_found; /* num errors found by memscrub */ 282 } memscrub_counts = { 283 { "done_early", KSTAT_DATA_UINT32 }, 284 { "early_sec", KSTAT_DATA_UINT32 }, 285 { "done_late", KSTAT_DATA_UINT32 }, 286 { "late_sec", KSTAT_DATA_UINT32 }, 287 { "interval_ticks", KSTAT_DATA_UINT32 }, 288 { "force_run", KSTAT_DATA_UINT32 }, 289 { "errors_found", KSTAT_DATA_UINT32 }, 290 }; 291 292 #define MEMSCRUB_STAT_INC(stat) memscrub_counts.stat.value.ui32++ 293 #define MEMSCRUB_STAT_SET(stat, val) memscrub_counts.stat.value.ui32 = (val) 294 #define MEMSCRUB_STAT_NINC(stat, val) memscrub_counts.stat.value.ui32 += (val) 295 296 static struct kstat *memscrub_ksp = (struct kstat *)NULL; 297 298 static timeout_id_t memscrub_tid = 0; /* keep track of timeout id */ 299 300 /* 301 * create memscrub_memlist from phys_install list 302 * initialize locks, set memscrub_phys_pages. 303 */ 304 int 305 memscrub_init(void) 306 { 307 struct memlist *src; 308 309 /* 310 * only startup the scrubber if we have a minimum 311 * number of pages 312 */ 313 if (physinstalled >= MEMSCRUB_MIN_PAGES) { 314 315 /* 316 * initialize locks 317 */ 318 mutex_init(&memscrub_lock, NULL, MUTEX_DRIVER, NULL); 319 cv_init(&memscrub_cv, NULL, CV_DRIVER, NULL); 320 321 /* 322 * copy phys_install to memscrub_memlist 323 */ 324 for (src = phys_install; src; src = src->ml_next) { 325 if (memscrub_add_span( 326 (pfn_t)(src->ml_address >> PAGESHIFT), 327 (pgcnt_t)(src->ml_size >> PAGESHIFT))) { 328 memscrub_cleanup(); 329 return (-1); 330 } 331 } 332 333 /* 334 * initialize kstats 335 */ 336 memscrub_ksp = kstat_create("unix", 0, "memscrub_kstat", 337 "misc", KSTAT_TYPE_NAMED, 338 sizeof (memscrub_counts) / sizeof (kstat_named_t), 339 KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE); 340 341 if (memscrub_ksp) { 342 memscrub_ksp->ks_data = (void *)&memscrub_counts; 343 kstat_install(memscrub_ksp); 344 } else { 345 cmn_err(CE_NOTE, "Memscrubber cannot create kstats\n"); 346 } 347 348 /* 349 * create memscrubber thread 350 */ 351 (void) thread_create(NULL, 0, (void (*)())memscrubber, 352 NULL, 0, &p0, TS_RUN, memscrub_thread_pri); 353 354 /* 355 * We don't want call backs changing the list 356 * if there is no thread running. We do not 357 * attempt to deal with stopping/starting scrubbing 358 * on memory size changes. 359 */ 360 memscrub_init_mem_config(); 361 } 362 363 return (0); 364 } 365 366 static void 367 memscrub_cleanup(void) 368 { 369 memscrub_uninit_mem_config(); 370 while (memscrub_memlist) { 371 (void) memscrub_delete_span( 372 (pfn_t)(memscrub_memlist->ml_address >> PAGESHIFT), 373 (pgcnt_t)(memscrub_memlist->ml_size >> PAGESHIFT)); 374 } 375 if (memscrub_ksp) 376 kstat_delete(memscrub_ksp); 377 cv_destroy(&memscrub_cv); 378 mutex_destroy(&memscrub_lock); 379 } 380 381 #ifdef MEMSCRUB_DEBUG 382 static void 383 memscrub_printmemlist(char *title, struct memlist *listp) 384 { 385 struct memlist *list; 386 387 cmn_err(CE_CONT, "%s:\n", title); 388 389 for (list = listp; list; list = list->ml_next) { 390 cmn_err(CE_CONT, "addr = 0x%llx, size = 0x%llx\n", 391 list->ml_address, list->ml_size); 392 } 393 } 394 #endif /* MEMSCRUB_DEBUG */ 395 396 /* ARGSUSED */ 397 static void 398 memscrub_wakeup(void *c) 399 { 400 /* 401 * grab mutex to guarantee that our wakeup call 402 * arrives after we go to sleep -- so we can't sleep forever. 403 */ 404 mutex_enter(&memscrub_lock); 405 cv_signal(&memscrub_cv); 406 mutex_exit(&memscrub_lock); 407 } 408 409 /* 410 * provide an interface external to the memscrubber 411 * which will force the memscrub thread to run vs. 412 * waiting for the timeout, if one is set 413 */ 414 void 415 memscrub_run(void) 416 { 417 MEMSCRUB_STAT_INC(force_run); 418 if (memscrub_tid) { 419 (void) untimeout(memscrub_tid); 420 memscrub_wakeup((void *)NULL); 421 } 422 } 423 424 /* 425 * this calculation doesn't account for the time 426 * that the actual scan consumes -- so we'd fall 427 * slightly behind schedule with this interval. 428 * It's very small. 429 */ 430 431 static uint_t 432 compute_interval_ticks(void) 433 { 434 /* 435 * We use msp_safe mpp_safe below to insure somebody 436 * doesn't set memscrub_span_pages or memscrub_phys_pages 437 * to 0 on us. 438 */ 439 static uint_t msp_safe, mpp_safe; 440 static uint_t interval_ticks, period_ticks; 441 msp_safe = memscrub_span_pages; 442 mpp_safe = memscrub_phys_pages; 443 444 period_ticks = memscrub_period_sec * hz; 445 interval_ticks = period_ticks; 446 447 ASSERT(mutex_owned(&memscrub_lock)); 448 449 if ((msp_safe != 0) && (mpp_safe != 0)) { 450 if (memscrub_phys_pages <= msp_safe) { 451 interval_ticks = period_ticks; 452 } else { 453 interval_ticks = (period_ticks / 454 (mpp_safe / msp_safe)); 455 } 456 } 457 return (interval_ticks); 458 } 459 460 void 461 memscrubber(void) 462 { 463 ms_paddr_t address, addr; 464 time_t deadline; 465 pgcnt_t pages; 466 uint_t reached_end = 1; 467 uint_t paused_message = 0; 468 uint_t interval_ticks = 0; 469 uint_t sleep_warn_printed = 0; 470 callb_cpr_t cprinfo; 471 472 /* 473 * notify CPR of our existence 474 */ 475 CALLB_CPR_INIT(&cprinfo, &memscrub_lock, callb_generic_cpr, "memscrub"); 476 477 mutex_enter(&memscrub_lock); 478 479 if (memscrub_memlist == NULL) { 480 cmn_err(CE_WARN, "memscrub_memlist not initialized."); 481 goto memscrub_exit; 482 } 483 484 address = memscrub_memlist->ml_address; 485 486 deadline = gethrestime_sec() + memscrub_delay_start_sec; 487 488 for (;;) { 489 if (disable_memscrub) 490 break; 491 492 /* 493 * compute interval_ticks 494 */ 495 interval_ticks = compute_interval_ticks(); 496 497 /* 498 * If the calculated sleep time is zero, and pause_memscrub 499 * has been set, make sure we sleep so that another thread 500 * can acquire memscrub_lock. 501 */ 502 if (interval_ticks == 0 && pause_memscrub) { 503 interval_ticks = hz; 504 } 505 506 /* 507 * And as a fail safe, under normal non-paused operation, do 508 * not allow the sleep time to be zero. 509 */ 510 if (interval_ticks == 0) { 511 interval_ticks = memscrub_override_ticks; 512 if (!sleep_warn_printed) { 513 cmn_err(CE_NOTE, MEMSCRUB_OVERRIDE_MSG); 514 sleep_warn_printed = 1; 515 } 516 } 517 518 MEMSCRUB_STAT_SET(interval_ticks, interval_ticks); 519 520 /* 521 * Did we just reach the end of memory? If we are at the 522 * end of memory, delay end of memory processing until 523 * pause_memscrub is not set. 524 */ 525 if (reached_end && !pause_memscrub) { 526 time_t now = gethrestime_sec(); 527 528 if (now >= deadline) { 529 MEMSCRUB_STAT_INC(done_late); 530 MEMSCRUB_STAT_NINC(late_sec, now - deadline); 531 /* 532 * past deadline, start right away 533 */ 534 interval_ticks = 0; 535 536 deadline = now + memscrub_period_sec; 537 } else { 538 /* 539 * we finished ahead of schedule. 540 * wait till previous deadline before re-start. 541 */ 542 interval_ticks = (deadline - now) * hz; 543 MEMSCRUB_STAT_INC(done_early); 544 MEMSCRUB_STAT_NINC(early_sec, deadline - now); 545 deadline += memscrub_period_sec; 546 } 547 reached_end = 0; 548 sleep_warn_printed = 0; 549 } 550 551 if (interval_ticks != 0) { 552 /* 553 * it is safe from our standpoint for CPR to 554 * suspend the system 555 */ 556 CALLB_CPR_SAFE_BEGIN(&cprinfo); 557 558 /* 559 * hit the snooze bar 560 */ 561 memscrub_tid = timeout(memscrub_wakeup, NULL, 562 interval_ticks); 563 564 /* 565 * go to sleep 566 */ 567 cv_wait(&memscrub_cv, &memscrub_lock); 568 569 /* 570 * at this point, no timeout should be set 571 */ 572 memscrub_tid = 0; 573 574 /* 575 * we need to goto work and will be modifying 576 * our internal state and mapping/unmapping 577 * TTEs 578 */ 579 CALLB_CPR_SAFE_END(&cprinfo, &memscrub_lock); 580 } 581 582 583 if (memscrub_phys_pages == 0) { 584 cmn_err(CE_WARN, "Memory scrubber has 0 pages to read"); 585 goto memscrub_exit; 586 } 587 588 if (!pause_memscrub) { 589 if (paused_message) { 590 paused_message = 0; 591 if (memscrub_verbose) 592 cmn_err(CE_NOTE, "Memory scrubber " 593 "resuming"); 594 } 595 596 if (read_all_memscrub) { 597 if (memscrub_verbose) 598 cmn_err(CE_NOTE, "Memory scrubber " 599 "reading all memory per request"); 600 601 addr = memscrub_memlist->ml_address; 602 reached_end = 0; 603 while (!reached_end) { 604 if (disable_memscrub) 605 break; 606 pages = memscrub_phys_pages; 607 reached_end = memscrub_verify_span( 608 &addr, &pages); 609 memscrub_scan(pages * 610 MEMSCRUB_BLOCKS_PER_PAGE, addr); 611 addr += ((uint64_t)pages * PAGESIZE); 612 } 613 read_all_memscrub = 0; 614 } 615 616 /* 617 * read 1 span 618 */ 619 pages = memscrub_span_pages; 620 621 if (disable_memscrub) 622 break; 623 624 /* 625 * determine physical address range 626 */ 627 reached_end = memscrub_verify_span(&address, 628 &pages); 629 630 memscrub_scan(pages * MEMSCRUB_BLOCKS_PER_PAGE, 631 address); 632 633 address += ((uint64_t)pages * PAGESIZE); 634 } 635 636 if (pause_memscrub && !paused_message) { 637 paused_message = 1; 638 if (memscrub_verbose) 639 cmn_err(CE_NOTE, "Memory scrubber paused"); 640 } 641 } 642 643 memscrub_exit: 644 cmn_err(CE_NOTE, "Memory scrubber exiting"); 645 CALLB_CPR_EXIT(&cprinfo); 646 memscrub_cleanup(); 647 thread_exit(); 648 /* NOTREACHED */ 649 } 650 651 /* 652 * condition address and size 653 * such that they span legal physical addresses. 654 * 655 * when appropriate, address will be rounded up to start of next 656 * struct memlist, and pages will be rounded down to the end of the 657 * memlist size. 658 * 659 * returns 1 if reached end of list, else returns 0. 660 */ 661 static int 662 memscrub_verify_span(ms_paddr_t *addrp, pgcnt_t *pagesp) 663 { 664 struct memlist *mlp; 665 ms_paddr_t address = *addrp; 666 uint64_t bytes = (uint64_t)*pagesp * PAGESIZE; 667 uint64_t bytes_remaining; 668 int reached_end = 0; 669 670 ASSERT(mutex_owned(&memscrub_lock)); 671 672 /* 673 * find memlist struct that contains addrp 674 * assumes memlist is sorted by ascending address. 675 */ 676 for (mlp = memscrub_memlist; mlp != NULL; mlp = mlp->ml_next) { 677 /* 678 * if before this chunk, round up to beginning 679 */ 680 if (address < mlp->ml_address) { 681 address = mlp->ml_address; 682 break; 683 } 684 /* 685 * if before end of chunk, then we found it 686 */ 687 if (address < (mlp->ml_address + mlp->ml_size)) 688 break; 689 690 /* else go to next struct memlist */ 691 } 692 /* 693 * if we hit end of list, start at beginning 694 */ 695 if (mlp == NULL) { 696 mlp = memscrub_memlist; 697 address = mlp->ml_address; 698 } 699 700 /* 701 * now we have legal address, and its mlp, condition bytes 702 */ 703 bytes_remaining = (mlp->ml_address + mlp->ml_size) - address; 704 705 if (bytes > bytes_remaining) 706 bytes = bytes_remaining; 707 708 /* 709 * will this span take us to end of list? 710 */ 711 if ((mlp->ml_next == NULL) && 712 ((mlp->ml_address + mlp->ml_size) == (address + bytes))) 713 reached_end = 1; 714 715 /* return values */ 716 *addrp = address; 717 *pagesp = bytes / PAGESIZE; 718 719 return (reached_end); 720 } 721 722 /* 723 * add a span to the memscrub list 724 * add to memscrub_phys_pages 725 */ 726 int 727 memscrub_add_span(pfn_t pfn, pgcnt_t pages) 728 { 729 #ifdef MEMSCRUB_DEBUG 730 ms_paddr_t address = (ms_paddr_t)pfn << PAGESHIFT; 731 uint64_t bytes = (uint64_t)pages << PAGESHIFT; 732 #endif /* MEMSCRUB_DEBUG */ 733 734 int retval; 735 736 mutex_enter(&memscrub_lock); 737 738 #ifdef MEMSCRUB_DEBUG 739 memscrub_printmemlist("memscrub_memlist before", memscrub_memlist); 740 cmn_err(CE_CONT, "memscrub_phys_pages: 0x%x\n", memscrub_phys_pages); 741 cmn_err(CE_CONT, "memscrub_add_span: address: 0x%llx" 742 " size: 0x%llx\n", address, bytes); 743 #endif /* MEMSCRUB_DEBUG */ 744 745 retval = memscrub_add_span_gen(pfn, pages, &memscrub_memlist, 746 &memscrub_phys_pages); 747 748 #ifdef MEMSCRUB_DEBUG 749 memscrub_printmemlist("memscrub_memlist after", memscrub_memlist); 750 cmn_err(CE_CONT, "memscrub_phys_pages: 0x%x\n", memscrub_phys_pages); 751 #endif /* MEMSCRUB_DEBUG */ 752 753 mutex_exit(&memscrub_lock); 754 755 return (retval); 756 } 757 758 static int 759 memscrub_add_span_gen( 760 pfn_t pfn, 761 pgcnt_t pages, 762 struct memlist **list, 763 uint_t *npgs) 764 { 765 ms_paddr_t address = (ms_paddr_t)pfn << PAGESHIFT; 766 uint64_t bytes = (uint64_t)pages << PAGESHIFT; 767 struct memlist *dst; 768 struct memlist *prev, *next; 769 int retval = 0; 770 771 /* 772 * allocate a new struct memlist 773 */ 774 775 dst = (struct memlist *) 776 kmem_alloc(sizeof (struct memlist), KM_NOSLEEP); 777 778 if (dst == NULL) { 779 retval = -1; 780 goto add_done; 781 } 782 783 dst->ml_address = address; 784 dst->ml_size = bytes; 785 786 /* 787 * first insert 788 */ 789 if (*list == NULL) { 790 dst->ml_prev = NULL; 791 dst->ml_next = NULL; 792 *list = dst; 793 794 goto add_done; 795 } 796 797 /* 798 * insert into sorted list 799 */ 800 for (prev = NULL, next = *list; 801 next != NULL; 802 prev = next, next = next->ml_next) { 803 if (address > (next->ml_address + next->ml_size)) 804 continue; 805 806 /* 807 * else insert here 808 */ 809 810 /* 811 * prepend to next 812 */ 813 if ((address + bytes) == next->ml_address) { 814 kmem_free(dst, sizeof (struct memlist)); 815 816 next->ml_address = address; 817 next->ml_size += bytes; 818 819 goto add_done; 820 } 821 822 /* 823 * append to next 824 */ 825 if (address == (next->ml_address + next->ml_size)) { 826 kmem_free(dst, sizeof (struct memlist)); 827 828 if (next->ml_next) { 829 /* 830 * don't overlap with next->ml_next 831 */ 832 if ((address + bytes) > 833 next->ml_next->ml_address) { 834 retval = -1; 835 goto add_done; 836 } 837 /* 838 * concatenate next and next->ml_next 839 */ 840 if ((address + bytes) == 841 next->ml_next->ml_address) { 842 struct memlist *mlp = next->ml_next; 843 844 if (next == *list) 845 *list = next->ml_next; 846 847 mlp->ml_address = next->ml_address; 848 mlp->ml_size += next->ml_size; 849 mlp->ml_size += bytes; 850 851 if (next->ml_prev) 852 next->ml_prev->ml_next = mlp; 853 mlp->ml_prev = next->ml_prev; 854 855 kmem_free(next, 856 sizeof (struct memlist)); 857 goto add_done; 858 } 859 } 860 861 next->ml_size += bytes; 862 863 goto add_done; 864 } 865 866 /* don't overlap with next */ 867 if ((address + bytes) > next->ml_address) { 868 retval = -1; 869 kmem_free(dst, sizeof (struct memlist)); 870 goto add_done; 871 } 872 873 /* 874 * insert before next 875 */ 876 dst->ml_prev = prev; 877 dst->ml_next = next; 878 next->ml_prev = dst; 879 if (prev == NULL) { 880 *list = dst; 881 } else { 882 prev->ml_next = dst; 883 } 884 goto add_done; 885 } /* end for */ 886 887 /* 888 * end of list, prev is valid and next is NULL 889 */ 890 prev->ml_next = dst; 891 dst->ml_prev = prev; 892 dst->ml_next = NULL; 893 894 add_done: 895 896 if (retval != -1) 897 *npgs += pages; 898 899 return (retval); 900 } 901 902 /* 903 * delete a span from the memscrub list 904 * subtract from memscrub_phys_pages 905 */ 906 int 907 memscrub_delete_span(pfn_t pfn, pgcnt_t pages) 908 { 909 ms_paddr_t address = (ms_paddr_t)pfn << PAGESHIFT; 910 uint64_t bytes = (uint64_t)pages << PAGESHIFT; 911 struct memlist *dst, *next; 912 int retval = 0; 913 914 mutex_enter(&memscrub_lock); 915 916 #ifdef MEMSCRUB_DEBUG 917 memscrub_printmemlist("memscrub_memlist Before", memscrub_memlist); 918 cmn_err(CE_CONT, "memscrub_phys_pages: 0x%x\n", memscrub_phys_pages); 919 cmn_err(CE_CONT, "memscrub_delete_span: 0x%llx 0x%llx\n", 920 address, bytes); 921 #endif /* MEMSCRUB_DEBUG */ 922 923 /* 924 * find struct memlist containing page 925 */ 926 for (next = memscrub_memlist; next != NULL; next = next->ml_next) { 927 if ((address >= next->ml_address) && 928 (address < next->ml_address + next->ml_size)) 929 break; 930 } 931 932 /* 933 * if start address not in list 934 */ 935 if (next == NULL) { 936 retval = -1; 937 goto delete_done; 938 } 939 940 /* 941 * error if size goes off end of this struct memlist 942 */ 943 if (address + bytes > next->ml_address + next->ml_size) { 944 retval = -1; 945 goto delete_done; 946 } 947 948 /* 949 * pages at beginning of struct memlist 950 */ 951 if (address == next->ml_address) { 952 /* 953 * if start & size match, delete from list 954 */ 955 if (bytes == next->ml_size) { 956 if (next == memscrub_memlist) 957 memscrub_memlist = next->ml_next; 958 if (next->ml_prev != NULL) 959 next->ml_prev->ml_next = next->ml_next; 960 if (next->ml_next != NULL) 961 next->ml_next->ml_prev = next->ml_prev; 962 963 kmem_free(next, sizeof (struct memlist)); 964 } else { 965 /* 966 * increment start address by bytes 967 */ 968 next->ml_address += bytes; 969 next->ml_size -= bytes; 970 } 971 goto delete_done; 972 } 973 974 /* 975 * pages at end of struct memlist 976 */ 977 if (address + bytes == next->ml_address + next->ml_size) { 978 /* 979 * decrement size by bytes 980 */ 981 next->ml_size -= bytes; 982 goto delete_done; 983 } 984 985 /* 986 * delete a span in the middle of the struct memlist 987 */ 988 { 989 /* 990 * create a new struct memlist 991 */ 992 dst = (struct memlist *) 993 kmem_alloc(sizeof (struct memlist), KM_NOSLEEP); 994 995 if (dst == NULL) { 996 retval = -1; 997 goto delete_done; 998 } 999 1000 /* 1001 * existing struct memlist gets address 1002 * and size up to pfn 1003 */ 1004 dst->ml_address = address + bytes; 1005 dst->ml_size = 1006 (next->ml_address + next->ml_size) - dst->ml_address; 1007 next->ml_size = address - next->ml_address; 1008 1009 /* 1010 * new struct memlist gets address starting 1011 * after pfn, until end 1012 */ 1013 1014 /* 1015 * link in new memlist after old 1016 */ 1017 dst->ml_next = next->ml_next; 1018 dst->ml_prev = next; 1019 1020 if (next->ml_next != NULL) 1021 next->ml_next->ml_prev = dst; 1022 next->ml_next = dst; 1023 } 1024 1025 delete_done: 1026 if (retval != -1) { 1027 memscrub_phys_pages -= pages; 1028 if (memscrub_phys_pages == 0) 1029 disable_memscrub = 1; 1030 } 1031 1032 #ifdef MEMSCRUB_DEBUG 1033 memscrub_printmemlist("memscrub_memlist After", memscrub_memlist); 1034 cmn_err(CE_CONT, "memscrub_phys_pages: 0x%x\n", memscrub_phys_pages); 1035 #endif /* MEMSCRUB_DEBUG */ 1036 1037 mutex_exit(&memscrub_lock); 1038 return (retval); 1039 } 1040 1041 static void 1042 memscrub_scan(uint_t blks, ms_paddr_t src) 1043 { 1044 uint_t psz, bpp, pgsread; 1045 pfn_t pfn; 1046 ms_paddr_t pa; 1047 caddr_t va; 1048 on_trap_data_t otd; 1049 int scan_mmu_pagesize = 0; 1050 int retired_pages = 0; 1051 1052 extern void memscrub_read(caddr_t src, uint_t blks); 1053 1054 ASSERT(mutex_owned(&memscrub_lock)); 1055 1056 pgsread = 0; 1057 pa = src; 1058 1059 if (memscrub_page_retire_span_list != NULL) { 1060 if (memscrub_page_retire_span_search(src)) { 1061 /* retired pages in current span */ 1062 scan_mmu_pagesize = 1; 1063 } 1064 } 1065 1066 #ifdef MEMSCRUB_DEBUG 1067 cmn_err(CE_NOTE, "scan_mmu_pagesize = %d\n" scan_mmu_pagesize); 1068 #endif /* MEMSCRUB_DEBUG */ 1069 1070 while (blks != 0) { 1071 /* Ensure the PA is properly aligned */ 1072 if (((pa & MMU_PAGEMASK4M) == pa) && 1073 (blks >= MEMSCRUB_BPP4M)) { 1074 psz = MMU_PAGESIZE4M; 1075 bpp = MEMSCRUB_BPP4M; 1076 } else if (((pa & MMU_PAGEMASK512K) == pa) && 1077 (blks >= MEMSCRUB_BPP512K)) { 1078 psz = MMU_PAGESIZE512K; 1079 bpp = MEMSCRUB_BPP512K; 1080 } else if (((pa & MMU_PAGEMASK64K) == pa) && 1081 (blks >= MEMSCRUB_BPP64K)) { 1082 psz = MMU_PAGESIZE64K; 1083 bpp = MEMSCRUB_BPP64K; 1084 } else if ((pa & MMU_PAGEMASK) == pa) { 1085 psz = MMU_PAGESIZE; 1086 bpp = MEMSCRUB_BPP; 1087 } else { 1088 if (memscrub_verbose) { 1089 cmn_err(CE_NOTE, "Memory scrubber ignoring " 1090 "non-page aligned block starting at 0x%" 1091 PRIx64, src); 1092 } 1093 return; 1094 } 1095 if (blks < bpp) bpp = blks; 1096 1097 #ifdef MEMSCRUB_DEBUG 1098 cmn_err(CE_NOTE, "Going to run psz=%x, " 1099 "bpp=%x pa=%llx\n", psz, bpp, pa); 1100 #endif /* MEMSCRUB_DEBUG */ 1101 1102 /* 1103 * MEMSCRUBBASE is a 4MB aligned page in the 1104 * kernel so that we can quickly map the PA 1105 * to a VA for the block loads performed in 1106 * memscrub_read. 1107 */ 1108 pfn = mmu_btop(pa); 1109 va = (caddr_t)MEMSCRUBBASE; 1110 hat_devload(kas.a_hat, va, psz, pfn, PROT_READ, 1111 HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK); 1112 1113 /* 1114 * Can't allow the memscrubber to migrate across CPUs as 1115 * we need to know whether CEEN is enabled for the current 1116 * CPU to enable us to scrub the memory. Don't use 1117 * kpreempt_disable as the time we take to scan a span (even 1118 * without cpu_check_ce having to manually cpu_check_block) 1119 * is too long to hold a higher priority thread (eg, RT) 1120 * off cpu. 1121 */ 1122 thread_affinity_set(curthread, CPU_CURRENT); 1123 1124 /* 1125 * Protect read scrub from async faults. For now, we simply 1126 * maintain a count of such faults caught. 1127 */ 1128 1129 if (!on_trap(&otd, OT_DATA_EC) && !scan_mmu_pagesize) { 1130 memscrub_read(va, bpp); 1131 /* 1132 * Check if CEs require logging 1133 */ 1134 cpu_check_ce(SCRUBBER_CEEN_CHECK, 1135 (uint64_t)pa, va, psz); 1136 no_trap(); 1137 thread_affinity_clear(curthread); 1138 } else { 1139 no_trap(); 1140 thread_affinity_clear(curthread); 1141 1142 /* 1143 * Got an async error.. 1144 * Try rescanning it at MMU_PAGESIZE 1145 * granularity if we were trying to 1146 * read at a larger page size. 1147 * This is to ensure we continue to 1148 * scan the rest of the span. 1149 * OR scanning MMU_PAGESIZE granularity to avoid 1150 * reading retired pages memory when scan_mmu_pagesize 1151 * is set. 1152 */ 1153 if (psz > MMU_PAGESIZE || scan_mmu_pagesize) { 1154 caddr_t vaddr = va; 1155 ms_paddr_t paddr = pa; 1156 int tmp = 0; 1157 for (; tmp < bpp; tmp += MEMSCRUB_BPP) { 1158 /* Don't scrub retired pages */ 1159 if (page_retire_check(paddr, NULL) 1160 == 0) { 1161 vaddr += MMU_PAGESIZE; 1162 paddr += MMU_PAGESIZE; 1163 retired_pages++; 1164 continue; 1165 } 1166 thread_affinity_set(curthread, 1167 CPU_CURRENT); 1168 if (!on_trap(&otd, OT_DATA_EC)) { 1169 memscrub_read(vaddr, 1170 MEMSCRUB_BPP); 1171 cpu_check_ce( 1172 SCRUBBER_CEEN_CHECK, 1173 (uint64_t)paddr, vaddr, 1174 MMU_PAGESIZE); 1175 no_trap(); 1176 } else { 1177 no_trap(); 1178 MEMSCRUB_STAT_INC(errors_found); 1179 } 1180 thread_affinity_clear(curthread); 1181 vaddr += MMU_PAGESIZE; 1182 paddr += MMU_PAGESIZE; 1183 } 1184 } 1185 } 1186 hat_unload(kas.a_hat, va, psz, HAT_UNLOAD_UNLOCK); 1187 1188 blks -= bpp; 1189 pa += psz; 1190 pgsread++; 1191 } 1192 1193 /* 1194 * If just finished scrubbing MMU_PAGESIZE at a time, but no retired 1195 * pages found so delete span from global list. 1196 */ 1197 if (scan_mmu_pagesize && retired_pages == 0) 1198 memscrub_page_retire_span_delete(src); 1199 1200 /* 1201 * Encountered CE/UE on a retired page during memscrub read of current 1202 * span. Adding span to global list to enable avoid reading further. 1203 */ 1204 if (add_to_page_retire_list) { 1205 if (!memscrub_page_retire_span_search(src)) 1206 memscrub_page_retire_span_add(src); 1207 add_to_page_retire_list = 0; 1208 } 1209 1210 if (memscrub_verbose) { 1211 cmn_err(CE_NOTE, "Memory scrubber read 0x%x pages starting " 1212 "at 0x%" PRIx64, pgsread, src); 1213 } 1214 } 1215 1216 /* 1217 * Called by cpu_async_log_err() when memscrub read causes 1218 * CE/UE on a retired page. 1219 */ 1220 void 1221 memscrub_induced_error(void) 1222 { 1223 add_to_page_retire_list = 1; 1224 } 1225 1226 /* 1227 * Called by page_retire() when toxic pages cannot be retired 1228 * immediately and are scheduled for retire. Memscrubber stops 1229 * scrubbing them to avoid further CE/UEs. 1230 */ 1231 void 1232 memscrub_notify(ms_paddr_t pa) 1233 { 1234 mutex_enter(&memscrub_lock); 1235 if (!memscrub_page_retire_span_search(pa)) 1236 memscrub_page_retire_span_add(pa); 1237 mutex_exit(&memscrub_lock); 1238 } 1239 1240 /* 1241 * Called by memscrub_scan() and memscrub_notify(). 1242 * pa: physical address of span with CE/UE, add to global list. 1243 */ 1244 static void 1245 memscrub_page_retire_span_add(ms_paddr_t pa) 1246 { 1247 memscrub_page_retire_span_t *new_span; 1248 1249 new_span = (memscrub_page_retire_span_t *) 1250 kmem_zalloc(sizeof (memscrub_page_retire_span_t), KM_NOSLEEP); 1251 1252 if (new_span == NULL) { 1253 #ifdef MEMSCRUB_DEBUG 1254 cmn_err(CE_NOTE, "failed to allocate new span - span with" 1255 " retired page/s not tracked.\n"); 1256 #endif /* MEMSCRUB_DEBUG */ 1257 return; 1258 } 1259 1260 new_span->address = pa; 1261 new_span->next = memscrub_page_retire_span_list; 1262 memscrub_page_retire_span_list = new_span; 1263 } 1264 1265 /* 1266 * Called by memscrub_scan(). 1267 * pa: physical address of span to be removed from global list. 1268 */ 1269 static void 1270 memscrub_page_retire_span_delete(ms_paddr_t pa) 1271 { 1272 memscrub_page_retire_span_t *prev_span, *next_span; 1273 1274 prev_span = memscrub_page_retire_span_list; 1275 next_span = memscrub_page_retire_span_list->next; 1276 1277 if (pa == prev_span->address) { 1278 memscrub_page_retire_span_list = next_span; 1279 kmem_free(prev_span, sizeof (memscrub_page_retire_span_t)); 1280 return; 1281 } 1282 1283 while (next_span) { 1284 if (pa == next_span->address) { 1285 prev_span->next = next_span->next; 1286 kmem_free(next_span, 1287 sizeof (memscrub_page_retire_span_t)); 1288 return; 1289 } 1290 prev_span = next_span; 1291 next_span = next_span->next; 1292 } 1293 } 1294 1295 /* 1296 * Called by memscrub_scan() and memscrub_notify(). 1297 * pa: physical address of span to be searched in global list. 1298 */ 1299 static int 1300 memscrub_page_retire_span_search(ms_paddr_t pa) 1301 { 1302 memscrub_page_retire_span_t *next_span = memscrub_page_retire_span_list; 1303 1304 while (next_span) { 1305 if (pa == next_span->address) 1306 return (1); 1307 next_span = next_span->next; 1308 } 1309 return (0); 1310 } 1311 1312 /* 1313 * Called from new_memscrub() as a result of memory delete. 1314 * Using page_numtopp_nolock() to determine if we have valid PA. 1315 */ 1316 static void 1317 memscrub_page_retire_span_list_update(void) 1318 { 1319 memscrub_page_retire_span_t *prev, *cur, *next; 1320 1321 if (memscrub_page_retire_span_list == NULL) 1322 return; 1323 1324 prev = cur = memscrub_page_retire_span_list; 1325 next = cur->next; 1326 1327 while (cur) { 1328 if (page_numtopp_nolock(mmu_btop(cur->address)) == NULL) { 1329 if (cur == memscrub_page_retire_span_list) { 1330 memscrub_page_retire_span_list = next; 1331 kmem_free(cur, 1332 sizeof (memscrub_page_retire_span_t)); 1333 prev = cur = memscrub_page_retire_span_list; 1334 } else { 1335 prev->next = cur->next; 1336 kmem_free(cur, 1337 sizeof (memscrub_page_retire_span_t)); 1338 cur = next; 1339 } 1340 } else { 1341 prev = cur; 1342 cur = next; 1343 } 1344 if (cur != NULL) 1345 next = cur->next; 1346 } 1347 } 1348 1349 /* 1350 * The memory add/delete callback mechanism does not pass in the 1351 * page ranges. The phys_install list has been updated though, so 1352 * create a new scrub list from it. 1353 */ 1354 1355 static int 1356 new_memscrub(int update_page_retire_list) 1357 { 1358 struct memlist *src, *list, *old_list; 1359 uint_t npgs; 1360 1361 /* 1362 * copy phys_install to memscrub_memlist 1363 */ 1364 list = NULL; 1365 npgs = 0; 1366 memlist_read_lock(); 1367 for (src = phys_install; src; src = src->ml_next) { 1368 if (memscrub_add_span_gen((pfn_t)(src->ml_address >> PAGESHIFT), 1369 (pgcnt_t)(src->ml_size >> PAGESHIFT), &list, &npgs)) { 1370 memlist_read_unlock(); 1371 while (list) { 1372 struct memlist *el; 1373 1374 el = list; 1375 list = list->ml_next; 1376 kmem_free(el, sizeof (struct memlist)); 1377 } 1378 return (-1); 1379 } 1380 } 1381 memlist_read_unlock(); 1382 1383 mutex_enter(&memscrub_lock); 1384 memscrub_phys_pages = npgs; 1385 old_list = memscrub_memlist; 1386 memscrub_memlist = list; 1387 1388 if (update_page_retire_list) 1389 memscrub_page_retire_span_list_update(); 1390 1391 mutex_exit(&memscrub_lock); 1392 1393 while (old_list) { 1394 struct memlist *el; 1395 1396 el = old_list; 1397 old_list = old_list->ml_next; 1398 kmem_free(el, sizeof (struct memlist)); 1399 } 1400 1401 return (0); 1402 } 1403 1404 /*ARGSUSED*/ 1405 static void 1406 memscrub_mem_config_post_add( 1407 void *arg, 1408 pgcnt_t delta_pages) 1409 { 1410 /* 1411 * We increment pause_memscrub before entering new_memscrub(). This 1412 * will force the memscrubber to sleep, allowing the DR callback 1413 * thread to acquire memscrub_lock in new_memscrub(). The use of 1414 * atomic_add_32() allows concurrent memory DR operations to use the 1415 * callbacks safely. 1416 */ 1417 atomic_add_32(&pause_memscrub, 1); 1418 ASSERT(pause_memscrub != 0); 1419 1420 /* 1421 * "Don't care" if we are not scrubbing new memory. 1422 */ 1423 (void) new_memscrub(0); /* retain page retire list */ 1424 1425 /* Restore the pause setting. */ 1426 atomic_add_32(&pause_memscrub, -1); 1427 } 1428 1429 /*ARGSUSED*/ 1430 static int 1431 memscrub_mem_config_pre_del( 1432 void *arg, 1433 pgcnt_t delta_pages) 1434 { 1435 /* Nothing to do. */ 1436 return (0); 1437 } 1438 1439 /*ARGSUSED*/ 1440 static void 1441 memscrub_mem_config_post_del( 1442 void *arg, 1443 pgcnt_t delta_pages, 1444 int cancelled) 1445 { 1446 /* 1447 * We increment pause_memscrub before entering new_memscrub(). This 1448 * will force the memscrubber to sleep, allowing the DR callback 1449 * thread to acquire memscrub_lock in new_memscrub(). The use of 1450 * atomic_add_32() allows concurrent memory DR operations to use the 1451 * callbacks safely. 1452 */ 1453 atomic_add_32(&pause_memscrub, 1); 1454 ASSERT(pause_memscrub != 0); 1455 1456 /* 1457 * Must stop scrubbing deleted memory as it may be disconnected. 1458 */ 1459 if (new_memscrub(1)) { /* update page retire list */ 1460 disable_memscrub = 1; 1461 } 1462 1463 /* Restore the pause setting. */ 1464 atomic_add_32(&pause_memscrub, -1); 1465 } 1466 1467 static kphysm_setup_vector_t memscrub_mem_config_vec = { 1468 KPHYSM_SETUP_VECTOR_VERSION, 1469 memscrub_mem_config_post_add, 1470 memscrub_mem_config_pre_del, 1471 memscrub_mem_config_post_del, 1472 }; 1473 1474 static void 1475 memscrub_init_mem_config() 1476 { 1477 int ret; 1478 1479 ret = kphysm_setup_func_register(&memscrub_mem_config_vec, 1480 (void *)NULL); 1481 ASSERT(ret == 0); 1482 } 1483 1484 static void 1485 memscrub_uninit_mem_config() 1486 { 1487 /* This call is OK if the register call was not done. */ 1488 kphysm_setup_func_unregister(&memscrub_mem_config_vec, (void *)NULL); 1489 } 1490