2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include "lint.h"
2N/A#include "thr_uberdata.h"
2N/A
2N/Astatic uint32_t _semvaluemax;
2N/A
2N/A/*
2N/A * Check to see if anyone is waiting for this semaphore.
2N/A */
2N/A#pragma weak _sema_held = sema_held
2N/Aint
2N/Asema_held(sema_t *sp)
2N/A{
2N/A return (sp->count == 0);
2N/A}
2N/A
2N/A#pragma weak _sema_init = sema_init
2N/A/* ARGSUSED3 */
2N/Aint
2N/Asema_init(sema_t *sp, unsigned int count, int type, void *arg)
2N/A{
2N/A if (_semvaluemax == 0)
2N/A _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX);
2N/A if ((type != USYNC_THREAD && type != USYNC_PROCESS) ||
2N/A (count > _semvaluemax))
2N/A return (EINVAL);
2N/A (void) memset(sp, 0, sizeof (*sp));
2N/A sp->count = count;
2N/A sp->type = (uint16_t)type;
2N/A sp->magic = SEMA_MAGIC;
2N/A
2N/A /*
2N/A * This should be at the beginning of the function,
2N/A * but for the sake of old broken applications that
2N/A * do not have proper alignment for their semaphores
2N/A * (and don't check the return code from sema_init),
2N/A * we put it here, after initializing the semaphore regardless.
2N/A */
2N/A if (((uintptr_t)sp & (_LONG_LONG_ALIGNMENT - 1)) &&
2N/A curthread->ul_misaligned == 0)
2N/A return (EINVAL);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A#pragma weak _sema_destroy = sema_destroy
2N/Aint
2N/Asema_destroy(sema_t *sp)
2N/A{
2N/A sp->magic = 0;
2N/A tdb_sync_obj_deregister(sp);
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Asema_wait_impl(sema_t *sp, timespec_t *tsp)
2N/A{
2N/A lwp_sema_t *lsp = (lwp_sema_t *)sp;
2N/A ulwp_t *self = curthread;
2N/A uberdata_t *udp = self->ul_uberdata;
2N/A tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
2N/A hrtime_t begin_sleep = 0;
2N/A uint_t count;
2N/A int error = 0;
2N/A
2N/A /*
2N/A * All variations of sema_wait() are cancellation points.
2N/A */
2N/A _cancelon();
2N/A
2N/A if (ssp)
2N/A tdb_incr(ssp->sema_wait);
2N/A
2N/A self->ul_sp = stkptr();
2N/A self->ul_wchan = lsp;
2N/A if (__td_event_report(self, TD_SLEEP, udp)) {
2N/A self->ul_td_evbuf.eventnum = TD_SLEEP;
2N/A self->ul_td_evbuf.eventdata = lsp;
2N/A tdb_event(TD_SLEEP, udp);
2N/A }
2N/A /* just a guess, but it looks like we will sleep */
2N/A if (ssp && lsp->count == 0) {
2N/A begin_sleep = gethrtime();
2N/A if (lsp->count == 0) /* still looks like sleep */
2N/A tdb_incr(ssp->sema_wait_sleep);
2N/A else /* we changed our mind */
2N/A begin_sleep = 0;
2N/A }
2N/A
2N/A if (lsp->type == USYNC_PROCESS) { /* kernel-level */
2N/A set_parking_flag(self, 1);
2N/A if (self->ul_cursig != 0 ||
2N/A (self->ul_cancelable && self->ul_cancel_pending))
2N/A set_parking_flag(self, 0);
2N/A /* the kernel always does FIFO queueing */
2N/A error = ___lwp_sema_timedwait(lsp, tsp, 1);
2N/A set_parking_flag(self, 0);
2N/A } else if (!udp->uberflags.uf_mt && /* single threaded */
2N/A lsp->count != 0) { /* and non-blocking */
2N/A /*
2N/A * Since we are single-threaded, we don't need the
2N/A * protection of queue_lock(). However, we do need
2N/A * to block signals while modifying the count.
2N/A */
2N/A sigoff(self);
2N/A lsp->count--;
2N/A sigon(self);
2N/A } else { /* multithreaded or blocking */
2N/A queue_head_t *qp;
2N/A ulwp_t *ulwp;
2N/A lwpid_t lwpid = 0;
2N/A
2N/A qp = queue_lock(lsp, CV);
2N/A while (error == 0 && lsp->count == 0) {
2N/A /*
2N/A * SUSV3 requires FIFO queueing for semaphores,
2N/A * at least for SCHED_FIFO and SCHED_RR scheduling.
2N/A */
2N/A enqueue(qp, self, 1);
2N/A lsp->sema_waiters = 1;
2N/A set_parking_flag(self, 1);
2N/A queue_unlock(qp);
2N/A /*
2N/A * We may have received SIGCANCEL before we
2N/A * called queue_lock(). If so and we are
2N/A * cancelable we should return EINTR.
2N/A */
2N/A if (self->ul_cursig != 0 ||
2N/A (self->ul_cancelable && self->ul_cancel_pending))
2N/A set_parking_flag(self, 0);
2N/A error = __lwp_park(tsp, 0);
2N/A set_parking_flag(self, 0);
2N/A qp = queue_lock(lsp, CV);
2N/A if (self->ul_sleepq) /* timeout or spurious wakeup */
2N/A lsp->sema_waiters = dequeue_self(qp);
2N/A }
2N/A if (error == 0)
2N/A lsp->count--;
2N/A if (lsp->count != 0 && lsp->sema_waiters) {
2N/A int more;
2N/A if ((ulwp = dequeue(qp, &more)) != NULL) {
2N/A no_preempt(self);
2N/A lwpid = ulwp->ul_lwpid;
2N/A }
2N/A lsp->sema_waiters = more;
2N/A }
2N/A queue_unlock(qp);
2N/A if (lwpid) {
2N/A (void) __lwp_unpark(lwpid);
2N/A preempt(self);
2N/A }
2N/A }
2N/A
2N/A self->ul_wchan = NULL;
2N/A self->ul_sp = 0;
2N/A if (ssp) {
2N/A if (error == 0) {
2N/A /* we just decremented the count */
2N/A count = lsp->count;
2N/A if (ssp->sema_min_count > count)
2N/A ssp->sema_min_count = count;
2N/A }
2N/A if (begin_sleep)
2N/A ssp->sema_wait_sleep_time += gethrtime() - begin_sleep;
2N/A }
2N/A
2N/A if (error == EINTR)
2N/A _canceloff();
2N/A else
2N/A _canceloff_nocancel();
2N/A return (error);
2N/A}
2N/A
2N/A#pragma weak _sema_wait = sema_wait
2N/Aint
2N/Asema_wait(sema_t *sp)
2N/A{
2N/A ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
2N/A return (sema_wait_impl(sp, NULL));
2N/A}
2N/A
2N/Aint
2N/Asema_reltimedwait(sema_t *sp, const timespec_t *reltime)
2N/A{
2N/A timespec_t tslocal = *reltime;
2N/A
2N/A ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
2N/A return (sema_wait_impl(sp, &tslocal));
2N/A}
2N/A
2N/Aint
2N/Asema_timedwait(sema_t *sp, const timespec_t *abstime)
2N/A{
2N/A timespec_t tslocal;
2N/A
2N/A ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
2N/A abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal);
2N/A return (sema_wait_impl(sp, &tslocal));
2N/A}
2N/A
2N/A#pragma weak _sema_trywait = sema_trywait
2N/Aint
2N/Asema_trywait(sema_t *sp)
2N/A{
2N/A lwp_sema_t *lsp = (lwp_sema_t *)sp;
2N/A ulwp_t *self = curthread;
2N/A uberdata_t *udp = self->ul_uberdata;
2N/A tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
2N/A uint_t count;
2N/A int error = 0;
2N/A
2N/A ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
2N/A
2N/A if (ssp)
2N/A tdb_incr(ssp->sema_trywait);
2N/A
2N/A if (lsp->type == USYNC_PROCESS) { /* kernel-level */
2N/A error = _lwp_sema_trywait(lsp);
2N/A } else if (!udp->uberflags.uf_mt) { /* single threaded */
2N/A sigoff(self);
2N/A if (lsp->count == 0)
2N/A error = EBUSY;
2N/A else
2N/A lsp->count--;
2N/A sigon(self);
2N/A } else { /* multithreaded */
2N/A queue_head_t *qp;
2N/A ulwp_t *ulwp;
2N/A lwpid_t lwpid = 0;
2N/A
2N/A qp = queue_lock(lsp, CV);
2N/A if (lsp->count == 0)
2N/A error = EBUSY;
2N/A else if (--lsp->count != 0 && lsp->sema_waiters) {
2N/A int more;
2N/A if ((ulwp = dequeue(qp, &more)) != NULL) {
2N/A no_preempt(self);
2N/A lwpid = ulwp->ul_lwpid;
2N/A }
2N/A lsp->sema_waiters = more;
2N/A }
2N/A queue_unlock(qp);
2N/A if (lwpid) {
2N/A (void) __lwp_unpark(lwpid);
2N/A preempt(self);
2N/A }
2N/A }
2N/A
2N/A if (error == 0) {
2N/A if (ssp) {
2N/A /* we just decremented the count */
2N/A count = lsp->count;
2N/A if (ssp->sema_min_count > count)
2N/A ssp->sema_min_count = count;
2N/A }
2N/A } else {
2N/A if (ssp)
2N/A tdb_incr(ssp->sema_trywait_fail);
2N/A if (__td_event_report(self, TD_LOCK_TRY, udp)) {
2N/A self->ul_td_evbuf.eventnum = TD_LOCK_TRY;
2N/A tdb_event(TD_LOCK_TRY, udp);
2N/A }
2N/A }
2N/A
2N/A return (error);
2N/A}
2N/A
2N/A#pragma weak _sema_post = sema_post
2N/Aint
2N/Asema_post(sema_t *sp)
2N/A{
2N/A lwp_sema_t *lsp = (lwp_sema_t *)sp;
2N/A ulwp_t *self = curthread;
2N/A uberdata_t *udp = self->ul_uberdata;
2N/A tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
2N/A uint_t count;
2N/A int error = 0;
2N/A
2N/A if (ssp)
2N/A tdb_incr(ssp->sema_post);
2N/A if (_semvaluemax == 0)
2N/A _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX);
2N/A
2N/A if (lsp->type == USYNC_PROCESS) { /* kernel-level */
2N/A error = _lwp_sema_post(lsp);
2N/A } else if (!udp->uberflags.uf_mt) { /* single threaded */
2N/A sigoff(self);
2N/A if (lsp->count >= _semvaluemax)
2N/A error = EOVERFLOW;
2N/A else
2N/A lsp->count++;
2N/A sigon(self);
2N/A } else { /* multithreaded */
2N/A queue_head_t *qp;
2N/A ulwp_t *ulwp;
2N/A lwpid_t lwpid = 0;
2N/A
2N/A qp = queue_lock(lsp, CV);
2N/A if (lsp->count >= _semvaluemax)
2N/A error = EOVERFLOW;
2N/A else if (lsp->count++ == 0 && lsp->sema_waiters) {
2N/A int more;
2N/A if ((ulwp = dequeue(qp, &more)) != NULL) {
2N/A no_preempt(self);
2N/A lwpid = ulwp->ul_lwpid;
2N/A }
2N/A lsp->sema_waiters = more;
2N/A }
2N/A queue_unlock(qp);
2N/A if (lwpid) {
2N/A (void) __lwp_unpark(lwpid);
2N/A preempt(self);
2N/A }
2N/A }
2N/A
2N/A if (error == 0) {
2N/A if (ssp) {
2N/A /* we just incremented the count */
2N/A count = lsp->count;
2N/A if (ssp->sema_max_count < count)
2N/A ssp->sema_max_count = count;
2N/A }
2N/A }
2N/A
2N/A return (error);
2N/A}