Module Name: src
Committed By: thorpej
Date: Sun Feb 3 03:20:24 UTC 2019
Modified Files:
src/distrib/sets/lists/tests: mi
src/lib/libpthread: Makefile
src/lib/librt: sem.c
src/sys/compat/netbsd32: netbsd32_sem.c
src/sys/kern: uipc_sem.c
src/sys/sys: ksem.h
src/tests/kernel: Makefile
src/tests/lib/librt: t_sem.c
src/usr.bin/fstat: misc.c
Added Files:
src/tests/kernel: t_ksem.c
Removed Files:
src/lib/libpthread: sem.c
Log Message:
Implement support for "pshared" POSIX semaphores.
Fixes lib/53273 (and Firefox's multi-process tab feature).
To generate a diff of this commit:
cvs rdiff -u -r1.804 -r1.805 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.90 -r1.91 src/lib/libpthread/Makefile
cvs rdiff -u -r1.24 -r0 src/lib/libpthread/sem.c
cvs rdiff -u -r1.7 -r1.8 src/lib/librt/sem.c
cvs rdiff -u -r1.11 -r1.12 src/sys/compat/netbsd32/netbsd32_sem.c
cvs rdiff -u -r1.51 -r1.52 src/sys/kern/uipc_sem.c
cvs rdiff -u -r1.14 -r1.15 src/sys/sys/ksem.h
cvs rdiff -u -r1.56 -r1.57 src/tests/kernel/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/kernel/t_ksem.c
cvs rdiff -u -r1.3 -r1.4 src/tests/lib/librt/t_sem.c
cvs rdiff -u -r1.20 -r1.21 src/usr.bin/fstat/misc.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.804 src/distrib/sets/lists/tests/mi:1.805
--- src/distrib/sets/lists/tests/mi:1.804 Fri Jan 25 18:33:58 2019
+++ src/distrib/sets/lists/tests/mi Sun Feb 3 03:20:23 2019
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.804 2019/01/25 18:33:58 christos Exp $
+# $NetBSD: mi,v 1.805 2019/02/03 03:20:23 thorpej Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -2180,6 +2180,7 @@
./usr/tests/kernel/t_filedesc tests-kernel-tests atf,rump
./usr/tests/kernel/t_interp tests-kernel-tests atf
./usr/tests/kernel/t_kauth_pr_47598 tests-kernel-tests compattestfile,atf
+./usr/tests/kernel/t_ksem tests-kernel-tests atf
./usr/tests/kernel/t_lock tests-kernel-tests compattestfile,atf
./usr/tests/kernel/t_lockf tests-kernel-tests compattestfile,atf
./usr/tests/kernel/t_lwpctl tests-obsolete obsolete
Index: src/lib/libpthread/Makefile
diff -u src/lib/libpthread/Makefile:1.90 src/lib/libpthread/Makefile:1.91
--- src/lib/libpthread/Makefile:1.90 Sat Jun 9 23:45:56 2018
+++ src/lib/libpthread/Makefile Sun Feb 3 03:20:24 2019
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.90 2018/06/09 23:45:56 christos Exp $
+# $NetBSD: Makefile,v 1.91 2019/02/03 03:20:24 thorpej Exp $
#
NOSANITIZER= # defined
@@ -66,6 +66,7 @@ SRCS+= pthread_specific.c
SRCS+= pthread_spin.c
SRCS+= pthread_tsd.c
SRCS+= res_state.c
+.PATH: ${.CURDIR}/../librt
SRCS+= sem.c
# Architecture-dependent files
.if exists(${ARCHDIR}/pthread_md.S)
Index: src/lib/librt/sem.c
diff -u src/lib/librt/sem.c:1.7 src/lib/librt/sem.c:1.8
--- src/lib/librt/sem.c:1.7 Sat Mar 10 19:59:21 2012
+++ src/lib/librt/sem.c Sun Feb 3 03:20:24 2019
@@ -1,7 +1,7 @@
-/* $NetBSD: sem.c,v 1.7 2012/03/10 19:59:21 joerg Exp $ */
+/* $NetBSD: sem.c,v 1.8 2019/02/03 03:20:24 thorpej Exp $ */
/*-
- * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * Copyright (c) 2003, 2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -59,12 +59,18 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: sem.c,v 1.7 2012/03/10 19:59:21 joerg Exp $");
+__RCSID("$NetBSD: sem.c,v 1.8 2019/02/03 03:20:24 thorpej Exp $");
+#ifndef __LIBPTHREAD_SOURCE__
/*
- * If an application is linked against both librt and libpthread, the
- * libpthread versions must be used. Provide weak aliases to cause
- * this behavior.
+ * There is no longer any difference between the libpthread and the librt
+ * versions of sem.c; both are fully kernel-assisted via the _ksem_*()
+ * system calls. The only difference is the need to lock some internal
+ * data structures in the pthread version, which could be achieved by
+ * different means. However, in order to maintain binary compatibility
+ * with applications that use POSIX semaphores and linked against only
+ * libpthread, we continue to maintain a copy of the implementation here
+ * that does not depend on any additional libraries (other than libc).
*/
#define sem_init _librt_sem_init
#define sem_destroy _librt_sem_destroy
@@ -76,7 +82,9 @@ __RCSID("$NetBSD: sem.c,v 1.7 2012/03/10
#define sem_trywait _librt_sem_trywait
#define sem_post _librt_sem_post
#define sem_getvalue _librt_sem_getvalue
+#endif /* ! __LIBPTHREAD_SOURCE__ */
+#undef _LIBC
#define _LIBC
#include <sys/types.h>
@@ -88,20 +96,47 @@ __RCSID("$NetBSD: sem.c,v 1.7 2012/03/10
#include <semaphore.h>
#include <stdarg.h>
+#ifdef __LIBPTHREAD_SOURCE__
+#include "pthread.h"
+#endif /* __LIBPTHREAD_SOURCE__ */
+
+#define SEM_NAMED 0x4e414d44U /* 'NAMD' */
+#define SEM_MAGIC 0x90af0421U
+#define SEM_MAGIC_NAMED (SEM_MAGIC ^ SEM_NAMED)
+
+#define SEM_IS_KSEMID(k) ((((intptr_t)(k)) & KSEM_MARKER_MASK) \
+ == KSEM_PSHARED_MARKER)
+
+#define SEM_IS_UNNAMED(k) (SEM_IS_KSEMID(k) || \
+ (k)->ksem_magic == SEM_MAGIC)
+
+#define SEM_IS_NAMED(k) (!SEM_IS_UNNAMED(k))
+
+#define SEM_MAGIC_OK(k) (SEM_IS_KSEMID(k) || \
+ (k)->ksem_magic == SEM_MAGIC || \
+ (k)->ksem_magic == SEM_MAGIC_NAMED)
+
struct _sem_st {
unsigned int ksem_magic;
-#define KSEM_MAGIC 0x90af0421U
+ intptr_t ksem_semid;
+ /* Used only to de-dup named semaphores. */
LIST_ENTRY(_sem_st) ksem_list;
- intptr_t ksem_semid; /* 0 -> user (non-shared) */
sem_t *ksem_identity;
};
-static int sem_alloc(unsigned int value, intptr_t semid, sem_t *semp);
-static void sem_free(sem_t sem);
-
static LIST_HEAD(, _sem_st) named_sems = LIST_HEAD_INITIALIZER(&named_sems);
+#ifdef __LIBPTHREAD_SOURCE__
+static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK_NAMED_SEMS() pthread_mutex_lock(&named_sems_mtx)
+#define UNLOCK_NAMED_SEMS() pthread_mutex_unlock(&named_sems_mtx)
+#else /* ! __LIBPTHREAD_SOURCE__ */
+#define LOCK_NAMED_SEMS() __nothing
+#define UNLOCK_NAMED_SEMS() __nothing
+#endif /* __LIBPTHREAD_SOURCE__ */
+
+#ifndef __LIBPTHREAD_SOURCE__
#ifdef __weak_alias
__weak_alias(sem_init,_librt_sem_init)
__weak_alias(sem_destroy,_librt_sem_destroy)
@@ -113,7 +148,20 @@ __weak_alias(sem_timedwait,_librt_sem_ti
__weak_alias(sem_trywait,_librt_sem_trywait)
__weak_alias(sem_post,_librt_sem_post)
__weak_alias(sem_getvalue,_librt_sem_getvalue)
-#endif
+#else
+#error Weak aliases required to build POSIX semaphore support.
+#endif /* __weak_alias */
+#endif /* __LIBPTHREAD_SOURCE__ */
+
+static inline intptr_t
+sem_to_semid(sem_t *sem)
+{
+
+ if (SEM_IS_KSEMID(*sem))
+ return (intptr_t)*sem;
+
+ return (*sem)->ksem_semid;
+}
static void
sem_free(sem_t sem)
@@ -124,7 +172,7 @@ sem_free(sem_t sem)
}
static int
-sem_alloc(unsigned int value, intptr_t semid, sem_t *semp)
+sem_alloc(unsigned int value, intptr_t semid, unsigned int magic, sem_t *semp)
{
sem_t sem;
@@ -134,7 +182,7 @@ sem_alloc(unsigned int value, intptr_t s
if ((sem = malloc(sizeof(struct _sem_st))) == NULL)
return (ENOSPC);
- sem->ksem_magic = KSEM_MAGIC;
+ sem->ksem_magic = magic;
sem->ksem_semid = semid;
*semp = sem;
@@ -145,13 +193,36 @@ sem_alloc(unsigned int value, intptr_t s
int
sem_init(sem_t *sem, int pshared, unsigned int value)
{
- intptr_t semid;
+ intptr_t semid = pshared ? KSEM_PSHARED : 0;
int error;
if (_ksem_init(value, &semid) == -1)
return (-1);
- if ((error = sem_alloc(value, semid, sem)) != 0) {
+ /*
+ * pshared anonymous semaphores are treated a little differently.
+ * We don't allocate a sem structure and return a pointer to it.
+ * That pointer might live in the shared memory segment that's
+ * shared between processes, but the _sem_st that contains the
+ * important bits certainly would not be.
+ *
+ * So, instead, we return the ksem ID given to us by the kernel.
+ * The kernel has arranged for the least-significant bit of the
+ * ksem ID to always be 1 so as to ensure we can always tell
+ * these IDs apart from the pointers that we vend out for other
+ * non-pshared semaphores.
+ */
+ if (pshared) {
+ if ((semid & KSEM_MARKER_MASK) != KSEM_PSHARED_MARKER) {
+ _ksem_destroy(semid);
+ errno = ENOTSUP;
+ return (-1);
+ }
+ *sem = (sem_t)semid;
+ return (0);
+ }
+
+ if ((error = sem_alloc(value, semid, SEM_MAGIC, sem)) != 0) {
_ksem_destroy(semid);
errno = error;
return (-1);
@@ -166,16 +237,25 @@ sem_destroy(sem_t *sem)
int error, save_errno;
#ifdef ERRORCHECK
- if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) {
+ if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
errno = EINVAL;
return (-1);
}
#endif
- error = _ksem_destroy((*sem)->ksem_semid);
- save_errno = errno;
- sem_free(*sem);
- errno = save_errno;
+ if (SEM_IS_KSEMID(*sem)) {
+ error = _ksem_destroy((intptr_t)*sem);
+ } else {
+ if (SEM_IS_NAMED(*sem)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ error = _ksem_destroy((*sem)->ksem_semid);
+ save_errno = errno;
+ sem_free(*sem);
+ errno = save_errno;
+ }
return error;
}
@@ -211,24 +291,29 @@ sem_open(const char *name, int oflag, ..
* Search for a duplicate ID, we must return the same sem_t *
* if we locate one.
*/
+ LOCK_NAMED_SEMS();
LIST_FOREACH(s, &named_sems, ksem_list) {
- if (s->ksem_semid == semid)
+ if (s->ksem_semid == semid) {
+ UNLOCK_NAMED_SEMS();
return (s->ksem_identity);
+ }
}
if ((sem = malloc(sizeof(*sem))) == NULL) {
error = ENOSPC;
goto bad;
}
- if ((error = sem_alloc(value, semid, sem)) != 0)
+ if ((error = sem_alloc(value, semid, SEM_MAGIC_NAMED, sem)) != 0)
goto bad;
LIST_INSERT_HEAD(&named_sems, *sem, ksem_list);
+ UNLOCK_NAMED_SEMS();
(*sem)->ksem_identity = sem;
return (sem);
bad:
+ UNLOCK_NAMED_SEMS();
_ksem_close(semid);
if (sem != NULL) {
if (*sem != NULL)
@@ -245,16 +330,22 @@ sem_close(sem_t *sem)
int error, save_errno;
#ifdef ERRORCHECK
- if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) {
+ if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
errno = EINVAL;
return (-1);
}
#endif
- error = _ksem_close((*sem)->ksem_semid);
+ if (!SEM_IS_NAMED(*sem)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ LOCK_NAMED_SEMS();
+ error = _ksem_close((*sem)->ksem_semid);
LIST_REMOVE((*sem), ksem_list);
save_errno = errno;
+ UNLOCK_NAMED_SEMS();
sem_free(*sem);
free(sem);
errno = save_errno;
@@ -273,13 +364,13 @@ sem_wait(sem_t *sem)
{
#ifdef ERRORCHECK
- if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) {
+ if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
errno = EINVAL;
return (-1);
}
#endif
- return (_ksem_wait((*sem)->ksem_semid));
+ return (_ksem_wait(sem_to_semid(sem)));
}
int
@@ -287,13 +378,13 @@ sem_timedwait(sem_t *sem, const struct t
{
#ifdef ERRORCHECK
- if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) {
+ if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
errno = EINVAL;
return (-1);
}
#endif
- return (_ksem_timedwait((*sem)->ksem_semid, abstime));
+ return (_ksem_timedwait(sem_to_semid(sem), abstime));
}
int
@@ -301,13 +392,13 @@ sem_trywait(sem_t *sem)
{
#ifdef ERRORCHECK
- if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) {
+ if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
errno = EINVAL;
return (-1);
}
#endif
- return (_ksem_trywait((*sem)->ksem_semid));
+ return (_ksem_trywait(sem_to_semid(sem)));
}
int
@@ -315,13 +406,13 @@ sem_post(sem_t *sem)
{
#ifdef ERRORCHECK
- if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) {
+ if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
errno = EINVAL;
return (-1);
}
#endif
- return (_ksem_post((*sem)->ksem_semid));
+ return (_ksem_post(sem_to_semid(sem)));
}
int
@@ -329,10 +420,11 @@ sem_getvalue(sem_t * __restrict sem, int
{
#ifdef ERRORCHECK
- if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) {
+ if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
errno = EINVAL;
return (-1);
}
#endif
- return (_ksem_getvalue((*sem)->ksem_semid, sval));
+
+ return (_ksem_getvalue(sem_to_semid(sem), sval));
}
Index: src/sys/compat/netbsd32/netbsd32_sem.c
diff -u src/sys/compat/netbsd32/netbsd32_sem.c:1.11 src/sys/compat/netbsd32/netbsd32_sem.c:1.12
--- src/sys/compat/netbsd32/netbsd32_sem.c:1.11 Fri Sep 19 17:25:33 2014
+++ src/sys/compat/netbsd32/netbsd32_sem.c Sun Feb 3 03:20:23 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: netbsd32_sem.c,v 1.11 2014/09/19 17:25:33 matt Exp $ */
+/* $NetBSD: netbsd32_sem.c,v 1.12 2019/02/03 03:20:23 thorpej Exp $ */
/*
* Copyright (c) 2006 The NetBSD Foundation.
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: netbsd32_sem.c,v 1.11 2014/09/19 17:25:33 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: netbsd32_sem.c,v 1.12 2019/02/03 03:20:23 thorpej Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -45,6 +45,21 @@ __KERNEL_RCSID(0, "$NetBSD: netbsd32_sem
#include <compat/netbsd32/netbsd32_conv.h>
static int
+netbsd32_ksem_copyin(const void *src, void *dst, size_t size)
+{
+ intptr_t *argp = dst;
+ netbsd32_intptr_t arg32;
+ int error;
+
+ KASSERT(size == sizeof(intptr_t));
+
+ if ((error = copyin(src, &arg32, sizeof(arg32))) != 0)
+ return error;
+ *argp = arg32;
+ return 0;
+}
+
+static int
netbsd32_ksem_copyout(const void *src, void *dst, size_t size)
{
const intptr_t *idp = src;
@@ -53,6 +68,7 @@ netbsd32_ksem_copyout(const void *src, v
KASSERT(size == sizeof(intptr_t));
/* Returning a kernel pointer to userspace sucks badly :-( */
+ /* (Luckily, it's not actually a pointer. --thorpej */
id32 = (netbsd32_intptr_t)*idp;
return copyout(&id32, outidp, sizeof(id32));
}
@@ -66,7 +82,7 @@ netbsd32__ksem_init(struct lwp *l, const
} */
return do_ksem_init(l, SCARG(uap, value),
- SCARG_P32(uap, idp), netbsd32_ksem_copyout);
+ SCARG_P32(uap, idp), netbsd32_ksem_copyin, netbsd32_ksem_copyout);
}
int
Index: src/sys/kern/uipc_sem.c
diff -u src/sys/kern/uipc_sem.c:1.51 src/sys/kern/uipc_sem.c:1.52
--- src/sys/kern/uipc_sem.c:1.51 Sun May 6 00:46:09 2018
+++ src/sys/kern/uipc_sem.c Sun Feb 3 03:20:23 2019
@@ -1,11 +1,11 @@
-/* $NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos Exp $ */
+/* $NetBSD: uipc_sem.c,v 1.52 2019/02/03 03:20:23 thorpej Exp $ */
/*-
- * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * Copyright (c) 2011, 2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
- * by Mindaugas Rasiukevicius.
+ * by Mindaugas Rasiukevicius and Jason R. Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -60,13 +60,14 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.52 2019/02/03 03:20:23 thorpej Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/atomic.h>
#include <sys/proc.h>
+#include <sys/lwp.h>
#include <sys/ksem.h>
#include <sys/syscall.h>
#include <sys/stat.h>
@@ -77,11 +78,14 @@ __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v
#include <sys/kauth.h>
#include <sys/module.h>
#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
#include <sys/semaphore.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
#include <sys/syscallvar.h>
#include <sys/sysctl.h>
+#include <sys/cprng.h>
MODULE(MODULE_CLASS_MISC, ksem, NULL);
@@ -95,6 +99,12 @@ static LIST_HEAD(,ksem) ksem_head __cach
static u_int nsems_total __cacheline_aligned;
static u_int nsems __cacheline_aligned;
+static krwlock_t ksem_pshared_lock __cacheline_aligned;
+static LIST_HEAD(, ksem) *ksem_pshared_hashtab __cacheline_aligned;
+static u_long ksem_pshared_hashmask __read_mostly;
+
+#define KSEM_PSHARED_HASHSIZE 32
+
static kauth_listener_t ksem_listener;
static int ksem_sysinit(void);
@@ -189,6 +199,11 @@ ksem_sysinit(void)
nsems_total = 0;
nsems = 0;
+ rw_init(&ksem_pshared_lock);
+ ksem_pshared_hashtab = hashinit(KSEM_PSHARED_HASHSIZE, HASH_LIST,
+ true, &ksem_pshared_hashmask);
+ KASSERT(ksem_pshared_hashtab != NULL);
+
error = syscall_establish(NULL, ksem_syscalls);
if (error) {
(void)ksem_sysfini(false);
@@ -245,6 +260,8 @@ ksem_sysfini(bool interface)
}
}
kauth_unlisten_scope(ksem_listener);
+ hashdone(ksem_pshared_hashtab, HASH_LIST, ksem_pshared_hashmask);
+ rw_destroy(&ksem_pshared_lock);
mutex_destroy(&ksem_lock);
sysctl_teardown(&ksem_clog);
return 0;
@@ -296,28 +313,121 @@ ksem_perm(lwp_t *l, ksem_t *ks)
}
/*
+ * Bits 1..23 are random, just pluck a few of those and assume the
+ * distribution is going to be pretty good.
+ */
+#define KSEM_PSHARED_HASH(id) (((id) >> 1) & ksem_pshared_hashmask)
+
+static void
+ksem_remove_pshared(ksem_t *ksem)
+{
+ rw_enter(&ksem_pshared_lock, RW_WRITER);
+ LIST_REMOVE(ksem, ks_entry);
+ rw_exit(&ksem_pshared_lock);
+}
+
+static ksem_t *
+ksem_lookup_pshared_locked(intptr_t id)
+{
+ u_long bucket = KSEM_PSHARED_HASH(id);
+ ksem_t *ksem = NULL;
+
+ /* ksem_t is locked and referenced upon return. */
+
+ LIST_FOREACH(ksem, &ksem_pshared_hashtab[bucket], ks_entry) {
+ if (ksem->ks_pshared_id == id) {
+ mutex_enter(&ksem->ks_lock);
+ if (ksem->ks_pshared_proc == NULL) {
+ /*
+ * This entry is dead, and in the process
+ * of being torn down; skip it.
+ */
+ mutex_exit(&ksem->ks_lock);
+ continue;
+ }
+ ksem->ks_ref++;
+ KASSERT(ksem->ks_ref != 0);
+ return ksem;
+ }
+ }
+
+ return NULL;
+}
+
+static ksem_t *
+ksem_lookup_pshared(intptr_t id)
+{
+ rw_enter(&ksem_pshared_lock, RW_READER);
+ ksem_t *ksem = ksem_lookup_pshared_locked(id);
+ rw_exit(&ksem_pshared_lock);
+ return ksem;
+}
+
+static void
+ksem_alloc_pshared_id(ksem_t *ksem)
+{
+ uint32_t try;
+
+ KASSERT(ksem->ks_pshared_proc != NULL);
+
+ rw_enter(&ksem_pshared_lock, RW_WRITER);
+ for (;;) {
+ try = (cprng_fast32() & ~KSEM_MARKER_MASK) |
+ KSEM_PSHARED_MARKER;
+
+ if (ksem_lookup_pshared_locked(try) == NULL) {
+ /* Got it! */
+ break;
+ }
+ }
+ ksem->ks_pshared_id = try;
+ u_long bucket = KSEM_PSHARED_HASH(ksem->ks_pshared_id);
+ LIST_INSERT_HEAD(&ksem_pshared_hashtab[bucket], ksem, ks_entry);
+ rw_exit(&ksem_pshared_lock);
+}
+
+/*
* ksem_get: get the semaphore from the descriptor.
*
- * => locks the semaphore, if found.
+ * => locks the semaphore, if found, and holds an extra reference.
* => holds a reference on the file descriptor.
*/
static int
-ksem_get(int fd, ksem_t **ksret)
+ksem_get(intptr_t id, ksem_t **ksret, int *fdp)
{
ksem_t *ks;
- file_t *fp;
+ int fd;
- fp = fd_getfile(fd);
- if (__predict_false(fp == NULL))
- return EINVAL;
- if (__predict_false(fp->f_type != DTYPE_SEM)) {
- fd_putfile(fd);
+ if ((id & KSEM_MARKER_MASK) == KSEM_PSHARED_MARKER) {
+ /*
+ * ksem_lookup_pshared() returns the ksem_t *
+ * locked and referenced.
+ */
+ ks = ksem_lookup_pshared(id);
+ if (ks == NULL)
+ return EINVAL;
+ KASSERT(ks->ks_pshared_id == id);
+ KASSERT(ks->ks_pshared_proc != NULL);
+ fd = -1;
+ } else if (id <= INT_MAX) {
+ fd = (int)id;
+ file_t *fp = fd_getfile(fd);
+
+ if (__predict_false(fp == NULL))
+ return EINVAL;
+ if (__predict_false(fp->f_type != DTYPE_SEM)) {
+ fd_putfile(fd);
+ return EINVAL;
+ }
+ ks = fp->f_ksem;
+ mutex_enter(&ks->ks_lock);
+ ks->ks_ref++;
+ } else {
return EINVAL;
}
- ks = fp->f_ksem;
- mutex_enter(&ks->ks_lock);
*ksret = ks;
+ *fdp = fd;
return 0;
}
@@ -388,6 +498,10 @@ ksem_free(ksem_t *ks)
KASSERT(!cv_has_waiters(&ks->ks_cv));
+ if (ks->ks_pshared_id) {
+ KASSERT(ks->ks_pshared_proc == NULL);
+ ksem_remove_pshared(ks);
+ }
if (ks->ks_name) {
KASSERT(ks->ks_namelen > 0);
kmem_free(ks->ks_name, ks->ks_namelen);
@@ -400,6 +514,35 @@ ksem_free(ksem_t *ks)
atomic_dec_uint(&curproc->p_nsems);
}
+#define KSEM_ID_IS_PSHARED(id) \
+ (((id) & KSEM_MARKER_MASK) == KSEM_PSHARED_MARKER)
+
+static void
+ksem_release(ksem_t *ksem, int fd)
+{
+ bool destroy = false;
+
+ KASSERT(mutex_owned(&ksem->ks_lock));
+
+ KASSERT(ksem->ks_ref > 0);
+ if (--ksem->ks_ref == 0) {
+ /*
+ * Destroy if the last reference and semaphore is unnamed,
+ * or unlinked (for named semaphore).
+ */
+ destroy = (ksem->ks_flags & KS_UNLINKED) ||
+ (ksem->ks_name == NULL);
+ }
+ mutex_exit(&ksem->ks_lock);
+
+ if (destroy) {
+ ksem_free(ksem);
+ }
+ if (fd != -1) {
+ fd_putfile(fd);
+ }
+}
+
int
sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap,
register_t *retval)
@@ -409,18 +552,31 @@ sys__ksem_init(struct lwp *l, const stru
intptr_t *idp;
} */
- return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
+ return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp),
+ copyin, copyout);
}
int
-do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout)
+do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyin_t docopyin,
+ copyout_t docopyout)
{
proc_t *p = l->l_proc;
ksem_t *ks;
file_t *fp;
- intptr_t id;
+ intptr_t id, arg;
int fd, error;
+ /*
+ * Newer versions of librt / libpthread pass us 'PSRD' in *idp to
+ * indicate that a pshared semaphore is wanted. In that case we
+ * allocate globally unique ID and return that, rather than the
+ * process-scoped file descriptor ID.
+ */
+ error = (*docopyin)(idp, &arg, sizeof(*idp));
+ if (error) {
+ return error;
+ }
+
error = fd_allocfile(&fp, &fd);
if (error) {
return error;
@@ -429,11 +585,14 @@ do_ksem_init(lwp_t *l, u_int val, intptr
fp->f_flag = FREAD | FWRITE;
fp->f_ops = &semops;
- id = (intptr_t)fd;
- error = (*docopyout)(&id, idp, sizeof(*idp));
- if (error) {
+ if (fd >= KSEM_MARKER_MIN) {
+ /*
+ * This is super-unlikely, but we check for it anyway
+ * because potential collisions with the pshared marker
+ * would be bad.
+ */
fd_abort(p, fp, fd);
- return error;
+ return EMFILE;
}
/* Note the mode does not matter for anonymous semaphores. */
@@ -442,6 +601,23 @@ do_ksem_init(lwp_t *l, u_int val, intptr
fd_abort(p, fp, fd);
return error;
}
+
+ if (arg == KSEM_PSHARED) {
+ ks->ks_pshared_proc = curproc;
+ ks->ks_pshared_fd = fd;
+ ksem_alloc_pshared_id(ks);
+ id = ks->ks_pshared_id;
+ } else {
+ id = (intptr_t)fd;
+ }
+
+ error = (*docopyout)(&id, idp, sizeof(*idp));
+ if (error) {
+ ksem_free(ks);
+ fd_abort(p, fp, fd);
+ return error;
+ }
+
fp->f_ksem = ks;
fd_affix(p, fp, fd);
return error;
@@ -487,6 +663,16 @@ do_ksem_open(struct lwp *l, const char *
fp->f_flag = FREAD | FWRITE;
fp->f_ops = &semops;
+ if (fd >= KSEM_MARKER_MIN) {
+ /*
+ * This is super-unlikely, but we check for it anyway
+ * because potential collisions with the pshared marker
+ * would be bad.
+ */
+ fd_abort(p, fp, fd);
+ return EMFILE;
+ }
+
/*
* The ID (file descriptor number) can be stored early.
* Note that zero is a special value for libpthread.
@@ -580,10 +766,24 @@ sys__ksem_close(struct lwp *l, const str
/* {
intptr_t id;
} */
- int fd = (int)SCARG(uap, id);
+ intptr_t id = SCARG(uap, id);
+ int fd, error;
+ ksem_t *ks;
+
+ error = ksem_get(id, &ks, &fd);
+ if (error) {
+ return error;
+ }
- if (fd_getfile(fd) == NULL) {
- return EBADF;
+ /* This is only for named semaphores. */
+ if (ks->ks_name == NULL) {
+ error = EINVAL;
+ }
+ ksem_release(ks, -1);
+ if (error) {
+ if (fd != -1)
+ fd_putfile(fd);
+ return error;
}
return fd_close(fd);
}
@@ -639,22 +839,14 @@ static int
ksem_close_fop(file_t *fp)
{
ksem_t *ks = fp->f_ksem;
- bool destroy = false;
- mutex_enter(&ks->ks_lock);
- KASSERT(ks->ks_ref > 0);
- if (--ks->ks_ref == 0) {
- /*
- * Destroy if the last reference and semaphore is unnamed,
- * or unlinked (for named semaphore).
- */
- destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL);
+ if (ks->ks_pshared_id != 0 && ks->ks_pshared_proc != curproc) {
+ /* Do nothing if this is not the creator. */
+ return 0;
}
- mutex_exit(&ks->ks_lock);
- if (destroy) {
- ksem_free(ks);
- }
+ mutex_enter(&ks->ks_lock);
+ ksem_release(ks, -1);
return 0;
}
@@ -716,10 +908,10 @@ sys__ksem_post(struct lwp *l, const stru
/* {
intptr_t id;
} */
- int fd = (int)SCARG(uap, id), error;
+ int fd, error;
ksem_t *ks;
- error = ksem_get(fd, &ks);
+ error = ksem_get(SCARG(uap, id), &ks, &fd);
if (error) {
return error;
}
@@ -733,18 +925,17 @@ sys__ksem_post(struct lwp *l, const stru
cv_broadcast(&ks->ks_cv);
}
out:
- mutex_exit(&ks->ks_lock);
- fd_putfile(fd);
+ ksem_release(ks, fd);
return error;
}
int
do_ksem_wait(lwp_t *l, intptr_t id, bool try_p, struct timespec *abstime)
{
- int fd = (int)id, error, timeo;
+ int fd, error, timeo;
ksem_t *ks;
- error = ksem_get(fd, &ks);
+ error = ksem_get(id, &ks, &fd);
if (error) {
return error;
}
@@ -767,8 +958,7 @@ do_ksem_wait(lwp_t *l, intptr_t id, bool
}
ks->ks_value--;
out:
- mutex_exit(&ks->ks_lock);
- fd_putfile(fd);
+ ksem_release(ks, fd);
return error;
}
@@ -826,18 +1016,17 @@ sys__ksem_getvalue(struct lwp *l, const
intptr_t id;
unsigned int *value;
} */
- int fd = (int)SCARG(uap, id), error;
+ int fd, error;
ksem_t *ks;
unsigned int val;
- error = ksem_get(fd, &ks);
+ error = ksem_get(SCARG(uap, id), &ks, &fd);
if (error) {
return error;
}
KASSERT(mutex_owned(&ks->ks_lock));
val = ks->ks_value;
- mutex_exit(&ks->ks_lock);
- fd_putfile(fd);
+ ksem_release(ks, fd);
return copyout(&val, SCARG(uap, value), sizeof(val));
}
@@ -849,10 +1038,12 @@ sys__ksem_destroy(struct lwp *l, const s
/* {
intptr_t id;
} */
- int fd = (int)SCARG(uap, id), error;
+ int fd, error;
ksem_t *ks;
- error = ksem_get(fd, &ks);
+ intptr_t id = SCARG(uap, id);
+
+ error = ksem_get(id, &ks, &fd);
if (error) {
return error;
}
@@ -868,10 +1059,29 @@ sys__ksem_destroy(struct lwp *l, const s
error = EBUSY;
goto out;
}
+ if (KSEM_ID_IS_PSHARED(id)) {
+ /* Cannot destroy if we did't create it. */
+ KASSERT(fd == -1);
+ KASSERT(ks->ks_pshared_proc != NULL);
+ if (ks->ks_pshared_proc != curproc) {
+ error = EINVAL;
+ goto out;
+ }
+ fd = ks->ks_pshared_fd;
+
+ /* Mark it dead so subsequent lookups fail. */
+ ks->ks_pshared_proc = NULL;
+
+ /* Do an fd_getfile() to for the benefit of fd_close(). */
+ file_t *fp __diagused = fd_getfile(fd);
+ KASSERT(fp != NULL);
+ KASSERT(fp->f_ksem == ks);
+ }
out:
- mutex_exit(&ks->ks_lock);
+ ksem_release(ks, -1);
if (error) {
- fd_putfile(fd);
+ if (!KSEM_ID_IS_PSHARED(id))
+ fd_putfile(fd);
return error;
}
return fd_close(fd);
Index: src/sys/sys/ksem.h
diff -u src/sys/sys/ksem.h:1.14 src/sys/sys/ksem.h:1.15
--- src/sys/sys/ksem.h:1.14 Sun Nov 25 01:05:04 2012
+++ src/sys/sys/ksem.h Sun Feb 3 03:20:24 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: ksem.h,v 1.14 2012/11/25 01:05:04 christos Exp $ */
+/* $NetBSD: ksem.h,v 1.15 2019/02/03 03:20:24 thorpej Exp $ */
/*
* Copyright (c) 2002 Alfred Perlstein <[email protected]>
@@ -38,8 +38,11 @@ struct timespec;
typedef struct ksem {
LIST_ENTRY(ksem) ks_entry; /* global list entry */
+ struct proc * ks_pshared_proc;/* owner of pshared sem */
+ intptr_t ks_pshared_id; /* global id for pshared sem */
kmutex_t ks_lock; /* lock on this ksem */
kcondvar_t ks_cv; /* condition variable */
+ u_int ks_pshared_fd; /* fd in owning proc */
u_int ks_ref; /* number of references */
u_int ks_value; /* current value */
u_int ks_waiters; /* number of waiters */
@@ -51,13 +54,21 @@ typedef struct ksem {
gid_t ks_gid; /* creator gid */
} ksem_t;
-int do_ksem_init(struct lwp *, unsigned int, intptr_t *, copyout_t);
+int do_ksem_init(struct lwp *, unsigned int, intptr_t *, copyin_t, copyout_t);
int do_ksem_open(struct lwp *, const char *, int, mode_t, unsigned int,
intptr_t *, copyout_t);
int do_ksem_wait(struct lwp *, intptr_t, bool, struct timespec *);
extern int ksem_max;
-#endif
+#endif /* _KERNEL */
+
+#if defined(_KERNEL) || defined(_LIBC)
+#define KSEM_PSHARED 0x50535244U /* 'PSRD' */
+
+#define KSEM_MARKER_MASK 0xff000001U
+#define KSEM_MARKER_MIN 0x01000001U
+#define KSEM_PSHARED_MARKER 0x70000001U /* 'p' << 24 | 1 */
+#endif /* _KERNEL || _LIBC */
#ifdef _LIBC
__BEGIN_DECLS
Index: src/tests/kernel/Makefile
diff -u src/tests/kernel/Makefile:1.56 src/tests/kernel/Makefile:1.57
--- src/tests/kernel/Makefile:1.56 Fri Jan 25 18:33:58 2019
+++ src/tests/kernel/Makefile Sun Feb 3 03:20:24 2019
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.56 2019/01/25 18:33:58 christos Exp $
+# $NetBSD: Makefile,v 1.57 2019/02/03 03:20:24 thorpej Exp $
NOMAN= # defined
@@ -14,6 +14,7 @@ TESTS_C+= t_mqueue
TESTS_C+= t_sysv
TESTS_C+= t_subr_prf
TESTS_C+= t_kauth_pr_47598
+TESTS_C+= t_ksem
TESTS_C+= t_sysctl
TESTS_C+= t_timeleft
TESTS_C+= t_zombie
Index: src/tests/lib/librt/t_sem.c
diff -u src/tests/lib/librt/t_sem.c:1.3 src/tests/lib/librt/t_sem.c:1.4
--- src/tests/lib/librt/t_sem.c:1.3 Sat Jan 14 20:58:20 2017
+++ src/tests/lib/librt/t_sem.c Sun Feb 3 03:20:24 2019
@@ -1,7 +1,7 @@
-/* $NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos Exp $ */
+/* $NetBSD: t_sem.c,v 1.4 2019/02/03 03:20:24 thorpej Exp $ */
/*
- * Copyright (c) 2008, 2010 The NetBSD Foundation, Inc.
+ * Copyright (c) 2008, 2010, 2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -56,10 +56,11 @@
*/
#include <sys/cdefs.h>
-__COPYRIGHT("@(#) Copyright (c) 2008, 2010\
+__COPYRIGHT("@(#) Copyright (c) 2008, 2010, 2019\
The NetBSD Foundation, inc. All rights reserved.");
-__RCSID("$NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos Exp $");
+__RCSID("$NetBSD: t_sem.c,v 1.4 2019/02/03 03:20:24 thorpej Exp $");
+#include <sys/mman.h>
#include <sys/wait.h>
#include <errno.h>
@@ -173,11 +174,152 @@ ATF_TC_CLEANUP(child, tc)
(void)sem_unlink("/sem_a");
}
+ATF_TC_WITH_CLEANUP(pshared);
+ATF_TC_HEAD(pshared, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks using pshared unnamed "
+ "semaphores to synchronize a master with multiple slave processes");
+}
+
+struct shared_region {
+ sem_t the_sem;
+};
+
+static struct shared_region *
+get_shared_region(int o_flags)
+{
+
+ int fd = shm_open("/shm_semtest_a", o_flags, 0644);
+ ATF_REQUIRE(fd != -1);
+
+ ATF_REQUIRE_EQ(ftruncate(fd, sizeof(struct shared_region)), 0);
+
+ void *rv = mmap(NULL, sizeof(struct shared_region),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(rv != MAP_FAILED);
+
+ (void)close(fd);
+
+ return rv;
+}
+
+static void
+put_shared_region(struct shared_region *r)
+{
+ ATF_REQUIRE_EQ(munmap(r, sizeof(struct shared_region)), 0);
+}
+
+ATF_TC_BODY(pshared, tc)
+{
+ struct shared_region *master_region, *slave_region;;
+
+ if (sysconf(_SC_SEMAPHORES) == -1)
+ atf_tc_skip("POSIX semaphores not supported");
+
+ /*
+ * Create a shared memory region to contain the pshared
+ * semaphore, create the semaphore there, and then detach
+ * from the shared memory region to ensure that our child
+ * processes will be getting at it from scratch.
+ */
+ master_region = get_shared_region(O_RDWR | O_CREAT | O_EXCL);
+ ATF_REQUIRE(sem_init(&master_region->the_sem, 1, 0) == 0);
+ put_shared_region(master_region);
+
+ /*
+ * Now execute a test that's essentially equivalent to the
+ * "child" test above, but using the pshared semaphore in the
+ * shared memory region.
+ */
+
+ pid_t pid, children[NCHILDREN];
+ unsigned i, j;
+ int status;
+
+ for (j = 1; j <= 2; j++) {
+ for (i = 0; i < NCHILDREN; i++) {
+ switch ((pid = fork())) {
+ case -1:
+ atf_tc_fail("fork() returned -1");
+ case 0:
+ slave_region = get_shared_region(O_RDWR);
+ printf("PID %d waiting for semaphore...\n",
+ getpid());
+ ATF_REQUIRE_MSG(sem_wait(&slave_region->the_sem)
+ == 0,
+ "sem_wait failed; iteration %d", j);
+ printf("PID %d got semaphore\n", getpid());
+ _exit(0);
+ default:
+ children[i] = pid;
+ break;
+ }
+ }
+
+ master_region = get_shared_region(O_RDWR);
+
+ for (i = 0; i < NCHILDREN; i++) {
+ sleep(1);
+ printf("main loop %d: posting...\n", j);
+ ATF_REQUIRE_EQ(sem_post(&master_region->the_sem), 0);
+ }
+
+ put_shared_region(master_region);
+
+ for (i = 0; i < NCHILDREN; i++) {
+ ATF_REQUIRE_EQ(waitpid(children[i], &status, 0), children[i]);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+ }
+ }
+
+ master_region = get_shared_region(O_RDWR);
+ ATF_REQUIRE_EQ(sem_destroy(&master_region->the_sem), 0);
+ put_shared_region(master_region);
+
+ ATF_REQUIRE_EQ(shm_unlink("/shm_semtest_a"), 0);
+}
+ATF_TC_CLEANUP(pshared, tc)
+{
+ /*
+ * The kernel will g/c the pshared semaphore when the process that
+ * created it exits, so no need to include that in the cleanup here.
+ */
+ (void)shm_unlink("/shm_semtest_a");
+}
+
+ATF_TC_WITH_CLEANUP(invalid_ops);
+ATF_TC_HEAD(invalid_ops, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Validates behavior when calling "
+ "bad operations for the semaphore type");
+}
+ATF_TC_BODY(invalid_ops, tc)
+{
+ sem_t *sem;
+ sem_t the_sem;
+
+ sem = sem_open("/sem_c", O_CREAT | O_EXCL, 0644, 0);
+ ATF_REQUIRE(sem != SEM_FAILED);
+ ATF_REQUIRE(sem_destroy(sem) == -1 && errno == EINVAL);
+ ATF_REQUIRE_EQ(sem_close(sem), 0);
+
+ ATF_REQUIRE_EQ(sem_init(&the_sem, 0, 0), 0);
+ ATF_REQUIRE(sem_close(&the_sem) == -1 && errno == EINVAL);
+ ATF_REQUIRE_EQ(sem_destroy(&the_sem), 0);
+}
+ATF_TC_CLEANUP(invalid_ops, tc)
+{
+ (void)sem_unlink("/sem_c");
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, basic);
ATF_TP_ADD_TC(tp, child);
+ ATF_TP_ADD_TC(tp, pshared);
+ ATF_TP_ADD_TC(tp, invalid_ops);
return atf_no_error();
}
Index: src/usr.bin/fstat/misc.c
diff -u src/usr.bin/fstat/misc.c:1.20 src/usr.bin/fstat/misc.c:1.21
--- src/usr.bin/fstat/misc.c:1.20 Tue Jun 26 10:00:25 2018
+++ src/usr.bin/fstat/misc.c Sun Feb 3 03:20:24 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: misc.c,v 1.20 2018/06/26 10:00:25 msaitoh Exp $ */
+/* $NetBSD: misc.c,v 1.21 2019/02/03 03:20:24 thorpej Exp $ */
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: misc.c,v 1.20 2018/06/26 10:00:25 msaitoh Exp $");
+__RCSID("$NetBSD: misc.c,v 1.21 2019/02/03 03:20:24 thorpej Exp $");
#include <stdbool.h>
#include <sys/param.h>
@@ -47,6 +47,7 @@ __RCSID("$NetBSD: misc.c,v 1.20 2018/06/
#include <sys/proc.h>
#define _KERNEL
#include <sys/file.h>
+#define copyin_t int
#define copyout_t int
#include <sys/ksem.h>
#define _LIB_LIBKERN_LIBKERN_H_
Added files:
Index: src/tests/kernel/t_ksem.c
diff -u /dev/null src/tests/kernel/t_ksem.c:1.1
--- /dev/null Sun Feb 3 03:20:24 2019
+++ src/tests/kernel/t_ksem.c Sun Feb 3 03:20:24 2019
@@ -0,0 +1,145 @@
+/* $NetBSD: t_ksem.c,v 1.1 2019/02/03 03:20:24 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2019 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2019\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_ksem.c,v 1.1 2019/02/03 03:20:24 thorpej Exp $");
+
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define _LIBC
+#include <sys/ksem.h>
+
+ATF_TC(close_on_unnamed);
+ATF_TC_HEAD(close_on_unnamed, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "test for correct error on close of unnamed semaphore");
+}
+ATF_TC_BODY(close_on_unnamed, tc)
+{
+ intptr_t ksem;
+
+ /* _ksem_close() is invalid on unnamed semaphore. */
+ ksem = 0;
+ ATF_REQUIRE_EQ(_ksem_init(0, &ksem), 0);
+ ATF_REQUIRE(_ksem_close(ksem) == -1 && errno == EINVAL);
+ ATF_REQUIRE_EQ(_ksem_destroy(ksem), 0);
+}
+
+ATF_TC(close_on_unnamed_pshared);
+ATF_TC_HEAD(close_on_unnamed_pshared, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "test for correct error on close of unnamed pshared semaphore");
+}
+ATF_TC_BODY(close_on_unnamed_pshared, tc)
+{
+ intptr_t ksem;
+
+ /* Similar, but lifecycle of pshared is slightly different. */
+ ksem = KSEM_PSHARED;
+ ATF_REQUIRE_EQ(_ksem_init(0, &ksem), 0);
+ ATF_REQUIRE(_ksem_close(ksem) == -1 && errno == EINVAL);
+ ATF_REQUIRE_EQ(_ksem_destroy(ksem), 0);
+}
+
+ATF_TC_WITH_CLEANUP(destroy_on_named);
+ATF_TC_HEAD(destroy_on_named, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "test for correct error on destroy of named semaphore");
+}
+ATF_TC_BODY(destroy_on_named, tc)
+{
+ intptr_t ksem;
+
+ /* Exercise open-unlinked semaphore lifecycle. */
+ ATF_REQUIRE_EQ(_ksem_open("/ksem_x", O_CREAT | O_EXCL, 0644, 0, &ksem),
+ 0);
+ ATF_REQUIRE(_ksem_destroy(ksem) == -1 && errno == EINVAL);
+ ATF_REQUIRE_EQ(_ksem_close(ksem), 0);
+ ATF_REQUIRE_EQ(_ksem_unlink("/ksem_x"), 0);
+}
+ATF_TC_CLEANUP(destroy_on_named, tc)
+{
+ (void)_ksem_unlink("/ksem_x");
+}
+
+ATF_TC_WITH_CLEANUP(open_unlinked_lifecycle);
+ATF_TC_HEAD(open_unlinked_lifecycle, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Exercises lifecycle of open-unlined semaphores");
+}
+ATF_TC_BODY(open_unlinked_lifecycle, tc)
+{
+ intptr_t ksem, ksem1;
+ int val;
+
+ /* Exercise open-unlinked semaphore lifecycle. */
+ ATF_REQUIRE_EQ(_ksem_open("/ksem_b", O_CREAT | O_EXCL, 0644, 0, &ksem),
+ 0);
+ ATF_REQUIRE_EQ(_ksem_unlink("/ksem_b"), 0);
+ val = 255;
+ ATF_REQUIRE(_ksem_getvalue(ksem, &val) == 0 && val == 0);
+
+ ATF_REQUIRE_EQ(_ksem_open("/ksem_b", O_CREAT | O_EXCL, 0644, 1, &ksem1),
+ 0);
+ ATF_REQUIRE_EQ(_ksem_unlink("/ksem_b"), 0);
+ val = 255;
+ ATF_REQUIRE(_ksem_getvalue(ksem1, &val) == 0 && val == 1);
+
+ ATF_REQUIRE_EQ(_ksem_close(ksem), 0);
+ ATF_REQUIRE_EQ(_ksem_close(ksem1), 0);
+}
+ATF_TC_CLEANUP(open_unlinked_lifecycle, tc)
+{
+ (void)_ksem_unlink("/ksem_a");
+ (void)_ksem_unlink("/ksem_b");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, close_on_unnamed);
+ ATF_TP_ADD_TC(tp, close_on_unnamed_pshared);
+ ATF_TP_ADD_TC(tp, destroy_on_named);
+ ATF_TP_ADD_TC(tp, open_unlinked_lifecycle);
+
+ return atf_no_error();
+}