NetBSD-5.0.2/sys/compat/mach/mach_semaphore.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: mach_semaphore.c,v 1.19 2008/04/28 20:23:44 martin Exp $ */

/*-
 * Copyright (c) 2002-2003 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Emmanuel Dreyfus
 *
 * 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>
__KERNEL_RCSID(0, "$NetBSD: mach_semaphore.c,v 1.19 2008/04/28 20:23:44 martin Exp $");

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signal.h>
#include <sys/pool.h>
#include <sys/rwlock.h>
#include <sys/malloc.h>
#include <sys/proc.h>

#include <compat/mach/mach_types.h>
#include <compat/mach/mach_message.h>
#include <compat/mach/mach_semaphore.h>
#include <compat/mach/mach_clock.h>
#include <compat/mach/mach_errno.h>
#include <compat/mach/mach_port.h>
#include <compat/mach/mach_services.h>
#include <compat/mach/mach_syscallargs.h>

/* Semaphore list, lock, pools */
static LIST_HEAD(mach_semaphore_list, mach_semaphore) mach_semaphore_list;
static krwlock_t mach_semaphore_list_lock;
static struct pool mach_semaphore_list_pool;
static struct pool mach_waiting_lwp_pool;

/* Function to manipulate them */
static struct mach_semaphore *mach_semaphore_get(int, int);
static void mach_semaphore_put(struct mach_semaphore *);
static struct mach_waiting_lwp *mach_waiting_lwp_get
    (struct lwp *, struct mach_semaphore *);
static void mach_waiting_lwp_put
    (struct mach_waiting_lwp *, struct mach_semaphore *, int);

int
mach_sys_semaphore_wait_trap(struct lwp *l, const struct mach_sys_semaphore_wait_trap_args *uap, register_t *retval)
{
	/* {
		syscallarg(mach_port_name_t) wait_name;
	} */
	struct mach_semaphore *ms;
	struct mach_waiting_lwp *mwl;
	struct mach_right *mr;
	mach_port_t mn;
	int blocked = 0;

	mn = SCARG(uap, wait_name);
	if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == 0)
		return EPERM;

	if (mr->mr_port->mp_datatype != MACH_MP_SEMAPHORE)
		return EINVAL;

	ms = (struct mach_semaphore *)mr->mr_port->mp_data;

	rw_enter(&ms->ms_lock, RW_WRITER);
	ms->ms_value--;
	if (ms->ms_value < 0)
		blocked = 1;
	rw_exit(&ms->ms_lock);

	if (blocked != 0) {
		mwl = mach_waiting_lwp_get(l, ms);
		while (ms->ms_value < 0)
			tsleep(mwl, PZERO|PCATCH, "sem_wait", 0);
		mach_waiting_lwp_put(mwl, ms, 0);
	}
	return 0;
}

int
mach_sys_semaphore_signal_trap(struct lwp *l, const struct mach_sys_semaphore_signal_trap_args *uap, register_t *retval)
{
	/* {
		syscallarg(mach_port_name_t) signal_name;
	} */
	struct mach_semaphore *ms;
	struct mach_waiting_lwp *mwl;
	struct mach_right *mr;
	mach_port_t mn;
	int unblocked = 0;

	mn = SCARG(uap, signal_name);
	if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == 0)
		return EPERM;

	if (mr->mr_port->mp_datatype != MACH_MP_SEMAPHORE)
		return EINVAL;

	ms = (struct mach_semaphore *)mr->mr_port->mp_data;

	rw_enter(&ms->ms_lock, RW_WRITER);
	ms->ms_value++;
	if (ms->ms_value >= 0)
		unblocked = 1;
	rw_exit(&ms->ms_lock);

	if (unblocked != 0) {
		rw_enter(&ms->ms_lock, RW_READER);
		mwl = TAILQ_FIRST(&ms->ms_waiting);
		wakeup(mwl);
		rw_exit(&ms->ms_lock);
	}
	return 0;
}

int
mach_semaphore_create(struct mach_trap_args *args)
{
	mach_semaphore_create_request_t *req = args->smsg;
	mach_semaphore_create_reply_t *rep = args->rmsg;
	size_t *msglen = args->rsize;
	struct lwp *l = args->l;
	struct mach_semaphore *ms;
	struct mach_port *mp;
	struct mach_right *mr;

	ms = mach_semaphore_get(req->req_value, req->req_policy);

	mp = mach_port_get();
	mp->mp_datatype = MACH_MP_SEMAPHORE;
	mp->mp_data = (void *)ms;

	mr = mach_right_get(mp, l, MACH_PORT_TYPE_SEND, 0);

	*msglen = sizeof(*rep);
	mach_set_header(rep, req, *msglen);
	mach_add_port_desc(rep, mr->mr_name);
	mach_set_trailer(rep, *msglen);

	return 0;
}

