]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/kern/coalition.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / osfmk / kern / coalition.c
index fd2afa85800b83596fbdbde5d1129377d36c1c67..e77ca4e68fb07a21094e9246519fe01d82cc32ee 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2013 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2019-2020 Apple 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
  * 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,
@@ -22,7 +22,7 @@
  * 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 <mach/boolean.h>
 
 #include <kern/coalition.h>
+#include <kern/exc_resource.h>
 #include <kern/host.h>
-#include <kern/kalloc.h>
 #include <kern/ledger.h>
 #include <kern/mach_param.h> /* for TASK_CHUNK */
+#if MONOTONIC
+#include <kern/monotonic.h>
+#endif /* MONOTONIC */
+#include <kern/policy_internal.h>
 #include <kern/task.h>
+#include <kern/thread_group.h>
 #include <kern/zalloc.h>
 
 #include <libkern/OSAtomic.h>
 #include <mach/host_priv.h>
 #include <mach/host_special_ports.h>
 
+#include <os/log.h>
+
 #include <sys/errno.h>
 
 /*
  * BSD interface functions
  */
 int coalitions_get_list(int type, struct procinfo_coalinfo *coal_list, int list_sz);
-boolean_t coalition_is_leader(task_t task, int coal_type, coalition_t *coal);
+coalition_t task_get_coalition(task_t task, int type);
+boolean_t coalition_is_leader(task_t task, coalition_t coal);
 task_t coalition_get_leader(coalition_t coal);
 int coalition_get_task_count(coalition_t coal);
 uint64_t coalition_get_page_count(coalition_t coal, int *ntasks);
 int coalition_get_pid_list(coalition_t coal, uint32_t rolemask, int sort_order,
-                                 int *pid_list, int list_sz);
+    int *pid_list, int list_sz);
 
 /* defined in task.c */
 extern ledger_template_t task_ledger_template;
 
+/*
+ * Templates; task template is copied due to potential allocation limits on
+ * task ledgers.
+ */
+ledger_template_t coalition_task_ledger_template = NULL;
+ledger_template_t coalition_ledger_template = NULL;
+
+extern int      proc_selfpid(void);
 /*
  * Coalition zone needs limits. We expect there will be as many coalitions as
  * tasks (same order of magnitude), so use the task zone's limits.
@@ -68,24 +84,23 @@ extern ledger_template_t task_ledger_template;
 #define COALITION_CHUNK TASK_CHUNK
 
 int unrestrict_coalition_syscalls;
+int merge_adaptive_coalitions;
 
-lck_attr_t coalitions_lck_attr;
-lck_grp_t coalitions_lck_grp;
-lck_grp_attr_t coalitions_lck_grp_attr;
+LCK_GRP_DECLARE(coalitions_lck_grp, "coalition");
 
 /* coalitions_list_lock protects coalition_count, coalitions queue, next_coalition_id. */
-decl_lck_mtx_data(static,coalitions_list_lock);
+static LCK_MTX_DECLARE(coalitions_list_lock, &coalitions_lck_grp);
 static uint64_t coalition_count;
 static uint64_t coalition_next_id = 1;
 static queue_head_t coalitions_q;
 
 coalition_t init_coalition[COALITION_NUM_TYPES];
+coalition_t corpse_coalition[COALITION_NUM_TYPES];
 
