root/usr/src/lib/libfakekernel/common/rwlock.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
 */

/*
 * rwlock(9f)
 */

/* This is the API we're emulating */
#include <sys/rwlock.h>

#include <sys/errno.h>
#include <sys/debug.h>
#include <sys/param.h>
#include <sys/thread.h>

/* avoiding synch.h */
int     rwlock_init(lwp_rwlock_t *, int, void *);
int     rwlock_destroy(lwp_rwlock_t *);
int     rw_rdlock(lwp_rwlock_t *);
int     rw_wrlock(lwp_rwlock_t *);
int     rw_unlock(lwp_rwlock_t *);
int     rw_tryrdlock(lwp_rwlock_t *);
int     rw_trywrlock(lwp_rwlock_t *);
int     _rw_read_held(void *);
int     _rw_write_held(void *);

/*ARGSUSED*/
void
rw_init(krwlock_t *rwlp, char *name, krw_type_t type, void *arg)
{
        (void) rwlock_init(&rwlp->rw_lock, USYNC_THREAD, NULL);
        rwlp->rw_owner = _KTHREAD_INVALID;
}

void
rw_destroy(krwlock_t *rwlp)
{
        (void) rwlock_destroy(&rwlp->rw_lock);
        rwlp->rw_owner = _KTHREAD_INVALID;
}

void
rw_enter(krwlock_t *rwlp, krw_t rw)
{
        int rc;

        if (rw == RW_READER) {
                rc = rw_rdlock(&rwlp->rw_lock);
        } else {
                rc = rw_wrlock(&rwlp->rw_lock);
                rwlp->rw_owner = _curthread();
        }
        VERIFY(rc == 0);
}

void
rw_exit(krwlock_t *rwlp)
{
        if (_rw_write_held(&rwlp->rw_lock)) {
                ASSERT(rwlp->rw_owner == _curthread());
                rwlp->rw_owner = _KTHREAD_INVALID;
        } else {
                ASSERT(_rw_read_held(&rwlp->rw_lock));
        }
        (void) rw_unlock(&rwlp->rw_lock);
}

int
rw_tryenter(krwlock_t *rwlp, krw_t rw)
{
        int rv;

        if (rw == RW_WRITER) {
                rv = rw_trywrlock(&rwlp->rw_lock);
                if (rv == 0)
                        rwlp->rw_owner = _curthread();
        } else
                rv = rw_tryrdlock(&rwlp->rw_lock);

        return ((rv == 0) ? 1 : 0);
}

/*ARGSUSED*/
int
rw_tryupgrade(krwlock_t *rwlp)
{

        return (0);
}

void
rw_downgrade(krwlock_t *rwlp)
{
        ASSERT(rwlp->rw_owner == _curthread());
        rwlp->rw_owner = _KTHREAD_INVALID;
        VERIFY(rw_unlock(&rwlp->rw_lock) == 0);
        VERIFY(rw_rdlock(&rwlp->rw_lock) == 0);
}

int
rw_read_held(krwlock_t *rwlp)
{
        return (_rw_read_held(rwlp));
}

int
rw_write_held(krwlock_t *rwlp)
{
        return (_rw_write_held(rwlp));
}

int
rw_lock_held(krwlock_t *rwlp)
{
        return (rw_read_held(rwlp) || rw_write_held(rwlp));
}

/*
 * Return the kthread_t * of the lock owner
 */
void *
rw_owner(krwlock_t *rwlp)
{
        return (rwlp->rw_owner);
}