]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
e5568f75 | 2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
e5568f75 A |
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. | |
1c79356b | 11 | * |
e5568f75 A |
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 | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
e5568f75 A |
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. | |
1c79356b A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | ||
1c79356b A |
23 | #include <sys/param.h> |
24 | #include <sys/systm.h> | |
25 | #include <sys/lock.h> | |
91447636 A |
26 | #include <sys/proc_internal.h> |
27 | #include <sys/kauth.h> | |
1c79356b A |
28 | #include <sys/buf.h> |
29 | #include <sys/uio.h> | |
91447636 | 30 | #include <sys/vnode_internal.h> |
1c79356b | 31 | #include <sys/namei.h> |
91447636 A |
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> | |
1c79356b | 37 | |
e5568f75 A |
38 | #include <bsm/audit_kernel.h> |
39 | #include <bsm/audit_kevents.h> | |
40 | ||
1c79356b | 41 | #include <mach/mach_types.h> |
91447636 A |
42 | #include <mach/host_priv.h> |
43 | #include <mach/mach_traps.h> | |
44 | #include <mach/boolean.h> | |
45 | ||
46 | #include <kern/kern_types.h> | |
1c79356b | 47 | #include <kern/host.h> |
91447636 | 48 | #include <kern/task.h> |
1c79356b A |
49 | #include <kern/zalloc.h> |
50 | #include <kern/kalloc.h> | |
91447636 A |
51 | #include <kern/assert.h> |
52 | ||
1c79356b | 53 | #include <libkern/libkern.h> |
1c79356b | 54 | |
91447636 A |
55 | #include <vm/vm_pageout.h> |
56 | #include <vm/vm_map.h> | |
57 | #include <vm/vm_kern.h> | |
1c79356b | 58 | #include <vm/vnode_pager.h> |
91447636 A |
59 | #include <vm/vm_protos.h> |
60 | ||
61 | extern thread_t current_act(void); | |
1c79356b A |
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 | ||
55e303ae A |
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( | |
91447636 | 93 | struct macx_backing_store_recovery_args *args) |
55e303ae | 94 | { |
91447636 | 95 | int pid = args->pid; |
55e303ae A |
96 | int error; |
97 | struct proc *p = current_proc(); | |
98 | boolean_t funnel_state; | |
99 | ||
100 | funnel_state = thread_funnel_set(kernel_flock, TRUE); | |
91447636 | 101 | if ((error = suser(kauth_cred_get(), 0))) |
55e303ae A |
102 | goto backing_store_recovery_return; |
103 | ||
104 | /* for now restrict backing_store_recovery */ | |
105 | /* usage to only present task */ | |
91447636 | 106 | if(pid != proc_selfpid()) { |
55e303ae A |
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( | |
91447636 | 127 | struct macx_backing_store_suspend_args *args) |
55e303ae | 128 | { |
91447636 | 129 | boolean_t suspend = args->suspend; |
55e303ae | 130 | int error; |
55e303ae A |
131 | boolean_t funnel_state; |
132 | ||
133 | funnel_state = thread_funnel_set(kernel_flock, TRUE); | |
91447636 | 134 | if ((error = suser(kauth_cred_get(), 0))) |
55e303ae A |
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 | ||
1c79356b A |
144 | /* |
145 | * Routine: macx_swapon | |
146 | * Function: | |
147 | * Syscall interface to add a file to backing store | |
148 | */ | |
149 | int | |
150 | macx_swapon( | |
91447636 | 151 | struct macx_swapon_args *args) |
1c79356b | 152 | { |
91447636 A |
153 | int size = args->size; |
154 | vnode_t vp = (vnode_t)NULL; | |
1c79356b A |
155 | struct nameidata nd, *ndp; |
156 | struct proc *p = current_proc(); | |
1c79356b A |
157 | register int error; |
158 | kern_return_t kr; | |
159 | mach_port_t backing_store; | |
0b4e3aa0 | 160 | memory_object_default_t default_pager; |
1c79356b A |
161 | int i; |
162 | boolean_t funnel_state; | |
91447636 A |
163 | off_t file_size; |
164 | struct vfs_context context; | |
1c79356b | 165 | |
91447636 A |
166 | context.vc_proc = p; |
167 | context.vc_ucred = kauth_cred_get(); | |
1c79356b | 168 | |
e5568f75 | 169 | AUDIT_MACH_SYSCALL_ENTER(AUE_SWAPON); |
91447636 | 170 | AUDIT_ARG(value, args->priority); |
e5568f75 | 171 | |
1c79356b A |
172 | funnel_state = thread_funnel_set(kernel_flock, TRUE); |
173 | ndp = &nd; | |
174 | ||
91447636 | 175 | if ((error = suser(kauth_cred_get(), 0))) |
1c79356b A |
176 | goto swapon_bailout; |
177 | ||
1c79356b A |
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 | */ | |
91447636 A |
186 | NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, |
187 | ((IS_64BIT_PROCESS(p)) ? UIO_USERSPACE64 : UIO_USERSPACE32), | |
188 | CAST_USER_ADDR_T(args->filename), &context); | |
1c79356b A |
189 | |
190 | if ((error = namei(ndp))) | |
191 | goto swapon_bailout; | |
91447636 | 192 | nameidone(ndp); |
1c79356b A |
193 | vp = ndp->ni_vp; |
194 | ||
195 | if (vp->v_type != VREG) { | |
196 | error = EINVAL; | |
1c79356b A |
197 | goto swapon_bailout; |
198 | } | |
199 | UBCINFOCHECK("macx_swapon", vp); | |
200 | ||
91447636 A |
201 | /* get file size */ |
202 | if ((error = vnode_size(vp, &file_size, &context)) != 0) | |
1c79356b | 203 | goto swapon_bailout; |
1c79356b | 204 | |
91447636 A |
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; | |
1c79356b A |
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; | |
1c79356b A |
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 */ | |
0b4e3aa0 A |
228 | default_pager = MEMORY_OBJECT_DEFAULT_NULL; |
229 | kr = host_default_memory_manager(host_priv_self(), &default_pager, 0); | |
1c79356b A |
230 | if(kr != KERN_SUCCESS) { |
231 | error = EAGAIN; | |
1c79356b A |
232 | bs_port_table[i].vp = 0; |
233 | goto swapon_bailout; | |
234 | } | |
235 | ||
0b4e3aa0 | 236 | kr = default_pager_backing_store_create(default_pager, |
1c79356b A |
237 | -1, /* default priority */ |
238 | 0, /* default cluster size */ | |
239 | &backing_store); | |
0b4e3aa0 A |
240 | memory_object_default_deallocate(default_pager); |
241 | ||
1c79356b A |
242 | if(kr != KERN_SUCCESS) { |
243 | error = ENOMEM; | |
1c79356b A |
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 | ||
91447636 A |
256 | kr = default_pager_add_file(backing_store, (vnode_ptr_t) vp, |
257 | PAGE_SIZE, (int)(file_size/PAGE_SIZE)); | |
1c79356b A |
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; | |
1c79356b A |
268 | |
269 | /* Mark this vnode as being used for swapfile */ | |
270 | SET(vp->v_flag, VSWAP); | |
271 | ||
55e303ae A |
272 | ubc_setcred(vp, p); |
273 | ||
1c79356b | 274 | /* |
91447636 | 275 | * take a long term reference on the vnode to keep |
1c79356b A |
276 | * vnreclaim() away from this vnode. |
277 | */ | |
91447636 | 278 | vnode_ref(vp); |
1c79356b A |
279 | |
280 | swapon_bailout: | |
281 | if (vp) { | |
91447636 | 282 | vnode_put(vp); |
1c79356b | 283 | } |
1c79356b | 284 | (void) thread_funnel_set(kernel_flock, FALSE); |
e5568f75 | 285 | AUDIT_MACH_SYSCALL_EXIT(error); |
1c79356b A |
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( | |
91447636 | 296 | struct macx_swapoff_args *args) |
1c79356b | 297 | { |
91447636 | 298 | __unused int flags = args->flags; |
1c79356b A |
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; | |
91447636 A |
308 | struct vfs_context context; |
309 | ||
310 | context.vc_proc = p; | |
311 | context.vc_ucred = kauth_cred_get(); | |
1c79356b | 312 | |
e5568f75 | 313 | AUDIT_MACH_SYSCALL_ENTER(AUE_SWAPOFF); |
91447636 | 314 | |
1c79356b A |
315 | funnel_state = thread_funnel_set(kernel_flock, TRUE); |
316 | backing_store = NULL; | |
317 | ndp = &nd; | |
318 | ||
91447636 | 319 | if ((error = suser(kauth_cred_get(), 0))) |
1c79356b A |
320 | goto swapoff_bailout; |
321 | ||
1c79356b A |
322 | /* |
323 | * Get the vnode for the paging area. | |
324 | */ | |
91447636 A |
325 | NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, |
326 | ((IS_64BIT_PROCESS(p)) ? UIO_USERSPACE64 : UIO_USERSPACE32), | |
327 | CAST_USER_ADDR_T(args->filename), &context); | |
1c79356b A |
328 | |
329 | if ((error = namei(ndp))) | |
330 | goto swapoff_bailout; | |
91447636 | 331 | nameidone(ndp); |
1c79356b A |
332 | vp = ndp->ni_vp; |
333 | ||
334 | if (vp->v_type != VREG) { | |
335 | error = EINVAL; | |
1c79356b A |
336 | goto swapoff_bailout; |
337 | } | |
338 | ||
339 | for(i = 0; i < MAX_BACKING_STORE; i++) { | |
340 | if(bs_port_table[i].vp == vp) { | |
1c79356b A |
341 | break; |
342 | } | |
343 | } | |
344 | if (i == MAX_BACKING_STORE) { | |
345 | error = EINVAL; | |
1c79356b A |
346 | goto swapoff_bailout; |
347 | } | |
348 | backing_store = (mach_port_t)bs_port_table[i].bs; | |
349 | ||
1c79356b A |
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; | |
1c79356b A |
355 | /* This vnode is no longer used for swapfile */ |
356 | CLR(vp->v_flag, VSWAP); | |
357 | ||
91447636 A |
358 | /* get rid of macx_swapon() "long term" reference */ |
359 | vnode_rele(vp); | |
1c79356b | 360 | |
1c79356b A |
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) | |
91447636 | 373 | vnode_put(vp); |
1c79356b | 374 | |
1c79356b | 375 | (void) thread_funnel_set(kernel_flock, FALSE); |
e5568f75 | 376 | AUDIT_MACH_SYSCALL_EXIT(error); |
1c79356b A |
377 | return(error); |
378 | } | |
91447636 A |
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 | } |