int
mach_semaphore_destroy(struct mach_trap_args *args)
{
	mach_semaphore_destroy_request_t *req = args->smsg;
	mach_semaphore_destroy_reply_t *rep = args->rmsg;
	struct lwp *l = args->l;
	size_t *msglen = args->rsize;
	struct mach_semaphore *ms;
	struct mach_right *mr;
	mach_port_t mn;

	mn = req->req_sem.name;
	if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == 0)
		return mach_msg_error(args, EPERM);

	if (mr->mr_port->mp_datatype != MACH_MP_SEMAPHORE)
		return mach_msg_error(args, EINVAL);

	ms = (struct mach_semaphore *)mr->mr_port->mp_data;
	mach_semaphore_put(ms);
	mach_right_put(mr, MACH_PORT_TYPE_REF_RIGHTS);

	*msglen = sizeof(*rep);
	mach_set_header(rep, req, *msglen);

	rep->rep_retval = 0;

	mach_set_trailer(rep, *msglen);

	return 0;
}

void
mach_semaphore_init(void)
{
	LIST_INIT(&mach_semaphore_list);
	rw_init(&mach_semaphore_list_lock);
	pool_init(&mach_semaphore_list_pool, sizeof (struct mach_semaphore),
	    0, 0, 0, "mach_sem_pool", NULL, IPL_NONE);
	pool_init(&mach_waiting_lwp_pool, sizeof (struct mach_waiting_lwp),
	    0, 0, 0, "mach_waitp_pool", NULL, IPL_NONE);

	return;
}

static struct mach_semaphore *
mach_semaphore_get(int value, int policy)
{
	struct mach_semaphore *ms;

	ms = (struct mach_semaphore *)pool_get(&mach_semaphore_list_pool,
	    M_WAITOK);
	ms->ms_value = value;
	ms->ms_policy = policy;
	TAILQ_INIT(&ms->ms_waiting);
	rw_init(&ms->ms_lock);

	rw_enter(&mach_semaphore_list_lock, RW_WRITER);
	LIST_INSERT_HEAD(&mach_semaphore_list, ms, ms_list);
	rw_exit(&mach_semaphore_list_lock);

	return ms;
}

static void
mach_semaphore_put(struct mach_semaphore *ms)
{
	struct mach_waiting_lwp *mwl;

	rw_enter(&ms->ms_lock, RW_WRITER);
	while ((mwl = TAILQ_FIRST(&ms->ms_waiting)) != NULL)
		mach_waiting_lwp_put(mwl, ms, 0);
	rw_exit(&ms->ms_lock);
	rw_destroy(&ms->ms_lock);

	rw_enter(&mach_semaphore_list_lock, RW_WRITER);
	LIST_REMOVE(ms, ms_list);
	rw_exit(&mach_semaphore_list_lock);

	pool_put(&mach_semaphore_list_pool, ms);

	return;
}

static struct mach_waiting_lwp *
mach_waiting_lwp_get(struct lwp *l, struct mach_semaphore *ms)
{
	struct mach_waiting_lwp *mwl;

	mwl = (struct mach_waiting_lwp *)pool_get(&mach_waiting_lwp_pool,
	    M_WAITOK);
	mwl->mwl_l = l;

	rw_enter(&ms->ms_lock, RW_WRITER);
	TAILQ_INSERT_TAIL(&ms->ms_waiting, mwl, mwl_list);
	rw_exit(&ms->ms_lock);

	return mwl;
}

static void
mach_waiting_lwp_put(struct mach_waiting_lwp *mwl, struct mach_semaphore *ms, int locked)
{
	if (!locked)
		rw_enter(&ms->ms_lock, RW_WRITER);
	TAILQ_REMOVE(&ms->ms_waiting, mwl, mwl_list);
	if (!locked)
		rw_exit(&ms->ms_lock);
	pool_put(&mach_waiting_lwp_pool, mwl);

	return;
}

/*
 * Cleanup after process exit. Need improvements, there
 * can be some memory leaks here.
 */
