]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/sys_coalition.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / bsd / kern / sys_coalition.c
index e35a8a87829ec60614e55cc1b34d0f1fb97610f1..28e3d3f402a5e4a41a1b93f44aa8a57ba6284fe3 100644 (file)
@@ -1,4 +1,5 @@
 #include <kern/kern_types.h>
+#include <kern/thread_group.h>
 #include <mach/mach_types.h>
 #include <mach/boolean.h>
 
@@ -6,6 +7,7 @@
 
 #include <sys/coalition.h>
 #include <sys/errno.h>
+#include <sys/kauth.h>
 #include <sys/kernel.h>
 #include <sys/sysproto.h>
 #include <sys/systm.h>
@@ -31,22 +33,23 @@ coalition_create_syscall(user_addr_t cidp, uint32_t flags)
        uint64_t cid;
        coalition_t coal;
        int type = COALITION_CREATE_FLAGS_GET_TYPE(flags);
+       int role = COALITION_CREATE_FLAGS_GET_ROLE(flags);
        boolean_t privileged = !!(flags & COALITION_CREATE_FLAGS_PRIVILEGED);
 
-       if ((flags & (~COALITION_CREATE_FLAGS_MASK)) != 0)
+       if ((flags & (~COALITION_CREATE_FLAGS_MASK)) != 0) {
                return EINVAL;
-       if (type < 0 || type > COALITION_TYPE_MAX)
+       }
+       if (type < 0 || type > COALITION_TYPE_MAX) {
                return EINVAL;
+       }
 
-       kr = coalition_create_internal(type, privileged, &coal);
+       kr = coalition_create_internal(type, role, privileged, &coal, &cid);
        if (kr != KERN_SUCCESS) {
                /* for now, the only kr is KERN_RESOURCE_SHORTAGE */
                error = ENOMEM;
                goto out;
        }
 
-       cid = coalition_id(coal);
-
        coal_dbg("(addr, %u) -> %llu", flags, cid);
        error = copyout(&cid, cidp, sizeof(cid));
 out:
@@ -118,7 +121,7 @@ coalition_request_terminate_syscall(user_addr_t cidp, uint32_t flags)
  * Request the kernel to deallocate the coalition identified by ID, which
  * must be both terminated and empty. This balances the reference taken
  * in coalition_create.
- * The memory containig the coalition object may not be freed just yet, if
+ * The memory containing the coalition object may not be freed just yet, if
  * other kernel operations still hold references to it.
  *
  * Returns:
@@ -181,7 +184,8 @@ coalition_reap_syscall(user_addr_t cidp, uint32_t flags)
 /* Syscall demux.
  * Returns EPERM if the calling process is not privileged to make this call.
  */
