/* * Copyright (c) 2000-2004 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 #if CONFIG_EMBEDDED #include #include #endif /* CONFIG_EMBEDDED */ #include #if CONFIG_MEMORYSTATUS extern void memorystatus_on_suspend(int pid); extern void memorystatus_on_resume(int pid); #endif static int proc_apply_bgtaskpolicy_internal(task_t, int, int); static int proc_restore_bgtaskpolicy_internal(task_t, int, int, int); static int task_get_cpuusage(task_t task, uint32_t * percentagep, uint64_t * intervalp, uint64_t * deadlinep); int task_set_cpuusage(task_t task, uint64_t percentage, uint64_t interval, uint64_t deadline, int scope); static int task_clear_cpuusage_locked(task_t task); static int task_apply_resource_actions(task_t task, int type); static void task_priority(task_t task, integer_t priority, integer_t max_priority); static kern_return_t task_role_default_handler(task_t task, task_role_t role); void task_action_cpuusage(thread_call_param_t param0, thread_call_param_t param1); static int proc_apply_bgthreadpolicy_locked(thread_t thread, int selfset); static void restore_bgthreadpolicy_locked(thread_t thread, int selfset, int importance); static int proc_get_task_selfdiskacc_internal(task_t task, thread_t thread); extern void unthrottle_thread(void * uthread); #if CONFIG_EMBEDDED static void set_thread_appbg(thread_t thread, int setbg,int importance); static void apply_bgthreadpolicy_external(thread_t thread); static void add_taskwatch_locked(task_t task, task_watch_t * twp); static void remove_taskwatch_locked(task_t task, task_watch_t * twp); static void task_watch_lock(void); static void task_watch_unlock(void); static void apply_appstate_watchers(task_t task, int setbg); void proc_apply_task_networkbg_internal(void *, thread_t); void proc_restore_task_networkbg_internal(void *, thread_t); int proc_pid(void * proc); typedef struct thread_watchlist { thread_t thread; /* thread being worked on for taskwatch action */ int importance; /* importance to be restored if thread is being made active */ } thread_watchlist_t; #endif /* CONFIG_EMBEDDED */ process_policy_t default_task_proc_policy = {0, 0, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, 0, TASK_POLICY_HWACCESS_CPU_ATTRIBUTE_FULLACCESS, TASK_POLICY_HWACCESS_NET_ATTRIBUTE_FULLACCESS, TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_FULLACCESS, TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_NORMAL, TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL }; process_policy_t default_task_null_policy = {0, 0, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, TASK_POLICY_RESOURCE_ATTRIBUTE_NONE, 0, TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NONE, TASK_POLICY_HWACCESS_NET_ATTRIBUTE_NONE, TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NONE, TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_NORMAL, TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE }; /* * This routine should always be called with the task lock held. * This routine handles Default operations for TASK_FOREGROUND_APPLICATION * and TASK_BACKGROUND_APPLICATION of task with no special app type. */ static kern_return_t task_role_default_handler(task_t task, task_role_t role) { kern_return_t result = KERN_SUCCESS; switch (task->role) { case TASK_FOREGROUND_APPLICATION: case TASK_BACKGROUND_APPLICATION: case TASK_UNSPECIFIED: /* if there are no process wide backgrounding ... */ if ((task->ext_appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) && (task->appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE)) { task_priority(task, ((role == TASK_FOREGROUND_APPLICATION)? BASEPRI_FOREGROUND: BASEPRI_BACKGROUND), task->max_priority); } task->role = role; break; case TASK_CONTROL_APPLICATION: case TASK_RENICED: /* else fail silently */ break; default: result = KERN_INVALID_ARGUMENT; break; } return(result); } kern_return_t task_policy_set( task_t task, task_policy_flavor_t flavor, task_policy_t policy_info, mach_msg_type_number_t count) { kern_return_t result = KERN_SUCCESS; void * bsdinfo = NULL; int setbg = 0; if (task == TASK_NULL || task == kernel_task) return (KERN_INVALID_ARGUMENT); switch (flavor) { case TASK_CATEGORY_POLICY: { task_category_policy_t info = (task_category_policy_t)policy_info; if (count < TASK_CATEGORY_POLICY_COUNT) return (KERN_INVALID_ARGUMENT); #if CONFIG_EMBEDDED if ((current_task() == task) && (info != NULL) && (info->role != TASK_THROTTLE_APPLICATION)) return (KERN_INVALID_ARGUMENT); #endif task_lock(task); switch(info->role) { case TASK_FOREGROUND_APPLICATION : { if (task->ext_appliedstate.apptype == PROC_POLICY_OSX_APPTYPE_NONE) { result = task_role_default_handler(task, info->role); } else { switch (task->ext_appliedstate.apptype) { #if !CONFIG_EMBEDDED case PROC_POLICY_OSX_APPTYPE_TAL: /* Move the app to foreground with no DarwinBG */ proc_restore_bgtaskpolicy_internal(task, 1, 1, BASEPRI_FOREGROUND); bsdinfo = task->bsd_info; setbg = 0; break; case PROC_POLICY_OSX_APPTYPE_DBCLIENT: /* reset the apptype so enforcement on background/foregound */ task->ext_appliedstate.apptype = PROC_POLICY_OSX_APPTYPE_NONE; /* Internal application and make it foreground pri */ proc_restore_bgtaskpolicy_internal(task, 1, 0, BASEPRI_FOREGROUND); bsdinfo = task->bsd_info; setbg = 0; break; #endif /* !CONFIG_EMBEDDED */ default: /* the app types cannot be in CONTROL, GRAPHICS STATE, so it will de default state here */ task_priority(task, BASEPRI_FOREGROUND, task->max_priority); break; } /* switch (task->ext_appliedstate.apptype) */ task->role = TASK_FOREGROUND_APPLICATION; } } break; case TASK_BACKGROUND_APPLICATION : { if (task->ext_appliedstate.apptype == PROC_POLICY_OSX_APPTYPE_NONE) { result = task_role_default_handler(task, info->role); } else { /* apptype != PROC_POLICY_OSX_APPTYPE_NONE */ switch (task->ext_appliedstate.apptype) { #if !CONFIG_EMBEDDED case PROC_POLICY_OSX_APPTYPE_TAL: /* TAL apps will get Darwin backgrounded if not already set */ if (task->ext_appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) { proc_apply_bgtaskpolicy_internal(task, 1, 1); bsdinfo = task->bsd_info; setbg = 1; } break; #endif /* !CONFIG_EMBEDDED */ default: task_priority(task, BASEPRI_BACKGROUND, task->max_priority); break; } /* switch (task->ext_appliedstate.apptype) */ task->role = TASK_BACKGROUND_APPLICATION; } } break; case TASK_CONTROL_APPLICATION: if (task != current_task()|| task->sec_token.val[0] != 0) result = KERN_INVALID_ARGUMENT; else { task_priority(task, BASEPRI_CONTROL, task->max_priority); task->role = info->role; } break; case TASK_GRAPHICS_SERVER: if (task != current_task() || task->sec_token.val[0] != 0) result = KERN_INVALID_ARGUMENT; else { task_priority(task, MAXPRI_RESERVED - 3, MAXPRI_RESERVED); task->role = info->role; } break; case TASK_DEFAULT_APPLICATION: task_priority(task, BASEPRI_DEFAULT, MAXPRI_USER); task->role = info->role; break; default : result = KERN_INVALID_ARGUMENT; break; } /* switch (info->role) */ task_unlock(task); /* if backgrounding action ... */ if (bsdinfo != NULL) proc_set_task_networkbg(bsdinfo, setbg); break; } default: result = KERN_INVALID_ARGUMENT; break; } return (result); } static void task_priority( task_t task, integer_t priority, integer_t max_priority) { thread_t thread; task->max_priority = max_priority; if (priority > task->max_priority) priority = task->max_priority; else if (priority < MINPRI) priority = MINPRI; task->priority = priority; queue_iterate(&task->threads, thread, thread_t, task_threads) { thread_mtx_lock(thread); if (thread->active) thread_task_priority(thread, priority, max_priority); thread_mtx_unlock(thread); } } kern_return_t task_importance( task_t task, integer_t importance) { if (task == TASK_NULL || task == kernel_task) return (KERN_INVALID_ARGUMENT); task_lock(task); if (!task->active) { task_unlock(task); return (KERN_TERMINATED); } if (task->role >= TASK_CONTROL_APPLICATION) { task_unlock(task); return (KERN_INVALID_ARGUMENT); } task_priority(task, importance + BASEPRI_DEFAULT, task->max_priority); task->role = TASK_RENICED; task_unlock(task); return (KERN_SUCCESS); } kern_return_t task_policy_get( task_t task, task_policy_flavor_t flavor, task_policy_t policy_info, mach_msg_type_number_t *count, boolean_t *get_default) { if (task == TASK_NULL || task == kernel_task) return (KERN_INVALID_ARGUMENT); switch (flavor) { case TASK_CATEGORY_POLICY: { task_category_policy_t info = (task_category_policy_t)policy_info; if (*count < TASK_CATEGORY_POLICY_COUNT) return (KERN_INVALID_ARGUMENT); if (*get_default) info->role = TASK_UNSPECIFIED; else { task_lock(task); info->role = task->role; task_unlock(task); } break; } default: return (KERN_INVALID_ARGUMENT); } return (KERN_SUCCESS); } /* task Darwin BG enforcement/settings related routines */ int proc_get_task_bg_policy(task_t task) { int selfset = 0; int val = 0; if (current_task() == task) selfset = 1; if (selfset == 0) { val = task->ext_policystate.hw_bg; } else { val = task->policystate.hw_bg; } return(val); } int proc_get_thread_bg_policy(task_t task, uint64_t tid) { int selfset = 0; thread_t self = current_thread(); thread_t thread = THREAD_NULL; int val = 0; if (tid == self->thread_id) selfset = 1; if (selfset == 0) { task_lock(task); thread = task_findtid(task, tid); if (thread != NULL) val = thread->ext_policystate.hw_bg; task_unlock(task); } else { val = self->policystate.hw_bg; } return(val); } int proc_get_self_isbackground(void) { task_t task = current_task();; thread_t thread = current_thread(); if ((task->ext_appliedstate.hw_bg != TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) || (task->appliedstate.hw_bg != TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) || (thread->ext_appliedstate.hw_bg != TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) || (thread->appliedstate.hw_bg != TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE)) return(1); else return(0); } int proc_get_selfthread_isbackground(void) { thread_t thread = current_thread(); if ((thread->ext_appliedstate.hw_bg != TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) || (thread->appliedstate.hw_bg != TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE)) return(1); else return(0); } int proc_set_bgtaskpolicy(task_t task, int intval) { int selfset = 0; if (current_task() == task) selfset = 1; task_lock(task); if (selfset == 0) { /* allready set? */ if (task->ext_policystate.hw_bg != intval) task->ext_policystate.hw_bg = intval; } else { if (task->policystate.hw_bg != intval) task->policystate.hw_bg = intval; } task_unlock(task); return(0); } /* set and apply as well , handles reset of NONUI due to setprio() task app state implmn side effect */ int proc_set_and_apply_bgtaskpolicy(task_t task, int prio) { int error = 0; if (prio == PRIO_DARWIN_BG) { error = proc_set_bgtaskpolicy(task, TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL); if (error == 0) { error = proc_apply_bgtaskpolicy(task); #if CONFIG_EMBEDDED /* XXX: till SB uses newer SPIs */ apply_appstate_watchers(task, 1); #endif /* CONFIG_EMBEDDED */ } } else { error = proc_restore_bgtaskpolicy(task); if (error == 0) { /* since prior impl of non UI was overloaded with bg state, need to reset */ error = proc_apply_task_gpuacc(task, TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_DEFAULT); #if CONFIG_EMBEDDED /* XXX: till SB uses newer SPIs */ apply_appstate_watchers(task, 0); #endif /* CONFIG_EMBEDDED */ } } return(error); } int proc_set_bgthreadpolicy(task_t task, uint64_t tid, int prio) { int selfset = 0; thread_t self = current_thread(); thread_t thread = THREAD_NULL; int reset; if (prio == 0) reset = 1; if (tid == self->thread_id) selfset = 1; task_lock(task); if (selfset == 0) { thread = task_findtid(task, tid); if (thread != NULL) thread->ext_policystate.hw_bg = prio; } else { self->policystate.hw_bg = prio; } task_unlock(task); return(0); } int proc_set_and_apply_bgthreadpolicy(task_t task, uint64_t tid, int prio) { int error = 0; if (prio == PRIO_DARWIN_BG) { error = proc_set_bgthreadpolicy(task, tid, TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL); if (error == 0) error = proc_apply_bgthreadpolicy(task, tid); } else { error = proc_restore_bgthreadpolicy(task, tid); } return(error); } int proc_add_bgtaskpolicy(task_t task, int val) { int selfset = 0; if (current_task() == task) selfset = 1; task_lock(task); if (selfset == 0) { task->policystate.hw_bg |= val; } else { task->ext_policystate.hw_bg |= val; } task_unlock(task); return(0); } int proc_add_bgthreadpolicy(task_t task, uint64_t tid, int val) { int selfset = 0; thread_t self = current_thread(); thread_t thread = THREAD_NULL; int reset; if (val == 0) reset = 1; if (tid == self->thread_id) selfset = 1; task_lock(task); if (selfset == 0) { thread = task_findtid(task, tid); if (thread != NULL) thread->ext_policystate.hw_bg |= val; } else { self->policystate.hw_bg |= val; } task_unlock(task); return(val); } int proc_remove_bgtaskpolicy(task_t task, int intval) { int selfset = 0; if (current_task() == task) selfset = 1; task_lock(task); if (selfset == 0) { task->policystate.hw_bg &= ~intval; } else { task->ext_policystate.hw_bg &= ~intval; } task_unlock(task); return(0); } int proc_remove_bgthreadpolicy(task_t task, uint64_t tid, int val) { int selfset = 0; thread_t self = current_thread(); thread_t thread = THREAD_NULL; int reset; if (val == 0) reset = 1; if (tid == self->thread_id) selfset = 1; task_lock(task); if (selfset == 0) { thread = task_findtid(task, tid); if (thread != NULL) thread->ext_policystate.hw_bg &= ~val; } else { self->policystate.hw_bg &= ~val; } task_unlock(task); return(val); } int proc_apply_bgtask_selfpolicy(void) { return(proc_apply_bgtaskpolicy(current_task())); } int proc_apply_bgtaskpolicy(task_t task) { int external = 1; if (task == current_task()) external = 0; return(proc_apply_bgtaskpolicy_internal(task, 0, external)); } int proc_apply_bgtaskpolicy_external(task_t task) { return(proc_apply_bgtaskpolicy_internal(task, 0, 1)); } static int proc_apply_bgtaskpolicy_internal(task_t task, int locked, int external) { if (locked == 0) task_lock(task); /* if the process is exiting, no action to be done */ if (task->proc_terminate != 0) goto out; if (external != 0) { /* allready set? */ if (task->ext_appliedstate.hw_bg != task->ext_policystate.hw_bg) { task->ext_appliedstate.hw_bg = task->ext_policystate.hw_bg; task_priority(task, MAXPRI_THROTTLE, MAXPRI_THROTTLE); /* background state applied */ } } else { if (task->appliedstate.hw_bg != task->policystate.hw_bg) { task->appliedstate.hw_bg = task->policystate.hw_bg; task_priority(task, MAXPRI_THROTTLE, MAXPRI_THROTTLE); } } out: if (locked == 0) task_unlock(task); return(0); } /* apply the self backgrounding even if the thread is not current thread */ int proc_apply_workq_bgthreadpolicy(thread_t thread) { int error; task_t wqtask = TASK_NULL; if (thread != THREAD_NULL) { wqtask = thread->task; task_lock(wqtask); /* apply the background as selfset internal one */ error = proc_apply_bgthreadpolicy_locked(thread, 1); task_unlock(wqtask); } else error = ESRCH; return(error); } int proc_apply_bgthreadpolicy(task_t task, uint64_t tid) { int selfset = 0, error = 0; thread_t self = current_thread(); thread_t thread = THREAD_NULL; task_t localtask = TASK_NULL; if (tid == self->thread_id) { selfset = 1; localtask = current_task(); } else localtask = task; task_lock(localtask); if (selfset != 0) { thread = self; } else { thread = task_findtid(localtask, tid); } error = proc_apply_bgthreadpolicy_locked(thread, selfset); task_unlock(localtask); return(error); } static int proc_apply_bgthreadpolicy_locked(thread_t thread, int selfset) { int set = 0; thread_precedence_policy_data_t policy; if (thread != NULL) { /* if the process is exiting, no action to be done */ if (thread->task->proc_terminate != 0) goto out; if (selfset != 0) { /* internal application */ if (thread->appliedstate.hw_bg != thread->policystate.hw_bg) { thread->appliedstate.hw_bg = thread->policystate.hw_bg; if (thread->ext_appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) set = 1; } } else { /* external application */ if (thread->ext_appliedstate.hw_bg != thread->ext_policystate.hw_bg) { thread->ext_appliedstate.hw_bg = thread->ext_policystate.hw_bg; if (thread->appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) set = 1; } } if (set != 0) { #if CONFIG_EMBEDDED if (thread->task->ext_appliedstate.apptype == PROC_POLICY_IOS_APPLE_DAEMON) { thread->saved_importance = thread->importance; } #endif /* CONFIG_EMBEDDED */ /* set thread priority (we did not save previous value) */ policy.importance = INT_MIN; thread_policy_set_internal(thread, THREAD_PRECEDENCE_POLICY, (thread_policy_t)&policy, THREAD_PRECEDENCE_POLICY_COUNT ); } } else return(ESRCH); out: return(0); } #if CONFIG_EMBEDDED /* set external application of background */ static void apply_bgthreadpolicy_external(thread_t thread) { int set = 0; thread_precedence_policy_data_t policy; /* if the process is exiting, no action to be done */ if (thread->task->proc_terminate != 0) return; thread->ext_policystate.hw_bg = TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL; if (thread->ext_appliedstate.hw_bg != thread->ext_policystate.hw_bg) { thread->ext_appliedstate.hw_bg = thread->ext_policystate.hw_bg; if (thread->appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) set = 1; } if (set != 0) { /* set thread priority (we did not save previous value) */ policy.importance = INT_MIN; thread_policy_set_internal(thread, THREAD_PRECEDENCE_POLICY, (thread_policy_t)&policy, THREAD_PRECEDENCE_POLICY_COUNT ); } } #endif /* CONFIG_EMBEDDED */ int proc_apply_bgthread_selfpolicy(void) { return(proc_apply_bgthreadpolicy(current_task(), current_thread()->thread_id)); } int proc_restore_bgtaskpolicy(task_t task) { int external = 1; if (current_task() == task) external = 0; return(proc_restore_bgtaskpolicy_internal(task, 0, external, BASEPRI_DEFAULT)); } static int proc_restore_bgtaskpolicy_internal(task_t task, int locked, int external, int pri) { if (locked == 0) task_lock(task); /* if the process is exiting, no action to be done */ if (task->proc_terminate != 0) goto out; if (external != 0) { task->ext_appliedstate.hw_bg = TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE; /* self BG in flight? */ if (task->appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) { task_priority(task, pri, MAXPRI_USER); #if CONFIG_EMBEDDED task->role = TASK_DEFAULT_APPLICATION; #endif /* CONFIG_EMBEDDED */ } } else { task->appliedstate.hw_bg = TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE; /* external BG in flight? */ if (task->ext_appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) { task_priority(task, pri, MAXPRI_USER); #if CONFIG_EMBEDDED task->role = TASK_DEFAULT_APPLICATION; #endif /* CONFIG_EMBEDDED */ } } out: if (locked == 0) task_unlock(task); return(0); } /* restore the self backgrounding even if the thread is not current thread */ int proc_restore_workq_bgthreadpolicy(thread_t thread) { int error = 0; task_t wqtask = TASK_NULL; int importance = 0; if (thread != THREAD_NULL) { wqtask = thread->task; task_lock(wqtask); /* remove the background and restore default importance as self(internal) removal */ #if CONFIG_EMBEDDED if (thread->task->ext_appliedstate.apptype == PROC_POLICY_IOS_APPLE_DAEMON) { /* restore prev set importnace */ importance = thread->saved_importance; thread->saved_importance = 0; } #endif /* CONFIG_EMBEDDED */ restore_bgthreadpolicy_locked(thread, 1, importance); task_unlock(wqtask); } else error = ESRCH; return(error); } int proc_restore_bgthread_selfpolicy(void) { return(proc_restore_bgthreadpolicy(current_task(), thread_tid(current_thread()))); } int proc_restore_bgthreadpolicy(task_t task, uint64_t tid) { int selfset = 0; thread_t self = current_thread(); thread_t thread = THREAD_NULL; int importance = 0; if (tid == self->thread_id) selfset = 1; task_lock(task); if (selfset == 0) { thread = task_findtid(task, tid); } else { thread = self; } if (thread != NULL) { #if CONFIG_EMBEDDED if (thread->task->ext_appliedstate.apptype == PROC_POLICY_IOS_APPLE_DAEMON) { /* restore prev set importnace */ importance = thread->saved_importance; thread->saved_importance = 0; } #endif /* CONFIG_EMBEDDED */ restore_bgthreadpolicy_locked(thread, selfset, importance); } task_unlock(task); if (thread != NULL) return(0); else return(1); } static void restore_bgthreadpolicy_locked(thread_t thread, int selfset, int importance) { thread_precedence_policy_data_t policy; int reset = 0; if (thread != NULL) { /* if the process is exiting, no action to be done */ if (thread->task->proc_terminate != 0) return; if (selfset != 0) { thread->appliedstate.hw_bg = TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE; /* external BG in flight? */ if (thread->ext_appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) reset = 1; } else { thread->ext_appliedstate.hw_bg = TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE; /* self BG in flight? */ if (thread->appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_NONE) reset = 1; } if (reset != 0) { /* reset thread priority (we did not save previous value) */ policy.importance = importance; thread_policy_set_internal(thread, THREAD_PRECEDENCE_POLICY, (thread_policy_t)&policy, THREAD_PRECEDENCE_POLICY_COUNT ); } } } void #if CONFIG_EMBEDDED proc_set_task_apptype(task_t task, int type, thread_t thread) #else proc_set_task_apptype(task_t task, int type, __unused thread_t thread) #endif { #if CONFIG_EMBEDDED thread_t th = THREAD_NULL; #endif /* CONFIG_EMBEDDED */ switch (type) { #if CONFIG_EMBEDDED case PROC_POLICY_IOS_RESV1_APPTYPE: task->ext_policystate.apptype = type; task->policystate.apptype = type; proc_apply_bgtaskpolicy_external(task); /* indicate that BG is set and next foreground needs to reset */ task->ext_appliedstate.apptype = type; break; case PROC_POLICY_IOS_APPLE_DAEMON: task->ext_policystate.apptype = type; task->policystate.apptype = type; task->ext_appliedstate.apptype = type; /* posix spawn will already have thread created, so backround it */ if (thread == NULL) th = current_thread(); else th = thread; if (th->appliedstate.hw_bg != TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL) { /* apply self backgrounding if not already set */ task_lock(th->task); proc_apply_bgthreadpolicy_locked(th, 1); task_unlock(th->task); } break; case PROC_POLICY_IOS_APPTYPE: task->ext_policystate.apptype = type; task->policystate.apptype = type; break; case PROC_POLICY_IOS_NONUITYPE: task->ext_policystate.apptype = type; task->policystate.apptype = type; /* set to deny access to gpu */ task->ext_appliedstate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; task->ext_policystate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; break; #else /* CONFIG_EMBEDDED */ case PROC_POLICY_OSX_APPTYPE_TAL: task->ext_policystate.apptype = type; task->policystate.apptype = type; proc_apply_bgtaskpolicy_external(task); /* indicate that BG is set and next foreground needs to reset */ task->ext_appliedstate.apptype = type; break; case PROC_POLICY_OSX_APPTYPE_DBCLIENT: task->ext_policystate.apptype = type; task->policystate.apptype = type; proc_apply_bgtaskpolicy_internal(task, 0, 0); break; #endif /* CONFIG_EMBEDDED */ default: break; } } /* update the darwin backdground action state in the flags field for libproc */ #define PROC_FLAG_DARWINBG 0x8000 /* process in darwin background */ #define PROC_FLAG_EXT_DARWINBG 0x10000 /* process in darwin background - external enforcement */ #define PROC_FLAG_IOS_APPLEDAEMON 0x20000 /* process is apple ios daemon */ int proc_get_darwinbgstate(task_t task, uint32_t * flagsp) { if (task->ext_appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL){ *flagsp |= PROC_FLAG_EXT_DARWINBG; } if (task->appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL){ *flagsp |= PROC_FLAG_DARWINBG; } #if CONFIG_EMBEDDED if (task->ext_appliedstate.apptype == PROC_POLICY_IOS_APPLE_DAEMON) { *flagsp |= PROC_FLAG_IOS_APPLEDAEMON; } #endif /* CONFIG_EMBEDDED */ return(0); } /* * HW disk access realted routines, they need to return * IOPOL_XXX equivalents for spec_xxx/throttle updates. */ int proc_get_task_disacc(task_t task) { #if CONFIG_EMBEDDED if ((task->ext_appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); #else /* CONFIG_EMBEDDED */ if ((task->ext_appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) { /* if it is a TAL or DBClient and not self throttled, return Utility */ if ((task->ext_appliedstate.apptype == PROC_POLICY_OSX_APPTYPE_TAL) || (task->ext_appliedstate.apptype == PROC_POLICY_OSX_APPTYPE_DBCLIENT)) { /* any setting for DBG, we need to honor that */ if ((task->ext_appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE) && ((task->appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE)!= 0) && (task->appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE)) { return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_UTILITY); } else return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); } else return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); } #endif /* CONFIG_EMBEDDED */ if (task->ext_appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS) return(task->ext_appliedstate.hw_disk); if ((task->appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); if (task->appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS) return(task->appliedstate.hw_disk); return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS); } int proc_get_task_selfdiskacc_internal(task_t task, thread_t thread) { /* if the task is marked for proc_terminate, no throttling for it */ if (task->proc_terminate != 0) goto out; /* * As per defined iopolicysys behavior, thread trumps task. * Do we need to follow that for external enforcements of BG or hw access? * Status quo for now.. */ if((thread->ext_appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); if (thread->ext_appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS) return(thread->ext_appliedstate.hw_disk); if((thread->appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); if (thread->appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS) return(thread->appliedstate.hw_disk); #if CONFIG_EMBEDDED if ((task->ext_appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); #else /* CONFIG_EMBEDDED */ if ((task->ext_appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) { /* if it is a TAL or DBClient and not self throttled, return Utility */ if ((task->ext_appliedstate.apptype == PROC_POLICY_OSX_APPTYPE_TAL) || (task->ext_appliedstate.apptype == PROC_POLICY_OSX_APPTYPE_DBCLIENT)) { /* any setting for DBG, we need to honor that */ if ((task->ext_appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE) && ((task->appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE)!= 0) && (task->appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE)) { return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_UTILITY); } else return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); } else return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); } #endif /* CONFIG_EMBEDDED */ if (task->ext_appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS) return(task->ext_appliedstate.hw_disk); if ((task->appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); if (task->appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS) return(task->appliedstate.hw_disk); out: return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS); } int proc_get_task_selfdiskacc(void) { return(proc_get_task_selfdiskacc_internal(current_task(), current_thread())); } int proc_get_diskacc(thread_t thread) { return(proc_get_task_selfdiskacc_internal(thread->task, thread)); } int proc_get_thread_selfdiskacc(void) { thread_t thread = current_thread(); if((thread->ext_appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); if (thread->ext_appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS) return(thread->ext_appliedstate.hw_disk); if((thread->appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_DISKTHROTTLE) != 0) return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_THROTTLE); if (thread->appliedstate.hw_disk != TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS) return(thread->appliedstate.hw_disk); return(TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS); } int proc_apply_task_diskacc(task_t task, int policy) { task_t self = current_task(); task_lock(task); if (task == self) { task->appliedstate.hw_disk = policy; task->policystate.hw_disk = policy; } else { task->ext_appliedstate.hw_disk = policy; task->ext_policystate.hw_disk = policy; } task_unlock(task); return(0); } int proc_apply_thread_diskacc(task_t task, uint64_t tid, int policy) { thread_t thread; if (tid == TID_NULL) { thread = current_thread(); proc_apply_thread_selfdiskacc(policy); } else { task_lock(task); thread = task_findtid(task, tid); if (thread != NULL) { thread->ext_appliedstate.hw_disk = policy; thread->ext_policystate.hw_disk = policy; } task_unlock(task); } if (thread != NULL) return(0); else return(0); } void proc_task_remove_throttle(task_t task) { thread_t thread; int importance = 0; task_lock(task); /* remove processwide internal DBG applicationn */ proc_restore_bgtaskpolicy_internal(task, 1, 0, BASEPRI_DEFAULT); /* remove processwide external DBG applicationn */ proc_restore_bgtaskpolicy_internal(task, 1, 1, BASEPRI_DEFAULT); for (thread = (thread_t)queue_first(&task->threads); !queue_end(&task->threads, (queue_entry_t)thread); ) { #if CONFIG_EMBEDDED if (thread->task->ext_appliedstate.apptype == PROC_POLICY_IOS_APPLE_DAEMON) { /* restore prev set importnace */ importance = thread->saved_importance; thread->saved_importance = 0; } #endif /* CONFIG_EMBEDDED */ /* remove thread level internal DBG application */ restore_bgthreadpolicy_locked(thread, 1, importance); /* remove thread level external DBG application */ restore_bgthreadpolicy_locked(thread, 0, importance); /* reset thread io policy */ thread->ext_appliedstate.hw_disk = TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS; thread->appliedstate.hw_disk = TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS; unthrottle_thread(thread->uthread); thread = (thread_t)queue_next(&thread->task_threads); } /* reset task iopolicy */ task->ext_appliedstate.hw_disk = TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS; task->appliedstate.hw_disk = TASK_POLICY_HWACCESS_DISK_ATTRIBUTE_FULLACCESS; task->proc_terminate = 1; task_unlock(task); } int proc_apply_thread_selfdiskacc(int policy) { task_t task = current_task(); thread_t thread = current_thread(); task_lock(task); thread->appliedstate.hw_disk = policy; thread->policystate.hw_disk = policy; task_unlock(task); return(0); } int proc_denyinherit_policy(__unused task_t task) { return(0); } int proc_denyselfset_policy(__unused task_t task) { return(0); } /* HW GPU access related routines */ int proc_get_task_selfgpuacc_deny(void) { task_t task = current_task(); #ifdef NOTYET thread_t thread = current_thread(); #endif /* NOTYET */ if (((task->ext_appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_NOGPU) != 0) || (task->ext_appliedstate.hw_gpu == TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS)) return(TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS); if (((task->appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_NOGPU) != 0) || (task->appliedstate.hw_gpu == TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS)) return(TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS); #ifdef NOTYET /* * Since background dispatch items run in a thread can also be * denied access, we need to make sure there are no unintended * consequences of background dispatch usage. So till this is * hashed out, disable thread level checking. */ if (((thread->ext_appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_NOGPU) != 0) || (thread->ext_appliedstate.hw_gpu == TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS)) return(TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS); if (((thread->appliedstate.hw_bg & TASK_POLICY_BACKGROUND_ATTRIBUTE_NOGPU) != 0) || (thread->appliedstate.hw_gpu == TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS)) return(TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS); #endif /* NOTYET */ return(TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_FULLACCESS); } int proc_apply_task_gpuacc(task_t task, int policy) { task_t self = current_task(); task_lock(task); if (task == self) { task->appliedstate.hw_gpu = policy; task->policystate.hw_gpu = policy; } else { task->ext_appliedstate.hw_gpu = policy; task->ext_policystate.hw_gpu = policy; } task_unlock(task); return(0); } /* Resource usage , CPU realted routines */ int proc_get_task_ruse_cpu(task_t task, uint32_t * policyp, uint32_t * percentagep, uint64_t * intervalp, uint64_t * deadlinep) { int error = 0; task_lock(task); if (task != current_task()) { *policyp = task->ext_policystate.ru_cpu; } else { *policyp = task->policystate.ru_cpu; } error = task_get_cpuusage(task, percentagep, intervalp, deadlinep); return(error); } /* * Currently supported configurations for CPU limits. * * Deadline-based CPU limit Percentage-based CPU limit * PROC_POLICY_RSRCACT_THROTTLE ENOTSUP Task-wide scope only * PROC_POLICY_RSRCACT_SUSPEND Task-wide scope only ENOTSUP * PROC_POLICY_RSRCACT_TERMINATE Task-wide scope only ENOTSUP * PROC_POLICY_RSRCACT_NOTIFY_KQ Task-wide scope only ENOTSUP * PROC_POLICY_RSRCACT_NOTIFY_EXC ENOTSUP Per-thread scope only * * A deadline-based CPU limit is actually a simple wallclock timer - the requested action is performed * after the specified amount of wallclock time has elapsed. * * A percentage-based CPU limit performs the requested action after the specified amount of actual CPU time * has been consumed -- regardless of how much wallclock time has elapsed -- by either the task as an * aggregate entity (so-called "Task-wide" or "Proc-wide" scope, whereby the CPU time consumed by all threads * in the task are added together), or by any one thread in the task (so-called "per-thread" scope). * * We support either deadline != 0 OR percentage != 0, but not both. The original intention in having them * share an API was to use actual CPU time as the basis of the deadline-based limit (as in: perform an action * after I have used some amount of CPU time; this is different than the recurring percentage/interval model) * but the potential consumer of the API at the time was insisting on wallclock time instead. * * Currently, requesting notification via an exception is the only way to get per-thread scope for a * CPU limit. All other types of notifications force task-wide scope for the limit. */ int proc_set_task_ruse_cpu(task_t task, uint32_t policy, uint32_t percentage, uint64_t interval, uint64_t deadline) { int error = 0; int scope; /* * Enforce the matrix of supported configurations for policy, percentage, and deadline. */ switch (policy) { // If no policy is explicitly given, the default is to throttle. case TASK_POLICY_RESOURCE_ATTRIBUTE_NONE: case TASK_POLICY_RESOURCE_ATTRIBUTE_THROTTLE: if (deadline != 0) return (ENOTSUP); scope = TASK_RUSECPU_FLAGS_PROC_LIMIT; break; case TASK_POLICY_RESOURCE_ATTRIBUTE_SUSPEND: case TASK_POLICY_RESOURCE_ATTRIBUTE_TERMINATE: case TASK_POLICY_RESOURCE_ATTRIBUTE_NOTIFY_KQ: if (percentage != 0) return (ENOTSUP); scope = TASK_RUSECPU_FLAGS_DEADLINE; break; case TASK_POLICY_RESOURCE_ATTRIBUTE_NOTIFY_EXC: if (deadline != 0) return (ENOTSUP); scope = TASK_RUSECPU_FLAGS_PERTHR_LIMIT; break; default: return (EINVAL); } task_lock(task); if (task != current_task()) { task->ext_policystate.ru_cpu = policy; } else { task->policystate.ru_cpu = policy; } error = task_set_cpuusage(task, percentage, interval, deadline, scope); task_unlock(task); return(error); } int proc_clear_task_ruse_cpu(task_t task) { int error = 0; int action; void * bsdinfo = NULL; task_lock(task); if (task != current_task()) { task->ext_policystate.ru_cpu = TASK_POLICY_RESOURCE_ATTRIBUTE_DEFAULT; } else { task->policystate.ru_cpu = TASK_POLICY_RESOURCE_ATTRIBUTE_DEFAULT; } error = task_clear_cpuusage_locked(task); if (error != 0) goto out; action = task->ext_appliedstate.ru_cpu; if (task->ext_appliedstate.ru_cpu != TASK_POLICY_RESOURCE_ATTRIBUTE_NONE) { /* reset action */ task->ext_appliedstate.ru_cpu = TASK_POLICY_RESOURCE_ATTRIBUTE_NONE; } if (action != TASK_POLICY_RESOURCE_ATTRIBUTE_NONE) { bsdinfo = task->bsd_info; task_unlock(task); proc_restore_resource_actions(bsdinfo, TASK_POLICY_CPU_RESOURCE_USAGE, action); goto out1; } out: task_unlock(task); out1: return(error); } /* used to apply resource limit related actions */ static int task_apply_resource_actions(task_t task, int type) { int action = TASK_POLICY_RESOURCE_ATTRIBUTE_NONE; void * bsdinfo = NULL; switch (type) { case TASK_POLICY_CPU_RESOURCE_USAGE: break; case TASK_POLICY_WIREDMEM_RESOURCE_USAGE: case TASK_POLICY_VIRTUALMEM_RESOURCE_USAGE: case TASK_POLICY_DISK_RESOURCE_USAGE: case TASK_POLICY_NETWORK_RESOURCE_USAGE: case TASK_POLICY_POWER_RESOURCE_USAGE: return(0); default: return(1); }; /* only cpu actions for now */ task_lock(task); if (task->ext_appliedstate.ru_cpu == TASK_POLICY_RESOURCE_ATTRIBUTE_NONE) { /* apply action */ task->ext_appliedstate.ru_cpu = task->ext_policystate.ru_cpu; action = task->ext_appliedstate.ru_cpu; } else { action = task->ext_appliedstate.ru_cpu; } if (action != TASK_POLICY_RESOURCE_ATTRIBUTE_NONE) { bsdinfo = task->bsd_info; task_unlock(task); proc_apply_resource_actions(bsdinfo, TASK_POLICY_CPU_RESOURCE_USAGE, action); } else task_unlock(task); return(0); } /* For ledger hookups */ static int task_get_cpuusage(task_t task, uint32_t * percentagep, uint64_t * intervalp, uint64_t * deadlinep) { *percentagep = task->rusage_cpu_percentage; *intervalp = task->rusage_cpu_interval; *deadlinep = task->rusage_cpu_deadline; return(0); } int task_set_cpuusage(task_t task, uint64_t percentage, uint64_t interval, uint64_t deadline, int scope) { uint64_t abstime = 0; uint64_t save_abstime = 0; uint64_t limittime = 0; thread_t thread; lck_mtx_assert(&task->lock, LCK_MTX_ASSERT_OWNED); /* By default, refill once per second */ if (interval == 0) interval = NSEC_PER_SEC; if (percentage != 0) { if (percentage > 100) percentage = 100; limittime = (interval * percentage)/ 100; nanoseconds_to_absolutetime(limittime, &abstime); if (scope == TASK_RUSECPU_FLAGS_PERTHR_LIMIT) { /* * A per-thread CPU limit on a task generates an exception * (LEDGER_ACTION_EXCEPTION) if any one thread in the task * exceeds the limit. */ task->rusage_cpu_flags |= TASK_RUSECPU_FLAGS_PERTHR_LIMIT; task->rusage_cpu_perthr_percentage = percentage; task->rusage_cpu_perthr_interval = interval; queue_iterate(&task->threads, thread, thread_t, task_threads) { set_astledger(thread); } } else if (scope == TASK_RUSECPU_FLAGS_PROC_LIMIT) { /* * Currently, a proc-wide CPU limit always blocks if the limit is * exceeded (LEDGER_ACTION_BLOCK). */ task->rusage_cpu_flags |= TASK_RUSECPU_FLAGS_PROC_LIMIT; task->rusage_cpu_percentage = percentage; task->rusage_cpu_interval = interval; ledger_set_limit(task->ledger, task_ledgers.cpu_time, abstime); ledger_set_period(task->ledger, task_ledgers.cpu_time, interval); ledger_set_action(task->ledger, task_ledgers.cpu_time, LEDGER_ACTION_BLOCK); } } if (deadline != 0) { assert(scope == TASK_RUSECPU_FLAGS_DEADLINE); /* if already in use, cancel and wait for it to cleanout */ if (task->rusage_cpu_callt != NULL) { task_unlock(task); thread_call_cancel_wait(task->rusage_cpu_callt); task_lock(task); } if (task->rusage_cpu_callt == NULL) { task->rusage_cpu_callt = thread_call_allocate_with_priority(task_action_cpuusage, (thread_call_param_t)task, THREAD_CALL_PRIORITY_KERNEL); } /* setup callout */ if (task->rusage_cpu_callt != 0) { task->rusage_cpu_flags |= TASK_RUSECPU_FLAGS_DEADLINE; task->rusage_cpu_deadline = deadline; nanoseconds_to_absolutetime(deadline, &abstime); save_abstime = abstime; clock_absolutetime_interval_to_deadline(save_abstime, &abstime); thread_call_enter_delayed(task->rusage_cpu_callt, abstime); } } return(0); } int task_clear_cpuusage(task_t task) { int retval = 0; task_lock(task); retval = task_clear_cpuusage_locked(task); task_unlock(task); return(retval); } int task_clear_cpuusage_locked(task_t task) { thread_call_t savecallt; thread_t thread; /* cancel percentage handling if set */ if (task->rusage_cpu_flags & TASK_RUSECPU_FLAGS_PROC_LIMIT) { task->rusage_cpu_flags &= ~TASK_RUSECPU_FLAGS_PROC_LIMIT; ledger_set_limit(task->ledger, task_ledgers.cpu_time, LEDGER_LIMIT_INFINITY); task->rusage_cpu_percentage = 0; task->rusage_cpu_interval = 0; } if (task->rusage_cpu_flags & TASK_RUSECPU_FLAGS_PERTHR_LIMIT) { task->rusage_cpu_flags &= ~TASK_RUSECPU_FLAGS_PERTHR_LIMIT; queue_iterate(&task->threads, thread, thread_t, task_threads) { set_astledger(thread); } task->rusage_cpu_perthr_percentage = 0; task->rusage_cpu_perthr_interval = 0; } /* cancel deadline handling if set */ if (task->rusage_cpu_flags & TASK_RUSECPU_FLAGS_DEADLINE) { task->rusage_cpu_flags &= ~TASK_RUSECPU_FLAGS_DEADLINE; if (task->rusage_cpu_callt != 0) { savecallt = task->rusage_cpu_callt; task->rusage_cpu_callt = NULL; task->rusage_cpu_deadline = 0; task_unlock(task); thread_call_cancel_wait(savecallt); thread_call_free(savecallt); task_lock(task); } } return(0); } /* called by ledger unit to enforce action due to resource usage criteria being met */ void task_action_cpuusage(thread_call_param_t param0, __unused thread_call_param_t param1) { task_t task = (task_t)param0; (void)task_apply_resource_actions(task, TASK_POLICY_CPU_RESOURCE_USAGE); return; } #if CONFIG_EMBEDDED /* return the appstate of a task */ int proc_lf_getappstate(task_t task) { return(task->appstate); } /* set appstate of a task and apply approp actions */ int proc_lf_setappstate(task_t task, int state) { int ret = 0, oldstate; kern_return_t kret = KERN_SUCCESS; int applywatch = 0, setbg = 0, setnetbg = 0; int sethib_suspend = 0, sethib_resume=0; if (state == TASK_APPSTATE_NONE) goto out; /* valid states? */ switch (state) { case TASK_APPSTATE_ACTIVE: case TASK_APPSTATE_BACKGROUND: case TASK_APPSTATE_NONUI: case TASK_APPSTATE_INACTIVE: break; default: ret = EINVAL; goto out; } task_lock(task); oldstate = task->appstate; if (oldstate == state) { /* no changes */ goto out1; } switch(oldstate) { case TASK_APPSTATE_ACTIVE: switch(state) { case TASK_APPSTATE_BACKGROUND: /* moving from active to app background */ task->ext_policystate.hw_bg = TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL; proc_apply_bgtaskpolicy_internal(task, 1, 1); /* watchers need update */ applywatch = 1; setbg = 1; /* set network part */ setnetbg = 1; break; case TASK_APPSTATE_NONUI: /* set no graphics */ task->ext_policystate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; task->ext_appliedstate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; break; case TASK_APPSTATE_INACTIVE: /* suspend the process */ kret = task_pidsuspend_locked(task); if (kret != KERN_SUCCESS) ret = EINVAL; else sethib_suspend = 1; break; } break; case TASK_APPSTATE_BACKGROUND: switch(state) { /* watchers need update */ applywatch = 1; setbg = 0; /* set network part */ setnetbg = 1; case TASK_APPSTATE_ACTIVE: /* remove app background */ ret = proc_restore_bgtaskpolicy_internal(task, 1, 1, BASEPRI_DEFAULT); /* going from BG to active */ break; case TASK_APPSTATE_NONUI: /* remove app background + no graphics */ task->ext_policystate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; task->ext_appliedstate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; ret = proc_restore_bgtaskpolicy_internal(task, 1, 1, BASEPRI_DEFAULT); break; case TASK_APPSTATE_INACTIVE: /* suspend and then remove app background */ kret = task_pidsuspend_locked(task); if (kret != KERN_SUCCESS) { ret = EINVAL; } else { ret = proc_restore_bgtaskpolicy_internal(task, 1, 1, BASEPRI_DEFAULT); sethib_suspend = 1; } break; } break; case TASK_APPSTATE_NONUI: switch(state) { case TASK_APPSTATE_ACTIVE: /* restore graphics access */ task->ext_policystate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; task->ext_appliedstate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_DEFAULT; break; case TASK_APPSTATE_BACKGROUND: /* set app background */ task->ext_policystate.hw_bg = TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL; ret = proc_apply_bgtaskpolicy_internal(task, 1, 1); if (ret == 0) { task->ext_policystate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_DEFAULT; task->ext_appliedstate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_DEFAULT; } /* watchers need update */ applywatch = 1; setbg = 1; /* set network part */ setnetbg = 1; break; case TASK_APPSTATE_INACTIVE: /* suspend & restore graphics access */ kret = task_pidsuspend_locked(task); if (kret != KERN_SUCCESS) { ret = EINVAL; } else { ret = proc_restore_bgtaskpolicy_internal(task, 1, 1, BASEPRI_DEFAULT); task->ext_policystate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_DEFAULT; task->ext_appliedstate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_DEFAULT; sethib_suspend = 1; } break; } break; case TASK_APPSTATE_INACTIVE: switch(state) { case TASK_APPSTATE_ACTIVE: /* resume process */ /* going from inactive to active */ break; case TASK_APPSTATE_BACKGROUND: task->ext_policystate.hw_bg = TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL; ret = proc_apply_bgtaskpolicy_internal(task, 1, 1); /* put in app background & resume process */ /* watchers need update */ applywatch = 1; setbg = 1; /* set network part */ setnetbg = 1; break; case TASK_APPSTATE_NONUI: /* remove graphics access and resume */ task->ext_policystate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; task->ext_appliedstate.hw_gpu = TASK_POLICY_HWACCESS_GPU_ATTRIBUTE_NOACCESS; break; } /* pidresume does drop task lock,so no need to have locked version */ task_unlock(task); kret = task_pidresume(task); task_lock(task); sethib_resume = 1; break; } /* set the new app state on the task */ task->appstate = state; out1: task_unlock(task); if (setnetbg != 0) { /* apply network background */ if (setbg != 0) proc_apply_task_networkbg_internal(task->bsd_info, NULL); else proc_restore_task_networkbg_internal(task->bsd_info, NULL); } #if CONFIG_MEMORYSTATUS if (sethib_suspend != 0) memorystatus_on_suspend(proc_pid(task->bsd_info)); if (sethib_resume != 0) memorystatus_on_resume(proc_pid(task->bsd_info)); #endif /* CONFIG_MEMORYSTATUS */ /* if watchers need update, safe point to do that */ if (applywatch != 0) apply_appstate_watchers(task, setbg); out: return(ret); } static void task_watch_lock(void) { lck_mtx_lock(&task_watch_mtx); } static void task_watch_unlock(void) { lck_mtx_unlock(&task_watch_mtx); } static void add_taskwatch_locked(task_t task, task_watch_t * twp) { queue_enter(&task->task_watchers, twp, task_watch_t *, tw_links); task->num_taskwatchers++; } static void remove_taskwatch_locked(task_t task, task_watch_t * twp) { queue_remove(&task->task_watchers, twp, task_watch_t *, tw_links); task->num_taskwatchers--; } int proc_lf_pidbind(task_t curtask, uint64_t tid, task_t target_task, int bind) { thread_t self = current_thread(); thread_t target_thread = NULL; int selfset = 0, ret = 0, setbg = 0; task_watch_t *twp = NULL; task_t task = TASK_NULL; if ((tid == 0) || (tid == self->thread_id)) { selfset = 1; target_thread = self; thread_reference(target_thread); } else { task_lock(curtask); target_thread = task_findtid(curtask, tid); if (target_thread != NULL) thread_reference(target_thread); else { ret = ESRCH; goto out; } task_unlock(curtask); } if (bind != 0) { /* task is still active ? */ task_lock(target_task); if (target_task->active == 0) { task_unlock(target_task); ret = ESRCH; goto out; } task_unlock(target_task); twp = (task_watch_t *)kalloc(sizeof(task_watch_t)); if (twp == NULL) { task_watch_unlock(); ret = ENOMEM; goto out; } bzero(twp, sizeof(task_watch_t)); task_watch_lock(); if (target_thread->taskwatch != NULL){ /* already bound to another task */ task_watch_unlock(); kfree(twp, sizeof(task_watch_t)); ret = EBUSY; goto out; } task_reference(target_task); twp->tw_task = target_task; /* holds the task reference */ twp->tw_thread = target_thread; /* holds the thread reference */ twp->tw_state = target_task->appstate; twp->tw_importance = target_thread->importance; add_taskwatch_locked(target_task, twp); target_thread->taskwatch = twp; if (target_task->appstate == TASK_APPSTATE_BACKGROUND) setbg = 1; task_watch_unlock(); if (setbg != 0) { set_thread_appbg(target_thread, setbg, INT_MIN); } /* retain the thread reference as it is in twp */ target_thread = NULL; } else { /* unbind */ task_watch_lock(); if ((twp = target_thread->taskwatch) != NULL) { task = twp->tw_task; target_thread->taskwatch = NULL; remove_taskwatch_locked(task, twp); task_watch_unlock(); task_deallocate(task); /* drop task ref in twp */ set_thread_appbg(target_thread, 0, twp->tw_importance); thread_deallocate(target_thread); /* drop thread ref in twp */ kfree(twp, sizeof(task_watch_t)); } else { task_watch_unlock(); ret = 0; /* return success if it not alredy bound */ goto out; } } out: if (target_thread != NULL) thread_deallocate(target_thread); /* drop thread ref acquired in this routine */ return(ret); } static void set_thread_appbg(thread_t thread, int setbg,int importance) { /* TBD: ensure the proc for network is fine */ if (setbg == 0) { restore_bgthreadpolicy_locked(thread, 0, importance); proc_restore_task_networkbg_internal(thread->task->bsd_info, thread); } else { apply_bgthreadpolicy_external(thread); proc_apply_task_networkbg_internal(thread->task->bsd_info, thread); } } static void apply_appstate_watchers(task_t task, int setbg) { int numwatchers = 0, i, j; thread_watchlist_t * threadlist; task_watch_t * twp; retry: /* if no watchers on the list return */ if ((numwatchers = task->num_taskwatchers) == 0) return; threadlist = (thread_watchlist_t *)kalloc(numwatchers*sizeof(thread_watchlist_t)); if (threadlist == NULL) return; bzero(threadlist, numwatchers*sizeof(thread_watchlist_t)); task_watch_lock(); /*serialize application of app state changes */ if (task->watchapplying != 0) { lck_mtx_sleep(&task_watch_mtx, LCK_SLEEP_DEFAULT, &task->watchapplying, THREAD_UNINT); task_watch_unlock(); kfree(threadlist, numwatchers*sizeof(thread_watchlist_t)); goto retry; } if (numwatchers != task->num_taskwatchers) { task_watch_unlock(); kfree(threadlist, numwatchers*sizeof(thread_watchlist_t)); goto retry; } task->watchapplying = 1; i = 0; queue_iterate(&task->task_watchers, twp, task_watch_t *, tw_links) { threadlist[i].thread = twp->tw_thread; thread_reference(threadlist[i].thread); if (setbg != 0) { twp->tw_importance = twp->tw_thread->importance; threadlist[i].importance = INT_MIN; } else threadlist[i].importance = twp->tw_importance; i++; if (i > numwatchers) break; } task_watch_unlock(); for (j = 0; j< i; j++) { set_thread_appbg(threadlist[j].thread, setbg, threadlist[j].importance); thread_deallocate(threadlist[j].thread); } kfree(threadlist, numwatchers*sizeof(thread_watchlist_t)); task_watch_lock(); task->watchapplying = 0; thread_wakeup_one(&task->watchapplying); task_watch_unlock(); } void thead_remove_taskwatch(thread_t thread) { task_watch_t * twp; int importance = 0; task_watch_lock(); if ((twp = thread->taskwatch) != NULL) { thread->taskwatch = NULL; remove_taskwatch_locked(twp->tw_task, twp); } task_watch_unlock(); if (twp != NULL) { thread_deallocate(twp->tw_thread); task_deallocate(twp->tw_task); importance = twp->tw_importance; kfree(twp, sizeof(task_watch_t)); /* remove the thread and networkbg */ set_thread_appbg(thread, 0, importance); } } void task_removewatchers(task_t task) { int numwatchers = 0, i, j; task_watch_t ** twplist = NULL; task_watch_t * twp = NULL; retry: if ((numwatchers = task->num_taskwatchers) == 0) return; twplist = (task_watch_t **)kalloc(numwatchers*sizeof(task_watch_t *)); if (twplist == NULL) return; bzero(twplist, numwatchers*sizeof(task_watch_t *)); task_watch_lock(); if (task->num_taskwatchers == 0) { task_watch_unlock(); goto out; } if (numwatchers != task->num_taskwatchers) { task_watch_unlock(); kfree(twplist, numwatchers*sizeof(task_watch_t *)); numwatchers = 0; goto retry; } i = 0; while((twp = (task_watch_t *)dequeue_head(&task->task_watchers)) != NULL) { twplist[i] = twp; task->num_taskwatchers--; /* * Since the linkage is removed and thead state cleanup is already set up, * remove the refernce from the thread. */ twp->tw_thread->taskwatch = NULL; /* removed linkage, clear thread holding ref */ i++; if ((task->num_taskwatchers == 0) || (i > numwatchers)) break; } task_watch_unlock(); for (j = 0; j< i; j++) { twp = twplist[j]; /* remove thread and network bg */ set_thread_appbg(twp->tw_thread, 0, twp->tw_importance); thread_deallocate(twp->tw_thread); task_deallocate(twp->tw_task); kfree(twp, sizeof(task_watch_t)); } out: kfree(twplist, numwatchers*sizeof(task_watch_t *)); } #endif /* CONFIG_EMBEDDED */ int proc_disable_task_apptype(task_t task, int policy_subtype) { void * bsdinfo = NULL; int ret = 0; int setbg = 0; #if !CONFIG_EMBEDDED int maxpri = BASEPRI_DEFAULT; #endif /* !CONFIG_EMBEDDED */ task_lock(task); if (task->ext_policystate.apptype != policy_subtype) { ret = EINVAL; goto out; } #if !CONFIG_EMBEDDED switch (task->role) { case TASK_FOREGROUND_APPLICATION: maxpri = BASEPRI_FOREGROUND; break; case TASK_BACKGROUND_APPLICATION: maxpri = BASEPRI_BACKGROUND; break; default: maxpri = BASEPRI_DEFAULT; } #endif /* !CONFIG_EMBEDDED */ /* TAL apps are cleared with BG handling on first foreground application */ if (task->ext_appliedstate.apptype != PROC_POLICY_OSX_APPTYPE_NONE) { switch (task->ext_appliedstate.apptype) { #if !CONFIG_EMBEDDED case PROC_POLICY_OSX_APPTYPE_TAL: /* disable foreground/background handling */ task->ext_appliedstate.apptype = PROC_POLICY_OSX_APPTYPE_NONE; /* external BG application removal */ proc_restore_bgtaskpolicy_internal(task, 1, 1, maxpri); bsdinfo = task->bsd_info; setbg = 0; break; case PROC_POLICY_OSX_APPTYPE_DBCLIENT: /* disable foreground/background handling */ task->ext_appliedstate.apptype = PROC_POLICY_OSX_APPTYPE_NONE; /* internal BG application removal */ proc_restore_bgtaskpolicy_internal(task, 1, 0, maxpri); bsdinfo = task->bsd_info; setbg = 0; break; #endif /* !CONFIG_EMBEDDED */ default: ret = EINVAL; break; } } else { ret = EINVAL; } out: task_unlock(task); /* if backgrounding action ... */ if (bsdinfo != NULL) proc_set_task_networkbg(bsdinfo, setbg); return(ret); } int proc_enable_task_apptype(task_t task, int policy_subtype) { void * bsdinfo = NULL; int setbg = 0; int ret = 0; task_lock(task); if (task->ext_policystate.apptype != policy_subtype) { ret = EINVAL; goto out; } if (task->ext_appliedstate.apptype == PROC_POLICY_OSX_APPTYPE_NONE) { switch (task->ext_policystate.apptype) { #if !CONFIG_EMBEDDED case PROC_POLICY_OSX_APPTYPE_TAL: /* TAL policy is activated again */ task->ext_appliedstate.apptype = task->ext_policystate.apptype; if (task->role == TASK_BACKGROUND_APPLICATION) { if (task->role == TASK_BACKGROUND_APPLICATION) { proc_apply_bgtaskpolicy_internal(task, 1, 1); bsdinfo = task->bsd_info; setbg = 1; } } ret = 0; break; #endif /* !CONFIG_EMBEDDED */ default: ret = EINVAL; } } else ret = EINVAL; out: task_unlock(task); /* if backgrounding action ... */ if (bsdinfo != NULL) proc_set_task_networkbg(bsdinfo, setbg); return(ret); } #if CONFIG_EMBEDDED int proc_setthread_saved_importance(thread_t thread, int importance) { if ((thread->task->ext_appliedstate.apptype == PROC_POLICY_IOS_APPLE_DAEMON) && (thread->appliedstate.hw_bg == TASK_POLICY_BACKGROUND_ATTRIBUTE_ALL)) { /* the thread is still backgrounded , save the importance for restore time */ thread->saved_importance = importance; return(1); } else return(0); } #endif /* CONFIG_EMBEDDED */