void
mach_semaphore_cleanup(struct lwp *l)
{
	struct mach_semaphore *ms;
	struct mach_waiting_lwp *mwl;

	rw_enter(&mach_semaphore_list_lock, RW_READER);
	LIST_FOREACH(ms, &mach_semaphore_list, ms_list) {
		rw_enter(&ms->ms_lock, RW_WRITER);
		TAILQ_FOREACH(mwl, &ms->ms_waiting, mwl_list)
			if (mwl->mwl_l == l) {
				mach_waiting_lwp_put(mwl, ms, 0);
				ms->ms_value++;
				if (ms->ms_value >= 0)
					wakeup(TAILQ_FIRST(&ms->ms_waiting));
			}
		rw_exit(&ms->ms_lock);
	}
	rw_exit(&mach_semaphore_list_lock);

	return;
}

int
mach_sys_semaphore_wait_signal_trap(struct lwp *l, const struct mach_sys_semaphore_wait_signal_trap_args *uap, register_t *retval)
{
	/* {
		syscallarg(mach_port_name_t) wait_name;
		syscallarg(mach_port_name_t) signal_name;
	} */
	struct mach_sys_semaphore_wait_trap_args cupwait;
	struct mach_sys_semaphore_signal_trap_args cupsig;
	int error;

	SCARG(&cupsig, signal_name) = SCARG(uap, signal_name);
	if ((error = mach_sys_semaphore_signal_trap(l, &cupsig, retval)) != 0)
		return error;

	SCARG(&cupwait, wait_name) = SCARG(uap, wait_name);
	if ((error = mach_sys_semaphore_wait_trap(l, &cupwait, retval)) != 0)
		return error;

	return 0;
}


int
mach_sys_semaphore_signal_thread_trap(struct lwp *l, const struct mach_sys_semaphore_signal_thread_trap_args *uap, register_t *retval)
{
	/* {
		syscallarg(mach_port_name_t) signal_name;
		syscallarg(mach_port_name_t) thread;
	} */
	struct mach_right *mr;
	struct mach_semaphore *ms;
	mach_port_t mn;
	struct mach_waiting_lwp *mwl;
	int unblocked = 0;

	/*
	 * Get the semaphore
	 */
	mn = SCARG(uap, signal_name);
	if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == NULL)
		return EINVAL;

	if (mr->mr_port->mp_datatype != MACH_MP_SEMAPHORE)
		return EINVAL;

	ms = (struct mach_semaphore *)mr->mr_port->mp_data;

	/*
	 * Get the thread, and check that it is waiting for our semaphore
	 * If no thread was supplied, pick up the first one.
	 */
	mn = SCARG(uap, thread);
	if (mn != 0) {
		if ((mr = mach_right_check(mn, l,
		    MACH_PORT_TYPE_ALL_RIGHTS)) == NULL)
			return EINVAL;

		if (mr->mr_port->mp_datatype != MACH_MP_LWP)
			return EINVAL;

		rw_enter(&ms->ms_lock, RW_WRITER);
		TAILQ_FOREACH(mwl, &ms->ms_waiting, mwl_list)
			if (mwl->mwl_l == (struct lwp *)mr->mr_port->mp_data)
				break;
	} else {
		rw_enter(&ms->ms_lock, RW_WRITER);
		mwl = TAILQ_FIRST(&ms->ms_waiting);
	}

	/*
	 * No thread was awaiting for this semaphore,
	 * exit without touching the semaphore.
	 */
	if (mwl == NULL) {
		rw_exit(&ms->ms_lock);
		return 0; /* Should be KERN_NOT_WAITING */
	}

	ms->ms_value++;
	if (ms->ms_value >= 0)
		unblocked = 1;
	rw_exit(&ms->ms_lock);

	if (unblocked != 0)
		wakeup(mwl);

	return 0;
}


int
mach_sys_semaphore_signal_all_trap(struct lwp *l, const struct mach_sys_semaphore_signal_all_trap_args *uap, register_t *retval)
{
	/* {
		syscallarg(mach_port_name_t) signal_name;
	} */
	struct mach_right *mr;
	struct mach_semaphore *ms;
	mach_port_t mn;
	struct mach_waiting_lwp *mwl;
	int unblocked = 0;

	/*
	 * Get the semaphore
	 */
	mn = SCARG(uap, signal_name);
	if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == NULL)
		return EINVAL;

	if (mr->mr_port->mp_datatype != MACH_MP_SEMAPHORE)
		return EINVAL;

	ms = (struct mach_semaphore *)mr->mr_port->mp_data;

	rw_enter(&ms->ms_lock, RW_WRITER);
	ms->ms_value++;
	if (ms->ms_value >= 0)
		unblocked = 1;

	/*
	 * Wakeup all threads sleeping on it.
	 */
	if (unblocked != 0)
		TAILQ_FOREACH(mwl, &ms->ms_waiting, mwl_list)
			wakeup(mwl);

	rw_exit(&ms->ms_lock);

	return 0;
}