summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2016-02-28 17:52:33 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2016-02-28 17:52:33 +0000
commit1bdbd705993eab79189dff87b69a9cff5c69b17e (patch)
tree93354adb0a612a635964c8498072087760a0f93b /sys/kern
parent3f3af790f95027d0c3b859a53eb82ef5d00dc4f8 (diff)
Notes
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_resource.c7
-rw-r--r--sys/kern/kern_umtx.c322
-rw-r--r--sys/kern/uipc_shm.c17
3 files changed, 335 insertions, 11 deletions
diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c
index cbcd680be243..dfe68a52b921 100644
--- a/sys/kern/kern_resource.c
+++ b/sys/kern/kern_resource.c
@@ -1432,3 +1432,10 @@ chgkqcnt(struct uidinfo *uip, int diff, rlim_t max)
return (chglimit(uip, &uip->ui_kqcnt, diff, max, "kqcnt"));
}
+
+int
+chgumtxcnt(struct uidinfo *uip, int diff, rlim_t max)
+{
+
+ return (chglimit(uip, &uip->ui_umtxcnt, diff, max, "umtxcnt"));
+}
diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
index 681120fd45ba..9474b0bf8b43 100644
--- a/sys/kern/kern_umtx.c
+++ b/sys/kern/kern_umtx.c
@@ -1,8 +1,12 @@
/*-
+ * Copyright (c) 2015 The FreeBSD Foundation
* Copyright (c) 2004, David Xu <davidxu@freebsd.org>
* Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org>
* All rights reserved.
*
+ * Portions of this software were developed by Konstantin Belousov
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -33,12 +37,19 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/malloc.h>
+#include <sys/mman.h>
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/resourcevar.h>
+#include <sys/rwlock.h>
#include <sys/sbuf.h>
#include <sys/sched.h>
#include <sys/smp.h>
@@ -47,9 +58,12 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/syscallsubr.h>
+#include <sys/taskqueue.h>
#include <sys/eventhandler.h>
#include <sys/umtx.h>
+#include <security/mac/mac_framework.h>
+
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
@@ -213,6 +227,7 @@ SYSCTL_LONG(_debug_umtx, OID_AUTO, max_length, CTLFLAG_RD, &max_length, 0, "max_
static SYSCTL_NODE(_debug_umtx, OID_AUTO, chains, CTLFLAG_RD, 0, "umtx chain stats");
#endif
+static void umtx_shm_init(void);
static void umtxq_sysinit(void *);
static void umtxq_hash(struct umtx_key *key);
static struct umtxq_chain *umtxq_getchain(struct umtx_key *key);
@@ -399,6 +414,7 @@ umtxq_sysinit(void *arg __unused)
mtx_init(&umtx_lock, "umtx lock", NULL, MTX_DEF);
EVENTHANDLER_REGISTER(process_exec, umtx_exec_hook, NULL,
EVENTHANDLER_PRI_ANY);
+ umtx_shm_init();
}
struct umtx_q *
@@ -3440,6 +3456,310 @@ __umtx_op_sem2_wake(struct thread *td, struct _umtx_op_args *uap)
return (do_sem2_wake(td, uap->obj));
}
+#define USHM_OBJ_UMTX(o) \
+ ((struct umtx_shm_obj_list *)(&(o)->umtx_data))
+
+#define USHMF_REG_LINKED 0x0001
+#define USHMF_OBJ_LINKED 0x0002
+struct umtx_shm_reg {
+ TAILQ_ENTRY(umtx_shm_reg) ushm_reg_link;
+ LIST_ENTRY(umtx_shm_reg) ushm_obj_link;
+ struct umtx_key ushm_key;
+ struct ucred *ushm_cred;
+ struct shmfd *ushm_obj;
+ u_int ushm_refcnt;
+ u_int ushm_flags;
+};
+
+LIST_HEAD(umtx_shm_obj_list, umtx_shm_reg);
+TAILQ_HEAD(umtx_shm_reg_head, umtx_shm_reg);
+
+static uma_zone_t umtx_shm_reg_zone;
+static struct umtx_shm_reg_head umtx_shm_registry[UMTX_CHAINS];
+static struct mtx umtx_shm_lock;
+static struct umtx_shm_reg_head umtx_shm_reg_delfree =
+ TAILQ_HEAD_INITIALIZER(umtx_shm_reg_delfree);
+
+static void umtx_shm_free_reg(struct umtx_shm_reg *reg);
+
+static void
+umtx_shm_reg_delfree_tq(void *context __unused, int pending __unused)
+{
+ struct umtx_shm_reg_head d;
+ struct umtx_shm_reg *reg, *reg1;
+
+ TAILQ_INIT(&d);
+ mtx_lock(&umtx_shm_lock);
+ TAILQ_CONCAT(&d, &umtx_shm_reg_delfree, ushm_reg_link);
+ mtx_unlock(&umtx_shm_lock);
+ TAILQ_FOREACH_SAFE(reg, &d, ushm_reg_link, reg1) {
+ TAILQ_REMOVE(&d, reg, ushm_reg_link);
+ umtx_shm_free_reg(reg);
+ }
+}
+
+static struct task umtx_shm_reg_delfree_task =
+ TASK_INITIALIZER(0, umtx_shm_reg_delfree_tq, NULL);
+
+static struct umtx_shm_reg *
+umtx_shm_find_reg_locked(const struct umtx_key *key)
+{
+ struct umtx_shm_reg *reg;
+ struct umtx_shm_reg_head *reg_head;
+
+ KASSERT(key->shared, ("umtx_p_find_rg: private key"));
+ mtx_assert(&umtx_shm_lock, MA_OWNED);
+ reg_head = &umtx_shm_registry[key->hash];
+ TAILQ_FOREACH(reg, reg_head, ushm_reg_link) {
+ KASSERT(reg->ushm_key.shared,
+ ("non-shared key on reg %p %d", reg, reg->ushm_key.shared));
+ if (reg->ushm_key.info.shared.object ==
+ key->info.shared.object &&
+ reg->ushm_key.info.shared.offset ==
+ key->info.shared.offset) {
+ KASSERT(reg->ushm_key.type == TYPE_SHM, ("TYPE_USHM"));
+ KASSERT(reg->ushm_refcnt > 0,
+ ("reg %p refcnt 0 onlist", reg));
+ KASSERT((reg->ushm_flags & USHMF_REG_LINKED) != 0,
+ ("reg %p not linked", reg));
+ reg->ushm_refcnt++;
+ return (reg);
+ }
+ }
+ return (NULL);
+}
+
+static struct umtx_shm_reg *
+umtx_shm_find_reg(const struct umtx_key *key)
+{
+ struct umtx_shm_reg *reg;
+
+ mtx_lock(&umtx_shm_lock);
+ reg = umtx_shm_find_reg_locked(key);
+ mtx_unlock(&umtx_shm_lock);
+ return (reg);
+}
+
+static void
+umtx_shm_free_reg(struct umtx_shm_reg *reg)
+{
+
+ chgumtxcnt(reg->ushm_cred->cr_ruidinfo, -1, 0);
+ crfree(reg->ushm_cred);
+ shm_drop(reg->ushm_obj);
+ uma_zfree(umtx_shm_reg_zone, reg);
+}
+
+static bool
+umtx_shm_unref_reg_locked(struct umtx_shm_reg *reg, bool force)
+{
+ bool res;
+
+ mtx_assert(&umtx_shm_lock, MA_OWNED);
+ KASSERT(reg->ushm_refcnt > 0, ("ushm_reg %p refcnt 0", reg));
+ reg->ushm_refcnt--;
+ res = reg->ushm_refcnt == 0;
+ if (res || force) {
+ if ((reg->ushm_flags & USHMF_REG_LINKED) != 0) {
+ TAILQ_REMOVE(&umtx_shm_registry[reg->ushm_key.hash],
+ reg, ushm_reg_link);
+ reg->ushm_flags &= ~USHMF_REG_LINKED;
+ }
+ if ((reg->ushm_flags & USHMF_OBJ_LINKED) != 0) {
+ LIST_REMOVE(reg, ushm_obj_link);
+ reg->ushm_flags &= ~USHMF_OBJ_LINKED;
+ }
+ }
+ return (res);
+}
+
+static void
+umtx_shm_unref_reg(struct umtx_shm_reg *reg, bool force)
+{
+ vm_object_t object;
+ bool dofree;
+
+ if (force) {
+ object = reg->ushm_obj->shm_object;
+ VM_OBJECT_WLOCK(object);
+ object->flags |= OBJ_UMTXDEAD;
+ VM_OBJECT_WUNLOCK(object);
+ }
+ mtx_lock(&umtx_shm_lock);
+ dofree = umtx_shm_unref_reg_locked(reg, force);
+ mtx_unlock(&umtx_shm_lock);
+ if (dofree)
+ umtx_shm_free_reg(reg);
+}
+
+void
+umtx_shm_object_init(vm_object_t object)
+{
+
+ LIST_INIT(USHM_OBJ_UMTX(object));
+}
+
+void
+umtx_shm_object_terminated(vm_object_t object)
+{
+ struct umtx_shm_reg *reg, *reg1;
+ bool dofree;
+
+ dofree = false;
+ mtx_lock(&umtx_shm_lock);
+ LIST_FOREACH_SAFE(reg, USHM_OBJ_UMTX(object), ushm_obj_link, reg1) {
+ if (umtx_shm_unref_reg_locked(reg, true)) {
+ TAILQ_INSERT_TAIL(&umtx_shm_reg_delfree, reg,
+ ushm_reg_link);
+ dofree = true;
+ }
+ }
+ mtx_unlock(&umtx_shm_lock);
+ if (dofree)
+ taskqueue_enqueue(taskqueue_thread, &umtx_shm_reg_delfree_task);
+}
+
+static int
+umtx_shm_create_reg(struct thread *td, const struct umtx_key *key,
+ struct umtx_shm_reg **res)
+{
+ struct umtx_shm_reg *reg, *reg1;
+ struct ucred *cred;
+ int error;
+
+ reg = umtx_shm_find_reg(key);
+ if (reg != NULL) {
+ *res = reg;
+ return (0);
+ }
+ cred = td->td_ucred;
+ if (!chgumtxcnt(cred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_UMTXP)))
+ return (ENOMEM);
+ reg = uma_zalloc(umtx_shm_reg_zone, M_WAITOK | M_ZERO);
+ reg->ushm_refcnt = 1;
+ bcopy(key, &reg->ushm_key, sizeof(*key));
+ reg->ushm_obj = shm_alloc(td->td_ucred, O_RDWR);
+ reg->ushm_cred = crhold(cred);
+ error = shm_dotruncate(reg->ushm_obj, PAGE_SIZE);
+ if (error != 0) {
+ umtx_shm_free_reg(reg);
+ return (error);
+ }
+ mtx_lock(&umtx_shm_lock);
+ reg1 = umtx_shm_find_reg_locked(key);
+ if (reg1 != NULL) {
+ mtx_unlock(&umtx_shm_lock);
+ umtx_shm_free_reg(reg);
+ *res = reg1;
+ return (0);
+ }
+ reg->ushm_refcnt++;
+ TAILQ_INSERT_TAIL(&umtx_shm_registry[key->hash], reg, ushm_reg_link);
+ LIST_INSERT_HEAD(USHM_OBJ_UMTX(key->info.shared.object), reg,
+ ushm_obj_link);
+ reg->ushm_flags = USHMF_REG_LINKED | USHMF_OBJ_LINKED;
+ mtx_unlock(&umtx_shm_lock);
+ *res = reg;
+ return (0);
+}
+
+static int
+umtx_shm_alive(struct thread *td, void *addr)
+{
+ vm_map_t map;
+ vm_map_entry_t entry;
+ vm_object_t object;
+ vm_pindex_t pindex;
+ vm_prot_t prot;
+ int res, ret;
+ boolean_t wired;
+
+ map = &td->td_proc->p_vmspace->vm_map;
+ res = vm_map_lookup(&map, (uintptr_t)addr, VM_PROT_READ, &entry,
+ &object, &pindex, &prot, &wired);
+ if (res != KERN_SUCCESS)
+ return (EFAULT);
+ if (object == NULL)
+ ret = EINVAL;
+ else
+ ret = (object->flags & OBJ_UMTXDEAD) != 0 ? ENOTTY : 0;
+ vm_map_lookup_done(map, entry);
+ return (ret);
+}
+
+static void
+umtx_shm_init(void)
+{
+ int i;
+
+ umtx_shm_reg_zone = uma_zcreate("umtx_shm", sizeof(struct umtx_shm_reg),
+ NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+ mtx_init(&umtx_shm_lock, "umtxshm", NULL, MTX_DEF);
+ for (i = 0; i < nitems(umtx_shm_registry); i++)
+ TAILQ_INIT(&umtx_shm_registry[i]);
+}
+
+static int
+umtx_shm(struct thread *td, void *addr, u_int flags)
+{
+ struct umtx_key key;
+ struct umtx_shm_reg *reg;
+ struct file *fp;
+ int error, fd;
+
+ if (__bitcount(flags & (UMTX_SHM_CREAT | UMTX_SHM_LOOKUP |
+ UMTX_SHM_DESTROY| UMTX_SHM_ALIVE)) != 1)
+ return (EINVAL);
+ if ((flags & UMTX_SHM_ALIVE) != 0)
+ return (umtx_shm_alive(td, addr));
+ error = umtx_key_get(addr, TYPE_SHM, PROCESS_SHARE, &key);
+ if (error != 0)
+ return (error);
+ KASSERT(key.shared == 1, ("non-shared key"));
+ if ((flags & UMTX_SHM_CREAT) != 0) {
+ error = umtx_shm_create_reg(td, &key, &reg);
+ } else {
+ reg = umtx_shm_find_reg(&key);
+ if (reg == NULL)
+ error = ESRCH;
+ }
+ umtx_key_release(&key);
+ if (error != 0)
+ return (error);
+ KASSERT(reg != NULL, ("no reg"));
+ if ((flags & UMTX_SHM_DESTROY) != 0) {
+ umtx_shm_unref_reg(reg, true);
+ } else {
+#if 0
+#ifdef MAC
+ error = mac_posixshm_check_open(td->td_ucred,
+ reg->ushm_obj, FFLAGS(O_RDWR));
+ if (error == 0)
+#endif
+ error = shm_access(reg->ushm_obj, td->td_ucred,
+ FFLAGS(O_RDWR));
+ if (error == 0)
+#endif
+ error = falloc_caps(td, &fp, &fd, O_CLOEXEC, NULL);
+ if (error == 0) {
+ shm_hold(reg->ushm_obj);
+ finit(fp, FFLAGS(O_RDWR), DTYPE_SHM, reg->ushm_obj,
+ &shm_ops);
+ td->td_retval[0] = fd;
+ fdrop(fp, td);
+ }
+ }
+ umtx_shm_unref_reg(reg, false);
+ return (error);
+}
+
+static int
+__umtx_op_shm(struct thread *td, struct _umtx_op_args *uap)
+{
+
+ return (umtx_shm(td, uap->uaddr1, uap->val));
+}
+
typedef int (*_umtx_op_func)(struct thread *td, struct _umtx_op_args *uap);
static const _umtx_op_func op_table[] = {
@@ -3473,6 +3793,7 @@ static const _umtx_op_func op_table[] = {
[UMTX_OP_MUTEX_WAKE2] = __umtx_op_wake2_umutex,
[UMTX_OP_SEM2_WAIT] = __umtx_op_sem2_wait,
[UMTX_OP_SEM2_WAKE] = __umtx_op_sem2_wake,
+ [UMTX_OP_SHM] = __umtx_op_shm,
};
int
@@ -3768,6 +4089,7 @@ static const _umtx_op_func op_table_compat32[] = {
[UMTX_OP_MUTEX_WAKE2] = __umtx_op_wake2_umutex,
[UMTX_OP_SEM2_WAIT] = __umtx_op_sem2_wait_compat32,
[UMTX_OP_SEM2_WAKE] = __umtx_op_sem2_wake,
+ [UMTX_OP_SHM] = __umtx_op_shm,
};
int
diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c
index 8df6e43db0c9..a2146b99cc3e 100644
--- a/sys/kern/uipc_shm.c
+++ b/sys/kern/uipc_shm.c
@@ -109,15 +109,10 @@ static dev_t shm_dev_ino;
#define SHM_HASH(fnv) (&shm_dictionary[(fnv) & shm_hash])
-static int shm_access(struct shmfd *shmfd, struct ucred *ucred, int flags);
-static struct shmfd *shm_alloc(struct ucred *ucred, mode_t mode);
static void shm_init(void *arg);
-static void shm_drop(struct shmfd *shmfd);
-static struct shmfd *shm_hold(struct shmfd *shmfd);
static void shm_insert(char *path, Fnv32_t fnv, struct shmfd *shmfd);
static struct shmfd *shm_lookup(char *path, Fnv32_t fnv);
static int shm_remove(char *path, Fnv32_t fnv, struct ucred *ucred);
-static int shm_dotruncate(struct shmfd *shmfd, off_t length);
static fo_rdwr_t shm_read;
static fo_rdwr_t shm_write;
@@ -131,7 +126,7 @@ static fo_fill_kinfo_t shm_fill_kinfo;
static fo_mmap_t shm_mmap;
/* File descriptor operations. */
-static struct fileops shm_ops = {
+struct fileops shm_ops = {
.fo_read = shm_read,
.fo_write = shm_write,
.fo_truncate = shm_truncate,
@@ -412,7 +407,7 @@ shm_close(struct file *fp, struct thread *td)
return (0);
}
-static int
+int
shm_dotruncate(struct shmfd *shmfd, off_t length)
{
vm_object_t object;
@@ -521,7 +516,7 @@ retry:
* shmfd object management including creation and reference counting
* routines.
*/
-static struct shmfd *
+struct shmfd *
shm_alloc(struct ucred *ucred, mode_t mode)
{
struct shmfd *shmfd;
@@ -559,7 +554,7 @@ shm_alloc(struct ucred *ucred, mode_t mode)
return (shmfd);
}
-static struct shmfd *
+struct shmfd *
shm_hold(struct shmfd *shmfd)
{
@@ -567,7 +562,7 @@ shm_hold(struct shmfd *shmfd)
return (shmfd);
}
-static void
+void
shm_drop(struct shmfd *shmfd)
{
@@ -588,7 +583,7 @@ shm_drop(struct shmfd *shmfd)
* Determine if the credentials have sufficient permissions for a
* specified combination of FREAD and FWRITE.
*/
-static int
+int
shm_access(struct shmfd *shmfd, struct ucred *ucred, int flags)
{
accmode_t accmode;