-int coalition(proc_t p, struct coalition_args *cap, __unused int32_t *retval)
+int
+coalition(proc_t p, struct coalition_args *cap, __unused int32_t *retval)
 {
        uint32_t operation = cap->operation;
        user_addr_t cidp = cap->cid;
@@ -214,7 +218,7 @@ static int __attribute__ ((noinline))
 coalition_info_resource_usage(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
 {
        kern_return_t kr;
-       struct coalition_resource_usage cru;
+       struct coalition_resource_usage cru = {};
 
        kr = coalition_resource_usage_internal(coal, &cru);
 
@@ -232,7 +236,80 @@ coalition_info_resource_usage(coalition_t coal, user_addr_t buffer, user_size_t
        return copyout(&cru, buffer, MIN(bufsize, sizeof(cru)));
 }
 
-int coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *retval)
+#if CONFIG_THREAD_GROUPS
+static int
+coalition_info_set_name_internal(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
+{
+       int error;
+       char name[THREAD_GROUP_MAXNAME];
+
+       if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
+               return EINVAL;
+       }
+       bzero(name, sizeof(name));
+       error = copyin(buffer, name, MIN(bufsize, sizeof(name) - 1));
+       if (error) {
+               return error;
+       }
+       struct thread_group *tg = coalition_get_thread_group(coal);
+       thread_group_set_name(tg, name);
+       thread_group_release(tg);
+       return error;
+}
+
+#else /* CONFIG_THREAD_GROUPS */
+#define coalition_info_set_name_internal(...) 0
+#endif /* CONFIG_THREAD_GROUPS */
+
+static int
+coalition_info_efficiency(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
+{
+       int error = 0;
+       if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
+               return EINVAL;
+       }
+       uint64_t flags = 0;
+       error = copyin(buffer, &flags, MIN(bufsize, sizeof(flags)));
+       if (error) {
+               return error;
+       }
+       if ((flags & COALITION_EFFICIENCY_VALID_FLAGS) == 0) {
+               return EINVAL;
+       }
+       if (flags & COALITION_FLAGS_EFFICIENT) {
+               coalition_set_efficient(coal);
+#if CONFIG_THREAD_GROUPS
+               struct thread_group *tg = coalition_get_thread_group(coal);
+               thread_group_set_flags(tg, THREAD_GROUP_FLAGS_EFFICIENT);
+               thread_group_release(tg);
+#endif /* CONFIG_THREAD_GROUPS */
+       }
+       return error;
+}
+
+static int
+coalition_ledger_logical_writes_limit(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
+{
+       int error = 0;
+       int64_t limit = 0;
+
+       if (coalition_type(coal) != COALITION_TYPE_RESOURCE) {
+               error = EINVAL;
+               goto out;
+       }
+       error = copyin(buffer, &limit, MIN(bufsize, sizeof(limit)));
+       if (error) {
+               goto out;
+       }
+
+
+       error = coalition_ledger_set_logical_writes_limit(coal, limit);
+out:
+       return error;
+}
+
+int
+coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *retval)
 {
        user_addr_t cidp = uap->cid;
        user_addr_t buffer = uap->buffer;
@@ -271,6 +348,12 @@ int coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *
        case COALITION_INFO_RESOURCE_USAGE:
                error = coalition_info_resource_usage(coal, buffer, bufsize);
                break;
+       case COALITION_INFO_SET_NAME:
+               error = coalition_info_set_name_internal(coal, buffer, bufsize);
+               break;
+       case COALITION_INFO_SET_EFFICIENCY:
+               error = coalition_info_efficiency(coal, buffer, bufsize);
+               break;
        default:
                error = EINVAL;
        }
@@ -280,23 +363,79 @@ bad:
        return error;
 }
 
-#if defined(DEVELOPMENT) || defined(DEBUG)
+int
+coalition_ledger(__unused proc_t p, __unused struct coalition_ledger_args *uap, __unused int32_t *retval)
+{
+       user_addr_t cidp = uap->cid;
+       user_addr_t buffer = uap->buffer;
+       user_addr_t bufsizep = uap->bufsize;
+       user_size_t bufsize;
+       uint32_t operation = uap->operation;
+       int error;
+       uint64_t cid;
+       coalition_t coal = COALITION_NULL;
+
+       if (!kauth_cred_issuser(kauth_cred_get())) {
+               error = EPERM;
+               goto out;
+       }
+
+       error = copyin(cidp, &cid, sizeof(cid));
+       if (error) {
+               goto out;
+       }
+
+       coal = coalition_find_by_id(cid);
+       if (coal == COALITION_NULL) {
+               error = ESRCH;
+               goto out;
+       }
+
+       if (IS_64BIT_PROCESS(p)) {
+               user64_size_t size64;
+               error = copyin(bufsizep, &size64, sizeof(size64));
+               bufsize = (user_size_t)size64;
+       } else {
+               user32_size_t size32;
+               error = copyin(bufsizep, &size32, sizeof(size32));
+               bufsize = (user_size_t)size32;
+       }
+       if (error) {
+               goto out;
+       }
+
+       switch (operation) {
+       case COALITION_LEDGER_SET_LOGICAL_WRITES_LIMIT:
+               error = coalition_ledger_logical_writes_limit(coal, buffer, bufsize);
+               break;
+       default:
+               error = EINVAL;
+       }
+out:
+       if (coal != COALITION_NULL) {
+               coalition_release(coal);
+       }
+       return error;
+}
+#if DEVELOPMENT || DEBUG
 static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
 {
 #pragma unused(oidp, arg1, arg2)
        int error, pid;
        proc_t tproc;
        uint64_t value;
-       uint64_t ids[COALITION_NUM_TYPES];
+       uint64_t ids[COALITION_NUM_TYPES] = {};
 
 
        error = SYSCTL_IN(req, &value, sizeof(value));
-       if (error)
+       if (error) {
                return error;
-       if (!req->newptr)
+       }
+       if (!req->newptr) {
                pid = req->p->p_pid;
-       else
+       } else {
                pid = (int)value;
+       }
 
        coal_dbg("looking up coalitions for pid:%d", pid);
        tproc = proc_find(pid);
@@ -312,7 +451,7 @@ static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
 }
 
 SYSCTL_PROC(_kern, OID_AUTO, coalitions, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
-           0, 0, sysctl_coalition_get_ids, "Q", "coalition ids of a given process");
+    0, 0, sysctl_coalition_get_ids, "Q", "coalition ids of a given process");
 
 
 static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