-zone_t coalition_zone;
-
-static const char *coal_type_str(int type)
+static const char *
+coal_type_str(int type)
 {
-       switch(type) {
+       switch (type) {
        case COALITION_TYPE_RESOURCE:
                return "RESOURCE";
        case COALITION_TYPE_JETSAM:
@@ -159,25 +174,45 @@ static void          i_coal_resource_dealloc(coalition_t coal);
 static kern_return_t i_coal_resource_adopt_task(coalition_t coal, task_t task);
 static kern_return_t i_coal_resource_remove_task(coalition_t coal, task_t task);
 static kern_return_t i_coal_resource_set_taskrole(coalition_t coal,
-                                                task_t task, int role);
+    task_t task, int role);
 static int           i_coal_resource_get_taskrole(coalition_t coal, task_t task);
 static void          i_coal_resource_iterate_tasks(coalition_t coal, void *ctx,
-                                                  void (*callback)(coalition_t, void *, task_t));
+    void (*callback)(coalition_t, void *, task_t));
+
+/*
+ * Ensure COALITION_NUM_THREAD_QOS_TYPES defined in mach/coalition.h still
+ * matches THREAD_QOS_LAST defined in mach/thread_policy.h
+ */
+static_assert(COALITION_NUM_THREAD_QOS_TYPES == THREAD_QOS_LAST);
 
 struct i_resource_coalition {
+       /*
+        * This keeps track of resource utilization of tasks that are no longer active
+        * in the coalition and is updated when a task is removed from the coalition.
+        */
        ledger_t ledger;
        uint64_t bytesread;
        uint64_t byteswritten;
+       uint64_t energy;
        uint64_t gpu_time;
        uint64_t logical_immediate_writes;
        uint64_t logical_deferred_writes;
        uint64_t logical_invalidated_writes;
        uint64_t logical_metadata_writes;
+       uint64_t logical_immediate_writes_to_external;
+       uint64_t logical_deferred_writes_to_external;
+       uint64_t logical_invalidated_writes_to_external;
+       uint64_t logical_metadata_writes_to_external;
+       uint64_t cpu_ptime;
+       uint64_t cpu_time_eqos[COALITION_NUM_THREAD_QOS_TYPES];      /* cpu time per effective QoS class */
+       uint64_t cpu_time_rqos[COALITION_NUM_THREAD_QOS_TYPES];      /* cpu time per requested QoS class */
+       uint64_t cpu_instructions;
+       uint64_t cpu_cycles;
 
        uint64_t task_count;      /* tasks that have started in this coalition */
        uint64_t dead_task_count; /* tasks that have exited in this coalition;
-                                    subtract from task_count to get count
-                                    of "active" tasks */
+                                  *  subtract from task_count to get count
+                                  *  of "active" tasks */
        /*
         * Count the length of time this coalition had at least one active task.
         * This can be a 'denominator' to turn e.g. cpu_time to %cpu.
@@ -186,6 +221,14 @@ struct i_resource_coalition {
        uint64_t time_nonempty;
 
        queue_head_t tasks;         /* List of active tasks in the coalition */
+       /*
+        * This ledger is used for triggering resource exception. For the tracked resources, this is updated
+        * when the member tasks' resource usage changes.
+        */
+       ledger_t resource_monitor_ledger;
+#if CONFIG_PHYS_WRITE_ACCT
+       uint64_t fs_metadata_writes;
+#endif /* CONFIG_PHYS_WRITE_ACCT */
 };
 
 /*
@@ -197,16 +240,17 @@ static void          i_coal_jetsam_dealloc(coalition_t coal);
 static kern_return_t i_coal_jetsam_adopt_task(coalition_t coal, task_t task);
 static kern_return_t i_coal_jetsam_remove_task(coalition_t coal, task_t task);
 static kern_return_t i_coal_jetsam_set_taskrole(coalition_t coal,
-                                              task_t task, int role);
-static int           i_coal_jetsam_get_taskrole(coalition_t coal, task_t task);
+    task_t task, int role);
+int           i_coal_jetsam_get_taskrole(coalition_t coal, task_t task);
 static void          i_coal_jetsam_iterate_tasks(coalition_t coal, void *ctx,
-                                                void (*callback)(coalition_t, void *, task_t));
+    void (*callback)(coalition_t, void *, task_t));
 
 struct i_jetsam_coalition {
        task_t       leader;
        queue_head_t extensions;
        queue_head_t services;
        queue_head_t other;
+       struct thread_group *thread_group;
 };
 
 
@@ -216,30 +260,32 @@ struct i_jetsam_coalition {
 struct coalition {
        uint64_t id;                /* monotonically increasing */
        uint32_t type;
+       uint32_t role;              /* default task role (background, adaptive, interactive, etc) */
        uint32_t ref_count;         /* Number of references to the memory containing this struct */
        uint32_t active_count;      /* Number of members of (tasks in) the
-                                      coalition, plus vouchers referring
-                                      to the coalition */
+                                    *  coalition, plus vouchers referring
+                                    *  to the coalition */
        uint32_t focal_task_count;   /* Number of TASK_FOREGROUND_APPLICATION tasks in the coalition */
        uint32_t nonfocal_task_count; /* Number of TASK_BACKGROUND_APPLICATION tasks in the coalition */
 
        /* coalition flags */
        uint32_t privileged : 1;    /* Members of this coalition may create
-                                      and manage coalitions and may posix_spawn
-                                      processes into selected coalitions */
+                                    *  and manage coalitions and may posix_spawn
+                                    *  processes into selected coalitions */
        /* ast? */
        /* voucher */
        uint32_t termrequested : 1; /* launchd has requested termination when coalition becomes empty */
        uint32_t terminated : 1;    /* coalition became empty and spawns are now forbidden */
        uint32_t reaped : 1;        /* reaped, invisible to userspace, but waiting for ref_count to go to zero */
        uint32_t notified : 1;      /* no-more-processes notification was sent via special port */
-#if defined(DEVELOPMENT) || defined(DEBUG)
+       uint32_t efficient : 1;     /* launchd has marked the coalition as efficient */
+#if DEVELOPMENT || DEBUG
        uint32_t should_notify : 1; /* should this coalition send notifications (default: yes) */
 #endif
 
        queue_chain_t coalitions;   /* global list of coalitions */
 
-       decl_lck_mtx_data(,lock)    /* Coalition lock. */
+       decl_lck_mtx_data(, lock);    /* Coalition lock. */
 
        /* put coalition type-specific structures here */
        union {
@@ -253,7 +299,7 @@ struct coalition {
  * these must be kept in the order specified in coalition.h
  */
 static const struct coalition_type
-s_coalition_types[COALITION_NUM_TYPES] = {
+    s_coalition_types[COALITION_NUM_TYPES] = {
        {
                COALITION_TYPE_RESOURCE,
                1,
@@ -278,6 +324,9 @@ s_coalition_types[COALITION_NUM_TYPES] = {
        },
 };
 
+ZONE_DECLARE(coalition_zone, "coalitions",
+    sizeof(struct coalition), ZC_NOENCRYPT | ZC_ZFREE_CLEARMEM);
+
 #define coal_call(coal, func, ...) \
        (s_coalition_types[(coal)->type].func)(coal, ## __VA_ARGS__)
 
@@ -285,6 +334,192 @@ s_coalition_types[COALITION_NUM_TYPES] = {
 #define coalition_lock(c) do{ lck_mtx_lock(&c->lock); }while(0)
 #define coalition_unlock(c) do{ lck_mtx_unlock(&c->lock); }while(0)
 
+/*
+ * Define the coalition type to track focal tasks.
+ * On embedded, track them using jetsam coalitions since they have associated thread
+ * groups which reflect this property as a flag (and pass it down to CLPC).
+ * On non-embedded platforms, since not all coalitions have jetsam coalitions
+ * track focal counts on the resource coalition.
+ */
+#if !XNU_TARGET_OS_OSX
+#define COALITION_FOCAL_TASKS_ACCOUNTING  COALITION_TYPE_JETSAM
+#else /* !XNU_TARGET_OS_OSX */
+#define COALITION_FOCAL_TASKS_ACCOUNTING  COALITION_TYPE_RESOURCE
+#endif /* !XNU_TARGET_OS_OSX */
+
+
+/*
+ *
+ * Coalition ledger implementation
+ *
+ */
+
+struct coalition_ledger_indices coalition_ledgers =
+{.logical_writes = -1, };
+void __attribute__((noinline)) SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO(int flavor);
+
+ledger_t
+coalition_ledger_get_from_task(task_t task)
+{
+       ledger_t ledger = LEDGER_NULL;
+       coalition_t coal = task->coalition[COALITION_TYPE_RESOURCE];
+
+       if (coal != NULL && (!queue_empty(&task->task_coalition[COALITION_TYPE_RESOURCE]))) {
+               ledger = coal->r.resource_monitor_ledger;
+               ledger_reference(ledger);
+       }
+       return ledger;
+}
+
+
+enum {
+       COALITION_IO_LEDGER_ENABLE,
+       COALITION_IO_LEDGER_DISABLE
+};
+
+void
+coalition_io_monitor_ctl(struct coalition *coalition, uint32_t flags, int64_t limit)
+{
+       ledger_t ledger = coalition->r.resource_monitor_ledger;
+
+       if (flags == COALITION_IO_LEDGER_ENABLE) {
+               /* Configure the logical I/O ledger */
+               ledger_set_limit(ledger, coalition_ledgers.logical_writes, (limit * 1024 * 1024), 0);
+               ledger_set_period(ledger, coalition_ledgers.logical_writes, (COALITION_LEDGER_MONITOR_INTERVAL_SECS * NSEC_PER_SEC));
+       } else if (flags == COALITION_IO_LEDGER_DISABLE) {
+               ledger_disable_refill(ledger, coalition_ledgers.logical_writes);
+               ledger_disable_callback(ledger, coalition_ledgers.logical_writes);
+       }
+}
+
+int
+coalition_ledger_set_logical_writes_limit(struct coalition *coalition, int64_t limit)
+{
+       int error = 0;
+
+       /*  limit = -1 will be used to disable the limit and the callback */
+       if (limit > COALITION_MAX_LOGICAL_WRITES_LIMIT || limit == 0 || limit < -1) {
+               error = EINVAL;
+               goto out;
+       }
+
+       coalition_lock(coalition);
+       if (limit == -1) {
+               coalition_io_monitor_ctl(coalition, COALITION_IO_LEDGER_DISABLE, limit);
+       } else {
+               coalition_io_monitor_ctl(coalition, COALITION_IO_LEDGER_ENABLE, limit);
+       }
+       coalition_unlock(coalition);
+out:
+       return error;
+}
+
+void __attribute__((noinline))
+SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO(int flavor)
+{
+       int pid = proc_selfpid();
+       ledger_amount_t new_limit;
+       task_t task = current_task();
+       struct ledger_entry_info lei;
+       kern_return_t kr;
+       ledger_t ledger;
+       struct coalition *coalition = task->coalition[COALITION_TYPE_RESOURCE];
+
+       assert(coalition != NULL);
+       ledger = coalition->r.resource_monitor_ledger;
+
+       switch (flavor) {
+       case FLAVOR_IO_LOGICAL_WRITES:
+               ledger_get_entry_info(ledger, coalition_ledgers.logical_writes, &lei);
+               trace_resource_violation(RMON_LOGWRITES_VIOLATED, &lei);
+               break;
+       default:
+               goto Exit;
+       }
+
+       os_log(OS_LOG_DEFAULT, "Coalition [%lld] caught causing excessive I/O (flavor: %d). Task I/O: %lld MB. [Limit : %lld MB per %lld secs]. Triggered by process [%d]\n",
+           coalition->id, flavor, (lei.lei_balance / (1024 * 1024)), (lei.lei_limit / (1024 * 1024)),
+           (lei.lei_refill_period / NSEC_PER_SEC), pid);
+
+       kr = send_resource_violation(send_disk_writes_violation, task, &lei, kRNFlagsNone);
+       if (kr) {
+               os_log(OS_LOG_DEFAULT, "ERROR %#x returned from send_resource_violation(disk_writes, ...)\n", kr);
+       }
+
+       /*
+        * Continue to monitor the coalition after it hits the initital limit, but increase
+        * the limit exponentially so that we don't spam the listener.
+        */
+       new_limit = (lei.lei_limit / 1024 / 1024) * 4;
+       coalition_lock(coalition);
+       if (new_limit > COALITION_MAX_LOGICAL_WRITES_LIMIT) {
+               coalition_io_monitor_ctl(coalition, COALITION_IO_LEDGER_DISABLE, -1);
+       } else {
+               coalition_io_monitor_ctl(coalition, COALITION_IO_LEDGER_ENABLE, new_limit);
+       }
+       coalition_unlock(coalition);
+
+Exit:
+       return;
+}
+
+void
+coalition_io_rate_exceeded(int warning, const void *param0, __unused const void *param1)
+{
+       if (warning == 0) {
+               SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO((int)param0);
+       }
+}
+
+void
+init_coalition_ledgers(void)
+{
+       ledger_template_t t;
+       assert(coalition_ledger_template == NULL);
+
+       if ((t = ledger_template_create("Per-coalition ledgers")) == NULL) {
+               panic("couldn't create coalition ledger template");
+       }
+
+       coalition_ledgers.logical_writes = ledger_entry_add(t, "logical_writes", "res", "bytes");
+
+       if (coalition_ledgers.logical_writes < 0) {
+               panic("couldn't create entries for coaliton ledger template");
+       }
+
+       ledger_set_callback(t, coalition_ledgers.logical_writes, coalition_io_rate_exceeded, (void *)FLAVOR_IO_LOGICAL_WRITES, NULL);
+       ledger_template_complete(t);
+
+       coalition_task_ledger_template = ledger_template_copy(task_ledger_template, "Coalition task ledgers");
+
+       if (coalition_task_ledger_template == NULL) {
+               panic("couldn't create coalition task ledger template");
+       }
+
+       ledger_template_complete(coalition_task_ledger_template);
+
+       coalition_ledger_template = t;
+}
+
+void
+coalition_io_ledger_update(task_t task, int32_t flavor, boolean_t is_credit, uint32_t io_size)
+{
+       ledger_t ledger;
+       coalition_t coal = task->coalition[COALITION_TYPE_RESOURCE];
+
+       assert(coal != NULL);
+       ledger = coal->r.resource_monitor_ledger;
+       if (LEDGER_VALID(ledger)) {
+               if (flavor == FLAVOR_IO_LOGICAL_WRITES) {
+                       if (is_credit) {
+                               ledger_credit(ledger, coalition_ledgers.logical_writes, io_size);
+                       } else {
+                               ledger_debit(ledger, coalition_ledgers.logical_writes, io_size);
+                       }
+               }
+       }
+}
+
 static void
 coalition_notify_user(uint64_t id, uint32_t flags)
 {
@@ -297,6 +532,7 @@ coalition_notify_user(uint64_t id, uint32_t flags)
        }
 
        coalition_notification(user_port, id, flags);
+       ipc_port_release_send(user_port);
 }
 
 /*
@@ -309,10 +545,17 @@ i_coal_resource_init(coalition_t coal, boolean_t privileged)
 {
        (void)privileged;
        assert(coal && coal->type == COALITION_TYPE_RESOURCE);
-       coal->r.ledger = ledger_instantiate(task_ledger_template,
-                                           LEDGER_CREATE_ACTIVE_ENTRIES);
-       if (coal->r.ledger == NULL)
+       coal->r.ledger = ledger_instantiate(coalition_task_ledger_template,
+           LEDGER_CREATE_ACTIVE_ENTRIES);
+       if (coal->r.ledger == NULL) {
+               return KERN_RESOURCE_SHORTAGE;
+       }
+
+       coal->r.resource_monitor_ledger = ledger_instantiate(coalition_ledger_template,
+           LEDGER_CREATE_ACTIVE_ENTRIES);
+       if (coal->r.resource_monitor_ledger == NULL) {
                return KERN_RESOURCE_SHORTAGE;
+       }
 
        queue_init(&coal->r.tasks);
 
@@ -323,7 +566,9 @@ static void
 i_coal_resource_dealloc(coalition_t coal)
 {
        assert(coal && coal->type == COALITION_TYPE_RESOURCE);
+
        ledger_dereference(coal->r.ledger);
+       ledger_dereference(coal->r.resource_monitor_ledger);
 }
 
 static kern_return_t
@@ -339,8 +584,8 @@ i_coal_resource_adopt_task(coalition_t coal, task_t task)
 
        if (cr->task_count < cr->dead_task_count) {
                panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)",
-                     __func__, coal, coal->id, coal_type_str(coal->type),
-                     cr->task_count, cr->dead_task_count);
+                   __func__, coal, coal->id, coal_type_str(coal->type),
+                   cr->task_count, cr->dead_task_count);
        }
 
        /* If moving from 0->1 active tasks */
@@ -352,8 +597,8 @@ i_coal_resource_adopt_task(coalition_t coal, task_t task)
        enqueue_tail(&cr->tasks, &task->task_coalition[COALITION_TYPE_RESOURCE]);
 
        coal_dbg("Added PID:%d to id:%llu, task_count:%llu, dead_count:%llu, nonempty_time:%llu",
-                task_pid(task), coal->id, cr->task_count, cr->dead_task_count,
-                cr->last_became_nonempty_time);
+           task_pid(task), coal->id, cr->task_count, cr->dead_task_count,
+           cr->last_became_nonempty_time);
 
        return KERN_SUCCESS;
 }
@@ -376,7 +621,7 @@ i_coal_resource_remove_task(coalition_t coal, task_t task)
 
        if (cr->task_count < cr->dead_task_count) {
                panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)",
-                     __func__, coal, coal->id, coal_type_str(coal->type), cr->task_count, cr->dead_task_count);
+                   __func__, coal, coal->id, coal_type_str(coal->type), cr->task_count, cr->dead_task_count);
        }
 
        /* If moving from 1->0 active tasks */
@@ -386,28 +631,55 @@ i_coal_resource_remove_task(coalition_t coal, task_t task)
                cr->time_nonempty += last_time_nonempty;
        }
 
