1 /*-
2 * Copyright (c) 2015 Adrian Chadd <adrian@FreeBSD.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include "opt_vm.h"
34 #include "opt_ddb.h"
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/lock.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/mutex.h>
42 #if MAXMEMDOM > 1
43 #include <sys/proc.h>
44 #endif
45 #include <sys/queue.h>
46 #include <sys/rwlock.h>
47 #include <sys/sbuf.h>
48 #include <sys/sysctl.h>
49 #include <sys/tree.h>
50 #include <sys/vmmeter.h>
51 #include <sys/seq.h>
52
53 #include <ddb/ddb.h>
54
55 #include <vm/vm.h>
56 #include <vm/vm_param.h>
57 #include <vm/vm_kern.h>
58 #include <vm/vm_object.h>
59 #include <vm/vm_page.h>
60 #include <vm/vm_phys.h>
61
62 #include <vm/vm_domain.h>
63
64 static __inline int
vm_domain_rr_selectdomain(void)65 vm_domain_rr_selectdomain(void)
66 {
67 #if MAXMEMDOM > 1
68 struct thread *td;
69
70 td = curthread;
71
72 td->td_dom_rr_idx++;
73 td->td_dom_rr_idx %= vm_ndomains;
74 return (td->td_dom_rr_idx);
75 #else
76 return (0);
77 #endif
78 }
79
80 /*
81 * This implements a very simple set of VM domain memory allocation
82 * policies and iterators.
83 */
84
85 /*
86 * A VM domain policy represents a desired VM domain policy.
87 * Iterators implement searching through VM domains in a specific
88 * order.
89 */
90
91 /*
92 * When setting a policy, the caller must establish their own
93 * exclusive write protection for the contents of the domain
94 * policy.
95 */
96 int
vm_domain_policy_init(struct vm_domain_policy * vp)97 vm_domain_policy_init(struct vm_domain_policy *vp)
98 {
99
100 bzero(vp, sizeof(*vp));
101 vp->p.policy = VM_POLICY_NONE;
102 vp->p.domain = -1;
103 return (0);
104 }
105
106 int
vm_domain_policy_set(struct vm_domain_policy * vp,vm_domain_policy_type_t vt,int domain)107 vm_domain_policy_set(struct vm_domain_policy *vp,
108 vm_domain_policy_type_t vt, int domain)
109 {
110
111 seq_write_begin(&vp->seq);
112 vp->p.policy = vt;
113 vp->p.domain = domain;
114 seq_write_end(&vp->seq);
115 return (0);
116 }
117
118 /*
119 * Take a local copy of a policy.
120 *
121 * The destination policy isn't write-barriered; this is used
122 * for doing local copies into something that isn't shared.
123 */
124 void
vm_domain_policy_localcopy(struct vm_domain_policy * dst,const struct vm_domain_policy * src)125 vm_domain_policy_localcopy(struct vm_domain_policy *dst,
126 const struct vm_domain_policy *src)
127 {
128 seq_t seq;
129
130 for (;;) {
131 seq = seq_read(&src->seq);
132 *dst = *src;
133 if (seq_consistent(&src->seq, seq))
134 return;
135 cpu_spinwait();
136 }
137 }
138
139 /*
140 * Take a write-barrier copy of a policy.
141 *
142 * The destination policy is write -barriered; this is used
143 * for doing copies into policies that may be read by other
144 * threads.
145 */
146 void
vm_domain_policy_copy(struct vm_domain_policy * dst,const struct vm_domain_policy * src)147 vm_domain_policy_copy(struct vm_domain_policy *dst,
148 const struct vm_domain_policy *src)
149 {
150 seq_t seq;
151 struct vm_domain_policy d;
152
153 for (;;) {
154 seq = seq_read(&src->seq);
155 d = *src;
156 if (seq_consistent(&src->seq, seq)) {
157 seq_write_begin(&dst->seq);
158 dst->p.domain = d.p.domain;
159 dst->p.policy = d.p.policy;
160 seq_write_end(&dst->seq);
161 return;
162 }
163 cpu_spinwait();
164 }
165 }
166
167 int
vm_domain_policy_validate(const struct vm_domain_policy * vp)168 vm_domain_policy_validate(const struct vm_domain_policy *vp)
169 {
170
171 switch (vp->p.policy) {
172 case VM_POLICY_NONE:
173 case VM_POLICY_ROUND_ROBIN:
174 case VM_POLICY_FIRST_TOUCH:
175 case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
176 if (vp->p.domain == -1)
177 return (0);
178 return (-1);
179 case VM_POLICY_FIXED_DOMAIN:
180 case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
181 if (vp->p.domain >= 0 && vp->p.domain < vm_ndomains)
182 return (0);
183 return (-1);
184 default:
185 return (-1);
186 }
187 return (-1);
188 }
189
190 int
vm_domain_policy_cleanup(struct vm_domain_policy * vp)191 vm_domain_policy_cleanup(struct vm_domain_policy *vp)
192 {
193
194 /* For now, empty */
195 return (0);
196 }
197
198 int
vm_domain_iterator_init(struct vm_domain_iterator * vi)199 vm_domain_iterator_init(struct vm_domain_iterator *vi)
200 {
201
202 /* Nothing to do for now */
203 return (0);
204 }
205
206 /*
207 * Manually setup an iterator with the given details.
208 */
209 int
vm_domain_iterator_set(struct vm_domain_iterator * vi,vm_domain_policy_type_t vt,int domain)210 vm_domain_iterator_set(struct vm_domain_iterator *vi,
211 vm_domain_policy_type_t vt, int domain)
212 {
213
214 switch (vt) {
215 case VM_POLICY_FIXED_DOMAIN:
216 vi->policy = VM_POLICY_FIXED_DOMAIN;
217 vi->domain = domain;
218 vi->n = 1;
219 break;
220 case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
221 vi->policy = VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN;
222 vi->domain = domain;
223 vi->n = vm_ndomains;
224 break;
225 case VM_POLICY_FIRST_TOUCH:
226 vi->policy = VM_POLICY_FIRST_TOUCH;
227 vi->domain = PCPU_GET(domain);
228 vi->n = 1;
229 break;
230 case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
231 vi->policy = VM_POLICY_FIRST_TOUCH_ROUND_ROBIN;
232 vi->domain = PCPU_GET(domain);
233 vi->n = vm_ndomains;
234 break;
235 case VM_POLICY_ROUND_ROBIN:
236 default:
237 vi->policy = VM_POLICY_ROUND_ROBIN;
238 vi->domain = -1;
239 vi->n = vm_ndomains;
240 break;
241 }
242 return (0);
243 }
244
245 /*
246 * Setup an iterator based on the given policy.
247 */
248 static inline void
_vm_domain_iterator_set_policy(struct vm_domain_iterator * vi,const struct vm_domain_policy * vt)249 _vm_domain_iterator_set_policy(struct vm_domain_iterator *vi,
250 const struct vm_domain_policy *vt)
251 {
252 /*
253 * Initialise the iterator.
254 *
255 * For first-touch, the initial domain is set
256 * via the current thread CPU domain.
257 *
258 * For fixed-domain, it's assumed that the
259 * caller has initialised the specific domain
260 * it is after.
261 */
262 switch (vt->p.policy) {
263 case VM_POLICY_FIXED_DOMAIN:
264 vi->policy = vt->p.policy;
265 vi->domain = vt->p.domain;
266 vi->n = 1;
267 break;
268 case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
269 vi->policy = vt->p.policy;
270 vi->domain = vt->p.domain;
271 vi->n = vm_ndomains;
272 break;
273 case VM_POLICY_FIRST_TOUCH:
274 vi->policy = vt->p.policy;
275 vi->domain = PCPU_GET(domain);
276 vi->n = 1;
277 break;
278 case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
279 vi->policy = vt->p.policy;
280 vi->domain = PCPU_GET(domain);
281 vi->n = vm_ndomains;
282 break;
283 case VM_POLICY_ROUND_ROBIN:
284 default:
285 /*
286 * Default to round-robin policy.
287 */
288 vi->policy = VM_POLICY_ROUND_ROBIN;
289 vi->domain = -1;
290 vi->n = vm_ndomains;
291 break;
292 }
293 }
294
295 void
vm_domain_iterator_set_policy(struct vm_domain_iterator * vi,const struct vm_domain_policy * vt)296 vm_domain_iterator_set_policy(struct vm_domain_iterator *vi,
297 const struct vm_domain_policy *vt)
298 {
299 seq_t seq;
300 struct vm_domain_policy vt_lcl;
301
302 for (;;) {
303 seq = seq_read(&vt->seq);
304 vt_lcl = *vt;
305 if (seq_consistent(&vt->seq, seq)) {
306 _vm_domain_iterator_set_policy(vi, &vt_lcl);
307 return;
308 }
309 cpu_spinwait();
310 }
311 }
312
313 /*
314 * Return the next VM domain to use.
315 *
316 * Returns 0 w/ domain set to the next domain to use, or
317 * -1 to indicate no more domains are available.
318 */
319 int
vm_domain_iterator_run(struct vm_domain_iterator * vi,int * domain)320 vm_domain_iterator_run(struct vm_domain_iterator *vi, int *domain)
321 {
322
323 /* General catch-all */
324 if (vi->n <= 0)
325 return (-1);
326
327 switch (vi->policy) {
328 case VM_POLICY_FIXED_DOMAIN:
329 case VM_POLICY_FIRST_TOUCH:
330 *domain = vi->domain;
331 vi->n--;
332 break;
333 case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
334 case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
335 /*
336 * XXX TODO: skip over the rr'ed domain
337 * if it equals the one we started with.
338 */
339 if (vi->n == vm_ndomains)
340 *domain = vi->domain;
341 else
342 *domain = vm_domain_rr_selectdomain();
343 vi->n--;
344 break;
345 case VM_POLICY_ROUND_ROBIN:
346 default:
347 *domain = vm_domain_rr_selectdomain();
348 vi->n--;
349 break;
350 }
351
352 return (0);
353 }
354
355 /*
356 * Returns 1 if the iteration is done, or 0 if it has not.
357
358 * This can only be called after at least one loop through
359 * the iterator. Ie, it's designed to be used as a tail
360 * check of a loop, not the head check of a loop.
361 */
362 int
vm_domain_iterator_isdone(struct vm_domain_iterator * vi)363 vm_domain_iterator_isdone(struct vm_domain_iterator *vi)
364 {
365
366 return (vi->n <= 0);
367 }
368
369 int
vm_domain_iterator_cleanup(struct vm_domain_iterator * vi)370 vm_domain_iterator_cleanup(struct vm_domain_iterator *vi)
371 {
372
373 return (0);
374 }
375