]>
Commit | Line | Data |
---|---|---|
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_setthreadcred(vp, p, current_thread()); | |
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 | } |