-       ledger_rollup(cr->ledger, task->ledger);
-       cr->bytesread += task->task_io_stats->disk_reads.size;
-       cr->byteswritten += task->task_io_stats->total_io.size - task->task_io_stats->disk_reads.size;
-       cr->gpu_time += task_gpu_utilisation(task);
-       cr->logical_immediate_writes += task->task_immediate_writes;    
-       cr->logical_deferred_writes += task->task_deferred_writes;
-       cr->logical_invalidated_writes += task->task_invalidated_writes;
-       cr->logical_metadata_writes += task->task_metadata_writes;
+       /* Do not roll up for exec'd task or exec copy task */
+       if (!task_is_exec_copy(task) && !task_did_exec(task)) {
+               ledger_rollup(cr->ledger, task->ledger);
+               cr->bytesread += task->task_io_stats->disk_reads.size;
+               cr->byteswritten += task->task_io_stats->total_io.size - task->task_io_stats->disk_reads.size;
+#if defined(__x86_64__)
+               cr->gpu_time += task_gpu_utilisation(task);
+#endif /* defined(__x86_64__) */
+
+#if defined(__arm__) || defined(__arm64__)
+               cr->energy += task_energy(task);
+#endif /* defined(__arm__) || defined(__arm64__) */
+
+               cr->logical_immediate_writes += task->task_writes_counters_internal.task_immediate_writes;
+               cr->logical_deferred_writes += task->task_writes_counters_internal.task_deferred_writes;
+               cr->logical_invalidated_writes += task->task_writes_counters_internal.task_invalidated_writes;
+               cr->logical_metadata_writes += task->task_writes_counters_internal.task_metadata_writes;
+               cr->logical_immediate_writes_to_external += task->task_writes_counters_external.task_immediate_writes;
+               cr->logical_deferred_writes_to_external += task->task_writes_counters_external.task_deferred_writes;
+               cr->logical_invalidated_writes_to_external += task->task_writes_counters_external.task_invalidated_writes;
+               cr->logical_metadata_writes_to_external += task->task_writes_counters_external.task_metadata_writes;
+#if CONFIG_PHYS_WRITE_ACCT
+               cr->fs_metadata_writes += task->task_fs_metadata_writes;
+#endif /* CONFIG_PHYS_WRITE_ACCT */
+               cr->cpu_ptime += task_cpu_ptime(task);
+               task_update_cpu_time_qos_stats(task, cr->cpu_time_eqos, cr->cpu_time_rqos);
+#if MONOTONIC
+               uint64_t counts[MT_CORE_NFIXED] = {};
+               (void)mt_fixed_task_counts(task, counts);
+               cr->cpu_cycles += counts[MT_CORE_CYCLES];
+#if defined(MT_CORE_INSTRS)
+               cr->cpu_instructions += counts[MT_CORE_INSTRS];
+#endif /* defined(MT_CORE_INSTRS) */
+#endif /* MONOTONIC */
+       }
 
        /* remove the task from the coalition's list */
        remqueue(&task->task_coalition[COALITION_TYPE_RESOURCE]);
        queue_chain_init(task->task_coalition[COALITION_TYPE_RESOURCE]);
 
        coal_dbg("removed PID:%d from id:%llu, task_count:%llu, dead_count:%llu",
-                task_pid(task), coal->id, cr->task_count, cr->dead_task_count);
+           task_pid(task), coal->id, cr->task_count, cr->dead_task_count);
 
        return KERN_SUCCESS;
 }
 
 static kern_return_t
 i_coal_resource_set_taskrole(__unused coalition_t coal,
-                           __unused task_t task, __unused int role)
+    __unused task_t task, __unused int role)
 {
        return KERN_SUCCESS;
 }
@@ -420,8 +692,9 @@ i_coal_resource_get_taskrole(__unused coalition_t coal, __unused task_t task)
        assert(coal && coal->type == COALITION_TYPE_RESOURCE);
 
        qe_foreach_element(t, &coal->r.tasks, task_coalition[COALITION_TYPE_RESOURCE]) {
-               if (t == task)
+               if (t == task) {
                        return COALITION_TASKROLE_UNDEF;
+               }
        }
 
        return -1;
