1*b0597d59Schs /* $NetBSD: uvm_object.c,v 1.25 2020/08/15 07:24:09 chs Exp $ */
20b30e150Syamt
30b30e150Syamt /*
468575131Sad * Copyright (c) 2006, 2010, 2019 The NetBSD Foundation, Inc.
50b30e150Syamt * All rights reserved.
60b30e150Syamt *
727c80c16Srmind * This code is derived from software contributed to The NetBSD Foundation
827c80c16Srmind * by Mindaugas Rasiukevicius.
927c80c16Srmind *
100b30e150Syamt * Redistribution and use in source and binary forms, with or without
110b30e150Syamt * modification, are permitted provided that the following conditions
120b30e150Syamt * are met:
130b30e150Syamt * 1. Redistributions of source code must retain the above copyright
140b30e150Syamt * notice, this list of conditions and the following disclaimer.
150b30e150Syamt * 2. Redistributions in binary form must reproduce the above copyright
160b30e150Syamt * notice, this list of conditions and the following disclaimer in the
170b30e150Syamt * documentation and/or other materials provided with the distribution.
180b30e150Syamt *
190b30e150Syamt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
200b30e150Syamt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
210b30e150Syamt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
220b30e150Syamt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
230b30e150Syamt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
240b30e150Syamt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
250b30e150Syamt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
260b30e150Syamt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
270b30e150Syamt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
280b30e150Syamt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
290b30e150Syamt * POSSIBILITY OF SUCH DAMAGE.
300b30e150Syamt */
310b30e150Syamt
320b30e150Syamt /*
330b30e150Syamt * uvm_object.c: operate with memory objects
340b30e150Syamt *
350b30e150Syamt * TODO:
360b30e150Syamt * 1. Support PG_RELEASED-using objects
370b30e150Syamt */
380b30e150Syamt
390b30e150Syamt #include <sys/cdefs.h>
40*b0597d59Schs __KERNEL_RCSID(0, "$NetBSD: uvm_object.c,v 1.25 2020/08/15 07:24:09 chs Exp $");
410b30e150Syamt
42d8e04c90Spooka #ifdef _KERNEL_OPT
43b4e5923fSthorpej #include "opt_ddb.h"
44d8e04c90Spooka #endif
450b30e150Syamt
460b30e150Syamt #include <sys/param.h>
47d2a0ebb6Sad #include <sys/rwlock.h>
48e225b7bdSrmind #include <sys/queue.h>
490b30e150Syamt
500b30e150Syamt #include <uvm/uvm.h>
51b4e5923fSthorpej #include <uvm/uvm_ddb.h>
52881d12e6Sad #include <uvm/uvm_page_array.h>
530b30e150Syamt
54e225b7bdSrmind /* Page count to fetch per single step. */
550b30e150Syamt #define FETCH_PAGECOUNT 16
560b30e150Syamt
570b30e150Syamt /*
58e225b7bdSrmind * uvm_obj_init: initialize UVM memory object.
59e225b7bdSrmind */
60e225b7bdSrmind void
uvm_obj_init(struct uvm_object * uo,const struct uvm_pagerops * ops,bool alock,u_int refs)61e225b7bdSrmind uvm_obj_init(struct uvm_object *uo, const struct uvm_pagerops *ops,
62e225b7bdSrmind bool alock, u_int refs)
63e225b7bdSrmind {
64e225b7bdSrmind
650b782e1eSmrg #if 0 /* notyet */
666e1ac70aSmrg KASSERT(ops);
670b782e1eSmrg #endif
68e225b7bdSrmind if (alock) {
69e225b7bdSrmind /* Allocate and assign a lock. */
70d2a0ebb6Sad uo->vmobjlock = rw_obj_alloc();
71e225b7bdSrmind } else {
72e225b7bdSrmind /* The lock will need to be set via uvm_obj_setlock(). */
73e225b7bdSrmind uo->vmobjlock = NULL;
74e225b7bdSrmind }
75e225b7bdSrmind uo->pgops = ops;
76e225b7bdSrmind LIST_INIT(&uo->uo_ubc);
77e225b7bdSrmind uo->uo_npages = 0;
78e225b7bdSrmind uo->uo_refs = refs;
7968575131Sad radix_tree_init_tree(&uo->uo_pages);
80e225b7bdSrmind }
81e225b7bdSrmind
82e225b7bdSrmind /*
83e225b7bdSrmind * uvm_obj_destroy: destroy UVM memory object.
84e225b7bdSrmind */
85e225b7bdSrmind void
uvm_obj_destroy(struct uvm_object * uo,bool dlock)86e225b7bdSrmind uvm_obj_destroy(struct uvm_object *uo, bool dlock)
87e225b7bdSrmind {
88e225b7bdSrmind
8968575131Sad KASSERT(radix_tree_empty_tree_p(&uo->uo_pages));
90e225b7bdSrmind
917a15ad24Srmind /* Purge any UBC entries associated with this object. */
92e225b7bdSrmind ubc_purge(uo);
937a15ad24Srmind
94e225b7bdSrmind /* Destroy the lock, if requested. */
95e225b7bdSrmind if (dlock) {
96d2a0ebb6Sad rw_obj_free(uo->vmobjlock);
97e225b7bdSrmind }
9868575131Sad radix_tree_fini_tree(&uo->uo_pages);
99e225b7bdSrmind }
100e225b7bdSrmind
101e225b7bdSrmind /*
102e225b7bdSrmind * uvm_obj_setlock: assign a vmobjlock to the UVM object.
103e225b7bdSrmind *
104e225b7bdSrmind * => Caller is responsible to ensure that UVM objects is not use.
105e225b7bdSrmind * => Only dynamic lock may be previously set. We drop the reference then.
106e225b7bdSrmind */
107e225b7bdSrmind void
uvm_obj_setlock(struct uvm_object * uo,krwlock_t * lockptr)108d2a0ebb6Sad uvm_obj_setlock(struct uvm_object *uo, krwlock_t *lockptr)
109e225b7bdSrmind {
110d2a0ebb6Sad krwlock_t *olockptr = uo->vmobjlock;
111e225b7bdSrmind
112e225b7bdSrmind if (olockptr) {
113e225b7bdSrmind /* Drop the reference on the old lock. */
114d2a0ebb6Sad rw_obj_free(olockptr);
115e225b7bdSrmind }
116e225b7bdSrmind if (lockptr == NULL) {
117e225b7bdSrmind /* If new lock is not passed - allocate default one. */
118d2a0ebb6Sad lockptr = rw_obj_alloc();
119e225b7bdSrmind }
120e225b7bdSrmind uo->vmobjlock = lockptr;
121e225b7bdSrmind }
122e225b7bdSrmind
123e225b7bdSrmind /*
124e225b7bdSrmind * uvm_obj_wirepages: wire the pages of entire UVM object.
1250b30e150Syamt *
1260b30e150Syamt * => NOTE: this function should only be used for types of objects
1270b30e150Syamt * where PG_RELEASED flag is never set (aobj objects)
1280b30e150Syamt * => caller must pass page-aligned start and end values
1290b30e150Syamt */
1300b30e150Syamt int
uvm_obj_wirepages(struct uvm_object * uobj,off_t start,off_t end,struct pglist * list)131262ad41dSchristos uvm_obj_wirepages(struct uvm_object *uobj, off_t start, off_t end,
132262ad41dSchristos struct pglist *list)
1330b30e150Syamt {
1340b30e150Syamt int i, npages, error;
1350b30e150Syamt struct vm_page *pgs[FETCH_PAGECOUNT], *pg = NULL;
1360b30e150Syamt off_t offset = start, left;
1370b30e150Syamt
1380b30e150Syamt left = (end - start) >> PAGE_SHIFT;
1390b30e150Syamt
140d2a0ebb6Sad rw_enter(uobj->vmobjlock, RW_WRITER);
1410b30e150Syamt while (left) {
1420b30e150Syamt
1430b30e150Syamt npages = MIN(FETCH_PAGECOUNT, left);
1440b30e150Syamt
1450b30e150Syamt /* Get the pages */
1460b30e150Syamt memset(pgs, 0, sizeof(pgs));
1470b30e150Syamt error = (*uobj->pgops->pgo_get)(uobj, offset, pgs, &npages, 0,
1480b30e150Syamt VM_PROT_READ | VM_PROT_WRITE, UVM_ADV_SEQUENTIAL,
149812b46dfSad PGO_SYNCIO);
1500b30e150Syamt
1510b30e150Syamt if (error)
1520b30e150Syamt goto error;
1530b30e150Syamt
154d2a0ebb6Sad rw_enter(uobj->vmobjlock, RW_WRITER);
1550b30e150Syamt for (i = 0; i < npages; i++) {
1560b30e150Syamt
1570b30e150Syamt KASSERT(pgs[i] != NULL);
1580b30e150Syamt KASSERT(!(pgs[i]->flags & PG_RELEASED));
1590b30e150Syamt
1600b30e150Syamt /*
1610b30e150Syamt * Loan break
1620b30e150Syamt */
1630b30e150Syamt if (pgs[i]->loan_count) {
1640b30e150Syamt while (pgs[i]->loan_count) {
1650b30e150Syamt pg = uvm_loanbreak(pgs[i]);
1660b30e150Syamt if (!pg) {
167d2a0ebb6Sad rw_exit(uobj->vmobjlock);
1680b30e150Syamt uvm_wait("uobjwirepg");
169d2a0ebb6Sad rw_enter(uobj->vmobjlock, RW_WRITER);
1700b30e150Syamt continue;
1710b30e150Syamt }
1720b30e150Syamt }
1730b30e150Syamt pgs[i] = pg;
1740b30e150Syamt }
1750b30e150Syamt
1765978ddc6Sad if (pgs[i]->flags & PG_AOBJ) {
17705a3457eSad uvm_pagemarkdirty(pgs[i],
17805a3457eSad UVM_PAGE_STATUS_DIRTY);
1790b30e150Syamt uao_dropswap(uobj, i);
1800b30e150Syamt }
1810b30e150Syamt }
1820b30e150Syamt
1830b30e150Syamt /* Wire the pages */
1840b30e150Syamt for (i = 0; i < npages; i++) {
18594843b13Sad uvm_pagelock(pgs[i]);
1860b30e150Syamt uvm_pagewire(pgs[i]);
18794843b13Sad uvm_pageunlock(pgs[i]);
188262ad41dSchristos if (list != NULL)
189262ad41dSchristos TAILQ_INSERT_TAIL(list, pgs[i], pageq.queue);
1900b30e150Syamt }
1910b30e150Syamt
1920b30e150Syamt /* Unbusy the pages */
1930b30e150Syamt uvm_page_unbusy(pgs, npages);
1940b30e150Syamt
1950b30e150Syamt left -= npages;
1960b30e150Syamt offset += npages << PAGE_SHIFT;
1970b30e150Syamt }
198d2a0ebb6Sad rw_exit(uobj->vmobjlock);
1990b30e150Syamt
2000b30e150Syamt return 0;
2010b30e150Syamt
2020b30e150Syamt error:
2030b30e150Syamt /* Unwire the pages which has been wired */
204e225b7bdSrmind uvm_obj_unwirepages(uobj, start, offset);
2050b30e150Syamt
2060b30e150Syamt return error;
2070b30e150Syamt }
2080b30e150Syamt
2090b30e150Syamt /*
210e225b7bdSrmind * uvm_obj_unwirepages: unwire the pages of entire UVM object.
2110b30e150Syamt *
2120b30e150Syamt * => NOTE: this function should only be used for types of objects
2130b30e150Syamt * where PG_RELEASED flag is never set
2140b30e150Syamt * => caller must pass page-aligned start and end values
2150b30e150Syamt */
2160b30e150Syamt void
uvm_obj_unwirepages(struct uvm_object * uobj,off_t start,off_t end)217e225b7bdSrmind uvm_obj_unwirepages(struct uvm_object *uobj, off_t start, off_t end)
2180b30e150Syamt {
2190b30e150Syamt struct vm_page *pg;
2200b30e150Syamt off_t offset;
2210b30e150Syamt
222d2a0ebb6Sad rw_enter(uobj->vmobjlock, RW_WRITER);
2230b30e150Syamt for (offset = start; offset < end; offset += PAGE_SIZE) {
2240b30e150Syamt pg = uvm_pagelookup(uobj, offset);
2250b30e150Syamt
2260b30e150Syamt KASSERT(pg != NULL);
2270b30e150Syamt KASSERT(!(pg->flags & PG_RELEASED));
2280b30e150Syamt
22994843b13Sad uvm_pagelock(pg);
2300b30e150Syamt uvm_pageunwire(pg);
23194843b13Sad uvm_pageunlock(pg);
2320b30e150Syamt }
233d2a0ebb6Sad rw_exit(uobj->vmobjlock);
2340b30e150Syamt }
235b4e5923fSthorpej
23619303cecSchs static inline bool
uvm_obj_notag_p(struct uvm_object * uobj,int tag)23719303cecSchs uvm_obj_notag_p(struct uvm_object *uobj, int tag)
23819303cecSchs {
23919303cecSchs
24019303cecSchs KASSERT(rw_lock_held(uobj->vmobjlock));
24119303cecSchs return radix_tree_empty_tagged_tree_p(&uobj->uo_pages, tag);
24219303cecSchs }
24319303cecSchs
24419303cecSchs bool
uvm_obj_clean_p(struct uvm_object * uobj)24519303cecSchs uvm_obj_clean_p(struct uvm_object *uobj)
24619303cecSchs {
24719303cecSchs
24819303cecSchs return uvm_obj_notag_p(uobj, UVM_PAGE_DIRTY_TAG);
24919303cecSchs }
25019303cecSchs
25119303cecSchs bool
uvm_obj_nowriteback_p(struct uvm_object * uobj)25219303cecSchs uvm_obj_nowriteback_p(struct uvm_object *uobj)
25319303cecSchs {
25419303cecSchs
25519303cecSchs return uvm_obj_notag_p(uobj, UVM_PAGE_WRITEBACK_TAG);
25619303cecSchs }
25719303cecSchs
25819303cecSchs static inline bool
uvm_obj_page_tag_p(struct vm_page * pg,int tag)25919303cecSchs uvm_obj_page_tag_p(struct vm_page *pg, int tag)
26019303cecSchs {
26119303cecSchs struct uvm_object *uobj = pg->uobject;
262*b0597d59Schs uint64_t pgidx = pg->offset >> PAGE_SHIFT;
26319303cecSchs
26419303cecSchs KASSERT(uobj != NULL);
26519303cecSchs KASSERT(rw_lock_held(uobj->vmobjlock));
26619303cecSchs return radix_tree_get_tag(&uobj->uo_pages, pgidx, tag) != 0;
26719303cecSchs }
26819303cecSchs
26919303cecSchs static inline void
uvm_obj_page_set_tag(struct vm_page * pg,int tag)27019303cecSchs uvm_obj_page_set_tag(struct vm_page *pg, int tag)
27119303cecSchs {
27219303cecSchs struct uvm_object *uobj = pg->uobject;
273*b0597d59Schs uint64_t pgidx = pg->offset >> PAGE_SHIFT;
27419303cecSchs
27519303cecSchs KASSERT(uobj != NULL);
27619303cecSchs KASSERT(rw_write_held(uobj->vmobjlock));
27719303cecSchs radix_tree_set_tag(&uobj->uo_pages, pgidx, tag);
27819303cecSchs }
27919303cecSchs
28019303cecSchs static inline void
uvm_obj_page_clear_tag(struct vm_page * pg,int tag)28119303cecSchs uvm_obj_page_clear_tag(struct vm_page *pg, int tag)
28219303cecSchs {
28319303cecSchs struct uvm_object *uobj = pg->uobject;
284*b0597d59Schs uint64_t pgidx = pg->offset >> PAGE_SHIFT;
28519303cecSchs
28619303cecSchs KASSERT(uobj != NULL);
28719303cecSchs KASSERT(rw_write_held(uobj->vmobjlock));
28819303cecSchs radix_tree_clear_tag(&uobj->uo_pages, pgidx, tag);
28919303cecSchs }
29019303cecSchs
29119303cecSchs bool
uvm_obj_page_dirty_p(struct vm_page * pg)29219303cecSchs uvm_obj_page_dirty_p(struct vm_page *pg)
29319303cecSchs {
29419303cecSchs
29519303cecSchs return uvm_obj_page_tag_p(pg, UVM_PAGE_DIRTY_TAG);
29619303cecSchs }
29719303cecSchs
29819303cecSchs void
uvm_obj_page_set_dirty(struct vm_page * pg)29919303cecSchs uvm_obj_page_set_dirty(struct vm_page *pg)
30019303cecSchs {
30119303cecSchs
30219303cecSchs uvm_obj_page_set_tag(pg, UVM_PAGE_DIRTY_TAG);
30319303cecSchs }
30419303cecSchs
30519303cecSchs void
uvm_obj_page_clear_dirty(struct vm_page * pg)30619303cecSchs uvm_obj_page_clear_dirty(struct vm_page *pg)
30719303cecSchs {
30819303cecSchs
30919303cecSchs uvm_obj_page_clear_tag(pg, UVM_PAGE_DIRTY_TAG);
31019303cecSchs }
31119303cecSchs
31219303cecSchs bool
uvm_obj_page_writeback_p(struct vm_page * pg)31319303cecSchs uvm_obj_page_writeback_p(struct vm_page *pg)
31419303cecSchs {
31519303cecSchs
31619303cecSchs return uvm_obj_page_tag_p(pg, UVM_PAGE_WRITEBACK_TAG);
31719303cecSchs }
31819303cecSchs
31919303cecSchs void
uvm_obj_page_set_writeback(struct vm_page * pg)32019303cecSchs uvm_obj_page_set_writeback(struct vm_page *pg)
32119303cecSchs {
32219303cecSchs
32319303cecSchs uvm_obj_page_set_tag(pg, UVM_PAGE_WRITEBACK_TAG);
32419303cecSchs }
32519303cecSchs
32619303cecSchs void
uvm_obj_page_clear_writeback(struct vm_page * pg)32719303cecSchs uvm_obj_page_clear_writeback(struct vm_page *pg)
32819303cecSchs {
32919303cecSchs
33019303cecSchs uvm_obj_page_clear_tag(pg, UVM_PAGE_WRITEBACK_TAG);
33119303cecSchs }
33219303cecSchs
33375234fdfSpooka #if defined(DDB) || defined(DEBUGPRINT)
334b4e5923fSthorpej
335b4e5923fSthorpej /*
336b4e5923fSthorpej * uvm_object_printit: actually prints the object
337b4e5923fSthorpej */
338b4e5923fSthorpej void
uvm_object_printit(struct uvm_object * uobj,bool full,void (* pr)(const char *,...))339b4e5923fSthorpej uvm_object_printit(struct uvm_object *uobj, bool full,
340b4e5923fSthorpej void (*pr)(const char *, ...))
341b4e5923fSthorpej {
342881d12e6Sad struct uvm_page_array a;
343b4e5923fSthorpej struct vm_page *pg;
344b4e5923fSthorpej int cnt = 0;
345881d12e6Sad voff_t off;
346b4e5923fSthorpej
347b4e5923fSthorpej (*pr)("OBJECT %p: locked=%d, pgops=%p, npages=%d, ",
348d2a0ebb6Sad uobj, rw_write_held(uobj->vmobjlock), uobj->pgops, uobj->uo_npages);
349b4e5923fSthorpej if (UVM_OBJ_IS_KERN_OBJECT(uobj))
350b4e5923fSthorpej (*pr)("refs=<SYSTEM>\n");
351b4e5923fSthorpej else
352b4e5923fSthorpej (*pr)("refs=%d\n", uobj->uo_refs);
353b4e5923fSthorpej
354b4e5923fSthorpej if (!full) {
355b4e5923fSthorpej return;
356b4e5923fSthorpej }
357b4e5923fSthorpej (*pr)(" PAGES <pg,offset>:\n ");
3584bfe0439Sad uvm_page_array_init(&a, uobj, 0);
359881d12e6Sad off = 0;
3604bfe0439Sad while ((pg = uvm_page_array_fill_and_peek(&a, off, 0)) != NULL) {
361b4e5923fSthorpej cnt++;
362b4e5923fSthorpej (*pr)("<%p,0x%llx> ", pg, (long long)pg->offset);
363b4e5923fSthorpej if ((cnt % 3) == 0) {
364b4e5923fSthorpej (*pr)("\n ");
365b4e5923fSthorpej }
366881d12e6Sad off = pg->offset + PAGE_SIZE;
367881d12e6Sad uvm_page_array_advance(&a);
368b4e5923fSthorpej }
369b4e5923fSthorpej if ((cnt % 3) != 0) {
370b4e5923fSthorpej (*pr)("\n");
371b4e5923fSthorpej }
372881d12e6Sad uvm_page_array_fini(&a);
373b4e5923fSthorpej }
374b4e5923fSthorpej
375b4e5923fSthorpej #endif /* DDB || DEBUGPRINT */
376