1 | /* $NetBSD: uvm_object.c,v 1.15 2015/10/26 09:02:49 mrg Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2006, 2010 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Mindaugas Rasiukevicius. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * uvm_object.c: operate with memory objects |
34 | * |
35 | * TODO: |
36 | * 1. Support PG_RELEASED-using objects |
37 | */ |
38 | |
39 | #include <sys/cdefs.h> |
40 | __KERNEL_RCSID(0, "$NetBSD: uvm_object.c,v 1.15 2015/10/26 09:02:49 mrg Exp $" ); |
41 | |
42 | #ifdef _KERNEL_OPT |
43 | #include "opt_ddb.h" |
44 | #endif |
45 | |
46 | #include <sys/param.h> |
47 | #include <sys/mutex.h> |
48 | #include <sys/queue.h> |
49 | #include <sys/rbtree.h> |
50 | |
51 | #include <uvm/uvm.h> |
52 | #include <uvm/uvm_ddb.h> |
53 | |
54 | /* Page count to fetch per single step. */ |
55 | #define FETCH_PAGECOUNT 16 |
56 | |
57 | /* |
58 | * uvm_obj_init: initialize UVM memory object. |
59 | */ |
60 | void |
61 | uvm_obj_init(struct uvm_object *uo, const struct uvm_pagerops *ops, |
62 | bool alock, u_int refs) |
63 | { |
64 | |
65 | #if 0 /* notyet */ |
66 | KASSERT(ops); |
67 | #endif |
68 | if (alock) { |
69 | /* Allocate and assign a lock. */ |
70 | uo->vmobjlock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE); |
71 | } else { |
72 | /* The lock will need to be set via uvm_obj_setlock(). */ |
73 | uo->vmobjlock = NULL; |
74 | } |
75 | uo->pgops = ops; |
76 | TAILQ_INIT(&uo->memq); |
77 | LIST_INIT(&uo->uo_ubc); |
78 | uo->uo_npages = 0; |
79 | uo->uo_refs = refs; |
80 | rb_tree_init(&uo->rb_tree, &uvm_page_tree_ops); |
81 | } |
82 | |
83 | /* |
84 | * uvm_obj_destroy: destroy UVM memory object. |
85 | */ |
86 | void |
87 | uvm_obj_destroy(struct uvm_object *uo, bool dlock) |
88 | { |
89 | |
90 | KASSERT(rb_tree_iterate(&uo->rb_tree, NULL, RB_DIR_LEFT) == NULL); |
91 | |
92 | /* Purge any UBC entries associated with this object. */ |
93 | ubc_purge(uo); |
94 | |
95 | /* Destroy the lock, if requested. */ |
96 | if (dlock) { |
97 | mutex_obj_free(uo->vmobjlock); |
98 | } |
99 | } |
100 | |
101 | /* |
102 | * uvm_obj_setlock: assign a vmobjlock to the UVM object. |
103 | * |
104 | * => Caller is responsible to ensure that UVM objects is not use. |
105 | * => Only dynamic lock may be previously set. We drop the reference then. |
106 | */ |
107 | void |
108 | uvm_obj_setlock(struct uvm_object *uo, kmutex_t *lockptr) |
109 | { |
110 | kmutex_t *olockptr = uo->vmobjlock; |
111 | |
112 | if (olockptr) { |
113 | /* Drop the reference on the old lock. */ |
114 | mutex_obj_free(olockptr); |
115 | } |
116 | if (lockptr == NULL) { |
117 | /* If new lock is not passed - allocate default one. */ |
118 | lockptr = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE); |
119 | } |
120 | uo->vmobjlock = lockptr; |
121 | } |
122 | |
123 | /* |
124 | * uvm_obj_wirepages: wire the pages of entire UVM object. |
125 | * |
126 | * => NOTE: this function should only be used for types of objects |
127 | * where PG_RELEASED flag is never set (aobj objects) |
128 | * => caller must pass page-aligned start and end values |
129 | */ |
130 | int |
131 | uvm_obj_wirepages(struct uvm_object *uobj, off_t start, off_t end, |
132 | struct pglist *list) |
133 | { |
134 | int i, npages, error; |
135 | struct vm_page *pgs[FETCH_PAGECOUNT], *pg = NULL; |
136 | off_t offset = start, left; |
137 | |
138 | left = (end - start) >> PAGE_SHIFT; |
139 | |
140 | mutex_enter(uobj->vmobjlock); |
141 | while (left) { |
142 | |
143 | npages = MIN(FETCH_PAGECOUNT, left); |
144 | |
145 | /* Get the pages */ |
146 | memset(pgs, 0, sizeof(pgs)); |
147 | error = (*uobj->pgops->pgo_get)(uobj, offset, pgs, &npages, 0, |
148 | VM_PROT_READ | VM_PROT_WRITE, UVM_ADV_SEQUENTIAL, |
149 | PGO_ALLPAGES | PGO_SYNCIO); |
150 | |
151 | if (error) |
152 | goto error; |
153 | |
154 | mutex_enter(uobj->vmobjlock); |
155 | for (i = 0; i < npages; i++) { |
156 | |
157 | KASSERT(pgs[i] != NULL); |
158 | KASSERT(!(pgs[i]->flags & PG_RELEASED)); |
159 | |
160 | /* |
161 | * Loan break |
162 | */ |
163 | if (pgs[i]->loan_count) { |
164 | while (pgs[i]->loan_count) { |
165 | pg = uvm_loanbreak(pgs[i]); |
166 | if (!pg) { |
167 | mutex_exit(uobj->vmobjlock); |
168 | uvm_wait("uobjwirepg" ); |
169 | mutex_enter(uobj->vmobjlock); |
170 | continue; |
171 | } |
172 | } |
173 | pgs[i] = pg; |
174 | } |
175 | |
176 | if (pgs[i]->pqflags & PQ_AOBJ) { |
177 | pgs[i]->flags &= ~(PG_CLEAN); |
178 | uao_dropswap(uobj, i); |
179 | } |
180 | } |
181 | |
182 | /* Wire the pages */ |
183 | mutex_enter(&uvm_pageqlock); |
184 | for (i = 0; i < npages; i++) { |
185 | uvm_pagewire(pgs[i]); |
186 | if (list != NULL) |
187 | TAILQ_INSERT_TAIL(list, pgs[i], pageq.queue); |
188 | } |
189 | mutex_exit(&uvm_pageqlock); |
190 | |
191 | /* Unbusy the pages */ |
192 | uvm_page_unbusy(pgs, npages); |
193 | |
194 | left -= npages; |
195 | offset += npages << PAGE_SHIFT; |
196 | } |
197 | mutex_exit(uobj->vmobjlock); |
198 | |
199 | return 0; |
200 | |
201 | error: |
202 | /* Unwire the pages which has been wired */ |
203 | uvm_obj_unwirepages(uobj, start, offset); |
204 | |
205 | return error; |
206 | } |
207 | |
208 | /* |
209 | * uvm_obj_unwirepages: unwire the pages of entire UVM object. |
210 | * |
211 | * => NOTE: this function should only be used for types of objects |
212 | * where PG_RELEASED flag is never set |
213 | * => caller must pass page-aligned start and end values |
214 | */ |
215 | void |
216 | uvm_obj_unwirepages(struct uvm_object *uobj, off_t start, off_t end) |
217 | { |
218 | struct vm_page *pg; |
219 | off_t offset; |
220 | |
221 | mutex_enter(uobj->vmobjlock); |
222 | mutex_enter(&uvm_pageqlock); |
223 | for (offset = start; offset < end; offset += PAGE_SIZE) { |
224 | pg = uvm_pagelookup(uobj, offset); |
225 | |
226 | KASSERT(pg != NULL); |
227 | KASSERT(!(pg->flags & PG_RELEASED)); |
228 | |
229 | uvm_pageunwire(pg); |
230 | } |
231 | mutex_exit(&uvm_pageqlock); |
232 | mutex_exit(uobj->vmobjlock); |
233 | } |
234 | |
235 | #if defined(DDB) || defined(DEBUGPRINT) |
236 | |
237 | /* |
238 | * uvm_object_printit: actually prints the object |
239 | */ |
240 | void |
241 | uvm_object_printit(struct uvm_object *uobj, bool full, |
242 | void (*pr)(const char *, ...)) |
243 | { |
244 | struct vm_page *pg; |
245 | int cnt = 0; |
246 | |
247 | (*pr)("OBJECT %p: locked=%d, pgops=%p, npages=%d, " , |
248 | uobj, mutex_owned(uobj->vmobjlock), uobj->pgops, uobj->uo_npages); |
249 | if (UVM_OBJ_IS_KERN_OBJECT(uobj)) |
250 | (*pr)("refs=<SYSTEM>\n" ); |
251 | else |
252 | (*pr)("refs=%d\n" , uobj->uo_refs); |
253 | |
254 | if (!full) { |
255 | return; |
256 | } |
257 | (*pr)(" PAGES <pg,offset>:\n " ); |
258 | TAILQ_FOREACH(pg, &uobj->memq, listq.queue) { |
259 | cnt++; |
260 | (*pr)("<%p,0x%llx> " , pg, (long long)pg->offset); |
261 | if ((cnt % 3) == 0) { |
262 | (*pr)("\n " ); |
263 | } |
264 | } |
265 | if ((cnt % 3) != 0) { |
266 | (*pr)("\n" ); |
267 | } |
268 | } |
269 | |
270 | #endif /* DDB || DEBUGPRINT */ |
271 | |