@@ -434,21 +707,35 @@ i_coal_resource_iterate_tasks(coalition_t coal, void *ctx, void (*callback)(coal
        assert(coal && coal->type == COALITION_TYPE_RESOURCE);
 
        qe_foreach_element(t, &coal->r.tasks, task_coalition[COALITION_TYPE_RESOURCE])
-               callback(coal, ctx, t);
+       callback(coal, ctx, t);
 }
 
+#if CONFIG_PHYS_WRITE_ACCT
+extern uint64_t kernel_pm_writes;
+#endif /* CONFIG_PHYS_WRITE_ACCT */
+
 kern_return_t
 coalition_resource_usage_internal(coalition_t coal, struct coalition_resource_usage *cru_out)
 {
        kern_return_t kr;
        ledger_amount_t credit, debit;
+       int i;
 
-       if (coal->type != COALITION_TYPE_RESOURCE)
+       if (coal->type != COALITION_TYPE_RESOURCE) {
                return KERN_INVALID_ARGUMENT;
+       }
 
-       ledger_t sum_ledger = ledger_instantiate(task_ledger_template, LEDGER_CREATE_ACTIVE_ENTRIES);
-       if (sum_ledger == LEDGER_NULL)
+       /* Return KERN_INVALID_ARGUMENT for Corpse coalition */
+       for (i = 0; i < COALITION_NUM_TYPES; i++) {
+               if (coal == corpse_coalition[i]) {
+                       return KERN_INVALID_ARGUMENT;
+               }
+       }
+
+       ledger_t sum_ledger = ledger_instantiate(coalition_task_ledger_template, LEDGER_CREATE_ACTIVE_ENTRIES);
+       if (sum_ledger == LEDGER_NULL) {
                return KERN_RESOURCE_SHORTAGE;
+       }
 
        coalition_lock(coal);
 
@@ -460,47 +747,97 @@ coalition_resource_usage_internal(coalition_t coal, struct coalition_resource_us
        uint64_t bytesread = coal->r.bytesread;
        uint64_t byteswritten = coal->r.byteswritten;
        uint64_t gpu_time = coal->r.gpu_time;
+       uint64_t energy = coal->r.energy;
        uint64_t logical_immediate_writes = coal->r.logical_immediate_writes;
        uint64_t logical_deferred_writes = coal->r.logical_deferred_writes;
        uint64_t logical_invalidated_writes = coal->r.logical_invalidated_writes;
        uint64_t logical_metadata_writes = coal->r.logical_metadata_writes;
+       uint64_t logical_immediate_writes_to_external = coal->r.logical_immediate_writes_to_external;
+       uint64_t logical_deferred_writes_to_external = coal->r.logical_deferred_writes_to_external;
+       uint64_t logical_invalidated_writes_to_external = coal->r.logical_invalidated_writes_to_external;
+       uint64_t logical_metadata_writes_to_external = coal->r.logical_metadata_writes_to_external;
+#if CONFIG_PHYS_WRITE_ACCT
+       uint64_t fs_metadata_writes = coal->r.fs_metadata_writes;
+#endif /* CONFIG_PHYS_WRITE_ACCT */
        int64_t cpu_time_billed_to_me = 0;
        int64_t cpu_time_billed_to_others = 0;
-
-       kr = ledger_get_balance(sum_ledger, task_ledgers.cpu_time_billed_to_me, (int64_t *)&cpu_time_billed_to_me);
-       if (kr != KERN_SUCCESS || cpu_time_billed_to_me < 0) {
-#if DEVELOPMENT || DEBUG
-               printf("ledger_get_balance failed or ledger negative in coalition_resource_usage_internal: %lld\n", cpu_time_billed_to_me);
-#endif /* DEVELOPMENT || DEBUG */
-               cpu_time_billed_to_me = 0;
-       }
-
-       kr = ledger_get_balance(sum_ledger, task_ledgers.cpu_time_billed_to_others, (int64_t *)&cpu_time_billed_to_others);
-       if (kr != KERN_SUCCESS || cpu_time_billed_to_others < 0) {
-#if DEVELOPMENT || DEBUG
-               printf("ledger_get_balance failed or ledger negative in coalition_resource_usage_internal: %lld\n", cpu_time_billed_to_others);
-#endif /* DEVELOPMENT || DEBUG */
-               cpu_time_billed_to_others = 0;
-       }
+       int64_t energy_billed_to_me = 0;
+       int64_t energy_billed_to_others = 0;
+       uint64_t cpu_ptime = coal->r.cpu_ptime;
+       uint64_t cpu_time_eqos[COALITION_NUM_THREAD_QOS_TYPES];
+       memcpy(cpu_time_eqos, coal->r.cpu_time_eqos, sizeof(cpu_time_eqos));
+       uint64_t cpu_time_rqos[COALITION_NUM_THREAD_QOS_TYPES];
+       memcpy(cpu_time_rqos, coal->r.cpu_time_rqos, sizeof(cpu_time_rqos));
+       uint64_t cpu_instructions = coal->r.cpu_instructions;
+       uint64_t cpu_cycles = coal->r.cpu_cycles;
 
        /*
         * Add to that all the active tasks' ledgers. Tasks cannot deallocate
         * out from under us, since we hold the coalition lock.
-        * Do not use the on-behalf of cpu time from ledger for live tasks, since
-        * it will not have cpu time for active linkages between tasks.
         */
        task_t task;
        qe_foreach_element(task, &coal->r.tasks, task_coalition[COALITION_TYPE_RESOURCE]) {
+               /*
+                * Rolling up stats for exec copy task or exec'd task will lead to double accounting.
+                * Cannot take task lock after taking coaliton lock
+                */
+               if (task_is_exec_copy(task) || task_did_exec(task)) {
+                       continue;
+               }
+
                ledger_rollup(sum_ledger, task->ledger);
                bytesread += task->task_io_stats->disk_reads.size;
                byteswritten += task->task_io_stats->total_io.size - task->task_io_stats->disk_reads.size;
+#if defined(__x86_64__)
                gpu_time += task_gpu_utilisation(task);
-               logical_immediate_writes += task->task_immediate_writes;
-               logical_deferred_writes += task->task_deferred_writes;
-               logical_invalidated_writes += task->task_invalidated_writes;
-               logical_metadata_writes += task->task_metadata_writes;
-               cpu_time_billed_to_me += (int64_t)bank_billed_time_safe(task);
-               cpu_time_billed_to_others += (int64_t)bank_serviced_time_safe(task);
+#endif /* defined(__x86_64__) */
+
+#if defined(__arm__) || defined(__arm64__)
+               energy += task_energy(task);
+#endif /* defined(__arm__) || defined(__arm64__) */
+
+               logical_immediate_writes += task->task_writes_counters_internal.task_immediate_writes;
+               logical_deferred_writes += task->task_writes_counters_internal.task_deferred_writes;
+               logical_invalidated_writes += task->task_writes_counters_internal.task_invalidated_writes;
+               logical_metadata_writes += task->task_writes_counters_internal.task_metadata_writes;
+               logical_immediate_writes_to_external += task->task_writes_counters_external.task_immediate_writes;
+               logical_deferred_writes_to_external += task->task_writes_counters_external.task_deferred_writes;
+               logical_invalidated_writes_to_external += task->task_writes_counters_external.task_invalidated_writes;
+               logical_metadata_writes_to_external += task->task_writes_counters_external.task_metadata_writes;
+#if CONFIG_PHYS_WRITE_ACCT
+               fs_metadata_writes += task->task_fs_metadata_writes;
+#endif /* CONFIG_PHYS_WRITE_ACCT */
+
+               cpu_ptime += task_cpu_ptime(task);
+               task_update_cpu_time_qos_stats(task, cpu_time_eqos, cpu_time_rqos);
+#if MONOTONIC
+               uint64_t counts[MT_CORE_NFIXED] = {};
+               (void)mt_fixed_task_counts(task, counts);
+               cpu_cycles += counts[MT_CORE_CYCLES];
+#if defined(MT_CORE_INSTRS)
+               cpu_instructions += counts[MT_CORE_INSTRS];
+#endif /* defined(MT_CORE_INSTRS) */
+#endif /* MONOTONIC */
+       }
+
+       kr = ledger_get_balance(sum_ledger, task_ledgers.cpu_time_billed_to_me, (int64_t *)&cpu_time_billed_to_me);
+       if (kr != KERN_SUCCESS || cpu_time_billed_to_me < 0) {
+               cpu_time_billed_to_me = 0;
+       }
+
+       kr = ledger_get_balance(sum_ledger, task_ledgers.cpu_time_billed_to_others, (int64_t *)&cpu_time_billed_to_others);
+       if (kr != KERN_SUCCESS || cpu_time_billed_to_others < 0) {
+               cpu_time_billed_to_others = 0;
+       }
+
+       kr = ledger_get_balance(sum_ledger, task_ledgers.energy_billed_to_me, (int64_t *)&energy_billed_to_me);
+       if (kr != KERN_SUCCESS || energy_billed_to_me < 0) {
+               energy_billed_to_me = 0;
+       }
+
+       kr = ledger_get_balance(sum_ledger, task_ledgers.energy_billed_to_others, (int64_t *)&energy_billed_to_others);
+       if (kr != KERN_SUCCESS || energy_billed_to_others < 0) {
+               energy_billed_to_others = 0;
        }
 
        /* collect information from the coalition itself */
@@ -514,23 +851,25 @@ coalition_resource_usage_internal(coalition_t coal, struct coalition_resource_us
 
        /* Copy the totals out of sum_ledger */
        kr = ledger_get_entries(sum_ledger, task_ledgers.cpu_time,
-                       &credit, &debit);
+           &credit, &debit);
        if (kr != KERN_SUCCESS) {
                credit = 0;
        }
        cru_out->cpu_time = credit;
        cru_out->cpu_time_billed_to_me = (uint64_t)cpu_time_billed_to_me;
        cru_out->cpu_time_billed_to_others = (uint64_t)cpu_time_billed_to_others;
+       cru_out->energy_billed_to_me = (uint64_t)energy_billed_to_me;
+       cru_out->energy_billed_to_others = (uint64_t)energy_billed_to_others;
 
        kr = ledger_get_entries(sum_ledger, task_ledgers.interrupt_wakeups,
-                       &credit, &debit);
+           &credit, &debit);
        if (kr != KERN_SUCCESS) {
                credit = 0;
        }
        cru_out->interrupt_wakeups = credit;
 
        kr = ledger_get_entries(sum_ledger, task_ledgers.platform_idle_wakeups,
-                       &credit, &debit);
+           &credit, &debit);
        if (kr != KERN_SUCCESS) {
                credit = 0;
        }
@@ -539,14 +878,39 @@ coalition_resource_usage_internal(coalition_t coal, struct coalition_resource_us
        cru_out->bytesread = bytesread;
        cru_out->byteswritten = byteswritten;
        cru_out->gpu_time = gpu_time;
+       cru_out->energy = energy;
        cru_out->logical_immediate_writes = logical_immediate_writes;
        cru_out->logical_deferred_writes = logical_deferred_writes;
        cru_out->logical_invalidated_writes = logical_invalidated_writes;
        cru_out->logical_metadata_writes = logical_metadata_writes;
-
+       cru_out->logical_immediate_writes_to_external = logical_immediate_writes_to_external;
+       cru_out->logical_deferred_writes_to_external = logical_deferred_writes_to_external;
+       cru_out->logical_invalidated_writes_to_external = logical_invalidated_writes_to_external;
+       cru_out->logical_metadata_writes_to_external = logical_metadata_writes_to_external;
+#if CONFIG_PHYS_WRITE_ACCT
+       cru_out->fs_metadata_writes = fs_metadata_writes;
+#else
+       cru_out->fs_metadata_writes = 0;
+#endif /* CONFIG_PHYS_WRITE_ACCT */
+       cru_out->cpu_ptime = cpu_ptime;
+       cru_out->cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES;
+       memcpy(cru_out->cpu_time_eqos, cpu_time_eqos, sizeof(cru_out->cpu_time_eqos));
+       cru_out->cpu_cycles = cpu_cycles;
+       cru_out->cpu_instructions = cpu_instructions;
        ledger_dereference(sum_ledger);
        sum_ledger = LEDGER_NULL;
 
+#if CONFIG_PHYS_WRITE_ACCT
+       // kernel_pm_writes are only recorded under kernel_task coalition
+       if (coalition_id(coal) == COALITION_ID_KERNEL) {
+               cru_out->pm_writes = kernel_pm_writes;
+       } else {
+               cru_out->pm_writes = 0;
+       }
+#else
+       cru_out->pm_writes = 0;
+#endif /* CONFIG_PHYS_WRITE_ACCT */
+
        if (last_became_nonempty_time) {
                time_nonempty += mach_absolute_time() - last_became_nonempty_time;
        }
@@ -566,11 +930,31 @@ i_coal_jetsam_init(coalition_t coal, boolean_t privileged)
        assert(coal && coal->type == COALITION_TYPE_JETSAM);
        (void)privileged;
 
-       coal->j.leader= TASK_NULL;
+       coal->j.leader = TASK_NULL;
        queue_head_init(coal->j.extensions);
        queue_head_init(coal->j.services);
        queue_head_init(coal->j.other);
 
+#if CONFIG_THREAD_GROUPS
+       switch (coal->role) {
+       case COALITION_ROLE_SYSTEM:
+               coal->j.thread_group = thread_group_find_by_id_and_retain(THREAD_GROUP_SYSTEM);
+               break;
+       case COALITION_ROLE_BACKGROUND:
+               coal->j.thread_group = thread_group_find_by_id_and_retain(THREAD_GROUP_BACKGROUND);
+               break;
+       case COALITION_ROLE_ADAPTIVE:
+               if (merge_adaptive_coalitions) {
+                       coal->j.thread_group = thread_group_find_by_id_and_retain(THREAD_GROUP_ADAPTIVE);
+               } else {
+                       coal->j.thread_group = thread_group_create_and_retain();
+               }
+               break;
+       default:
+               coal->j.thread_group = thread_group_create_and_retain();
+       }
+       assert(coal->j.thread_group != NULL);
+#endif
        return KERN_SUCCESS;
 }
 
@@ -584,6 +968,13 @@ i_coal_jetsam_dealloc(__unused coalition_t coal)
        assert(queue_empty(&coal->j.services));
        assert(queue_empty(&coal->j.other));
        assert(coal->j.leader == TASK_NULL);
+
+#if CONFIG_THREAD_GROUPS
+       /* disassociate from the thread group */
+       assert(coal->j.thread_group != NULL);
+       thread_group_release(coal->j.thread_group);
+       coal->j.thread_group = NULL;
+#endif
 }
 
 static kern_return_t
@@ -599,7 +990,7 @@ i_coal_jetsam_adopt_task(coalition_t coal, task_t task)
        /* put each task initially in the "other" list */
        enqueue_tail(&cj->other, &task->task_coalition[COALITION_TYPE_JETSAM]);
        coal_dbg("coalition %lld adopted PID:%d as UNDEF",
-                coal->id, task_pid(task));
+           coal->id, task_pid(task));
 
        return KERN_SUCCESS;
 }
@@ -611,7 +1002,7 @@ i_coal_jetsam_remove_task(coalition_t coal, task_t task)
        assert(task->coalition[COALITION_TYPE_JETSAM] == coal);
 
        coal_dbg("removing PID:%d from coalition id:%lld",
-                task_pid(task), coal->id);
+           task_pid(task), coal->id);
 
        if (task == coal->j.leader) {
                coal->j.leader = NULL;
@@ -640,11 +1031,11 @@ i_coal_jetsam_set_taskrole(coalition_t coal, task_t task, int role)
        switch (role) {
        case COALITION_TASKROLE_LEADER:
                coal_dbg("setting PID:%d as LEADER of %lld",
-                        task_pid(task), coal->id);
+                   task_pid(task), coal->id);
                if (cj->leader != TASK_NULL) {
                        /* re-queue the exiting leader onto the "other" list */
                        coal_dbg("    re-queue existing leader (%d) as OTHER",
-                                task_pid(cj->leader));
+                           task_pid(cj->leader));
                        re_queue_tail(&cj->other, &cj->leader->task_coalition[COALITION_TYPE_JETSAM]);
                }
                /*
@@ -657,33 +1048,46 @@ i_coal_jetsam_set_taskrole(coalition_t coal, task_t task, int role)
                /* set the coalition leader */
                cj->leader = task;
                break;
-       case COALITION_TASKROLE_UNDEF:
-               coal_dbg("setting PID:%d as UNDEF in %lld",
-                        task_pid(task), coal->id);
-               q = (queue_t)&cj->other;
-               break;
        case COALITION_TASKROLE_XPC:
                coal_dbg("setting PID:%d as XPC in %lld",
-                        task_pid(task), coal->id);
+                   task_pid(task), coal->id);
                q = (queue_t)&cj->services;
                break;
        case COALITION_TASKROLE_EXT:
                coal_dbg("setting PID:%d as EXT in %lld",
-                        task_pid(task), coal->id);
+                   task_pid(task), coal->id);
                q = (queue_t)&cj->extensions;
                break;
+       case COALITION_TASKROLE_NONE:
+               /*
+                * Tasks with a role of "none" should fall through to an
+                * undefined role so long as the task is currently a member
+                * of the coalition. This scenario can happen if a task is
+                * killed (usually via jetsam) during exec.
+                */
+               if (task->coalition[COALITION_TYPE_JETSAM] != coal) {
+                       panic("%s: task %p attempting to set role %d "
+                           "in coalition %p to which it does not belong!", __func__, task, role, coal);
+               }
+               OS_FALLTHROUGH;
+       case COALITION_TASKROLE_UNDEF:
+               coal_dbg("setting PID:%d as UNDEF in %lld",
+                   task_pid(task), coal->id);
+               q = (queue_t)&cj->other;
+               break;
        default:
                panic("%s: invalid role(%d) for task", __func__, role);
                return KERN_INVALID_ARGUMENT;
        }
 
