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