xref: /NextBSD/sys/vm/vm_domain.c (revision cfe5aa9ba837b52a161c1e5a35d8b6d4d75e2cb2)
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