-       if (q != NULL)
+       if (q != NULL) {
                re_queue_tail(q, &task->task_coalition[COALITION_TYPE_JETSAM]);
+       }
 
        return KERN_SUCCESS;
 }
 
-static int
+int
 i_coal_jetsam_get_taskrole(coalition_t coal, task_t task)
 {
        struct i_jetsam_coalition *cj;
@@ -694,26 +1098,30 @@ i_coal_jetsam_get_taskrole(coalition_t coal, task_t task)
 
        cj = &coal->j;
 
-       if (task == cj->leader)
+       if (task == cj->leader) {
                return COALITION_TASKROLE_LEADER;
+       }
 
        qe_foreach_element(t, &cj->services, task_coalition[COALITION_TYPE_JETSAM]) {
-               if (t == task)
+               if (t == task) {
                        return COALITION_TASKROLE_XPC;
+               }
        }
 
        qe_foreach_element(t, &cj->extensions, task_coalition[COALITION_TYPE_JETSAM]) {
-               if (t == task)
+               if (t == task) {
                        return COALITION_TASKROLE_EXT;
+               }
        }
 
        qe_foreach_element(t, &cj->other, task_coalition[COALITION_TYPE_JETSAM]) {
-               if (t == task)
+               if (t == task) {
                        return COALITION_TASKROLE_UNDEF;
+               }
        }
 
        /* task not in the coalition?! */
-       return -1;
+       return COALITION_TASKROLE_NONE;
 }
 
 static void
@@ -726,17 +1134,18 @@ i_coal_jetsam_iterate_tasks(coalition_t coal, void *ctx, void (*callback)(coalit
 
        cj = &coal->j;
 
-       if (cj->leader)
+       if (cj->leader) {
                callback(coal, ctx, cj->leader);
+       }
 
        qe_foreach_element(t, &cj->services, task_coalition[COALITION_TYPE_JETSAM])
-               callback(coal, ctx, t);
+       callback(coal, ctx, t);
 
        qe_foreach_element(t, &cj->extensions, task_coalition[COALITION_TYPE_JETSAM])
-               callback(coal, ctx, t);
+       callback(coal, ctx, t);
 
        qe_foreach_element(t, &cj->other, task_coalition[COALITION_TYPE_JETSAM])
-               callback(coal, ctx, t);
+       callback(coal, ctx, t);
 }
 
 
@@ -752,20 +1161,25 @@ i_coal_jetsam_iterate_tasks(coalition_t coal, void *ctx, void (*callback)(coalit
  * Condition: coalitions_list_lock must be UNLOCKED.
  */
 kern_return_t
-coalition_create_internal(int type, boolean_t privileged, coalition_t *out)
+coalition_create_internal(int type, int role, boolean_t privileged, coalition_t *out, uint64_t *coalition_id)
 {
        kern_return_t kr;
        struct coalition *new_coal;
+       uint64_t cid;
+       uint32_t ctype;
 
-       if (type < 0 || type > COALITION_TYPE_MAX)
+       if (type < 0 || type > COALITION_TYPE_MAX) {
                return KERN_INVALID_ARGUMENT;
+       }
 
        new_coal = (struct coalition *)zalloc(coalition_zone);
-       if (new_coal == COALITION_NULL)
+       if (new_coal == COALITION_NULL) {
                return KERN_RESOURCE_SHORTAGE;
+       }
        bzero(new_coal, sizeof(*new_coal));
 
        new_coal->type = type;
+       new_coal->role = role;
 
        /* initialize type-specific resources */
        kr = coal_call(new_coal, init, privileged);
@@ -778,19 +1192,36 @@ coalition_create_internal(int type, boolean_t privileged, coalition_t *out)
        new_coal->ref_count = 2;
 
        new_coal->privileged = privileged ? TRUE : FALSE;
-#if defined(DEVELOPMENT) || defined(DEBUG)
+#if DEVELOPMENT || DEBUG
        new_coal->should_notify = 1;
 #endif
 
-       lck_mtx_init(&new_coal->lock, &coalitions_lck_grp, &coalitions_lck_attr);
+       lck_mtx_init(&new_coal->lock, &coalitions_lck_grp, LCK_ATTR_NULL);
 
        lck_mtx_lock(&coalitions_list_lock);
        new_coal->id = coalition_next_id++;
        coalition_count++;
        enqueue_tail(&coalitions_q, &new_coal->coalitions);
+
+#if CONFIG_THREAD_GROUPS
+       KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_NEW),
+           new_coal->id, new_coal->type,
+           (new_coal->type == COALITION_TYPE_JETSAM && new_coal->j.thread_group) ?
+           thread_group_get_id(new_coal->j.thread_group) : 0);
+
+#else
+       KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_NEW),
+           new_coal->id, new_coal->type);
+#endif
+       cid = new_coal->id;
+       ctype = new_coal->type;
        lck_mtx_unlock(&coalitions_list_lock);
 
-       coal_dbg("id:%llu, type:%s", new_coal->id, coal_type_str(new_coal->type));
+       coal_dbg("id:%llu, type:%s", cid, coal_type_str(ctype));
+
+       if (coalition_id != NULL) {
+               *coalition_id = cid;
+       }
 
        *out = new_coal;
        return KERN_SUCCESS;
@@ -813,8 +1244,8 @@ coalition_release(coalition_t coal)
 #endif /* COALITION_DEBUG */
 
        coal_dbg("id:%llu type:%s ref_count:%u active_count:%u%s",
-                coal->id, coal_type_str(coal->type), rc, ac,
-                rc <= 0 ? ", will deallocate now" : "");
+           coal->id, coal_type_str(coal->type), rc, ac,
+           rc <= 0 ? ", will deallocate now" : "");
 
        if (coal->ref_count > 0) {
                coalition_unlock(coal);
@@ -827,6 +1258,15 @@ coalition_release(coalition_t coal)
        assert(coal->reaped);
        assert(coal->focal_task_count == 0);
        assert(coal->nonfocal_task_count == 0);
+#if CONFIG_THREAD_GROUPS
+       KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_FREE),
+           coal->id, coal->type,
+           coal->type == COALITION_TYPE_JETSAM ?
+           coal->j.thread_group : 0);
+#else
+       KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_FREE),
+           coal->id, coal->type);
+#endif
 
        coal_call(coal, dealloc);
 
@@ -890,7 +1330,7 @@ coalition_find_by_id(uint64_t cid)
 
        if (coal->ref_count == 0) {
                panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n",
-                               coal, coal->id, coal_type_str(coal->type), coal->active_count);
+                   coal, coal->id, coal_type_str(coal->type), coal->active_count);
        }
        coal->ref_count++;
 #if COALITION_DEBUG
@@ -901,7 +1341,7 @@ coalition_find_by_id(uint64_t cid)
        lck_mtx_unlock(&coalitions_list_lock);
 
        coal_dbg("id:%llu type:%s ref_count:%u",
-                coal->id, coal_type_str(coal->type), rc);
+           coal->id, coal_type_str(coal->type), rc);
 
        return coal;
 }
@@ -941,7 +1381,7 @@ coalition_find_and_activate_by_id(uint64_t cid)
 
        if (coal->ref_count == 0) {
                panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n",
-                               coal, coal->id, coal_type_str(coal->type), coal->active_count);
+                   coal, coal->id, coal_type_str(coal->type), coal->active_count);
        }
 
        coal->ref_count++;
@@ -956,7 +1396,7 @@ coalition_find_and_activate_by_id(uint64_t cid)
        lck_mtx_unlock(&coalitions_list_lock);
 
        coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u",
-                coal->id, coal_type_str(coal->type), rc, ac);
+           coal->id, coal_type_str(coal->type), rc, ac);
 
        return coal;
 }
@@ -964,6 +1404,7 @@ coalition_find_and_activate_by_id(uint64_t cid)
 uint64_t
 coalition_id(coalition_t coal)
 {
+       assert(coal != COALITION_NULL);
        return coal->id;
 }
 
@@ -972,10 +1413,11 @@ task_coalition_ids(task_t task, uint64_t ids[COALITION_NUM_TYPES])
 {
        int i;
        for (i = 0; i < COALITION_NUM_TYPES; i++) {
-               if (task->coalition[i])
+               if (task->coalition[i]) {
                        ids[i] = task->coalition[i]->id;
-               else
+               } else {
                        ids[i] = 0;
+               }
        }
 }
 
@@ -989,10 +1431,10 @@ task_coalition_roles(task_t task, int roles[COALITION_NUM_TYPES])
                if (task->coalition[i]) {
                        coalition_lock(task->coalition[i]);
                        roles[i] = coal_call(task->coalition[i],
-                                            get_taskrole, task);
+                           get_taskrole, task);
                        coalition_unlock(task->coalition[i]);
                } else {
-                       roles[i] = -1;
+                       roles[i] = COALITION_TASKROLE_NONE;
                }
        }
 }
@@ -1004,6 +1446,24 @@ coalition_type(coalition_t coal)
        return coal->type;
 }
 
+boolean_t
+coalition_term_requested(coalition_t coal)
+{
+       return coal->termrequested;
+}
+
+boolean_t
+coalition_is_terminated(coalition_t coal)
+{
+       return coal->terminated;
+}
+
+boolean_t
+coalition_is_reaped(coalition_t coal)
+{
+       return coal->reaped;
+}
+
 boolean_t
 coalition_is_privileged(coalition_t coal)
 {
@@ -1013,22 +1473,27 @@ coalition_is_privileged(coalition_t coal)
 boolean_t
 task_is_in_privileged_coalition(task_t task, int type)
 {
-       if (type < 0 || type > COALITION_TYPE_MAX)
+       if (type < 0 || type > COALITION_TYPE_MAX) {
                return FALSE;
-       if (unrestrict_coalition_syscalls)
+       }
+       if (unrestrict_coalition_syscalls) {
                return TRUE;
-       if (!task->coalition[type])
+       }
+       if (!task->coalition[type]) {
                return FALSE;
+       }
        return task->coalition[type]->privileged;
 }
 
-void task_coalition_update_gpu_stats(task_t task, uint64_t gpu_ns_delta)
+void
+task_coalition_update_gpu_stats(task_t task, uint64_t gpu_ns_delta)
 {
        coalition_t coal;
 
        assert(task != TASK_NULL);
-       if (gpu_ns_delta == 0)
+       if (gpu_ns_delta == 0) {
                return;
+       }
 
        coal = task->coalition[COALITION_TYPE_RESOURCE];
        assert(coal != COALITION_NULL);
@@ -1038,71 +1503,138 @@ void task_coalition_update_gpu_stats(task_t task, uint64_t gpu_ns_delta)
        coalition_unlock(coal);
 }
 
-uint32_t task_coalition_adjust_focal_count(task_t task, int count)
+boolean_t
+task_coalition_adjust_focal_count(task_t task, int count, uint32_t *new_count)
 {
-       coalition_t coal;
-       uint32_t ret;
+       coalition_t coal = task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING];
+       if (coal == COALITION_NULL) {
+               return FALSE;
+       }
 
-       /*
-        * For now: only use the resource coalition. Perhaps in the
-        * future we may combine all coalition types, or even make
-        * a special coalition type just for this.
-        */
-       coal = task->coalition[COALITION_TYPE_RESOURCE];
-       assert(coal != COALITION_NULL);
+       *new_count = os_atomic_add(&coal->focal_task_count, count, relaxed);
+       assert(*new_count != UINT32_MAX);
+       return TRUE;
+}
 
