X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..143464d58d2bd6378e74eec636961ceb0d32fb91:/bsd/vm/dp_backing_file.c diff --git a/bsd/vm/dp_backing_file.c b/bsd/vm/dp_backing_file.c index 137e53eb6..cc52be8fb 100644 --- a/bsd/vm/dp_backing_file.c +++ b/bsd/vm/dp_backing_file.c @@ -1,47 +1,83 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2010 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. */ -#include #include #include #include -#include +#include +#include #include #include -#include +#include #include -#include +#include +#include +#include +#if CONFIG_PROTECT +#include +#endif + +#include +#include + +#include +#include #include -#include -#include +#include +#include +#include + +#include #include -#include +#include #include #include +#include + #include -#include +#include +#include +#include #include +#include +#if CONFIG_MACF +#include +#endif + +void kprintf(const char *fmt, ...); /* * temporary support for delayed instantiation @@ -64,7 +100,153 @@ struct bs_map bs_port_table[MAX_BACKING_STORE] = { /* ###################################################### */ -#include +/* + * Routine: macx_backing_store_recovery + * Function: + * Syscall interface to set a tasks privilege + * level so that it is not subject to + * macx_backing_store_suspend + */ +int +macx_backing_store_recovery( + struct macx_backing_store_recovery_args *args) +{ + int pid = args->pid; + int error; + struct proc *p = current_proc(); + boolean_t funnel_state; + + funnel_state = thread_funnel_set(kernel_flock, TRUE); + if ((error = suser(kauth_cred_get(), 0))) + goto backing_store_recovery_return; + + /* for now restrict backing_store_recovery */ + /* usage to only present task */ + if(pid != proc_selfpid()) { + error = EINVAL; + goto backing_store_recovery_return; + } + + task_backing_store_privileged(p->task); + +backing_store_recovery_return: + (void) thread_funnel_set(kernel_flock, FALSE); + return(error); +} + +/* + * Routine: macx_backing_store_suspend + * Function: + * Syscall interface to stop new demand for + * backing store when backing store is low + */ + +int +macx_backing_store_suspend( + struct macx_backing_store_suspend_args *args) +{ + boolean_t suspend = args->suspend; + int error; + boolean_t funnel_state; + + funnel_state = thread_funnel_set(kernel_flock, TRUE); + if ((error = suser(kauth_cred_get(), 0))) + goto backing_store_suspend_return; + + vm_backing_store_disable(suspend); + +backing_store_suspend_return: + (void) thread_funnel_set(kernel_flock, FALSE); + return(error); +} + +extern boolean_t backing_store_stop_compaction; +extern boolean_t compressor_store_stop_compaction; + +/* + * Routine: macx_backing_store_compaction + * Function: + * Turn compaction of swap space on or off. This is + * used during shutdown/restart so that the kernel + * doesn't waste time compacting swap files that are + * about to be deleted anyway. Compaction is always + * on by default when the system comes up and is turned + * off when a shutdown/restart is requested. It is + * re-enabled if the shutdown/restart is aborted for any reason. + */ + +int +macx_backing_store_compaction(int flags) +{ + int error; + + if ((error = suser(kauth_cred_get(), 0))) + return error; + + if (flags & SWAP_COMPACT_DISABLE) { + backing_store_stop_compaction = TRUE; + compressor_store_stop_compaction = TRUE; + + kprintf("backing_store_stop_compaction = TRUE\n"); + + } else if (flags & SWAP_COMPACT_ENABLE) { + backing_store_stop_compaction = FALSE; + compressor_store_stop_compaction = FALSE; + + kprintf("backing_store_stop_compaction = FALSE\n"); + } + + return 0; +} + +/* + * Routine: macx_triggers + * Function: + * Syscall interface to set the call backs for low and + * high water marks. + */ +int +macx_triggers( + struct macx_triggers_args *args) +{ + int error; + + error = suser(kauth_cred_get(), 0); + if (error) + return error; + + return mach_macx_triggers(args); +} + + +extern boolean_t dp_isssd; +extern void vm_swap_init(void); +extern int vm_compressor_mode; + +/* + * In the compressed pager world, the swapfiles are created by the kernel. + * Well, all except the first one. That swapfile is absorbed by the kernel at + * the end of the macx_swapon function (if swap is enabled). That's why + * we allow the first invocation of macx_swapon to succeed. + * + * If the compressor pool is running low, the kernel messages the dynamic pager + * on the port it has registered with the kernel. That port can transport 1 of 2 + * pieces of information to dynamic pager: create a swapfile or delete a swapfile. + * + * We choose to transmit the former. So, that message tells dynamic pager + * to create a swapfile and activate it by calling macx_swapon. + * + * We deny this new macx_swapon request. That leads dynamic pager to interpret the + * failure as a serious error and notify all it's clients that swap is running low. + * That's how we get the loginwindow "Resume / Force Quit Applications" dialog to appear. + * + * NOTE: + * If the kernel has already created multiple swapfiles by the time the compressor + * pool is running low (and it has to play this trick), dynamic pager won't be able to + * create a file in user-space and, that too will lead to a similar notification blast + * to all of it's clients. So, that behaves as desired too. + */ +boolean_t macx_swapon_allowed = TRUE; /* * Routine: macx_swapon @@ -73,67 +255,90 @@ struct bs_map bs_port_table[MAX_BACKING_STORE] = { */ int macx_swapon( - char *filename, - int flags, - long size, - long priority) + struct macx_swapon_args *args) { - struct vnode *vp = 0; + int size = args->size; + vnode_t vp = (vnode_t)NULL; struct nameidata nd, *ndp; - struct proc *p = current_proc(); - pager_file_t pf; register int error; kern_return_t kr; mach_port_t backing_store; - mach_port_t default_pager_port = MACH_PORT_NULL; + memory_object_default_t default_pager; int i; boolean_t funnel_state; + off_t file_size; + vfs_context_t ctx = vfs_context_current(); + struct proc *p = current_proc(); + int dp_cluster_size; + + if (COMPRESSED_PAGER_IS_ACTIVE) { + if (macx_swapon_allowed == FALSE) { + return EINVAL; + } else { + if ((vm_compressor_mode == VM_PAGER_COMPRESSOR_WITH_SWAP) || + (vm_compressor_mode == VM_PAGER_FREEZER_COMPRESSOR_WITH_SWAP)) { + vm_swap_init(); + } + + macx_swapon_allowed = FALSE; + return 0; + } + } - struct vattr vattr; + AUDIT_MACH_SYSCALL_ENTER(AUE_SWAPON); + AUDIT_ARG(value32, args->priority); funnel_state = thread_funnel_set(kernel_flock, TRUE); ndp = &nd; - if ((error = suser(p->p_ucred, &p->p_acflag))) + if ((error = suser(kauth_cred_get(), 0))) goto swapon_bailout; - unix_master(); - - if(default_pager_init_flag == 0) { - start_def_pager(NULL); - default_pager_init_flag = 1; - } - /* * Get a vnode for the paging area. */ - NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, - filename, p); + NDINIT(ndp, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, + ((IS_64BIT_PROCESS(p)) ? UIO_USERSPACE64 : UIO_USERSPACE32), + (user_addr_t) args->filename, ctx); if ((error = namei(ndp))) goto swapon_bailout; + nameidone(ndp); vp = ndp->ni_vp; if (vp->v_type != VREG) { error = EINVAL; - VOP_UNLOCK(vp, 0, p); goto swapon_bailout; } - UBCINFOCHECK("macx_swapon", vp); - if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) { - VOP_UNLOCK(vp, 0, p); + /* get file size */ + if ((error = vnode_size(vp, &file_size, ctx)) != 0) goto swapon_bailout; - } +#if CONFIG_MACF + vnode_lock(vp); + error = mac_system_check_swapon(vfs_context_ucred(ctx), vp); + vnode_unlock(vp); + if (error) + goto swapon_bailout; +#endif - if (vattr.va_size < (u_quad_t)size) { - vattr_null(&vattr); - vattr.va_size = (u_quad_t)size; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); - if (error) { - VOP_UNLOCK(vp, 0, p); + /* resize to desired size if it's too small */ + if ((file_size < (off_t)size) && ((error = vnode_setsize(vp, (off_t)size, 0, ctx)) != 0)) + goto swapon_bailout; + +#if CONFIG_PROTECT + { + /* initialize content protection keys manually */ + if ((error = cp_handle_vnop(vp, CP_WRITE_ACCESS, 0)) != 0) { goto swapon_bailout; - } + } + } +#endif + + + if (default_pager_init_flag == 0) { + start_def_pager(NULL); + default_pager_init_flag = 1; } /* add new backing store to list */ @@ -145,7 +350,6 @@ macx_swapon( } if(i == MAX_BACKING_STORE) { error = ENOMEM; - VOP_UNLOCK(vp, 0, p); goto swapon_bailout; } @@ -156,25 +360,44 @@ macx_swapon( * Look to see if we are already paging to this file. */ /* make certain the copy send of kernel call will work */ - kr = host_default_memory_manager(host_priv_self(), &default_pager_port, 0); + default_pager = MEMORY_OBJECT_DEFAULT_NULL; + kr = host_default_memory_manager(host_priv_self(), &default_pager, 0); if(kr != KERN_SUCCESS) { error = EAGAIN; - VOP_UNLOCK(vp, 0, p); bs_port_table[i].vp = 0; goto swapon_bailout; } - kr = default_pager_backing_store_create(default_pager_port, + if ((dp_isssd = vnode_pager_isSSD(vp)) == TRUE) { + /* + * keep the cluster size small since the + * seek cost is effectively 0 which means + * we don't care much about fragmentation + */ + dp_cluster_size = 2 * PAGE_SIZE; + } else { + /* + * use the default cluster size + */ + dp_cluster_size = 0; + } + kr = default_pager_backing_store_create(default_pager, -1, /* default priority */ - 0, /* default cluster size */ + dp_cluster_size, &backing_store); + memory_object_default_deallocate(default_pager); + if(kr != KERN_SUCCESS) { error = ENOMEM; - VOP_UNLOCK(vp, 0, p); bs_port_table[i].vp = 0; goto swapon_bailout; } + /* Mark this vnode as being used for swapfile */ + vnode_lock_spin(vp); + SET(vp->v_flag, VSWAP); + vnode_unlock(vp); + /* * NOTE: we are able to supply PAGE_SIZE here instead of * an actual record size or block number because: @@ -183,40 +406,45 @@ macx_swapon( * b: because allow paging will be done modulo page size */ - VOP_UNLOCK(vp, 0, p); - kr = default_pager_add_file(backing_store, vp, PAGE_SIZE, - ((int)vattr.va_size)/PAGE_SIZE); + kr = default_pager_add_file(backing_store, (vnode_ptr_t) vp, + PAGE_SIZE, (int)(file_size/PAGE_SIZE)); if(kr != KERN_SUCCESS) { bs_port_table[i].vp = 0; if(kr == KERN_INVALID_ARGUMENT) error = EINVAL; else error = ENOMEM; + + /* This vnode is not to be used for swapfile */ + vnode_lock_spin(vp); + CLR(vp->v_flag, VSWAP); + vnode_unlock(vp); + goto swapon_bailout; } bs_port_table[i].bs = (void *)backing_store; error = 0; - if (!ubc_hold(vp)) - panic("macx_swapon: hold"); - /* Mark this vnode as being used for swapfile */ - SET(vp->v_flag, VSWAP); + ubc_setthreadcred(vp, p, current_thread()); /* - * take an extra reference on the vnode to keep + * take a long term reference on the vnode to keep * vnreclaim() away from this vnode. */ - VREF(vp); - - /* Hold on to the namei reference to the paging file vnode */ - vp = 0; + vnode_ref(vp); swapon_bailout: if (vp) { - vrele(vp); + vnode_put(vp); } - unix_release(); (void) thread_funnel_set(kernel_flock, FALSE); + AUDIT_MACH_SYSCALL_EXIT(error); + + if (error) + printf("macx_swapon FAILED - %d\n", error); + else + printf("macx_swapon SUCCESS\n"); + return(error); } @@ -227,9 +455,9 @@ swapon_bailout: */ int macx_swapoff( - char *filename, - int flags) + struct macx_swapoff_args *args) { + __unused int flags = args->flags; kern_return_t kr; mach_port_t backing_store; @@ -239,60 +467,76 @@ macx_swapoff( int i; int error; boolean_t funnel_state; + vfs_context_t ctx = vfs_context_current(); + int orig_iopol_disk; + + AUDIT_MACH_SYSCALL_ENTER(AUE_SWAPOFF); funnel_state = thread_funnel_set(kernel_flock, TRUE); backing_store = NULL; ndp = &nd; - if ((error = suser(p->p_ucred, &p->p_acflag))) + if ((error = suser(kauth_cred_get(), 0))) goto swapoff_bailout; - unix_master(); - /* * Get the vnode for the paging area. */ - NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, - filename, p); + NDINIT(ndp, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, + ((IS_64BIT_PROCESS(p)) ? UIO_USERSPACE64 : UIO_USERSPACE32), + (user_addr_t) args->filename, ctx); if ((error = namei(ndp))) goto swapoff_bailout; + nameidone(ndp); vp = ndp->ni_vp; if (vp->v_type != VREG) { error = EINVAL; - VOP_UNLOCK(vp, 0, p); goto swapoff_bailout; } +#if CONFIG_MACF + vnode_lock(vp); + error = mac_system_check_swapoff(vfs_context_ucred(ctx), vp); + vnode_unlock(vp); + if (error) + goto swapoff_bailout; +#endif for(i = 0; i < MAX_BACKING_STORE; i++) { if(bs_port_table[i].vp == vp) { - backing_store; break; } } if (i == MAX_BACKING_STORE) { error = EINVAL; - VOP_UNLOCK(vp, 0, p); goto swapoff_bailout; } backing_store = (mach_port_t)bs_port_table[i].bs; - VOP_UNLOCK(vp, 0, p); + orig_iopol_disk = proc_get_task_policy(current_task(), current_thread(), + TASK_POLICY_INTERNAL, TASK_POLICY_IOPOL); + + proc_set_task_policy(current_task(), current_thread(), TASK_POLICY_INTERNAL, + TASK_POLICY_IOPOL, IOPOL_THROTTLE); + kr = default_pager_backing_store_delete(backing_store); + + proc_set_task_policy(current_task(), current_thread(), TASK_POLICY_INTERNAL, + TASK_POLICY_IOPOL, orig_iopol_disk); + switch (kr) { case KERN_SUCCESS: error = 0; bs_port_table[i].vp = 0; - ubc_rele(vp); /* This vnode is no longer used for swapfile */ + vnode_lock_spin(vp); CLR(vp->v_flag, VSWAP); + vnode_unlock(vp); - /* get rid of macx_swapon() namei() reference */ - vrele(vp); + /* get rid of macx_swapon() "long term" reference */ + vnode_rele(vp); - /* get rid of macx_swapon() "extra" reference */ - vrele(vp); break; case KERN_FAILURE: error = EAGAIN; @@ -305,9 +549,107 @@ macx_swapoff( swapoff_bailout: /* get rid of macx_swapoff() namei() reference */ if (vp) - vrele(vp); + vnode_put(vp); - unix_release(); (void) thread_funnel_set(kernel_flock, FALSE); + AUDIT_MACH_SYSCALL_EXIT(error); + + if (error) + printf("macx_swapoff FAILED - %d\n", error); + else + printf("macx_swapoff SUCCESS\n"); + return(error); } + +/* + * Routine: macx_swapinfo + * Function: + * Syscall interface to get general swap statistics + */ +extern uint64_t vm_swap_get_total_space(void); +extern uint64_t vm_swap_get_used_space(void); +extern uint64_t vm_swap_get_free_space(void); +extern boolean_t vm_swap_up; + +int +macx_swapinfo( + memory_object_size_t *total_p, + memory_object_size_t *avail_p, + vm_size_t *pagesize_p, + boolean_t *encrypted_p) +{ + int error; + memory_object_default_t default_pager; + default_pager_info_64_t dpi64; + kern_return_t kr; + + error = 0; + if (COMPRESSED_PAGER_IS_ACTIVE) { + + if (vm_swap_up == TRUE) { + + *total_p = vm_swap_get_total_space(); + *avail_p = vm_swap_get_free_space(); + *pagesize_p = PAGE_SIZE_64; + *encrypted_p = TRUE; + + } else { + + *total_p = 0; + *avail_p = 0; + *pagesize_p = 0; + *encrypted_p = FALSE; + } + } else { + + /* + * Get a handle on the default pager. + */ + default_pager = MEMORY_OBJECT_DEFAULT_NULL; + kr = host_default_memory_manager(host_priv_self(), &default_pager, 0); + if (kr != KERN_SUCCESS) { + error = EAGAIN; /* XXX why EAGAIN ? */ + goto done; + } + if (default_pager == MEMORY_OBJECT_DEFAULT_NULL) { + /* + * The default pager has not initialized yet, + * so it can't be using any swap space at all. + */ + *total_p = 0; + *avail_p = 0; + *pagesize_p = 0; + *encrypted_p = FALSE; + goto done; + } + + /* + * Get swap usage data from default pager. + */ + kr = default_pager_info_64(default_pager, &dpi64); + if (kr != KERN_SUCCESS) { + error = ENOTSUP; + goto done; + } + + /* + * Provide default pager info to caller. + */ + *total_p = dpi64.dpi_total_space; + *avail_p = dpi64.dpi_free_space; + *pagesize_p = dpi64.dpi_page_size; + if (dpi64.dpi_flags & DPI_ENCRYPTED) { + *encrypted_p = TRUE; + } else { + *encrypted_p = FALSE; + } + +done: + if (default_pager != MEMORY_OBJECT_DEFAULT_NULL) { + /* release our handle on default pager */ + memory_object_default_deallocate(default_pager); + } + } + return error; +}