#include <kern/kern_types.h>
+#include <kern/thread_group.h>
#include <mach/mach_types.h>
#include <mach/boolean.h>
#include <sys/coalition.h>
#include <sys/errno.h>
+#include <sys/kauth.h>
#include <sys/kernel.h>
#include <sys/sysproto.h>
#include <sys/systm.h>
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:
* 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:
/* 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;
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);
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;
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;
}
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);
}
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
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);
}
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
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);
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]);
}
}
}
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
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;
} 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
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) {
return ESRCH;
}
- if (should_set)
+ if (should_set) {
coalition_set_notify(coal, (int)value[1]);
+ }
value[0] = (uint64_t)coalition_should_notify(coal);
}
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 */