uvm_page_array.c revision 1.5
1/* $NetBSD: uvm_page_array.c,v 1.5 2020/03/17 00:30:17 ad Exp $ */ 2 3/*- 4 * Copyright (c)2011 YAMAMOTO Takashi, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: uvm_page_array.c,v 1.5 2020/03/17 00:30:17 ad Exp $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34 35#include <uvm/uvm_extern.h> 36#include <uvm/uvm_object.h> 37#include <uvm/uvm_page.h> 38#include <uvm/uvm_page_array.h> 39 40/* 41 * uvm_page_array_init: initialize the array. 42 */ 43 44void 45uvm_page_array_init(struct uvm_page_array *ar) 46{ 47 48 ar->ar_idx = ar->ar_npages = 0; 49} 50 51/* 52 * uvm_page_array_fini: clean up the array. 53 */ 54 55void 56uvm_page_array_fini(struct uvm_page_array *ar) 57{ 58 59 /* 60 * currently nothing to do. 61 */ 62#if defined(DIAGNOSTIC) 63 /* 64 * poison to trigger assertion in uvm_page_array_peek to 65 * detect usage errors. 66 */ 67 ar->ar_npages = 1; 68 ar->ar_idx = 1000; 69#endif /* defined(DIAGNOSTIC) */ 70} 71 72/* 73 * uvm_page_array_clear: forget the cached pages and initialize the array. 74 */ 75 76void 77uvm_page_array_clear(struct uvm_page_array *ar) 78{ 79 80 KASSERT(ar->ar_idx <= ar->ar_npages); 81 uvm_page_array_init(ar); 82} 83 84/* 85 * uvm_page_array_peek: return the next cached page. 86 */ 87 88struct vm_page * 89uvm_page_array_peek(struct uvm_page_array *ar) 90{ 91 92 KASSERT(ar->ar_idx <= ar->ar_npages); 93 if (ar->ar_idx == ar->ar_npages) { 94 return NULL; 95 } 96 return ar->ar_pages[ar->ar_idx]; 97} 98 99/* 100 * uvm_page_array_advance: advance the array to the next cached page 101 */ 102 103void 104uvm_page_array_advance(struct uvm_page_array *ar) 105{ 106 107 KASSERT(ar->ar_idx <= ar->ar_npages); 108 ar->ar_idx++; 109 KASSERT(ar->ar_idx <= ar->ar_npages); 110} 111 112/* 113 * uvm_page_array_fill: lookup pages and keep them cached. 114 * 115 * return 0 on success. in that case, cache the result in the array 116 * so that they will be picked by later uvm_page_array_peek. 117 * 118 * nwant is a number of pages to fetch. a caller should consider it a hint. 119 * nwant == 0 means a caller have no specific idea. 120 * 121 * return ENOENT if no pages are found. 122 * 123 * called with object lock held. 124 */ 125 126int 127uvm_page_array_fill(struct uvm_page_array *ar, struct uvm_object *uobj, 128 voff_t off, unsigned int nwant, unsigned int flags) 129{ 130 unsigned int npages; 131#if defined(DEBUG) 132 unsigned int i; 133#endif /* defined(DEBUG) */ 134 unsigned int maxpages = __arraycount(ar->ar_pages); 135 const bool dense = (flags & UVM_PAGE_ARRAY_FILL_DENSE) != 0; 136 const bool backward = (flags & UVM_PAGE_ARRAY_FILL_BACKWARD) != 0; 137 138 if (nwant != 0 && nwant < maxpages) { 139 maxpages = nwant; 140 } 141#if 0 /* called from DDB for "show obj/f" without lock */ 142 KASSERT(rw_lock_held(uobj->vmobjlock)); 143#endif 144 KASSERT(uvm_page_array_peek(ar) == NULL); 145 if ((flags & UVM_PAGE_ARRAY_FILL_DIRTY) != 0) { 146 unsigned int tagmask = UVM_PAGE_DIRTY_TAG; 147 148 if ((flags & UVM_PAGE_ARRAY_FILL_WRITEBACK) != 0) { 149 tagmask |= UVM_PAGE_WRITEBACK_TAG; 150 } 151 npages = 152 (backward ? radix_tree_gang_lookup_tagged_node_reverse : 153 radix_tree_gang_lookup_tagged_node)( 154 &uobj->uo_pages, off >> PAGE_SHIFT, (void **)ar->ar_pages, 155 maxpages, dense, tagmask); 156 } else { 157 npages = 158 (backward ? radix_tree_gang_lookup_node_reverse : 159 radix_tree_gang_lookup_node)( 160 &uobj->uo_pages, off >> PAGE_SHIFT, (void **)ar->ar_pages, 161 maxpages, dense); 162 } 163 if (npages == 0) { 164 uvm_page_array_clear(ar); 165 return ENOENT; 166 } 167 KASSERT(npages <= maxpages); 168 ar->ar_npages = npages; 169 ar->ar_idx = 0; 170#if defined(DEBUG) 171 for (i = 0; i < ar->ar_npages; i++) { 172 struct vm_page * const pg = ar->ar_pages[i]; 173 174 KDASSERT(pg != NULL); 175 KDASSERT(pg->uobject == uobj); 176 if (backward) { 177 KDASSERT(pg->offset <= off); 178 KDASSERT(i == 0 || 179 pg->offset < ar->ar_pages[i - 1]->offset); 180 } else { 181 KDASSERT(pg->offset >= off); 182 KDASSERT(i == 0 || 183 pg->offset > ar->ar_pages[i - 1]->offset); 184 } 185 } 186#endif /* defined(DEBUG) */ 187 return 0; 188} 189 190/* 191 * uvm_page_array_fill_and_peek: 192 * same as uvm_page_array_peek except that, if the array is empty, try to fill 193 * it first. 194 */ 195 196struct vm_page * 197uvm_page_array_fill_and_peek(struct uvm_page_array *a, struct uvm_object *uobj, 198 voff_t off, unsigned int nwant, unsigned int flags) 199{ 200 struct vm_page *pg; 201 int error; 202 203 pg = uvm_page_array_peek(a); 204 if (pg != NULL) { 205 return pg; 206 } 207 error = uvm_page_array_fill(a, uobj, off, nwant, flags); 208 if (error != 0) { 209 return NULL; 210 } 211 pg = uvm_page_array_peek(a); 212 KASSERT(pg != NULL); 213 return pg; 214} 215