-       ret = hw_atomic_add(&coal->focal_task_count, count);
+uint32_t
+task_coalition_focal_count(task_t task)
+{
+       coalition_t coal = task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING];
+       if (coal == COALITION_NULL) {
+               return 0;
+       }
 
-       /* catch underflow */
-       assert(ret != UINT32_MAX);
-       return ret;
+       return coal->focal_task_count;
 }
 
-uint32_t task_coalition_focal_count(task_t task)
+boolean_t
+task_coalition_adjust_nonfocal_count(task_t task, int count, uint32_t *new_count)
 {
-       coalition_t coal;
-       coal = task->coalition[COALITION_TYPE_RESOURCE];
-       assert(coal != COALITION_NULL);
+       coalition_t coal = task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING];
+       if (coal == COALITION_NULL) {
+               return FALSE;
+       }
 
-       return coal->focal_task_count;
+       *new_count = os_atomic_add(&coal->nonfocal_task_count, count, relaxed);
+       assert(*new_count != UINT32_MAX);
+       return TRUE;
 }
 
-uint32_t task_coalition_adjust_nonfocal_count(task_t task, int count)
+uint32_t
+task_coalition_nonfocal_count(task_t task)
 {
-       coalition_t coal;
-       uint32_t ret;
+       coalition_t coal = task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING];
+       if (coal == COALITION_NULL) {
+               return 0;
+       }
 
-       /*
-        * For now: only use the resource coalition. Perhaps in the
-        * future we may combine all coalition types, or even make
-        * a special coalition type just for this.
-        */
-       coal = task->coalition[COALITION_TYPE_RESOURCE];
-       assert(coal != COALITION_NULL);
+       return coal->nonfocal_task_count;
+}
 
-       ret = hw_atomic_add(&coal->nonfocal_task_count, count);
+void
+coalition_set_efficient(coalition_t coal)
+{
+       coalition_lock(coal);
+       coal->efficient = TRUE;
+       coalition_unlock(coal);
+}
 
-       /* catch underflow */
-       assert(ret != UINT32_MAX);
-       return ret;
+#if CONFIG_THREAD_GROUPS
+struct thread_group *
+task_coalition_get_thread_group(task_t task)
+{
+       coalition_t coal = task->coalition[COALITION_TYPE_JETSAM];
+       /* return system thread group for non-jetsam coalitions */
+       if (coal == COALITION_NULL) {
+               return init_coalition[COALITION_TYPE_JETSAM]->j.thread_group;
+       }
+       return coal->j.thread_group;
 }
 
-uint32_t task_coalition_nonfocal_count(task_t task)
+
+struct thread_group *
+kdp_coalition_get_thread_group(coalition_t coal)
+{
+       if (coal->type != COALITION_TYPE_JETSAM) {
+               return NULL;
+       }
+       assert(coal->j.thread_group != NULL);
+       return coal->j.thread_group;
+}
+
+struct thread_group *
+coalition_get_thread_group(coalition_t coal)
+{
+       if (coal->type != COALITION_TYPE_JETSAM) {
+               return NULL;
+       }
+       assert(coal->j.thread_group != NULL);
+       return thread_group_retain(coal->j.thread_group);
+}
+
+void
+coalition_set_thread_group(coalition_t coal, struct thread_group *tg)
 {
-       coalition_t coal;
-       coal = task->coalition[COALITION_TYPE_RESOURCE];
        assert(coal != COALITION_NULL);
+       assert(tg != NULL);
 
-       return coal->nonfocal_task_count;
+       if (coal->type != COALITION_TYPE_JETSAM) {
+               return;
+       }
+       struct thread_group *old_tg = coal->j.thread_group;
+       assert(old_tg != NULL);
+       coal->j.thread_group = tg;
+
+       KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_THREAD_GROUP_SET),
+           coal->id, coal->type, thread_group_get_id(tg));
+
+       thread_group_release(old_tg);
+}
+
+void
+task_coalition_thread_group_focal_update(task_t task)
+{
+       assert(task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING] != COALITION_NULL);
+       thread_group_flags_update_lock();
+       uint32_t focal_count = task_coalition_focal_count(task);
+       if (focal_count) {
+               thread_group_set_flags_locked(task_coalition_get_thread_group(task), THREAD_GROUP_FLAGS_UI_APP);
+       } else {
+               thread_group_clear_flags_locked(task_coalition_get_thread_group(task), THREAD_GROUP_FLAGS_UI_APP);
+       }
+       thread_group_flags_update_unlock();
 }
 
-void coalition_for_each_task(coalition_t coal, void *ctx,
-                            void (*callback)(coalition_t, void *, task_t))
+#endif
+
+void
+coalition_for_each_task(coalition_t coal, void *ctx,
+    void (*callback)(coalition_t, void *, task_t))
 {
        assert(coal != COALITION_NULL);
 
        coal_dbg("iterating tasks in coalition %p id:%llu type:%s, active_count:%u",
-                coal, coal->id, coal_type_str(coal->type), coal->active_count);
+           coal, coal->id, coal_type_str(coal->type), coal->active_count);
 
        coalition_lock(coal);
 
@@ -1136,7 +1668,7 @@ coalition_remove_active(coalition_t coal)
                assert(!coal->notified);
 
                coal->notified = TRUE;
-#if defined(DEVELOPMENT) || defined(DEBUG)
+#if DEVELOPMENT || DEBUG
                do_notify = coal->should_notify;
 #else
                do_notify = TRUE;
@@ -1154,7 +1686,7 @@ coalition_remove_active(coalition_t coal)
        coalition_unlock(coal);
 
        coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u,%s",
-                cid, coal_type_str(ct), rc, ac, do_notify ? " NOTIFY" : " ");
+           cid, coal_type_str(ct), rc, ac, do_notify ? " NOTIFY" : " ");
 
        if (do_notify) {
                coalition_notify_user(notify_id, notify_flags);
@@ -1173,6 +1705,18 @@ coalitions_adopt_init_task(task_t task)
        return kr;
 }
 
+/* Used for forked corpses. */
+kern_return_t
+coalitions_adopt_corpse_task(task_t task)
+{
+       kern_return_t kr;
+       kr = coalitions_adopt_task(corpse_coalition, task);
+       if (kr != KERN_SUCCESS) {
+               panic("failed to adopt task %p into corpse coalition: %d", task, kr);
+       }
+       return kr;
+}
+
 /*
  * coalition_adopt_task_internal
  * Condition: Coalition must be referenced and unlocked. Will fail if coalition
@@ -1195,8 +1739,9 @@ coalition_adopt_task_internal(coalition_t coal, task_t task)
        }
 
        kr = coal_call(coal, adopt_task, task);
-       if (kr != KERN_SUCCESS)
+       if (kr != KERN_SUCCESS) {
                goto out_unlock;
+       }
 
        coal->active_count++;
 
@@ -1211,10 +1756,16 @@ out_unlock:
        uint32_t rc = coal->ref_count;
        uint32_t ct = coal->type;
 #endif
+       if (get_task_uniqueid(task) != UINT64_MAX) {
+               /* On 32-bit targets, uniqueid will get truncated to 32 bits */
+               KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_ADOPT),
+                   coal->id, get_task_uniqueid(task));
+       }
+
        coalition_unlock(coal);
 
        coal_dbg("task:%d, id:%llu type:%s ref_count:%u, kr=%d",
-                task_pid(task), cid, coal_type_str(ct), rc, kr);
+           task_pid(task), cid, coal_type_str(ct), rc, kr);
        return kr;
 }
 
@@ -1225,8 +1776,9 @@ coalition_remove_task_internal(task_t task, int type)
 
        coalition_t coal = task->coalition[type];
 
-       if (!coal)
+       if (!coal) {
                return KERN_SUCCESS;
+       }
 
        assert(coal->type == (uint32_t)type);
 
@@ -1240,10 +1792,12 @@ coalition_remove_task_internal(task_t task, int type)
        int      ac = coal->active_count;
        int      ct = coal->type;
 #endif
+       KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_REMOVE),
+           coal->id, get_task_uniqueid(task));
        coalition_unlock(coal);
 
        coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u, kr=%d",
-                cid, coal_type_str(ct), rc, ac, kr);
+           cid, coal_type_str(ct), rc, ac, kr);
 
        coalition_remove_active(coal);
 
@@ -1261,23 +1815,28 @@ coalitions_adopt_task(coalition_t *coals, task_t task)
        int i;
        kern_return_t kr;
 
-       if (!coals || coals[COALITION_TYPE_RESOURCE] == COALITION_NULL)
+       if (!coals || coals[COALITION_TYPE_RESOURCE] == COALITION_NULL) {
                return KERN_INVALID_ARGUMENT;
+       }
 
        /* verify that the incoming coalitions are what they say they are */
