]> git.saurik.com Git - apple/xnu.git/blob - bsd/vm/dp_backing_file.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / bsd / vm / dp_backing_file.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/lock.h>
34 #include <sys/proc_internal.h>
35 #include <sys/kauth.h>
36 #include <sys/buf.h>
37 #include <sys/uio.h>
38 #include <sys/vnode_internal.h>
39 #include <sys/namei.h>
40 #include <sys/ubc_internal.h>
41 #include <sys/malloc.h>
42
43 #include <default_pager/default_pager_types.h>
44 #include <default_pager/default_pager_object.h>
45
46 #include <bsm/audit_kernel.h>
47 #include <bsm/audit_kevents.h>
48
49 #include <mach/mach_types.h>
50 #include <mach/host_priv.h>
51 #include <mach/mach_traps.h>
52 #include <mach/boolean.h>
53
54 #include <kern/kern_types.h>
55 #include <kern/host.h>
56 #include <kern/task.h>
57 #include <kern/zalloc.h>
58 #include <kern/kalloc.h>
59 #include <kern/assert.h>
60
61 #include <libkern/libkern.h>
62
63 #include <vm/vm_pageout.h>
64 #include <vm/vm_map.h>
65 #include <vm/vm_kern.h>
66 #include <vm/vnode_pager.h>
67 #include <vm/vm_protos.h>
68
69 extern thread_t current_act(void);
70
71 /*
72 * temporary support for delayed instantiation
73 * of default_pager
74 */
75 int default_pager_init_flag = 0;
76
77 struct bs_map bs_port_table[MAX_BACKING_STORE] = {
78 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
79 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
80 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
81 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
82 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
83 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
84 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
85 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
86 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
87 {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}};
88
89 /* ###################################################### */
90
91
92 /*
93 * Routine: macx_backing_store_recovery
94 * Function:
95 * Syscall interface to set a tasks privilege
96 * level so that it is not subject to
97 * macx_backing_store_suspend
98 */
99 int
100 macx_backing_store_recovery(
101 struct macx_backing_store_recovery_args *args)
102 {
103 int pid = args->pid;
104 int error;
105 struct proc *p = current_proc();
106 boolean_t funnel_state;
107
108 funnel_state = thread_funnel_set(kernel_flock, TRUE);
109 if ((error = suser(kauth_cred_get(), 0)))
110 goto backing_store_recovery_return;
111
112 /* for now restrict backing_store_recovery */
113 /* usage to only present task */
114 if(pid != proc_selfpid()) {
115 error = EINVAL;
116 goto backing_store_recovery_return;
117 }
118
119 task_backing_store_privileged(p->task);
120
121 backing_store_recovery_return:
122 (void) thread_funnel_set(kernel_flock, FALSE);
123 return(error);
124 }
125
126 /*
127 * Routine: macx_backing_store_suspend
128 * Function:
129 * Syscall interface to stop new demand for
130 * backing store when backing store is low
131 */
132
133 int
134 macx_backing_store_suspend(
135 struct macx_backing_store_suspend_args *args)
136 {
137 boolean_t suspend = args->suspend;
138 int error;
139 boolean_t funnel_state;
140
141 funnel_state = thread_funnel_set(kernel_flock, TRUE);
142 if ((error = suser(kauth_cred_get(), 0)))
143 goto backing_store_suspend_return;
144
145 vm_backing_store_disable(suspend);
146
147 backing_store_suspend_return:
148 (void) thread_funnel_set(kernel_flock, FALSE);
149 return(error);
150 }
151
152 /*
153 * Routine: macx_swapon
154 * Function:
155 * Syscall interface to add a file to backing store
156 */
157 int
158 macx_swapon(
159 struct macx_swapon_args *args)
160 {
161 int size = args->size;
162 vnode_t vp = (vnode_t)NULL;
163 struct nameidata nd, *ndp;
164 struct proc *p = current_proc();
165 register int error;
166 kern_return_t kr;
167 mach_port_t backing_store;
168 memory_object_default_t default_pager;
169 int i;
170 boolean_t funnel_state;
171 off_t file_size;
172 struct vfs_context context;
173
174 context.vc_proc = p;
175 context.vc_ucred = kauth_cred_get();
176
177 AUDIT_MACH_SYSCALL_ENTER(AUE_SWAPON);
178 AUDIT_ARG(value, args->priority);
179
180 funnel_state = thread_funnel_set(kernel_flock, TRUE);
181 ndp = &nd;
182
183 if ((error = suser(kauth_cred_get(), 0)))
184 goto swapon_bailout;
185
186 if(default_pager_init_flag == 0) {
187 start_def_pager(NULL);
188 default_pager_init_flag = 1;
189 }
190
191 /*
192 * Get a vnode for the paging area.
193 */
194 NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
195 ((IS_64BIT_PROCESS(p)) ? UIO_USERSPACE64 : UIO_USERSPACE32),
196 CAST_USER_ADDR_T(args->filename), &context);
197
198 if ((error = namei(ndp)))
199 goto swapon_bailout;
200 nameidone(ndp);
201 vp = ndp->ni_vp;
202
203 if (vp->v_type != VREG) {
204 error = EINVAL;
205 goto swapon_bailout;
206 }
207 UBCINFOCHECK("macx_swapon", vp);
208
209 /* get file size */
210 if ((error = vnode_size(vp, &file_size, &context)) != 0)
211 goto swapon_bailout;
212
213 /* resize to desired size if it's too small */
214 if ((file_size < (off_t)size) && ((error = vnode_setsize(vp, (off_t)size, 0, &context)) != 0))
215 goto swapon_bailout;
216
217 /* add new backing store to list */
218 i = 0;
219 while(bs_port_table[i].vp != 0) {
220 if(i == MAX_BACKING_STORE)
221 break;
222 i++;
223 }
224 if(i == MAX_BACKING_STORE) {
225 error = ENOMEM;
226 goto swapon_bailout;
227 }
228
229 /* remember the vnode. This vnode has namei() reference */
230 bs_port_table[i].vp = vp;
231
232 /*
233 * Look to see if we are already paging to this file.
234 */
235 /* make certain the copy send of kernel call will work */
236 default_pager = MEMORY_OBJECT_DEFAULT_NULL;
237 kr = host_default_memory_manager(host_priv_self(), &default_pager, 0);
238 if(kr != KERN_SUCCESS) {
239 error = EAGAIN;
240 bs_port_table[i].vp = 0;
241 goto swapon_bailout;
242 }
243
244 kr = default_pager_backing_store_create(default_pager,
245 -1, /* default priority */
246 0, /* default cluster size */
247 &backing_store);
248 memory_object_default_deallocate(default_pager);
249
250 if(kr != KERN_SUCCESS) {
251 error = ENOMEM;
252 bs_port_table[i].vp = 0;
253 goto swapon_bailout;
254 }
255
256 /*
257 * NOTE: we are able to supply PAGE_SIZE here instead of
258 * an actual record size or block number because:
259 * a: we do not support offsets from the beginning of the
260 * file (allowing for non page size/record modulo offsets.
261 * b: because allow paging will be done modulo page size
262 */
263
264 kr = default_pager_add_file(backing_store, (vnode_ptr_t) vp,
265 PAGE_SIZE, (int)(file_size/PAGE_SIZE));
266 if(kr != KERN_SUCCESS) {
267 bs_port_table[i].vp = 0;
268 if(kr == KERN_INVALID_ARGUMENT)
269 error = EINVAL;
270 else
271 error = ENOMEM;
272 goto swapon_bailout;
273 }
274 bs_port_table[i].bs = (void *)backing_store;
275 error = 0;
276
277 /* Mark this vnode as being used for swapfile */
278 SET(vp->v_flag, VSWAP);
279
280 ubc_setthreadcred(vp, p, current_thread());
281
282 /*
283 * take a long term reference on the vnode to keep
284 * vnreclaim() away from this vnode.
285 */
286 vnode_ref(vp);
287
288 swapon_bailout:
289 if (vp) {
290 vnode_put(vp);
291 }
292 (void) thread_funnel_set(kernel_flock, FALSE);
293 AUDIT_MACH_SYSCALL_EXIT(error);
294 return(error);
295 }
296
297 /*
298 * Routine: macx_swapoff
299 * Function:
300 * Syscall interface to remove a file from backing store
301 */
302 int
303 macx_swapoff(
304 struct macx_swapoff_args *args)
305 {
306 __unused int flags = args->flags;
307 kern_return_t kr;
308 mach_port_t backing_store;
309
310 struct vnode *vp = 0;
311 struct nameidata nd, *ndp;
312 struct proc *p = current_proc();
313 int i;
314 int error;
315 boolean_t funnel_state;
316 struct vfs_context context;
317
318 context.vc_proc = p;
319 context.vc_ucred = kauth_cred_get();
320
321 AUDIT_MACH_SYSCALL_ENTER(AUE_SWAPOFF);
322
323 funnel_state = thread_funnel_set(kernel_flock, TRUE);
324 backing_store = NULL;
325 ndp = &nd;
326
327 if ((error = suser(kauth_cred_get(), 0)))
328 goto swapoff_bailout;
329
330 /*
331 * Get the vnode for the paging area.
332 */
333 NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
334 ((IS_64BIT_PROCESS(p)) ? UIO_USERSPACE64 : UIO_USERSPACE32),
335 CAST_USER_ADDR_T(args->filename), &context);
336
337 if ((error = namei(ndp)))
338 goto swapoff_bailout;
339 nameidone(ndp);
340 vp = ndp->ni_vp;
341
342 if (vp->v_type != VREG) {
343 error = EINVAL;
344 goto swapoff_bailout;
345 }
346
347 for(i = 0; i < MAX_BACKING_STORE; i++) {
348 if(bs_port_table[i].vp == vp) {
349 break;
350 }
351 }
352 if (i == MAX_BACKING_STORE) {
353 error = EINVAL;
354 goto swapoff_bailout;
355 }
356 backing_store = (mach_port_t)bs_port_table[i].bs;
357
358 kr = default_pager_backing_store_delete(backing_store);
359 switch (kr) {
360 case KERN_SUCCESS:
361 error = 0;
362 bs_port_table[i].vp = 0;
363 /* This vnode is no longer used for swapfile */
364 CLR(vp->v_flag, VSWAP);
365
366 /* get rid of macx_swapon() "long term" reference */
367 vnode_rele(vp);
368
369 break;
370 case KERN_FAILURE:
371 error = EAGAIN;
372 break;
373 default:
374 error = EAGAIN;
375 break;
376 }
377
378 swapoff_bailout:
379 /* get rid of macx_swapoff() namei() reference */
380 if (vp)
381 vnode_put(vp);
382
383 (void) thread_funnel_set(kernel_flock, FALSE);
384 AUDIT_MACH_SYSCALL_EXIT(error);
385 return(error);
386 }
387
388 /*
389 * Routine: macx_swapinfo
390 * Function:
391 * Syscall interface to get general swap statistics
392 */
393 int
394 macx_swapinfo(
395 memory_object_size_t *total_p,
396 memory_object_size_t *avail_p,
397 vm_size_t *pagesize_p,
398 boolean_t *encrypted_p)
399 {
400 int error;
401 memory_object_default_t default_pager;
402 default_pager_info_64_t dpi64;
403 kern_return_t kr;
404
405 error = 0;
406
407 /*
408 * Get a handle on the default pager.
409 */
410 default_pager = MEMORY_OBJECT_DEFAULT_NULL;
411 kr = host_default_memory_manager(host_priv_self(), &default_pager, 0);
412 if (kr != KERN_SUCCESS) {
413 error = EAGAIN; /* XXX why EAGAIN ? */
414 goto done;
415 }
416 if (default_pager == MEMORY_OBJECT_DEFAULT_NULL) {
417 /*
418 * The default pager has not initialized yet,
419 * so it can't be using any swap space at all.
420 */
421 *total_p = 0;
422 *avail_p = 0;
423 *pagesize_p = 0;
424 *encrypted_p = FALSE;
425 goto done;
426 }
427
428 /*
429 * Get swap usage data from default pager.
430 */
431 kr = default_pager_info_64(default_pager, &dpi64);
432 if (kr != KERN_SUCCESS) {
433 error = ENOTSUP;
434 goto done;
435 }
436
437 /*
438 * Provide default pager info to caller.
439 */
440 *total_p = dpi64.dpi_total_space;
441 *avail_p = dpi64.dpi_free_space;
442 *pagesize_p = dpi64.dpi_page_size;
443 if (dpi64.dpi_flags & DPI_ENCRYPTED) {
444 *encrypted_p = TRUE;
445 } else {
446 *encrypted_p = FALSE;
447 }
448
449 done:
450 if (default_pager != MEMORY_OBJECT_DEFAULT_NULL) {
451 /* release our handle on default pager */
452 memory_object_default_deallocate(default_pager);
453 }
454 return error;
455 }