/* * Copyright (c) 2013 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * 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. * * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Mutex for global system override state */ static LCK_GRP_DECLARE(sys_override_mtx_grp, "system_override"); static LCK_MTX_DECLARE(sys_override_lock, &sys_override_mtx_grp); /* * Assertion counts for system properties (add new ones for each new mechanism) * * The assertion count management for system overrides is as follows: * * - All assertion counts are protected by the sys_override_lock. * * - Each caller of system_override() increments the assertion count for the * mechanism it specified in the flags. The caller then blocks for the * timeout specified in the system call. * * - At the end of the timeout, the caller thread wakes up and decrements the * assertion count for the mechanism it originally took an assertion on. * * - If another caller calls the system_override() to disable the override * for a mechanism, it simply disables the mechanism without changing any * assertion counts. That way, the assertion counts are properly balanced. * * One thing to note is that a SYS_OVERRIDE_DISABLE disables the overrides * for a mechanism irrespective of how many clients requested that override. * That makes the implementation simpler and avoids keeping a lot of process * specific state in the kernel. * */ static int64_t io_throttle_assert_cnt; static int64_t cpu_throttle_assert_cnt; static int64_t fast_jetsam_assert_cnt; /* Wait Channel for system override */ static uint64_t sys_override_wait; /* Helper routines */ static void system_override_begin(uint64_t flags); static void system_override_end(uint64_t flags); static void system_override_abort(uint64_t flags); static void system_override_callouts(uint64_t flags, boolean_t enable_override); static __attribute__((noinline)) int PROCESS_OVERRIDING_SYSTEM_DEFAULTS(uint64_t timeout); /* system call implementation */ int system_override(__unused struct proc *p, struct system_override_args * uap, __unused int32_t *retval) { uint64_t timeout = uap->timeout; uint64_t flags = uap->flags; int error = 0; /* Check credentials for caller. Only entitled processes are allowed to make this call. */ if ((error = priv_check_cred(kauth_cred_get(), PRIV_SYSTEM_OVERRIDE, 0))) { goto out; } /* Check to see if sane flags are specified. */ if ((flags & ~SYS_OVERRIDE_FLAGS_MASK) != 0) { error = EINVAL; goto out; } lck_mtx_lock(&sys_override_lock); if (flags & SYS_OVERRIDE_DISABLE) { flags &= ~SYS_OVERRIDE_DISABLE; system_override_abort(flags); } else { system_override_begin(flags); error = PROCESS_OVERRIDING_SYSTEM_DEFAULTS(timeout); system_override_end(flags); } lck_mtx_unlock(&sys_override_lock); out: return error; } /* * Helper routines for enabling/disabling system overrides for various mechanisms. * These routines should be called with the sys_override_lock held. Each subsystem * which is hooked into the override service provides two routines: * * - void sys_override_foo_init(void); * Routine to initialize the subsystem or the data needed for the override to work. * This routine is optional and if a subsystem needs it, it should be invoked from * init_system_override(). * * - void sys_override_foo(boolean_t enable_override); * Routine to enable/disable the override mechanism for that subsystem. A value of * true indicates that the mechanism should be overridden and the special behavior * should begin. A false value indicates that the subsystem should return to default * behavior. This routine is mandatory and should be invoked as part of the helper * routines if the flags passed in the syscall match the subsystem. Also, this * routine should preferably be idempotent. */ static void system_override_callouts(uint64_t flags, boolean_t enable_override) { switch (flags) { case SYS_OVERRIDE_IO_THROTTLE: if (enable_override) { KERNEL_DEBUG_CONSTANT(FSDBG_CODE(DBG_THROTTLE, IO_THROTTLE_DISABLE) | DBG_FUNC_START, proc_getpid(current_proc()), 0, 0, 0, 0); } else { KERNEL_DEBUG_CONSTANT(FSDBG_CODE(DBG_THROTTLE, IO_THROTTLE_DISABLE) | DBG_FUNC_END, proc_getpid(current_proc()), 0, 0, 0, 0); } sys_override_io_throttle(enable_override); break; case SYS_OVERRIDE_CPU_THROTTLE: if (enable_override) { KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_CPU_THROTTLE_DISABLE) | DBG_FUNC_START, proc_getpid(current_proc()), 0, 0, 0, 0); } else { KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_CPU_THROTTLE_DISABLE) | DBG_FUNC_END, proc_getpid(current_proc()), 0, 0, 0, 0); } sys_override_cpu_throttle(enable_override); break; case SYS_OVERRIDE_FAST_JETSAM: if (enable_override) { KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_FAST_JETSAM) | DBG_FUNC_START, proc_getpid(current_proc()), 0, 0, 0, 0); } else { KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_FAST_JETSAM) | DBG_FUNC_END, proc_getpid(current_proc()), 0, 0, 0, 0); } #if CONFIG_JETSAM memorystatus_fast_jetsam_override(enable_override); #endif /* CONFIG_JETSAM */ break; default: panic("Unknown option to system_override_callouts(): %llu", flags); } } /* * system_override_begin(uint64_t flags) * * Routine to start a system override if the assertion count * transitions from 0->1 for a specified mechanism. */ static void system_override_begin(uint64_t flags) { lck_mtx_assert(&sys_override_lock, LCK_MTX_ASSERT_OWNED); if (flags & SYS_OVERRIDE_IO_THROTTLE) { if (io_throttle_assert_cnt == 0) { system_override_callouts(SYS_OVERRIDE_IO_THROTTLE, true); } io_throttle_assert_cnt++; } if (flags & SYS_OVERRIDE_CPU_THROTTLE) { if (cpu_throttle_assert_cnt == 0) { system_override_callouts(SYS_OVERRIDE_CPU_THROTTLE, true); } cpu_throttle_assert_cnt++; } if (flags & SYS_OVERRIDE_FAST_JETSAM) { if (fast_jetsam_assert_cnt == 0) { system_override_callouts(SYS_OVERRIDE_FAST_JETSAM, true); } fast_jetsam_assert_cnt++; } } /* * system_override_end(uint64_t flags) * * Routine to end a system override if the assertion count * transitions from 1->0 for a specified mechanism. */ static void system_override_end(uint64_t flags) { lck_mtx_assert(&sys_override_lock, LCK_MTX_ASSERT_OWNED); if (flags & SYS_OVERRIDE_IO_THROTTLE) { assert(io_throttle_assert_cnt > 0); io_throttle_assert_cnt--; if (io_throttle_assert_cnt == 0) { system_override_callouts(SYS_OVERRIDE_IO_THROTTLE, false); } } if (flags & SYS_OVERRIDE_CPU_THROTTLE) { assert(cpu_throttle_assert_cnt > 0); cpu_throttle_assert_cnt--; if (cpu_throttle_assert_cnt == 0) { system_override_callouts(SYS_OVERRIDE_CPU_THROTTLE, false); } } if (flags & SYS_OVERRIDE_FAST_JETSAM) { assert(fast_jetsam_assert_cnt > 0); fast_jetsam_assert_cnt--; if (fast_jetsam_assert_cnt == 0) { system_override_callouts(SYS_OVERRIDE_FAST_JETSAM, false); } } } /* * system_override_abort(uint64_t flags) * * Routine to abort a system override (if one was active) * irrespective of the assertion counts and number of blocked * requestors. */ static void system_override_abort(uint64_t flags) { lck_mtx_assert(&sys_override_lock, LCK_MTX_ASSERT_OWNED); if ((flags & SYS_OVERRIDE_IO_THROTTLE) && (io_throttle_assert_cnt > 0)) { system_override_callouts(SYS_OVERRIDE_IO_THROTTLE, false); } if ((flags & SYS_OVERRIDE_CPU_THROTTLE) && (cpu_throttle_assert_cnt > 0)) { system_override_callouts(SYS_OVERRIDE_CPU_THROTTLE, false); } if ((flags & SYS_OVERRIDE_FAST_JETSAM) && (fast_jetsam_assert_cnt > 0)) { system_override_callouts(SYS_OVERRIDE_FAST_JETSAM, false); } } static __attribute__((noinline)) int PROCESS_OVERRIDING_SYSTEM_DEFAULTS(uint64_t timeout) { struct timespec ts; ts.tv_sec = timeout / NSEC_PER_SEC; ts.tv_nsec = timeout - ((long)ts.tv_sec * NSEC_PER_SEC); int error = msleep((caddr_t)&sys_override_wait, &sys_override_lock, PRIBIO | PCATCH, "system_override", &ts); /* msleep returns EWOULDBLOCK if timeout expires, treat that as success */ return (error == EWOULDBLOCK) ? 0 : error; }