-       for (i = 0; i < COALITION_NUM_TYPES; i++)
-               if (coals[i] && coals[i]->type != (uint32_t)i)
+       for (i = 0; i < COALITION_NUM_TYPES; i++) {
+               if (coals[i] && coals[i]->type != (uint32_t)i) {
                        return KERN_INVALID_ARGUMENT;
+               }
+       }
 
        for (i = 0; i < COALITION_NUM_TYPES; i++) {
                kr = KERN_SUCCESS;
-               if (coals[i])
+               if (coals[i]) {
                        kr = coalition_adopt_task_internal(coals[i], task);
+               }
                if (kr != KERN_SUCCESS) {
                        /* dis-associate any coalitions that just adopted this task */
                        while (--i >= 0) {
-                               if (task->coalition[i])
+                               if (task->coalition[i]) {
                                        coalition_remove_task_internal(task, i);
+                               }
                        }
                        break;
                }
@@ -1313,8 +1872,11 @@ task_release_coalitions(task_t task)
 {
        int i;
        for (i = 0; i < COALITION_NUM_TYPES; i++) {
-               if (task->coalition[i])
+               if (task->coalition[i]) {
                        coalition_release(task->coalition[i]);
+               } else if (i == COALITION_TYPE_RESOURCE) {
+                       panic("deallocating task %p was not a member of a resource coalition", task);
+               }
        }
 }
 
@@ -1324,15 +1886,17 @@ task_release_coalitions(task_t task)
  * that type (given in the coalitions parameter) then set the role of
  * the task within that that coalition.
  */
-kern_return_t coalitions_set_roles(coalition_t coalitions[COALITION_NUM_TYPES],
-                                  task_t task, int roles[COALITION_NUM_TYPES])
+kern_return_t
+coalitions_set_roles(coalition_t coalitions[COALITION_NUM_TYPES],
+    task_t task, int roles[COALITION_NUM_TYPES])
 {
        kern_return_t kr = KERN_SUCCESS;
        int i;
 
        for (i = 0; i < COALITION_NUM_TYPES; i++) {
-               if (!coalitions[i])
+               if (!coalitions[i]) {
                        continue;
+               }
                coalition_lock(coalitions[i]);
                kr = coal_call(coalitions[i], set_taskrole, task, roles[i]);
                coalition_unlock(coalitions[i]);
@@ -1386,7 +1950,7 @@ coalition_request_terminate_internal(coalition_t coal)
                assert(!coal->notified);
 
                coal->notified = TRUE;
-#if defined(DEVELOPMENT) || defined(DEBUG)
+#if DEVELOPMENT || DEBUG
                do_notify = coal->should_notify;
 #else
                do_notify = TRUE;
@@ -1451,12 +2015,14 @@ coalition_reap_internal(coalition_t coal)
        return KERN_SUCCESS;
 }
 
-#if defined(DEVELOPMENT) || defined(DEBUG)
-int coalition_should_notify(coalition_t coal)
+#if DEVELOPMENT || DEBUG
+int
+coalition_should_notify(coalition_t coal)
 {
        int should;
-       if (!coal)
+       if (!coal) {
                return -1;
+       }
        coalition_lock(coal);
        should = coal->should_notify;
        coalition_unlock(coal);
@@ -1464,10 +2030,12 @@ int coalition_should_notify(coalition_t coal)
        return should;
 }
 
-void coalition_set_notify(coalition_t coal, int notify)
+void
+coalition_set_notify(coalition_t coal, int notify)
 {
-       if (!coal)
+       if (!coal) {
                return;
+       }
        coalition_lock(coal);
        coal->should_notify = !!notify;
        coalition_unlock(coal);
@@ -1481,26 +2049,22 @@ coalitions_init(void)
        int i;
        const struct coalition_type *ctype;
 
-       coalition_zone = zinit(
-                       sizeof(struct coalition),
-                       CONFIG_COALITION_MAX * sizeof(struct coalition),
-                       COALITION_CHUNK * sizeof(struct coalition),
-                       "coalitions");
-       zone_change(coalition_zone, Z_NOENCRYPT, TRUE);
        queue_head_init(coalitions_q);
 
        if (!PE_parse_boot_argn("unrestrict_coalition_syscalls", &unrestrict_coalition_syscalls,
-               sizeof (unrestrict_coalition_syscalls))) {
+           sizeof(unrestrict_coalition_syscalls))) {
                unrestrict_coalition_syscalls = 0;
        }
 
-       lck_grp_attr_setdefault(&coalitions_lck_grp_attr);
-       lck_grp_init(&coalitions_lck_grp, "coalition", &coalitions_lck_grp_attr);
-       lck_attr_setdefault(&coalitions_lck_attr);
-       lck_mtx_init(&coalitions_list_lock, &coalitions_lck_grp, &coalitions_lck_attr);
+       if (!PE_parse_boot_argn("tg_adaptive", &merge_adaptive_coalitions,
+           sizeof(merge_adaptive_coalitions))) {
+               merge_adaptive_coalitions = 0;
+       }
 
        init_task_ledgers();
 
+       init_coalition_ledgers();
+
        for (i = 0, ctype = &s_coalition_types[0]; i < COALITION_NUM_TYPES; ctype++, i++) {
                /* verify the entry in the global coalition types array */
                if (ctype->type != i ||
@@ -1509,14 +2073,24 @@ coalitions_init(void)
                    !ctype->adopt_task ||
                    !ctype->remove_task) {
                        panic("%s: Malformed coalition type %s(%d) in slot for type:%s(%d)",
-                             __func__, coal_type_str(ctype->type), ctype->type, coal_type_str(i), i);
+                           __func__, coal_type_str(ctype->type), ctype->type, coal_type_str(i), i);
                }
-               if (!ctype->has_default)
+               if (!ctype->has_default) {
                        continue;
-               kr = coalition_create_internal(ctype->type, TRUE, &init_coalition[ctype->type]);
-               if (kr != KERN_SUCCESS)
+               }
+               kr = coalition_create_internal(ctype->type, COALITION_ROLE_SYSTEM, TRUE, &init_coalition[ctype->type], NULL);
+               if (kr != KERN_SUCCESS) {
                        panic("%s: could not create init %s coalition: kr:%d",
-                             __func__, coal_type_str(i), kr);
+                           __func__, coal_type_str(i), kr);
+               }
+               if (i == COALITION_TYPE_RESOURCE) {
+                       assert(COALITION_ID_KERNEL == init_coalition[ctype->type]->id);
+               }
+               kr = coalition_create_internal(ctype->type, COALITION_ROLE_SYSTEM, FALSE, &corpse_coalition[ctype->type], NULL);
+               if (kr != KERN_SUCCESS) {
+                       panic("%s: could not create corpse %s coalition: kr:%d",
+                           __func__, coal_type_str(i), kr);
+               }
        }
 
        /* "Leak" our reference to the global object */
@@ -1526,8 +2100,9 @@ coalitions_init(void)
  * BSD Kernel interface functions
  *
  */