@@ -321,16 +460,18 @@ static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
        int error, pid;
        proc_t tproc;
        int value;
-       int roles[COALITION_NUM_TYPES];
+       int roles[COALITION_NUM_TYPES] = {};
 
 
        error = SYSCTL_IN(req, &value, sizeof(value));
-       if (error)
+       if (error) {
                return error;
-       if (!req->newptr)
+       }
+       if (!req->newptr) {
                pid = req->p->p_pid;
-       else
+       } else {
                pid = (int)value;
+       }
 
        coal_dbg("looking up coalitions for pid:%d", pid);
        tproc = proc_find(pid);
@@ -346,7 +487,7 @@ static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
 }
 
 SYSCTL_PROC(_kern, OID_AUTO, coalition_roles, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
-           0, 0, sysctl_coalition_get_roles, "I", "coalition roles of a given process");
+    0, 0, sysctl_coalition_get_roles, "I", "coalition roles of a given process");
 
 
 static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
@@ -360,12 +501,14 @@ static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
 
 
        error = SYSCTL_IN(req, &value, sizeof(value));
-       if (error)
+       if (error) {
                return error;
-       if (!req->newptr)
+       }
+       if (!req->newptr) {
                pid = req->p->p_pid;
-       else
+       } else {
                pid = (int)value;
+       }
 
        coal_dbg("looking up coalitions for pid:%d", pid);
        tproc = proc_find(pid);
@@ -377,13 +520,12 @@ static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
        memset(pgcount, 0, sizeof(pgcount));
 
        for (int t = 0; t < COALITION_NUM_TYPES; t++) {
-               coal = COALITION_NULL;
-               coalition_is_leader(tproc->task, t, &coal);
+               coal = task_get_coalition(tproc->task, t);
                if (coal != COALITION_NULL) {
                        int ntasks = 0;
                        pgcount[t] = coalition_get_page_count(coal, &ntasks);
                        coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
-                                pid, coalition_id(coal), t, pgcount[t]);
+                           pid, coalition_id(coal), t, pgcount[t]);
                }
        }
 
@@ -393,7 +535,7 @@ static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
 }
 
 SYSCTL_PROC(_kern, OID_AUTO, coalition_page_count, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
-           0, 0, sysctl_coalition_get_page_count, "Q", "coalition page count of a specified process");
+    0, 0, sysctl_coalition_get_page_count, "Q", "coalition page count of a specified process");
 
 
 static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
@@ -414,8 +556,9 @@ static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
                has_pid = 0;
                error = SYSCTL_IN(req, &value, sizeof(value) - sizeof(value[0]));
        }
