1 1.42 bouyer /* $NetBSD: uvm_pdpolicy_clock.c,v 1.42 2025/05/20 10:22:27 bouyer Exp $ */ 2 1.2 yamt /* NetBSD: uvm_pdaemon.c,v 1.72 2006/01/05 10:47:33 yamt Exp $ */ 3 1.2 yamt 4 1.28 ad /*- 5 1.34 ad * Copyright (c) 2019, 2020 The NetBSD Foundation, Inc. 6 1.28 ad * All rights reserved. 7 1.28 ad * 8 1.28 ad * This code is derived from software contributed to The NetBSD Foundation 9 1.28 ad * by Andrew Doran. 10 1.28 ad * 11 1.28 ad * Redistribution and use in source and binary forms, with or without 12 1.28 ad * modification, are permitted provided that the following conditions 13 1.28 ad * are met: 14 1.28 ad * 1. Redistributions of source code must retain the above copyright 15 1.28 ad * notice, this list of conditions and the following disclaimer. 16 1.28 ad * 2. Redistributions in binary form must reproduce the above copyright 17 1.28 ad * notice, this list of conditions and the following disclaimer in the 18 1.28 ad * documentation and/or other materials provided with the distribution. 19 1.28 ad * 20 1.28 ad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.28 ad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.28 ad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.28 ad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.28 ad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.28 ad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.28 ad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.28 ad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.28 ad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.28 ad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.28 ad * POSSIBILITY OF SUCH DAMAGE. 31 1.28 ad */ 32 1.28 ad 33 1.2 yamt /* 34 1.2 yamt * Copyright (c) 1997 Charles D. Cranor and Washington University. 35 1.2 yamt * Copyright (c) 1991, 1993, The Regents of the University of California. 36 1.2 yamt * 37 1.2 yamt * All rights reserved. 38 1.2 yamt * 39 1.2 yamt * This code is derived from software contributed to Berkeley by 40 1.2 yamt * The Mach Operating System project at Carnegie-Mellon University. 41 1.2 yamt * 42 1.2 yamt * Redistribution and use in source and binary forms, with or without 43 1.2 yamt * modification, are permitted provided that the following conditions 44 1.2 yamt * are met: 45 1.2 yamt * 1. Redistributions of source code must retain the above copyright 46 1.2 yamt * notice, this list of conditions and the following disclaimer. 47 1.2 yamt * 2. Redistributions in binary form must reproduce the above copyright 48 1.2 yamt * notice, this list of conditions and the following disclaimer in the 49 1.2 yamt * documentation and/or other materials provided with the distribution. 50 1.13 chuck * 3. Neither the name of the University nor the names of its contributors 51 1.2 yamt * may be used to endorse or promote products derived from this software 52 1.2 yamt * without specific prior written permission. 53 1.2 yamt * 54 1.2 yamt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 55 1.2 yamt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56 1.2 yamt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57 1.2 yamt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 58 1.2 yamt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 59 1.2 yamt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 60 1.2 yamt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 61 1.2 yamt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 62 1.2 yamt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63 1.2 yamt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64 1.2 yamt * SUCH DAMAGE. 65 1.2 yamt * 66 1.2 yamt * @(#)vm_pageout.c 8.5 (Berkeley) 2/14/94 67 1.2 yamt * from: Id: uvm_pdaemon.c,v 1.1.2.32 1998/02/06 05:26:30 chs Exp 68 1.2 yamt * 69 1.2 yamt * 70 1.2 yamt * Copyright (c) 1987, 1990 Carnegie-Mellon University. 71 1.2 yamt * All rights reserved. 72 1.2 yamt * 73 1.2 yamt * Permission to use, copy, modify and distribute this software and 74 1.2 yamt * its documentation is hereby granted, provided that both the copyright 75 1.2 yamt * notice and this permission notice appear in all copies of the 76 1.2 yamt * software, derivative works or modified versions, and any portions 77 1.2 yamt * thereof, and that both notices appear in supporting documentation. 78 1.2 yamt * 79 1.2 yamt * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 80 1.2 yamt * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 81 1.2 yamt * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 82 1.2 yamt * 83 1.2 yamt * Carnegie Mellon requests users of this software to return to 84 1.2 yamt * 85 1.2 yamt * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU 86 1.2 yamt * School of Computer Science 87 1.2 yamt * Carnegie Mellon University 88 1.2 yamt * Pittsburgh PA 15213-3890 89 1.2 yamt * 90 1.2 yamt * any improvements or extensions that they make and grant Carnegie the 91 1.2 yamt * rights to redistribute these changes. 92 1.2 yamt */ 93 1.2 yamt 94 1.2 yamt #if defined(PDSIM) 95 1.2 yamt 96 1.2 yamt #include "pdsim.h" 97 1.2 yamt 98 1.2 yamt #else /* defined(PDSIM) */ 99 1.2 yamt 100 1.2 yamt #include <sys/cdefs.h> 101 1.42 bouyer __KERNEL_RCSID(0, "$NetBSD: uvm_pdpolicy_clock.c,v 1.42 2025/05/20 10:22:27 bouyer Exp $"); 102 1.2 yamt 103 1.2 yamt #include <sys/param.h> 104 1.2 yamt #include <sys/proc.h> 105 1.2 yamt #include <sys/systm.h> 106 1.2 yamt #include <sys/kernel.h> 107 1.28 ad #include <sys/kmem.h> 108 1.29 mlelstv #include <sys/atomic.h> 109 1.2 yamt 110 1.2 yamt #include <uvm/uvm.h> 111 1.2 yamt #include <uvm/uvm_pdpolicy.h> 112 1.2 yamt #include <uvm/uvm_pdpolicy_impl.h> 113 1.18 ad #include <uvm/uvm_stat.h> 114 1.2 yamt 115 1.2 yamt #endif /* defined(PDSIM) */ 116 1.2 yamt 117 1.28 ad /* 118 1.28 ad * per-CPU queue of pending page status changes. 128 entries makes for a 119 1.28 ad * 1kB queue on _LP64 and has been found to be a reasonable compromise that 120 1.28 ad * keeps lock contention events and wait times low, while not using too much 121 1.28 ad * memory nor allowing global state to fall too far behind. 122 1.28 ad */ 123 1.28 ad #if !defined(CLOCK_PDQ_SIZE) 124 1.28 ad #define CLOCK_PDQ_SIZE 128 125 1.28 ad #endif /* !defined(CLOCK_PDQ_SIZE) */ 126 1.28 ad 127 1.28 ad #define PQ_INACTIVE 0x00000010 /* page is in inactive list */ 128 1.28 ad #define PQ_ACTIVE 0x00000020 /* page is in active list */ 129 1.2 yamt 130 1.2 yamt #if !defined(CLOCK_INACTIVEPCT) 131 1.2 yamt #define CLOCK_INACTIVEPCT 33 132 1.2 yamt #endif /* !defined(CLOCK_INACTIVEPCT) */ 133 1.2 yamt 134 1.2 yamt struct uvmpdpol_globalstate { 135 1.18 ad kmutex_t lock; /* lock on state */ 136 1.18 ad /* <= compiler pads here */ 137 1.18 ad struct pglist s_activeq /* allocated pages, in use */ 138 1.18 ad __aligned(COHERENCY_UNIT); 139 1.2 yamt struct pglist s_inactiveq; /* pages between the clock hands */ 140 1.2 yamt int s_active; 141 1.2 yamt int s_inactive; 142 1.2 yamt int s_inactarg; 143 1.2 yamt struct uvm_pctparam s_anonmin; 144 1.2 yamt struct uvm_pctparam s_filemin; 145 1.2 yamt struct uvm_pctparam s_execmin; 146 1.2 yamt struct uvm_pctparam s_anonmax; 147 1.2 yamt struct uvm_pctparam s_filemax; 148 1.2 yamt struct uvm_pctparam s_execmax; 149 1.2 yamt struct uvm_pctparam s_inactivepct; 150 1.2 yamt }; 151 1.2 yamt 152 1.2 yamt struct uvmpdpol_scanstate { 153 1.7 thorpej bool ss_anonreact, ss_filereact, ss_execreact; 154 1.24 ad struct vm_page ss_marker; 155 1.2 yamt }; 156 1.2 yamt 157 1.18 ad static void uvmpdpol_pageactivate_locked(struct vm_page *); 158 1.18 ad static void uvmpdpol_pagedeactivate_locked(struct vm_page *); 159 1.18 ad static void uvmpdpol_pagedequeue_locked(struct vm_page *); 160 1.28 ad static bool uvmpdpol_pagerealize_locked(struct vm_page *); 161 1.28 ad static struct uvm_cpu *uvmpdpol_flush(void); 162 1.18 ad 163 1.18 ad static struct uvmpdpol_globalstate pdpol_state __cacheline_aligned; 164 1.2 yamt static struct uvmpdpol_scanstate pdpol_scanstate; 165 1.2 yamt 166 1.2 yamt PDPOL_EVCNT_DEFINE(reactexec) 167 1.2 yamt PDPOL_EVCNT_DEFINE(reactfile) 168 1.2 yamt PDPOL_EVCNT_DEFINE(reactanon) 169 1.2 yamt 170 1.2 yamt static void 171 1.2 yamt clock_tune(void) 172 1.2 yamt { 173 1.2 yamt struct uvmpdpol_globalstate *s = &pdpol_state; 174 1.2 yamt 175 1.2 yamt s->s_inactarg = UVM_PCTPARAM_APPLY(&s->s_inactivepct, 176 1.2 yamt s->s_active + s->s_inactive); 177 1.2 yamt if (s->s_inactarg <= uvmexp.freetarg) { 178 1.2 yamt s->s_inactarg = uvmexp.freetarg + 1; 179 1.2 yamt } 180 1.2 yamt } 181 1.2 yamt 182 1.2 yamt void 183 1.2 yamt uvmpdpol_scaninit(void) 184 1.2 yamt { 185 1.2 yamt struct uvmpdpol_globalstate *s = &pdpol_state; 186 1.2 yamt struct uvmpdpol_scanstate *ss = &pdpol_scanstate; 187 1.2 yamt int t; 188 1.7 thorpej bool anonunder, fileunder, execunder; 189 1.7 thorpej bool anonover, fileover, execover; 190 1.7 thorpej bool anonreact, filereact, execreact; 191 1.20 ad int64_t freepg, anonpg, filepg, execpg; 192 1.2 yamt 193 1.2 yamt /* 194 1.2 yamt * decide which types of pages we want to reactivate instead of freeing 195 1.2 yamt * to keep usage within the minimum and maximum usage limits. 196 1.39 ad * uvm_availmem() will sync the counters. 197 1.2 yamt */ 198 1.2 yamt 199 1.38 ad freepg = uvm_availmem(false); 200 1.39 ad anonpg = cpu_count_get(CPU_COUNT_ANONCLEAN) + 201 1.39 ad cpu_count_get(CPU_COUNT_ANONDIRTY) + 202 1.39 ad cpu_count_get(CPU_COUNT_ANONUNKNOWN); 203 1.20 ad execpg = cpu_count_get(CPU_COUNT_EXECPAGES); 204 1.39 ad filepg = cpu_count_get(CPU_COUNT_FILECLEAN) + 205 1.39 ad cpu_count_get(CPU_COUNT_FILEDIRTY) + 206 1.39 ad cpu_count_get(CPU_COUNT_FILEUNKNOWN) - 207 1.39 ad execpg; 208 1.20 ad 209 1.18 ad mutex_enter(&s->lock); 210 1.20 ad t = s->s_active + s->s_inactive + freepg; 211 1.20 ad anonunder = anonpg <= UVM_PCTPARAM_APPLY(&s->s_anonmin, t); 212 1.20 ad fileunder = filepg <= UVM_PCTPARAM_APPLY(&s->s_filemin, t); 213 1.20 ad execunder = execpg <= UVM_PCTPARAM_APPLY(&s->s_execmin, t); 214 1.20 ad anonover = anonpg > UVM_PCTPARAM_APPLY(&s->s_anonmax, t); 215 1.20 ad fileover = filepg > UVM_PCTPARAM_APPLY(&s->s_filemax, t); 216 1.20 ad execover = execpg > UVM_PCTPARAM_APPLY(&s->s_execmax, t); 217 1.2 yamt anonreact = anonunder || (!anonover && (fileover || execover)); 218 1.2 yamt filereact = fileunder || (!fileover && (anonover || execover)); 219 1.2 yamt execreact = execunder || (!execover && (anonover || fileover)); 220 1.2 yamt if (filereact && execreact && (anonreact || uvm_swapisfull())) { 221 1.8 thorpej anonreact = filereact = execreact = false; 222 1.2 yamt } 223 1.2 yamt ss->ss_anonreact = anonreact; 224 1.2 yamt ss->ss_filereact = filereact; 225 1.2 yamt ss->ss_execreact = execreact; 226 1.24 ad memset(&ss->ss_marker, 0, sizeof(ss->ss_marker)); 227 1.24 ad ss->ss_marker.flags = PG_MARKER; 228 1.24 ad TAILQ_INSERT_HEAD(&pdpol_state.s_inactiveq, &ss->ss_marker, pdqueue); 229 1.24 ad mutex_exit(&s->lock); 230 1.24 ad } 231 1.24 ad 232 1.24 ad void 233 1.24 ad uvmpdpol_scanfini(void) 234 1.24 ad { 235 1.24 ad struct uvmpdpol_globalstate *s = &pdpol_state; 236 1.24 ad struct uvmpdpol_scanstate *ss = &pdpol_scanstate; 237 1.2 yamt 238 1.24 ad mutex_enter(&s->lock); 239 1.24 ad TAILQ_REMOVE(&pdpol_state.s_inactiveq, &ss->ss_marker, pdqueue); 240 1.18 ad mutex_exit(&s->lock); 241 1.2 yamt } 242 1.2 yamt 243 1.2 yamt struct vm_page * 244 1.33 ad uvmpdpol_selectvictim(krwlock_t **plock) 245 1.2 yamt { 246 1.18 ad struct uvmpdpol_globalstate *s = &pdpol_state; 247 1.2 yamt struct uvmpdpol_scanstate *ss = &pdpol_scanstate; 248 1.2 yamt struct vm_page *pg; 249 1.33 ad krwlock_t *lock; 250 1.2 yamt 251 1.18 ad mutex_enter(&s->lock); 252 1.2 yamt while (/* CONSTCOND */ 1) { 253 1.2 yamt struct vm_anon *anon; 254 1.2 yamt struct uvm_object *uobj; 255 1.2 yamt 256 1.24 ad pg = TAILQ_NEXT(&ss->ss_marker, pdqueue); 257 1.2 yamt if (pg == NULL) { 258 1.2 yamt break; 259 1.2 yamt } 260 1.24 ad KASSERT((pg->flags & PG_MARKER) == 0); 261 1.2 yamt uvmexp.pdscans++; 262 1.2 yamt 263 1.2 yamt /* 264 1.40 andvar * acquire interlock to stabilize page identity. 265 1.18 ad * if we have caught the page in a state of flux 266 1.28 ad * deal with it and retry. 267 1.2 yamt */ 268 1.18 ad mutex_enter(&pg->interlock); 269 1.28 ad if (uvmpdpol_pagerealize_locked(pg)) { 270 1.28 ad mutex_exit(&pg->interlock); 271 1.28 ad continue; 272 1.2 yamt } 273 1.2 yamt 274 1.2 yamt /* 275 1.24 ad * now prepare to move on to the next page. 276 1.24 ad */ 277 1.24 ad TAILQ_REMOVE(&pdpol_state.s_inactiveq, &ss->ss_marker, 278 1.24 ad pdqueue); 279 1.24 ad TAILQ_INSERT_AFTER(&pdpol_state.s_inactiveq, pg, 280 1.24 ad &ss->ss_marker, pdqueue); 281 1.24 ad 282 1.24 ad /* 283 1.2 yamt * enforce the minimum thresholds on different 284 1.2 yamt * types of memory usage. if reusing the current 285 1.2 yamt * page would reduce that type of usage below its 286 1.2 yamt * minimum, reactivate the page instead and move 287 1.2 yamt * on to the next page. 288 1.2 yamt */ 289 1.18 ad anon = pg->uanon; 290 1.18 ad uobj = pg->uobject; 291 1.2 yamt if (uobj && UVM_OBJ_IS_VTEXT(uobj) && ss->ss_execreact) { 292 1.28 ad uvmpdpol_pageactivate_locked(pg); 293 1.18 ad mutex_exit(&pg->interlock); 294 1.2 yamt PDPOL_EVCNT_INCR(reactexec); 295 1.2 yamt continue; 296 1.2 yamt } 297 1.2 yamt if (uobj && UVM_OBJ_IS_VNODE(uobj) && 298 1.2 yamt !UVM_OBJ_IS_VTEXT(uobj) && ss->ss_filereact) { 299 1.28 ad uvmpdpol_pageactivate_locked(pg); 300 1.18 ad mutex_exit(&pg->interlock); 301 1.2 yamt PDPOL_EVCNT_INCR(reactfile); 302 1.2 yamt continue; 303 1.2 yamt } 304 1.2 yamt if ((anon || UVM_OBJ_IS_AOBJ(uobj)) && ss->ss_anonreact) { 305 1.28 ad uvmpdpol_pageactivate_locked(pg); 306 1.18 ad mutex_exit(&pg->interlock); 307 1.2 yamt PDPOL_EVCNT_INCR(reactanon); 308 1.2 yamt continue; 309 1.2 yamt } 310 1.2 yamt 311 1.18 ad /* 312 1.18 ad * try to lock the object that owns the page. 313 1.18 ad * 314 1.18 ad * with the page interlock held, we can drop s->lock, which 315 1.18 ad * could otherwise serve as a barrier to us getting the 316 1.18 ad * object locked, because the owner of the object's lock may 317 1.18 ad * be blocked on s->lock (i.e. a deadlock). 318 1.18 ad * 319 1.18 ad * whatever happens, uvmpd_trylockowner() will release the 320 1.18 ad * interlock. with the interlock dropped we can then 321 1.18 ad * re-acquire our own lock. the order is: 322 1.18 ad * 323 1.18 ad * object -> pdpol -> interlock. 324 1.18 ad */ 325 1.18 ad mutex_exit(&s->lock); 326 1.18 ad lock = uvmpd_trylockowner(pg); 327 1.18 ad /* pg->interlock now released */ 328 1.18 ad mutex_enter(&s->lock); 329 1.18 ad if (lock == NULL) { 330 1.18 ad /* didn't get it - try the next page. */ 331 1.18 ad continue; 332 1.18 ad } 333 1.18 ad 334 1.18 ad /* 335 1.18 ad * move referenced pages back to active queue and skip to 336 1.18 ad * next page. 337 1.18 ad */ 338 1.18 ad if (pmap_is_referenced(pg)) { 339 1.28 ad mutex_enter(&pg->interlock); 340 1.18 ad uvmpdpol_pageactivate_locked(pg); 341 1.28 ad mutex_exit(&pg->interlock); 342 1.18 ad uvmexp.pdreact++; 343 1.33 ad rw_exit(lock); 344 1.18 ad continue; 345 1.18 ad } 346 1.18 ad 347 1.18 ad /* we have a potential victim. */ 348 1.18 ad *plock = lock; 349 1.2 yamt break; 350 1.2 yamt } 351 1.18 ad mutex_exit(&s->lock); 352 1.2 yamt return pg; 353 1.2 yamt } 354 1.2 yamt 355 1.2 yamt void 356 1.2 yamt uvmpdpol_balancequeue(int swap_shortage) 357 1.2 yamt { 358 1.18 ad struct uvmpdpol_globalstate *s = &pdpol_state; 359 1.2 yamt int inactive_shortage; 360 1.24 ad struct vm_page *p, marker; 361 1.33 ad krwlock_t *lock; 362 1.2 yamt 363 1.2 yamt /* 364 1.2 yamt * we have done the scan to get free pages. now we work on meeting 365 1.2 yamt * our inactive target. 366 1.2 yamt */ 367 1.2 yamt 368 1.24 ad memset(&marker, 0, sizeof(marker)); 369 1.24 ad marker.flags = PG_MARKER; 370 1.24 ad 371 1.18 ad mutex_enter(&s->lock); 372 1.24 ad TAILQ_INSERT_HEAD(&pdpol_state.s_activeq, &marker, pdqueue); 373 1.24 ad for (;;) { 374 1.24 ad inactive_shortage = 375 1.24 ad pdpol_state.s_inactarg - pdpol_state.s_inactive; 376 1.24 ad if (inactive_shortage <= 0 && swap_shortage <= 0) { 377 1.24 ad break; 378 1.2 yamt } 379 1.24 ad p = TAILQ_NEXT(&marker, pdqueue); 380 1.24 ad if (p == NULL) { 381 1.24 ad break; 382 1.14 rmind } 383 1.24 ad KASSERT((p->flags & PG_MARKER) == 0); 384 1.14 rmind 385 1.18 ad /* 386 1.40 andvar * acquire interlock to stabilize page identity. 387 1.18 ad * if we have caught the page in a state of flux 388 1.28 ad * deal with it and retry. 389 1.18 ad */ 390 1.18 ad mutex_enter(&p->interlock); 391 1.28 ad if (uvmpdpol_pagerealize_locked(p)) { 392 1.28 ad mutex_exit(&p->interlock); 393 1.28 ad continue; 394 1.18 ad } 395 1.24 ad 396 1.24 ad /* 397 1.24 ad * now prepare to move on to the next page. 398 1.24 ad */ 399 1.24 ad TAILQ_REMOVE(&pdpol_state.s_activeq, &marker, pdqueue); 400 1.24 ad TAILQ_INSERT_AFTER(&pdpol_state.s_activeq, p, &marker, 401 1.24 ad pdqueue); 402 1.24 ad 403 1.24 ad /* 404 1.24 ad * try to lock the object that owns the page. see comments 405 1.24 ad * in uvmpdol_selectvictim(). 406 1.24 ad */ 407 1.24 ad mutex_exit(&s->lock); 408 1.24 ad lock = uvmpd_trylockowner(p); 409 1.24 ad /* p->interlock now released */ 410 1.24 ad mutex_enter(&s->lock); 411 1.24 ad if (lock == NULL) { 412 1.24 ad /* didn't get it - try the next page. */ 413 1.24 ad continue; 414 1.24 ad } 415 1.24 ad 416 1.24 ad /* 417 1.24 ad * if there's a shortage of swap slots, try to free it. 418 1.24 ad */ 419 1.24 ad if (swap_shortage > 0 && (p->flags & PG_SWAPBACKED) != 0 && 420 1.24 ad (p->flags & PG_BUSY) == 0) { 421 1.24 ad if (uvmpd_dropswap(p)) { 422 1.24 ad swap_shortage--; 423 1.24 ad } 424 1.24 ad } 425 1.24 ad 426 1.24 ad /* 427 1.24 ad * if there's a shortage of inactive pages, deactivate. 428 1.24 ad */ 429 1.24 ad if (inactive_shortage > 0) { 430 1.28 ad pmap_clear_reference(p); 431 1.28 ad mutex_enter(&p->interlock); 432 1.18 ad uvmpdpol_pagedeactivate_locked(p); 433 1.28 ad mutex_exit(&p->interlock); 434 1.2 yamt uvmexp.pddeact++; 435 1.2 yamt inactive_shortage--; 436 1.2 yamt } 437 1.33 ad rw_exit(lock); 438 1.2 yamt } 439 1.24 ad TAILQ_REMOVE(&pdpol_state.s_activeq, &marker, pdqueue); 440 1.18 ad mutex_exit(&s->lock); 441 1.2 yamt } 442 1.2 yamt 443 1.18 ad static void 444 1.18 ad uvmpdpol_pagedeactivate_locked(struct vm_page *pg) 445 1.2 yamt { 446 1.28 ad struct uvmpdpol_globalstate *s __diagused = &pdpol_state; 447 1.2 yamt 448 1.28 ad KASSERT(mutex_owned(&s->lock)); 449 1.28 ad KASSERT(mutex_owned(&pg->interlock)); 450 1.28 ad KASSERT((pg->pqflags & (PQ_INTENT_MASK | PQ_INTENT_SET)) != 451 1.28 ad (PQ_INTENT_D | PQ_INTENT_SET)); 452 1.14 rmind 453 1.2 yamt if (pg->pqflags & PQ_ACTIVE) { 454 1.23 ad TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pdqueue); 455 1.2 yamt KASSERT(pdpol_state.s_active > 0); 456 1.2 yamt pdpol_state.s_active--; 457 1.2 yamt } 458 1.2 yamt if ((pg->pqflags & PQ_INACTIVE) == 0) { 459 1.2 yamt KASSERT(pg->wire_count == 0); 460 1.23 ad TAILQ_INSERT_TAIL(&pdpol_state.s_inactiveq, pg, pdqueue); 461 1.2 yamt pdpol_state.s_inactive++; 462 1.2 yamt } 463 1.34 ad pg->pqflags &= ~(PQ_ACTIVE | PQ_INTENT_SET); 464 1.34 ad pg->pqflags |= PQ_INACTIVE; 465 1.2 yamt } 466 1.2 yamt 467 1.2 yamt void 468 1.18 ad uvmpdpol_pagedeactivate(struct vm_page *pg) 469 1.18 ad { 470 1.18 ad 471 1.35 ad KASSERT(uvm_page_owner_locked_p(pg, false)); 472 1.28 ad KASSERT(mutex_owned(&pg->interlock)); 473 1.28 ad 474 1.28 ad /* 475 1.28 ad * we have to clear the reference bit now, as when it comes time to 476 1.28 ad * realize the intent we won't have the object locked any more. 477 1.28 ad */ 478 1.28 ad pmap_clear_reference(pg); 479 1.28 ad uvmpdpol_set_intent(pg, PQ_INTENT_I); 480 1.18 ad } 481 1.18 ad 482 1.18 ad static void 483 1.18 ad uvmpdpol_pageactivate_locked(struct vm_page *pg) 484 1.2 yamt { 485 1.28 ad struct uvmpdpol_globalstate *s __diagused = &pdpol_state; 486 1.28 ad 487 1.28 ad KASSERT(mutex_owned(&s->lock)); 488 1.28 ad KASSERT(mutex_owned(&pg->interlock)); 489 1.28 ad KASSERT((pg->pqflags & (PQ_INTENT_MASK | PQ_INTENT_SET)) != 490 1.28 ad (PQ_INTENT_D | PQ_INTENT_SET)); 491 1.2 yamt 492 1.18 ad uvmpdpol_pagedequeue_locked(pg); 493 1.23 ad TAILQ_INSERT_TAIL(&pdpol_state.s_activeq, pg, pdqueue); 494 1.2 yamt pdpol_state.s_active++; 495 1.34 ad pg->pqflags &= ~(PQ_INACTIVE | PQ_INTENT_SET); 496 1.34 ad pg->pqflags |= PQ_ACTIVE; 497 1.2 yamt } 498 1.2 yamt 499 1.2 yamt void 500 1.18 ad uvmpdpol_pageactivate(struct vm_page *pg) 501 1.18 ad { 502 1.28 ad 503 1.35 ad KASSERT(uvm_page_owner_locked_p(pg, false)); 504 1.28 ad KASSERT(mutex_owned(&pg->interlock)); 505 1.18 ad 506 1.31 ad uvmpdpol_set_intent(pg, PQ_INTENT_A); 507 1.18 ad } 508 1.18 ad 509 1.18 ad static void 510 1.18 ad uvmpdpol_pagedequeue_locked(struct vm_page *pg) 511 1.2 yamt { 512 1.28 ad struct uvmpdpol_globalstate *s __diagused = &pdpol_state; 513 1.28 ad 514 1.28 ad KASSERT(mutex_owned(&s->lock)); 515 1.28 ad KASSERT(mutex_owned(&pg->interlock)); 516 1.2 yamt 517 1.2 yamt if (pg->pqflags & PQ_ACTIVE) { 518 1.23 ad TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pdqueue); 519 1.28 ad KASSERT((pg->pqflags & PQ_INACTIVE) == 0); 520 1.2 yamt KASSERT(pdpol_state.s_active > 0); 521 1.2 yamt pdpol_state.s_active--; 522 1.2 yamt } else if (pg->pqflags & PQ_INACTIVE) { 523 1.23 ad TAILQ_REMOVE(&pdpol_state.s_inactiveq, pg, pdqueue); 524 1.2 yamt KASSERT(pdpol_state.s_inactive > 0); 525 1.2 yamt pdpol_state.s_inactive--; 526 1.2 yamt } 527 1.34 ad pg->pqflags &= ~(PQ_ACTIVE | PQ_INACTIVE | PQ_INTENT_SET); 528 1.2 yamt } 529 1.2 yamt 530 1.2 yamt void 531 1.18 ad uvmpdpol_pagedequeue(struct vm_page *pg) 532 1.18 ad { 533 1.18 ad 534 1.33 ad KASSERT(uvm_page_owner_locked_p(pg, true)); 535 1.28 ad KASSERT(mutex_owned(&pg->interlock)); 536 1.28 ad 537 1.28 ad uvmpdpol_set_intent(pg, PQ_INTENT_D); 538 1.18 ad } 539 1.18 ad 540 1.18 ad void 541 1.2 yamt uvmpdpol_pageenqueue(struct vm_page *pg) 542 1.2 yamt { 543 1.2 yamt 544 1.35 ad KASSERT(uvm_page_owner_locked_p(pg, false)); 545 1.28 ad KASSERT(mutex_owned(&pg->interlock)); 546 1.28 ad 547 1.28 ad uvmpdpol_set_intent(pg, PQ_INTENT_E); 548 1.2 yamt } 549 1.2 yamt 550 1.2 yamt void 551 1.5 yamt uvmpdpol_anfree(struct vm_anon *an) 552 1.2 yamt { 553 1.2 yamt } 554 1.2 yamt 555 1.7 thorpej bool 556 1.2 yamt uvmpdpol_pageisqueued_p(struct vm_page *pg) 557 1.2 yamt { 558 1.28 ad uint32_t pqflags; 559 1.2 yamt 560 1.28 ad /* 561 1.28 ad * if there's an intent set, we have to consider it. otherwise, 562 1.28 ad * return the actual state. we may be called unlocked for the 563 1.28 ad * purpose of assertions, which is safe due to the page lifecycle. 564 1.28 ad */ 565 1.28 ad pqflags = atomic_load_relaxed(&pg->pqflags); 566 1.28 ad if ((pqflags & PQ_INTENT_SET) != 0) { 567 1.28 ad return (pqflags & PQ_INTENT_MASK) != PQ_INTENT_D; 568 1.28 ad } else { 569 1.28 ad return (pqflags & (PQ_ACTIVE | PQ_INACTIVE)) != 0; 570 1.28 ad } 571 1.2 yamt } 572 1.2 yamt 573 1.37 ad bool 574 1.37 ad uvmpdpol_pageactivate_p(struct vm_page *pg) 575 1.37 ad { 576 1.37 ad uint32_t pqflags; 577 1.37 ad 578 1.37 ad /* consider intent in preference to actual state. */ 579 1.37 ad pqflags = atomic_load_relaxed(&pg->pqflags); 580 1.37 ad if ((pqflags & PQ_INTENT_SET) != 0) { 581 1.37 ad pqflags &= PQ_INTENT_MASK; 582 1.37 ad return pqflags != PQ_INTENT_A && pqflags != PQ_INTENT_E; 583 1.37 ad } else { 584 1.37 ad /* 585 1.37 ad * TODO: Enabling this may be too much of a big hammer, 586 1.37 ad * since we do get useful information from activations. 587 1.37 ad * Think about it more and maybe come up with a heuristic 588 1.37 ad * or something. 589 1.37 ad * 590 1.37 ad * return (pqflags & PQ_ACTIVE) == 0; 591 1.37 ad */ 592 1.37 ad return true; 593 1.37 ad } 594 1.37 ad } 595 1.37 ad 596 1.2 yamt void 597 1.2 yamt uvmpdpol_estimatepageable(int *active, int *inactive) 598 1.2 yamt { 599 1.18 ad struct uvmpdpol_globalstate *s = &pdpol_state; 600 1.2 yamt 601 1.32 ad /* 602 1.32 ad * Don't take any locks here. This can be called from DDB, and in 603 1.32 ad * any case the numbers are stale the instant the lock is dropped, 604 1.32 ad * so it just doesn't matter. 605 1.32 ad */ 606 1.2 yamt if (active) { 607 1.32 ad *active = s->s_active; 608 1.2 yamt } 609 1.2 yamt if (inactive) { 610 1.32 ad *inactive = s->s_inactive; 611 1.2 yamt } 612 1.2 yamt } 613 1.2 yamt 614 1.2 yamt #if !defined(PDSIM) 615 1.2 yamt static int 616 1.2 yamt min_check(struct uvm_pctparam *pct, int t) 617 1.2 yamt { 618 1.2 yamt struct uvmpdpol_globalstate *s = &pdpol_state; 619 1.2 yamt int total = t; 620 1.2 yamt 621 1.2 yamt if (pct != &s->s_anonmin) { 622 1.2 yamt total += uvm_pctparam_get(&s->s_anonmin); 623 1.2 yamt } 624 1.2 yamt if (pct != &s->s_filemin) { 625 1.2 yamt total += uvm_pctparam_get(&s->s_filemin); 626 1.2 yamt } 627 1.2 yamt if (pct != &s->s_execmin) { 628 1.2 yamt total += uvm_pctparam_get(&s->s_execmin); 629 1.2 yamt } 630 1.2 yamt if (total > 95) { 631 1.2 yamt return EINVAL; 632 1.2 yamt } 633 1.2 yamt return 0; 634 1.2 yamt } 635 1.2 yamt #endif /* !defined(PDSIM) */ 636 1.2 yamt 637 1.2 yamt void 638 1.2 yamt uvmpdpol_init(void) 639 1.2 yamt { 640 1.2 yamt struct uvmpdpol_globalstate *s = &pdpol_state; 641 1.2 yamt 642 1.18 ad mutex_init(&s->lock, MUTEX_DEFAULT, IPL_NONE); 643 1.2 yamt TAILQ_INIT(&s->s_activeq); 644 1.2 yamt TAILQ_INIT(&s->s_inactiveq); 645 1.2 yamt uvm_pctparam_init(&s->s_inactivepct, CLOCK_INACTIVEPCT, NULL); 646 1.2 yamt uvm_pctparam_init(&s->s_anonmin, 10, min_check); 647 1.2 yamt uvm_pctparam_init(&s->s_filemin, 10, min_check); 648 1.2 yamt uvm_pctparam_init(&s->s_execmin, 5, min_check); 649 1.2 yamt uvm_pctparam_init(&s->s_anonmax, 80, NULL); 650 1.2 yamt uvm_pctparam_init(&s->s_filemax, 50, NULL); 651 1.2 yamt uvm_pctparam_init(&s->s_execmax, 30, NULL); 652 1.2 yamt } 653 1.2 yamt 654 1.2 yamt void 655 1.28 ad uvmpdpol_init_cpu(struct uvm_cpu *ucpu) 656 1.28 ad { 657 1.28 ad 658 1.28 ad ucpu->pdq = 659 1.28 ad kmem_alloc(CLOCK_PDQ_SIZE * sizeof(struct vm_page *), KM_SLEEP); 660 1.28 ad ucpu->pdqhead = CLOCK_PDQ_SIZE; 661 1.28 ad ucpu->pdqtail = CLOCK_PDQ_SIZE; 662 1.28 ad } 663 1.28 ad 664 1.28 ad void 665 1.2 yamt uvmpdpol_reinit(void) 666 1.2 yamt { 667 1.2 yamt } 668 1.2 yamt 669 1.7 thorpej bool 670 1.2 yamt uvmpdpol_needsscan_p(void) 671 1.2 yamt { 672 1.2 yamt 673 1.28 ad /* 674 1.28 ad * this must be an unlocked check: can be called from interrupt. 675 1.28 ad */ 676 1.17 para return pdpol_state.s_inactive < pdpol_state.s_inactarg; 677 1.2 yamt } 678 1.2 yamt 679 1.2 yamt void 680 1.2 yamt uvmpdpol_tune(void) 681 1.2 yamt { 682 1.18 ad struct uvmpdpol_globalstate *s = &pdpol_state; 683 1.2 yamt 684 1.18 ad mutex_enter(&s->lock); 685 1.2 yamt clock_tune(); 686 1.18 ad mutex_exit(&s->lock); 687 1.2 yamt } 688 1.2 yamt 689 1.28 ad /* 690 1.30 ad * uvmpdpol_pagerealize_locked: take the intended state set on a page and 691 1.30 ad * make it real. return true if any work was done. 692 1.28 ad */ 693 1.28 ad static bool 694 1.28 ad uvmpdpol_pagerealize_locked(struct vm_page *pg) 695 1.28 ad { 696 1.28 ad struct uvmpdpol_globalstate *s __diagused = &pdpol_state; 697 1.28 ad 698 1.28 ad KASSERT(mutex_owned(&s->lock)); 699 1.28 ad KASSERT(mutex_owned(&pg->interlock)); 700 1.28 ad 701 1.28 ad switch (pg->pqflags & (PQ_INTENT_MASK | PQ_INTENT_SET)) { 702 1.28 ad case PQ_INTENT_A | PQ_INTENT_SET: 703 1.28 ad case PQ_INTENT_E | PQ_INTENT_SET: 704 1.28 ad uvmpdpol_pageactivate_locked(pg); 705 1.28 ad return true; 706 1.28 ad case PQ_INTENT_I | PQ_INTENT_SET: 707 1.28 ad uvmpdpol_pagedeactivate_locked(pg); 708 1.28 ad return true; 709 1.28 ad case PQ_INTENT_D | PQ_INTENT_SET: 710 1.28 ad uvmpdpol_pagedequeue_locked(pg); 711 1.28 ad return true; 712 1.28 ad default: 713 1.28 ad return false; 714 1.28 ad } 715 1.28 ad } 716 1.28 ad 717 1.28 ad /* 718 1.28 ad * uvmpdpol_flush: return the current uvm_cpu with all of its pending 719 1.28 ad * updates flushed to the global queues. this routine may block, and 720 1.28 ad * so can switch cpu. the idea is to empty to queue on whatever cpu 721 1.28 ad * we finally end up on. 722 1.41 bouyer * Must be called at splsoftbio() 723 1.28 ad */ 724 1.28 ad static struct uvm_cpu * 725 1.28 ad uvmpdpol_flush(void) 726 1.28 ad { 727 1.28 ad struct uvmpdpol_globalstate *s __diagused = &pdpol_state; 728 1.28 ad struct uvm_cpu *ucpu; 729 1.28 ad struct vm_page *pg; 730 1.28 ad 731 1.28 ad KASSERT(kpreempt_disabled()); 732 1.28 ad 733 1.28 ad mutex_enter(&s->lock); 734 1.28 ad for (;;) { 735 1.28 ad /* 736 1.28 ad * prefer scanning forwards (even though mutex_enter() is 737 1.28 ad * serializing) so as to not defeat any prefetch logic in 738 1.28 ad * the CPU. that means elsewhere enqueuing backwards, like 739 1.28 ad * a stack, but not so important there as pages are being 740 1.28 ad * added singularly. 741 1.28 ad * 742 1.28 ad * prefetch the next "struct vm_page" while working on the 743 1.28 ad * current one. this has a measurable and very positive 744 1.28 ad * effect in reducing the amount of time spent here under 745 1.28 ad * the global lock. 746 1.28 ad */ 747 1.28 ad ucpu = curcpu()->ci_data.cpu_uvm; 748 1.28 ad KASSERT(ucpu->pdqhead <= ucpu->pdqtail); 749 1.28 ad if (__predict_false(ucpu->pdqhead == ucpu->pdqtail)) { 750 1.28 ad break; 751 1.28 ad } 752 1.28 ad pg = ucpu->pdq[ucpu->pdqhead++]; 753 1.28 ad if (__predict_true(ucpu->pdqhead != ucpu->pdqtail)) { 754 1.28 ad __builtin_prefetch(ucpu->pdq[ucpu->pdqhead]); 755 1.28 ad } 756 1.28 ad mutex_enter(&pg->interlock); 757 1.28 ad pg->pqflags &= ~PQ_INTENT_QUEUED; 758 1.28 ad (void)uvmpdpol_pagerealize_locked(pg); 759 1.28 ad mutex_exit(&pg->interlock); 760 1.28 ad } 761 1.28 ad mutex_exit(&s->lock); 762 1.28 ad return ucpu; 763 1.28 ad } 764 1.28 ad 765 1.28 ad /* 766 1.28 ad * uvmpdpol_pagerealize: realize any intent set on the page. in this 767 1.28 ad * implementation, that means putting the page on a per-CPU queue to be 768 1.28 ad * dealt with later. 769 1.28 ad */ 770 1.28 ad void 771 1.28 ad uvmpdpol_pagerealize(struct vm_page *pg) 772 1.28 ad { 773 1.28 ad struct uvm_cpu *ucpu; 774 1.41 bouyer int s; 775 1.28 ad 776 1.28 ad /* 777 1.28 ad * drain the per per-CPU queue if full, then enter the page. 778 1.28 ad */ 779 1.41 bouyer s = splsoftbio(); 780 1.28 ad ucpu = curcpu()->ci_data.cpu_uvm; 781 1.41 bouyer while (__predict_false(ucpu->pdqhead == 0)) { 782 1.28 ad ucpu = uvmpdpol_flush(); 783 1.28 ad } 784 1.28 ad ucpu->pdq[--(ucpu->pdqhead)] = pg; 785 1.41 bouyer splx(s); 786 1.28 ad } 787 1.28 ad 788 1.28 ad /* 789 1.28 ad * uvmpdpol_idle: called from the system idle loop. periodically purge any 790 1.28 ad * pending updates back to the global queues. 791 1.28 ad */ 792 1.28 ad void 793 1.28 ad uvmpdpol_idle(struct uvm_cpu *ucpu) 794 1.28 ad { 795 1.28 ad struct uvmpdpol_globalstate *s = &pdpol_state; 796 1.28 ad struct vm_page *pg; 797 1.41 bouyer int s_spl; 798 1.28 ad 799 1.28 ad KASSERT(kpreempt_disabled()); 800 1.28 ad 801 1.28 ad /* 802 1.28 ad * if no pages in the queue, we have nothing to do. 803 1.28 ad */ 804 1.28 ad if (ucpu->pdqhead == ucpu->pdqtail) { 805 1.36 maxv ucpu->pdqtime = getticks(); 806 1.28 ad return; 807 1.28 ad } 808 1.28 ad 809 1.28 ad /* 810 1.28 ad * don't do this more than ~8 times a second as it would needlessly 811 1.28 ad * exert pressure. 812 1.28 ad */ 813 1.36 maxv if (getticks() - ucpu->pdqtime < (hz >> 3)) { 814 1.28 ad return; 815 1.28 ad } 816 1.28 ad 817 1.28 ad /* 818 1.28 ad * the idle LWP can't block, so we have to try for the lock. if we 819 1.28 ad * get it, purge the per-CPU pending update queue. continually 820 1.28 ad * check for a pending resched: in that case exit immediately. 821 1.28 ad */ 822 1.28 ad if (mutex_tryenter(&s->lock)) { 823 1.41 bouyer s_spl = splsoftbio(); 824 1.28 ad while (ucpu->pdqhead != ucpu->pdqtail) { 825 1.28 ad pg = ucpu->pdq[ucpu->pdqhead]; 826 1.28 ad if (!mutex_tryenter(&pg->interlock)) { 827 1.28 ad break; 828 1.28 ad } 829 1.28 ad ucpu->pdqhead++; 830 1.28 ad pg->pqflags &= ~PQ_INTENT_QUEUED; 831 1.28 ad (void)uvmpdpol_pagerealize_locked(pg); 832 1.28 ad mutex_exit(&pg->interlock); 833 1.28 ad if (curcpu()->ci_want_resched) { 834 1.28 ad break; 835 1.28 ad } 836 1.28 ad } 837 1.28 ad if (ucpu->pdqhead == ucpu->pdqtail) { 838 1.36 maxv ucpu->pdqtime = getticks(); 839 1.28 ad } 840 1.41 bouyer splx(s_spl); 841 1.28 ad mutex_exit(&s->lock); 842 1.28 ad } 843 1.28 ad } 844 1.28 ad 845 1.2 yamt #if !defined(PDSIM) 846 1.2 yamt 847 1.2 yamt #include <sys/sysctl.h> /* XXX SYSCTL_DESCR */ 848 1.2 yamt 849 1.2 yamt void 850 1.2 yamt uvmpdpol_sysctlsetup(void) 851 1.2 yamt { 852 1.2 yamt struct uvmpdpol_globalstate *s = &pdpol_state; 853 1.2 yamt 854 1.2 yamt uvm_pctparam_createsysctlnode(&s->s_anonmin, "anonmin", 855 1.2 yamt SYSCTL_DESCR("Percentage of physical memory reserved " 856 1.2 yamt "for anonymous application data")); 857 1.2 yamt uvm_pctparam_createsysctlnode(&s->s_filemin, "filemin", 858 1.2 yamt SYSCTL_DESCR("Percentage of physical memory reserved " 859 1.11 martin "for cached file data")); 860 1.2 yamt uvm_pctparam_createsysctlnode(&s->s_execmin, "execmin", 861 1.2 yamt SYSCTL_DESCR("Percentage of physical memory reserved " 862 1.11 martin "for cached executable data")); 863 1.2 yamt 864 1.2 yamt uvm_pctparam_createsysctlnode(&s->s_anonmax, "anonmax", 865 1.2 yamt SYSCTL_DESCR("Percentage of physical memory which will " 866 1.2 yamt "be reclaimed from other usage for " 867 1.2 yamt "anonymous application data")); 868 1.2 yamt uvm_pctparam_createsysctlnode(&s->s_filemax, "filemax", 869 1.2 yamt SYSCTL_DESCR("Percentage of physical memory which will " 870 1.2 yamt "be reclaimed from other usage for cached " 871 1.2 yamt "file data")); 872 1.2 yamt uvm_pctparam_createsysctlnode(&s->s_execmax, "execmax", 873 1.2 yamt SYSCTL_DESCR("Percentage of physical memory which will " 874 1.2 yamt "be reclaimed from other usage for cached " 875 1.2 yamt "executable data")); 876 1.2 yamt 877 1.2 yamt uvm_pctparam_createsysctlnode(&s->s_inactivepct, "inactivepct", 878 1.2 yamt SYSCTL_DESCR("Percentage of inactive queue of " 879 1.2 yamt "the entire (active + inactive) queue")); 880 1.2 yamt } 881 1.2 yamt 882 1.2 yamt #endif /* !defined(PDSIM) */ 883 1.2 yamt 884 1.2 yamt #if defined(PDSIM) 885 1.2 yamt void 886 1.2 yamt pdsim_dump(const char *id) 887 1.2 yamt { 888 1.2 yamt #if defined(DEBUG) 889 1.2 yamt /* XXX */ 890 1.2 yamt #endif /* defined(DEBUG) */ 891 1.2 yamt } 892 1.2 yamt #endif /* defined(PDSIM) */ 893