-static void coalition_fill_procinfo(struct coalition *coal,
-                                   struct procinfo_coalinfo *coalinfo)
+static void
+coalition_fill_procinfo(struct coalition *coal,
+    struct procinfo_coalinfo *coalinfo)
 {
        coalinfo->coalition_id = coal->id;
        coalinfo->coalition_type = coal->type;
@@ -1535,7 +2110,8 @@ static void coalition_fill_procinfo(struct coalition *coal,
 }
 
 
-int coalitions_get_list(int type, struct procinfo_coalinfo *coal_list, int list_sz)
+int
+coalitions_get_list(int type, struct procinfo_coalinfo *coal_list, int list_sz)
 {
        int ncoals = 0;
        struct coalition *coal;
@@ -1543,8 +2119,9 @@ int coalitions_get_list(int type, struct procinfo_coalinfo *coal_list, int list_
        lck_mtx_lock(&coalitions_list_lock);
        qe_foreach_element(coal, &coalitions_q, coalitions) {
                if (!coal->reaped && (type < 0 || type == (int)coal->type)) {
-                       if (coal_list && ncoals < list_sz)
+                       if (coal_list && ncoals < list_sz) {
                                coalition_fill_procinfo(coal, &coal_list[ncoals]);
+                       }
                        ++ncoals;
                }
        }
@@ -1554,58 +2131,91 @@ int coalitions_get_list(int type, struct procinfo_coalinfo *coal_list, int list_
 }
 
 /*
- * Jetsam coalition interface
- *
+ * Return the coaltion of the given type to which the task belongs.
  */
-boolean_t coalition_is_leader(task_t task, int coal_type, coalition_t *coal)
+coalition_t
+task_get_coalition(task_t task, int coal_type)
 {
        coalition_t c;
-       boolean_t ret;
 
-       if (coal) /* handle the error cases gracefully */
-               *coal = COALITION_NULL;
+       if (task == NULL || coal_type > COALITION_TYPE_MAX) {
+               return COALITION_NULL;
+       }
 
-       if (!task)
-               return FALSE;
+       c = task->coalition[coal_type];
+       assert(c == COALITION_NULL || (int)c->type == coal_type);
+       return c;
+}
 
-       if (coal_type > COALITION_TYPE_MAX)
-               return FALSE;
+/*
+ * Report if the given task is the leader of the given jetsam coalition.
+ */
+boolean_t
+coalition_is_leader(task_t task, coalition_t coal)
+{
+       boolean_t ret = FALSE;
 
-       c = task->coalition[coal_type];
-       if (!c)
-               return FALSE;
+       if (coal != COALITION_NULL) {
+               coalition_lock(coal);
 
-       assert((int)c->type == coal_type);
+               ret = (coal->type == COALITION_TYPE_JETSAM && coal->j.leader == task);
 
-       coalition_lock(c);
+               coalition_unlock(coal);
+       }
 
-       if (coal)
-               *coal = c;
+       return ret;
+}
 
-       ret = FALSE;
-       if (c->type == COALITION_TYPE_JETSAM && c->j.leader == task)
-               ret = TRUE;
+kern_return_t
+coalition_iterate_stackshot(coalition_iterate_fn_t callout, void *arg, uint32_t coalition_type)
+{
+       coalition_t coal;
+       int i = 0;
 
-       coalition_unlock(c);
+       qe_foreach_element(coal, &coalitions_q, coalitions) {
+               if (coal == NULL || !ml_validate_nofault((vm_offset_t)coal, sizeof(struct coalition))) {
+                       return KERN_FAILURE;
+               }
 
-       return ret;
+               if (coalition_type == coal->type) {
+                       callout(arg, i++, coal);
+               }
+       }
+
+       return KERN_SUCCESS;
 }
 
+task_t
+kdp_coalition_get_leader(coalition_t coal)
+{
+       if (!coal) {
+               return TASK_NULL;
+       }
+
+       if (coal->type == COALITION_TYPE_JETSAM) {
+               return coal->j.leader;
+       }
+       return TASK_NULL;
+}
 
-task_t coalition_get_leader(coalition_t coal)
+task_t
+coalition_get_leader(coalition_t coal)
 {
        task_t leader = TASK_NULL;
 
-       if (!coal)
+       if (!coal) {
                return TASK_NULL;
+       }
 
        coalition_lock(coal);
-       if (coal->type != COALITION_TYPE_JETSAM)
+       if (coal->type != COALITION_TYPE_JETSAM) {
                goto out_unlock;
+       }
 
        leader = coal->j.leader;
-       if (leader != TASK_NULL)
+       if (leader != TASK_NULL) {
                task_reference(leader);
+       }
 
 out_unlock:
        coalition_unlock(coal);
@@ -1613,28 +2223,31 @@ out_unlock:
 }
 
 
-int coalition_get_task_count(coalition_t coal)
+int
+coalition_get_task_count(coalition_t coal)
 {
        int ntasks = 0;
        struct queue_entry *qe;
-       if (!coal)
+       if (!coal) {
                return 0;
+       }
 
        coalition_lock(coal);
        switch (coal->type) {
        case COALITION_TYPE_RESOURCE:
                qe_foreach(qe, &coal->r.tasks)
-                       ntasks++;
+               ntasks++;
                break;
        case COALITION_TYPE_JETSAM:
-               if (coal->j.leader)
+               if (coal->j.leader) {
                        ntasks++;
+               }
                qe_foreach(qe, &coal->j.other)
-                       ntasks++;
+               ntasks++;
                qe_foreach(qe, &coal->j.extensions)
-                       ntasks++;
+               ntasks++;
                qe_foreach(qe, &coal->j.services)
-                       ntasks++;
+               ntasks++;
                break;
        default:
                break;
@@ -1645,7 +2258,8 @@ int coalition_get_task_count(coalition_t coal)
 }
 
 
-static uint64_t i_get_list_footprint(queue_t list, int type, int *ntasks)
+static uint64_t
+i_get_list_footprint(queue_t list, int type, int *ntasks)
 {
        task_t task;
        uint64_t bytes = 0;
@@ -1653,22 +2267,25 @@ static uint64_t i_get_list_footprint(queue_t list, int type, int *ntasks)
        qe_foreach_element(task, list, task_coalition[type]) {
                bytes += get_task_phys_footprint(task);
                coal_dbg("    [%d] task_pid:%d, type:%d, footprint:%lld",
-                        *ntasks, task_pid(task), type, bytes);
+                   *ntasks, task_pid(task), type, bytes);
                *ntasks += 1;
        }
 
        return bytes;
 }
 
-uint64_t coalition_get_page_count(coalition_t coal, int *ntasks)
+uint64_t
+coalition_get_page_count(coalition_t coal, int *ntasks)
 {
        uint64_t bytes = 0;
        int num_tasks = 0;
 
-       if (ntasks)
+       if (ntasks) {
                *ntasks = 0;
-       if (!coal)
+       }
+       if (!coal) {
                return bytes;
+       }
 
        coalition_lock(coal);
 
@@ -1691,8 +2308,9 @@ uint64_t coalition_get_page_count(coalition_t coal, int *ntasks)
 
        coalition_unlock(coal);
 
-       if (ntasks)
+       if (ntasks) {
                *ntasks = num_tasks;
+       }
 
        return bytes / PAGE_SIZE_64;
 }
@@ -1713,7 +2331,8 @@ typedef int (*cmpfunc_t)(const void *a, const void *b);
 extern void
 qsort(void *a, size_t n, size_t es, cmpfunc_t cmp);
 
-static int dflt_cmp(const void *a, const void *b)
+static int
+dflt_cmp(const void *a, const void *b)
 {
        const struct coal_sort_s *csA = (const struct coal_sort_s *)a;
        const struct coal_sort_s *csB = (const struct coal_sort_s *)b;
@@ -1721,14 +2340,16 @@ static int dflt_cmp(const void *a, const void *b)
        /*
         * if both A and B are equal, use a memory descending sort
         */
-       if (csA->usr_order == csB->usr_order)
+       if (csA->usr_order == csB->usr_order) {
                return (int)((int64_t)csB->bytes - (int64_t)csA->bytes);
+       }
 
        /* otherwise, return the relationship between user specified orders */
-       return (csA->usr_order - csB->usr_order);
+       return csA->usr_order - csB->usr_order;
 }
 
-static int mem_asc_cmp(const void *a, const void *b)
+static int
+mem_asc_cmp(const void *a, const void *b)
 {
        const struct coal_sort_s *csA = (const struct coal_sort_s *)a;
        const struct coal_sort_s *csB = (const struct coal_sort_s *)b;
@@ -1736,7 +2357,8 @@ static int mem_asc_cmp(const void *a, const void *b)
        return (int)((int64_t)csA->bytes - (int64_t)csB->bytes);
 }
 
-static int mem_dec_cmp(const void *a, const void *b)
+static int
+mem_dec_cmp(const void *a, const void *b)
 {
        const struct coal_sort_s *csA = (const struct coal_sort_s *)a;
        const struct coal_sort_s *csB = (const struct coal_sort_s *)b;
@@ -1744,35 +2366,39 @@ static int mem_dec_cmp(const void *a, const void *b)
        return (int)((int64_t)csB->bytes - (int64_t)csA->bytes);
 }
 
-static int usr_asc_cmp(const void *a, const void *b)
+static int
+usr_asc_cmp(const void *a, const void *b)
 {
        const struct coal_sort_s *csA = (const struct coal_sort_s *)a;
        const struct coal_sort_s *csB = (const struct coal_sort_s *)b;
 
-       return (csA->usr_order - csB->usr_order);
+       return csA->usr_order - csB->usr_order;
 }
 
-static int usr_dec_cmp(const void *a, const void *b)
+static int
+usr_dec_cmp(const void *a, const void *b)
 {
        const struct coal_sort_s *csA = (const struct coal_sort_s *)a;
        const struct coal_sort_s *csB = (const struct coal_sort_s *)b;
 
-       return (csB->usr_order - csA->usr_order);
+       return csB->usr_order - csA->usr_order;
 }
 
 /* avoid dynamic allocation in this path */
 #define MAX_SORTED_PIDS  80
 
-static int coalition_get_sort_list(coalition_t coal, int sort_order, queue_t list,
-                                  struct coal_sort_s *sort_array, int array_sz)
+static int
+coalition_get_sort_list(coalition_t coal, int sort_order, queue_t list,
+    struct coal_sort_s *sort_array, int array_sz)
 {
        int ntasks = 0;
        task_t task;
 
        assert(sort_array != NULL);
 
-       if (array_sz <= 0)
+       if (array_sz <= 0) {
                return 0;
+       }
 
        if (!list) {
                /*
@@ -1781,13 +2407,14 @@ static int coalition_get_sort_list(coalition_t coal, int sort_order, queue_t lis
                 * to investigate the leader process
                 */
                if (coal->type != COALITION_TYPE_JETSAM ||
-                   coal->j.leader == TASK_NULL)
+                   coal->j.leader == TASK_NULL) {
                        return 0;
+               }
                sort_array[0].pid = task_pid(coal->j.leader);
                switch (sort_order) {
                case COALITION_SORT_DEFAULT:
                        sort_array[0].usr_order = 0;
-                       /* fall-through */
+                       OS_FALLTHROUGH;
                case COALITION_SORT_MEM_ASC:
                case COALITION_SORT_MEM_DEC:
                        sort_array[0].bytes = get_task_phys_footprint(coal->j.leader);
@@ -1805,7 +2432,7 @@ static int coalition_get_sort_list(coalition_t coal, int sort_order, queue_t lis
        qe_foreach_element(task, list, task_coalition[coal->type]) {
                if (ntasks >= array_sz) {
                        printf("WARNING: more than %d pids in coalition %llu\n",
-                              MAX_SORTED_PIDS, coal->id);
+                           MAX_SORTED_PIDS, coal->id);
                        break;
                }
 
@@ -1814,7 +2441,7 @@ static int coalition_get_sort_list(coalition_t coal, int sort_order, queue_t lis
                switch (sort_order) {
                case COALITION_SORT_DEFAULT:
                        sort_array[ntasks].usr_order = 0;
-                       /* fall-through */
+                       OS_FALLTHROUGH;
                case COALITION_SORT_MEM_ASC:
                case COALITION_SORT_MEM_DEC:
                        sort_array[ntasks].bytes = get_task_phys_footprint(task);
@@ -1833,20 +2460,21 @@ static int coalition_get_sort_list(coalition_t coal, int sort_order, queue_t lis
        return ntasks;
 }
 
-int coalition_get_pid_list(coalition_t coal, uint32_t rolemask, int sort_order,
-                          int *pid_list, int list_sz)
+int
+coalition_get_pid_list(coalition_t coal, uint32_t rolemask, int sort_order,
+    int *pid_list, int list_sz)
 {
        struct i_jetsam_coalition *cj;
        int ntasks = 0;
        cmpfunc_t cmp_func = NULL;
-       struct coal_sort_s sort_array[MAX_SORTED_PIDS] = { {0,0,0} }; /* keep to < 2k */
+       struct coal_sort_s sort_array[MAX_SORTED_PIDS] = { {0, 0, 0} }; /* keep to < 2k */
 
        if (!coal ||
            !(rolemask & COALITION_ROLEMASK_ALLROLES) ||
            !pid_list || list_sz < 1) {
                coal_dbg("Invalid parameters: coal:%p, type:%d, rolemask:0x%x, "
-                        "pid_list:%p, list_sz:%d", coal, coal ? coal->type : -1,
-                        rolemask, pid_list, list_sz);
+                   "pid_list:%p, list_sz:%d", coal, coal ? coal->type : -1,
+                   rolemask, pid_list, list_sz);
                return -EINVAL;
        }
 
@@ -1877,45 +2505,51 @@ int coalition_get_pid_list(coalition_t coal, uint32_t rolemask, int sort_order,
 
        if (coal->type == COALITION_TYPE_RESOURCE) {
                ntasks += coalition_get_sort_list(coal, sort_order, &coal->r.tasks,
-                                                 sort_array, MAX_SORTED_PIDS);
+                   sort_array, MAX_SORTED_PIDS);
                goto unlock_coal;
        }
 
        cj = &coal->j;
 
-       if (rolemask & COALITION_ROLEMASK_UNDEF)
+       if (rolemask & COALITION_ROLEMASK_UNDEF) {
                ntasks += coalition_get_sort_list(coal, sort_order, &cj->other,
-                                                 sort_array + ntasks,
-                                                 MAX_SORTED_PIDS - ntasks);
+                   sort_array + ntasks,
+                   MAX_SORTED_PIDS - ntasks);
+       }
 
-       if (rolemask & COALITION_ROLEMASK_XPC)
+       if (rolemask & COALITION_ROLEMASK_XPC) {
                ntasks += coalition_get_sort_list(coal, sort_order, &cj->services,
-                                                 sort_array + ntasks,
-                                                 MAX_SORTED_PIDS - ntasks);
+                   sort_array + ntasks,
+                   MAX_SORTED_PIDS - ntasks);
+       }
 
-       if (rolemask & COALITION_ROLEMASK_EXT)
+       if (rolemask & COALITION_ROLEMASK_EXT) {
                ntasks += coalition_get_sort_list(coal, sort_order, &cj->extensions,
-                                                 sort_array + ntasks,
-                                                 MAX_SORTED_PIDS - ntasks);
+                   sort_array + ntasks,
+                   MAX_SORTED_PIDS - ntasks);
+       }
 
-       if (rolemask & COALITION_ROLEMASK_LEADER)
+       if (rolemask & COALITION_ROLEMASK_LEADER) {
                ntasks += coalition_get_sort_list(coal, sort_order, NULL,
-                                                 sort_array + ntasks,
-                                                 MAX_SORTED_PIDS - ntasks);
+                   sort_array + ntasks,
+                   MAX_SORTED_PIDS - ntasks);
+       }
 
 unlock_coal:
        coalition_unlock(coal);
 
        /* sort based on the chosen criterion (no sense sorting 1 item) */
-       if (cmp_func && ntasks > 1)
+       if (cmp_func && ntasks > 1) {
                qsort(sort_array, ntasks, sizeof(struct coal_sort_s), cmp_func);
+       }
 
        for (int i = 0; i < ntasks; i++) {
-               if (i >= list_sz)
+               if (i >= list_sz) {
                        break;
+               }
                coal_dbg(" [%d] PID:%d, footprint:%lld, usr_order:%d",
-                        i, sort_array[i].pid, sort_array[i].bytes,
-                        sort_array[i].usr_order);
+                   i, sort_array[i].pid, sort_array[i].bytes,
+                   sort_array[i].usr_order);
                pid_list[i] = sort_array[i].pid;
        }