-       if (error)
+       if (error) {
                return error;
+       }
        if (!req->newptr) {
                type = COALITION_TYPE_RESOURCE;
                sort_order = COALITION_SORT_DEFAULT;
@@ -423,47 +566,55 @@ static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
        } else {
                type = value[0];
                sort_order = value[1];
-               if (has_pid)
+               if (has_pid) {
                        pid = value[2];
-               else
+               } else {
                        pid = req->p->p_pid;
+               }
        }
 
-       if (type < 0 || type >= COALITION_NUM_TYPES)
+       if (type < 0 || type >= COALITION_NUM_TYPES) {
                return EINVAL;
+       }
 
        coal_dbg("getting constituent PIDS for coalition of type %d "
-                "containing pid:%d (sort:%d)", type, pid, sort_order);
+           "containing pid:%d (sort:%d)", type, pid, sort_order);
        tproc = proc_find(pid);
        if (tproc == NULL) {
                coal_dbg("ERROR: Couldn't find pid:%d", pid);
                return ESRCH;
        }
 
-       (void)coalition_is_leader(tproc->task, type, &coal);
+       coal = task_get_coalition(tproc->task, type);
        if (coal == COALITION_NULL) {
                goto out;
        }
 
        npids = coalition_get_pid_list(coal, COALITION_ROLEMASK_ALLROLES, sort_order,
-                                      pidlist, sizeof(pidlist) / sizeof(pidlist[0]));
+           pidlist, sizeof(pidlist) / sizeof(pidlist[0]));
        if (npids > (int)(sizeof(pidlist) / sizeof(pidlist[0]))) {
                coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
-                        coalition_id(coal), pid, npids);
+                   coalition_id(coal), pid, npids);
                npids = sizeof(pidlist) / sizeof(pidlist[0]);
        }
 
 out:
        proc_rele(tproc);
 
-       if (npids == 0)
+       if (npids < 0) {
+               /* npids is a negative errno */
+               return -npids;
+       }
+
+       if (npids == 0) {
                return ENOENT;
+       }
 
        return SYSCTL_OUT(req, pidlist, sizeof(pidlist[0]) * npids);
 }
 
 SYSCTL_PROC(_kern, OID_AUTO, coalition_pid_list, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
-           0, 0, sysctl_coalition_get_pid_list, "I", "list of PIDS which are members of the coalition of the current process");
+    0, 0, sysctl_coalition_get_pid_list, "I", "list of PIDS which are members of the coalition of the current process");
 
 #if DEVELOPMENT
 static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
@@ -477,12 +628,14 @@ static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
        error = SYSCTL_IN(req, value, sizeof(value));
        if (error) {
                error = SYSCTL_IN(req, value, sizeof(value) - sizeof(value[0]));
-               if (error)
+               if (error) {
                        return error;
+               }
                should_set = 0;
        }
-       if (!req->newptr)
+       if (!req->newptr) {
                return error;
+       }
 
        coal = coalition_find_by_id(value[0]);
        if (coal == COALITION_NULL) {
@@ -490,8 +643,9 @@ static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
                return ESRCH;
        }
 
-       if (should_set)
+       if (should_set) {
                coalition_set_notify(coal, (int)value[1]);
+       }
 
        value[0] = (uint64_t)coalition_should_notify(coal);
 
@@ -501,12 +655,12 @@ static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
 }
 
 SYSCTL_PROC(_kern, OID_AUTO, coalition_notify, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
-           0, 0, sysctl_coalition_notify, "Q", "get/set coalition notification flag");
+    0, 0, sysctl_coalition_notify, "Q", "get/set coalition notification flag");
 
 extern int unrestrict_coalition_syscalls;
 SYSCTL_INT(_kern, OID_AUTO, unrestrict_coalitions,
-          CTLFLAG_RW, &unrestrict_coalition_syscalls, 0,
-          "unrestrict the coalition interface");
+    CTLFLAG_RW, &unrestrict_coalition_syscalls, 0,
+    "unrestrict the coalition interface");
 
 #endif /* DEVELOPMENT */