xref: /openbsd-src/sys/uvm/uvm_anon.c (revision 0b4f1452d8df729a522c5216b33d28622069a3aa)
1*0b4f1452Smpi /*	$OpenBSD: uvm_anon.c,v 1.61 2024/12/27 12:04:40 mpi Exp $	*/
22c932f6fSmiod /*	$NetBSD: uvm_anon.c,v 1.10 2000/11/25 06:27:59 chs Exp $	*/
3cd7ee8acSart 
4cd7ee8acSart /*
5cd7ee8acSart  * Copyright (c) 1997 Charles D. Cranor and Washington University.
6cd7ee8acSart  * All rights reserved.
7cd7ee8acSart  *
8cd7ee8acSart  * Redistribution and use in source and binary forms, with or without
9cd7ee8acSart  * modification, are permitted provided that the following conditions
10cd7ee8acSart  * are met:
11cd7ee8acSart  * 1. Redistributions of source code must retain the above copyright
12cd7ee8acSart  *    notice, this list of conditions and the following disclaimer.
13cd7ee8acSart  * 2. Redistributions in binary form must reproduce the above copyright
14cd7ee8acSart  *    notice, this list of conditions and the following disclaimer in the
15cd7ee8acSart  *    documentation and/or other materials provided with the distribution.
16cd7ee8acSart  *
17cd7ee8acSart  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18cd7ee8acSart  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19cd7ee8acSart  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20cd7ee8acSart  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21cd7ee8acSart  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22cd7ee8acSart  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23cd7ee8acSart  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24cd7ee8acSart  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25cd7ee8acSart  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26cd7ee8acSart  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27cd7ee8acSart  */
28cd7ee8acSart 
29cd7ee8acSart /*
30cd7ee8acSart  * uvm_anon.c: uvm anon ops
31cd7ee8acSart  */
32cd7ee8acSart 
33cd7ee8acSart #include <sys/param.h>
34cd7ee8acSart #include <sys/systm.h>
35cd7ee8acSart #include <sys/malloc.h>
36cd7ee8acSart #include <sys/pool.h>
3728fbabcfSart #include <sys/kernel.h>
3803d1830dStedu #include <sys/atomic.h>
39cd7ee8acSart 
40cd7ee8acSart #include <uvm/uvm.h>
41cd7ee8acSart #include <uvm/uvm_swap.h>
42cd7ee8acSart 
438d0b5bafSpedro struct pool uvm_anon_pool;
4428fbabcfSart 
45cd7ee8acSart void
462023d591Soga uvm_anon_init(void)
47cd7ee8acSart {
4819dcab73Smpi 	pool_init(&uvm_anon_pool, sizeof(struct vm_anon), 0, IPL_MPFLOOR,
49ac6cf2c1Stedu 	    PR_WAITOK, "anonpl", NULL);
508d0b5bafSpedro 	pool_sethiwat(&uvm_anon_pool, uvmexp.free / 16);
5128fbabcfSart }
5228fbabcfSart 
5328fbabcfSart /*
5452887a38Smpi  * uvm_analloc: allocate a new anon.
5552887a38Smpi  *
5652887a38Smpi  * => anon will have no lock associated.
57cd7ee8acSart  */
58cd7ee8acSart struct vm_anon *
592023d591Soga uvm_analloc(void)
60cd7ee8acSart {
618d0b5bafSpedro 	struct vm_anon *anon;
62cd7ee8acSart 
638d0b5bafSpedro 	anon = pool_get(&uvm_anon_pool, PR_NOWAIT);
648d0b5bafSpedro 	if (anon) {
6519dcab73Smpi 		anon->an_lock = NULL;
668d0b5bafSpedro 		anon->an_ref = 1;
678d0b5bafSpedro 		anon->an_page = NULL;
688d0b5bafSpedro 		anon->an_swslot = 0;
69cd7ee8acSart 	}
70b9df1565Smpi 	return anon;
71cd7ee8acSart }
72cd7ee8acSart 
73cd7ee8acSart /*
7419dcab73Smpi  * uvm_anfree_list: free a single anon structure
75cd7ee8acSart  *
7619dcab73Smpi  * => anon must be removed from the amap (if anon was in an amap).
7719dcab73Smpi  * => amap must be locked, if anon was owned by amap.
78cd7ee8acSart  * => we may lock the pageq's.
79cd7ee8acSart  */
80cd7ee8acSart void
8139ee2284Sbeck uvm_anfree_list(struct vm_anon *anon, struct pglist *pgl)
82cd7ee8acSart {
8319dcab73Smpi 	struct vm_page *pg = anon->an_page;
84cd7ee8acSart 
8519dcab73Smpi 	KASSERT(anon->an_lock == NULL || rw_write_held(anon->an_lock));
8619dcab73Smpi 	KASSERT(anon->an_ref == 0);
87cd7ee8acSart 
88cd7ee8acSart 	/*
8919dcab73Smpi 	 * Dispose of the page, if it is resident.
90cd7ee8acSart 	 */
9119dcab73Smpi 	if (pg != NULL) {
9219dcab73Smpi 		KASSERT(anon->an_lock != NULL);
9319dcab73Smpi 
94cd7ee8acSart 		/*
9552887a38Smpi 		 * If the page is busy, mark it as PG_RELEASED, so
9652887a38Smpi 		 * that uvm_anon_release(9) would release it later.
97cd7ee8acSart 		 */
989662fca4Sart 		if ((pg->pg_flags & PG_BUSY) != 0) {
9965d6360cSart 			atomic_setbits_int(&pg->pg_flags, PG_RELEASED);
10019dcab73Smpi 			rw_obj_hold(anon->an_lock);
101cd7ee8acSart 			return;
102cd7ee8acSart 		}
1031e8cdc2eSderaadt 		pmap_page_protect(pg, PROT_NONE);
10439ee2284Sbeck 		if (pgl != NULL) {
10539ee2284Sbeck 			/*
106b3af768dSjsg 			 * clean page, and put it on pglist
10739ee2284Sbeck 			 * for later freeing.
10839ee2284Sbeck 			 */
109d21d8ab4Smpi 			uvm_lock_pageq();
11039ee2284Sbeck 			uvm_pageclean(pg);
111d21d8ab4Smpi 			uvm_unlock_pageq();
11239ee2284Sbeck 			TAILQ_INSERT_HEAD(pgl, pg, pageq);
11339ee2284Sbeck 		} else {
114cd7ee8acSart 			uvm_lock_pageq();	/* lock out pagedaemon */
115cd7ee8acSart 			uvm_pagefree(pg);	/* bye bye */
116cd7ee8acSart 			uvm_unlock_pageq();	/* free the daemon */
117cd7ee8acSart 		}
11819dcab73Smpi 	} else {
119c3c0e069Smpi 		if (anon->an_swslot != 0 && anon->an_swslot != SWSLOT_BAD) {
120c4a864baSmpi 			/* This page is no longer only in swap. */
1214ceac6c1Smiod 			KASSERT(uvmexp.swpgonly > 0);
122c4a864baSmpi 			atomic_dec_int(&uvmexp.swpgonly);
1234ceac6c1Smiod 		}
12419dcab73Smpi 	}
12519dcab73Smpi 	anon->an_lock = NULL;
126cd7ee8acSart 
12752887a38Smpi 	/*
12852887a38Smpi 	 * Free any swap resources, leave a page replacement hint.
12952887a38Smpi 	 */
1308a42ed70Sart 	uvm_anon_dropswap(anon);
131cd7ee8acSart 
1328d0b5bafSpedro 	KASSERT(anon->an_page == NULL);
1338d0b5bafSpedro 	KASSERT(anon->an_swslot == 0);
1348d0b5bafSpedro 
1358d0b5bafSpedro 	pool_put(&uvm_anon_pool, anon);
136cd7ee8acSart }
137cd7ee8acSart 
138cd7ee8acSart /*
1394bfd0d76Sstefan  * uvm_anwait: wait for memory to become available to allocate an anon.
1404bfd0d76Sstefan  */
1414bfd0d76Sstefan void
1424bfd0d76Sstefan uvm_anwait(void)
1434bfd0d76Sstefan {
1444bfd0d76Sstefan 	struct vm_anon *anon;
1454bfd0d76Sstefan 
1464bfd0d76Sstefan 	/* XXX: Want something like pool_wait()? */
1474bfd0d76Sstefan 	anon = pool_get(&uvm_anon_pool, PR_WAITOK);
1484bfd0d76Sstefan 	pool_put(&uvm_anon_pool, anon);
1494bfd0d76Sstefan }
1504bfd0d76Sstefan 
1514bfd0d76Sstefan /*
15252887a38Smpi  * uvm_anon_pagein: fetch an anon's page.
15328fbabcfSart  *
15452887a38Smpi  * => anon must be locked, and is unlocked upon return.
15552887a38Smpi  * => returns true if pagein was aborted due to lack of memory.
15628fbabcfSart  */
15728fbabcfSart 
1588d0b5bafSpedro boolean_t
15919dcab73Smpi uvm_anon_pagein(struct vm_amap *amap, struct vm_anon *anon)
16028fbabcfSart {
16128fbabcfSart 	struct vm_page *pg;
16228fbabcfSart 	int rv;
16328fbabcfSart 
16419dcab73Smpi 	KASSERT(rw_write_held(anon->an_lock));
16519dcab73Smpi 	KASSERT(anon->an_lock == amap->am_lock);
16619dcab73Smpi 
16752887a38Smpi 	/*
16852887a38Smpi 	 * Get the page of the anon.
16952887a38Smpi 	 */
17019dcab73Smpi 	rv = uvmfault_anonget(NULL, amap, anon);
17128fbabcfSart 
17228fbabcfSart 	switch (rv) {
17334e43087Smpi 	case 0:
17434e43087Smpi 		/* Success - we have the page. */
17519dcab73Smpi 		KASSERT(rw_write_held(anon->an_lock));
17628fbabcfSart 		break;
17734e43087Smpi 	case EACCES:
17834e43087Smpi 	case ERESTART:
17928fbabcfSart 		/*
18034e43087Smpi 		 * Nothing more to do on errors.  ERESTART means that the
18134e43087Smpi 		 * anon was freed.
18228fbabcfSart 		 */
18328fbabcfSart 		return FALSE;
1842c932f6fSmiod 	default:
1852c932f6fSmiod #ifdef DIAGNOSTIC
1862c932f6fSmiod 		panic("anon_pagein: uvmfault_anonget -> %d", rv);
1872c932f6fSmiod #else
1882c932f6fSmiod 		return FALSE;
1892c932f6fSmiod #endif
19028fbabcfSart 	}
19128fbabcfSart 
19228fbabcfSart 	/*
19352887a38Smpi 	 * Mark the page as dirty and clear its swslot.
19428fbabcfSart 	 */
1958d0b5bafSpedro 	pg = anon->an_page;
19619dcab73Smpi 	if (anon->an_swslot > 0) {
19728fbabcfSart 		uvm_swap_free(anon->an_swslot, 1);
19819dcab73Smpi 	}
19928fbabcfSart 	anon->an_swslot = 0;
20065d6360cSart 	atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
20128fbabcfSart 
20252887a38Smpi 	/*
20352887a38Smpi 	 * Deactivate the page (to put it on a page queue).
20452887a38Smpi 	 */
20528fbabcfSart 	uvm_lock_pageq();
20628fbabcfSart 	uvm_pagedeactivate(pg);
20728fbabcfSart 	uvm_unlock_pageq();
20819dcab73Smpi 	rw_exit(anon->an_lock);
20928fbabcfSart 
21028fbabcfSart 	return FALSE;
21128fbabcfSart }
21219dcab73Smpi 
21319dcab73Smpi /*
21419dcab73Smpi  * uvm_anon_dropswap: release any swap resources from this anon.
21519dcab73Smpi  *
21619dcab73Smpi  * => anon must be locked or have a reference count of 0.
21719dcab73Smpi  */
21819dcab73Smpi void
21919dcab73Smpi uvm_anon_dropswap(struct vm_anon *anon)
22019dcab73Smpi {
22119dcab73Smpi 	KASSERT(anon->an_ref == 0 || rw_lock_held(anon->an_lock));
22219dcab73Smpi 
22319dcab73Smpi 	if (anon->an_swslot == 0)
22419dcab73Smpi 		return;
22519dcab73Smpi 
22619dcab73Smpi 	uvm_swap_free(anon->an_swslot, 1);
22719dcab73Smpi 	anon->an_swslot = 0;
22819dcab73Smpi }
22919dcab73Smpi 
23019dcab73Smpi 
23119dcab73Smpi /*
23219dcab73Smpi  * uvm_anon_release: release an anon and its page.
23319dcab73Smpi  *
23419dcab73Smpi  * => anon should not have any references.
23519dcab73Smpi  * => anon must be locked.
23619dcab73Smpi  */
23719dcab73Smpi 
23819dcab73Smpi void
23919dcab73Smpi uvm_anon_release(struct vm_anon *anon)
24019dcab73Smpi {
24119dcab73Smpi 	struct vm_page *pg = anon->an_page;
24219dcab73Smpi 	struct rwlock *lock;
24319dcab73Smpi 
24419dcab73Smpi 	KASSERT(rw_write_held(anon->an_lock));
24519dcab73Smpi 	KASSERT(pg != NULL);
24619dcab73Smpi 	KASSERT((pg->pg_flags & PG_RELEASED) != 0);
24719dcab73Smpi 	KASSERT((pg->pg_flags & PG_BUSY) != 0);
24819dcab73Smpi 	KASSERT(pg->uobject == NULL);
24919dcab73Smpi 	KASSERT(pg->uanon == anon);
25019dcab73Smpi 	KASSERT(anon->an_ref == 0);
25119dcab73Smpi 
25219dcab73Smpi 	uvm_lock_pageq();
2534dac787cSmpi 	pmap_page_protect(pg, PROT_NONE);
25419dcab73Smpi 	uvm_pagefree(pg);
25519dcab73Smpi 	uvm_unlock_pageq();
25619dcab73Smpi 	KASSERT(anon->an_page == NULL);
25719dcab73Smpi 	lock = anon->an_lock;
2583087914dSmpi 	uvm_anon_dropswap(anon);
2593087914dSmpi 	pool_put(&uvm_anon_pool, anon);
26019dcab73Smpi 	rw_exit(lock);
26119dcab73Smpi 	/* Note: extra reference is held for PG_RELEASED case. */
26219dcab73Smpi 	rw_obj_free(lock);
26319dcab73Smpi }
264