1*246be0cdSriastradh /* $NetBSD: linux_rwsem.c,v 1.4 2021/12/19 11:21:54 riastradh Exp $ */
20163f9c1Sriastradh
30163f9c1Sriastradh /*-
40163f9c1Sriastradh * Copyright (c) 2021 The NetBSD Foundation, Inc.
50163f9c1Sriastradh * All rights reserved.
60163f9c1Sriastradh *
70163f9c1Sriastradh * Redistribution and use in source and binary forms, with or without
80163f9c1Sriastradh * modification, are permitted provided that the following conditions
90163f9c1Sriastradh * are met:
100163f9c1Sriastradh * 1. Redistributions of source code must retain the above copyright
110163f9c1Sriastradh * notice, this list of conditions and the following disclaimer.
120163f9c1Sriastradh * 2. Redistributions in binary form must reproduce the above copyright
130163f9c1Sriastradh * notice, this list of conditions and the following disclaimer in the
140163f9c1Sriastradh * documentation and/or other materials provided with the distribution.
150163f9c1Sriastradh *
160163f9c1Sriastradh * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
170163f9c1Sriastradh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
180163f9c1Sriastradh * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
190163f9c1Sriastradh * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
200163f9c1Sriastradh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
210163f9c1Sriastradh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
220163f9c1Sriastradh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
230163f9c1Sriastradh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
240163f9c1Sriastradh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
250163f9c1Sriastradh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
260163f9c1Sriastradh * POSSIBILITY OF SUCH DAMAGE.
270163f9c1Sriastradh */
280163f9c1Sriastradh
290163f9c1Sriastradh #include <sys/cdefs.h>
30*246be0cdSriastradh __KERNEL_RCSID(0, "$NetBSD: linux_rwsem.c,v 1.4 2021/12/19 11:21:54 riastradh Exp $");
310163f9c1Sriastradh
320163f9c1Sriastradh #include <sys/types.h>
330163f9c1Sriastradh
340163f9c1Sriastradh #include <sys/condvar.h>
350163f9c1Sriastradh #include <sys/lwp.h>
3677628746Sriastradh #include <sys/lockdebug.h>
370163f9c1Sriastradh #include <sys/rwlock.h>
380163f9c1Sriastradh
390163f9c1Sriastradh #include <machine/limits.h>
400163f9c1Sriastradh
410163f9c1Sriastradh #include <lib/libkern/libkern.h>
420163f9c1Sriastradh
430163f9c1Sriastradh #include <linux/rwsem.h>
440163f9c1Sriastradh
4577628746Sriastradh #define RWSEM_WANTLOCK(RWSEM) \
4677628746Sriastradh LOCKDEBUG_WANTLOCK((RWSEM)->rws_debug, (RWSEM), \
4777628746Sriastradh (uintptr_t)__builtin_return_address(0), 0)
4877628746Sriastradh #define RWSEM_LOCKED_EX(RWSEM) \
4977628746Sriastradh LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL, \
5077628746Sriastradh (uintptr_t)__builtin_return_address(0), 0)
5177628746Sriastradh #define RWSEM_LOCKED_SH(RWSEM) \
5277628746Sriastradh LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL, \
5377628746Sriastradh (uintptr_t)__builtin_return_address(0), 1)
5477628746Sriastradh #define RWSEM_UNLOCKED_EX(RWSEM) \
5577628746Sriastradh LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM), \
5677628746Sriastradh (uintptr_t)__builtin_return_address(0), 0)
5777628746Sriastradh #define RWSEM_UNLOCKED_SH(RWSEM) \
5877628746Sriastradh LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM), \
5977628746Sriastradh (uintptr_t)__builtin_return_address(0), 1)
6077628746Sriastradh
6177628746Sriastradh #ifdef LOCKDEBUG
6277628746Sriastradh static void
rwsem_dump(const volatile void * cookie,lockop_printer_t pr)6377628746Sriastradh rwsem_dump(const volatile void *cookie, lockop_printer_t pr)
6477628746Sriastradh {
6577628746Sriastradh const volatile struct rw_semaphore *rwsem = cookie;
6677628746Sriastradh
6777628746Sriastradh pr("%-13s: %p", "writer", rwsem->rws_writer);
6877628746Sriastradh pr("%-13s: %u", "readers", rwsem->rws_readers);
6977628746Sriastradh pr("%-13s: %s", "writewanted", rwsem->rws_writewanted ? "yes" : "no");
7077628746Sriastradh }
7177628746Sriastradh
7277628746Sriastradh static lockops_t rwsem_lockops = {
7377628746Sriastradh .lo_name = "Linux read/write semaphore",
7477628746Sriastradh .lo_type = LOCKOPS_SLEEP,
7577628746Sriastradh .lo_dump = rwsem_dump,
7677628746Sriastradh };
7777628746Sriastradh #endif
7877628746Sriastradh
790163f9c1Sriastradh void
init_rwsem(struct rw_semaphore * rwsem)800163f9c1Sriastradh init_rwsem(struct rw_semaphore *rwsem)
810163f9c1Sriastradh {
820163f9c1Sriastradh
83a2c74e12Sriastradh mutex_init(&rwsem->rws_lock, MUTEX_DEFAULT, IPL_VM);
840163f9c1Sriastradh cv_init(&rwsem->rws_cv, "lnxrwsem");
850163f9c1Sriastradh rwsem->rws_writer = NULL;
860163f9c1Sriastradh rwsem->rws_readers = 0;
8777628746Sriastradh
8877628746Sriastradh #ifdef LOCKDEBUG
8977628746Sriastradh rwsem->rws_debug = LOCKDEBUG_ALLOC(rwsem, &rwsem_lockops,
9077628746Sriastradh (uintptr_t)__builtin_return_address(0));
9177628746Sriastradh #endif
920163f9c1Sriastradh }
930163f9c1Sriastradh
940163f9c1Sriastradh void
destroy_rwsem(struct rw_semaphore * rwsem)950163f9c1Sriastradh destroy_rwsem(struct rw_semaphore *rwsem)
960163f9c1Sriastradh {
970163f9c1Sriastradh
980163f9c1Sriastradh KASSERT(rwsem->rws_readers == 0);
990163f9c1Sriastradh KASSERT(rwsem->rws_writer == NULL);
10077628746Sriastradh
10177628746Sriastradh #ifdef LOCKDEBUG
10277628746Sriastradh LOCKDEBUG_FREE(rwsem->rws_debug, rwsem);
10377628746Sriastradh #endif
10477628746Sriastradh
1050163f9c1Sriastradh cv_destroy(&rwsem->rws_cv);
1060163f9c1Sriastradh mutex_destroy(&rwsem->rws_lock);
1070163f9c1Sriastradh }
1080163f9c1Sriastradh
1090163f9c1Sriastradh void
down_read(struct rw_semaphore * rwsem)1100163f9c1Sriastradh down_read(struct rw_semaphore *rwsem)
1110163f9c1Sriastradh {
1120163f9c1Sriastradh
11377628746Sriastradh RWSEM_WANTLOCK(rwsem);
11477628746Sriastradh
1150163f9c1Sriastradh mutex_enter(&rwsem->rws_lock);
1160163f9c1Sriastradh while (rwsem->rws_writer || rwsem->rws_writewanted)
1170163f9c1Sriastradh cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
1180163f9c1Sriastradh KASSERT(rwsem->rws_readers < UINT_MAX);
1190163f9c1Sriastradh rwsem->rws_readers++;
1200163f9c1Sriastradh mutex_exit(&rwsem->rws_lock);
12177628746Sriastradh
12277628746Sriastradh RWSEM_LOCKED_SH(rwsem);
1230163f9c1Sriastradh }
1240163f9c1Sriastradh
1250163f9c1Sriastradh bool
down_read_trylock(struct rw_semaphore * rwsem)1260163f9c1Sriastradh down_read_trylock(struct rw_semaphore *rwsem)
1270163f9c1Sriastradh {
1280163f9c1Sriastradh bool ret = false;
1290163f9c1Sriastradh
1300163f9c1Sriastradh /*
1310163f9c1Sriastradh * Note: Linux apparently relies on down_read_trylock to
1320163f9c1Sriastradh * quietly succeed when the caller already holds a reader lock.
1330163f9c1Sriastradh * This is why we can't use rwlock(9), which absolutely
1340163f9c1Sriastradh * prohibits recursive use and crashes immediately under
1350163f9c1Sriastradh * LOCKDEBUG if you try it.
1360163f9c1Sriastradh */
1370163f9c1Sriastradh
1380163f9c1Sriastradh mutex_enter(&rwsem->rws_lock);
1390163f9c1Sriastradh if (rwsem->rws_writer == NULL && !rwsem->rws_writewanted) {
1400163f9c1Sriastradh KASSERT(rwsem->rws_readers < UINT_MAX);
1410163f9c1Sriastradh rwsem->rws_readers++;
1420163f9c1Sriastradh ret = true;
1430163f9c1Sriastradh }
1440163f9c1Sriastradh mutex_exit(&rwsem->rws_lock);
1450163f9c1Sriastradh
146*246be0cdSriastradh if (ret) {
14777628746Sriastradh RWSEM_LOCKED_SH(rwsem);
148*246be0cdSriastradh }
14977628746Sriastradh
1500163f9c1Sriastradh return ret;
1510163f9c1Sriastradh }
1520163f9c1Sriastradh
1530163f9c1Sriastradh void
up_read(struct rw_semaphore * rwsem)1540163f9c1Sriastradh up_read(struct rw_semaphore *rwsem)
1550163f9c1Sriastradh {
1560163f9c1Sriastradh
15777628746Sriastradh RWSEM_UNLOCKED_SH(rwsem);
15877628746Sriastradh
1590163f9c1Sriastradh mutex_enter(&rwsem->rws_lock);
1600163f9c1Sriastradh KASSERT(rwsem->rws_readers);
1610163f9c1Sriastradh KASSERT(rwsem->rws_writer == NULL);
1620163f9c1Sriastradh if (--rwsem->rws_readers == 0)
1630163f9c1Sriastradh cv_broadcast(&rwsem->rws_cv);
1640163f9c1Sriastradh mutex_exit(&rwsem->rws_lock);
1650163f9c1Sriastradh }
1660163f9c1Sriastradh
1670163f9c1Sriastradh void
down_write(struct rw_semaphore * rwsem)1680163f9c1Sriastradh down_write(struct rw_semaphore *rwsem)
1690163f9c1Sriastradh {
1700163f9c1Sriastradh
17177628746Sriastradh RWSEM_WANTLOCK(rwsem);
17277628746Sriastradh
1730163f9c1Sriastradh mutex_enter(&rwsem->rws_lock);
1740163f9c1Sriastradh
1750163f9c1Sriastradh /* If another writer is waiting, get in the queue. */
1760163f9c1Sriastradh while (rwsem->rws_writewanted)
1770163f9c1Sriastradh cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
1780163f9c1Sriastradh
1790163f9c1Sriastradh /*
1800163f9c1Sriastradh * No other writers waiting. Our turn. Announce our intent to
1810163f9c1Sriastradh * readers, and wait for the writer or readers to finish.
1820163f9c1Sriastradh */
1830163f9c1Sriastradh rwsem->rws_writewanted = true;
1840163f9c1Sriastradh while (rwsem->rws_writer || rwsem->rws_readers) {
1850163f9c1Sriastradh KASSERTMSG(rwsem->rws_writer != curlwp,
1860163f9c1Sriastradh "locking against myself: rwsem=%p lwp=%p", rwsem, curlwp);
1870163f9c1Sriastradh cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
1880163f9c1Sriastradh }
1890163f9c1Sriastradh
1900163f9c1Sriastradh /* At last, it is ours! */
1910163f9c1Sriastradh KASSERT(rwsem->rws_readers == 0);
1920163f9c1Sriastradh KASSERT(rwsem->rws_writer == NULL);
1930163f9c1Sriastradh KASSERT(rwsem->rws_writewanted);
1940163f9c1Sriastradh rwsem->rws_writewanted = false;
1950163f9c1Sriastradh rwsem->rws_writer = curlwp;
1960163f9c1Sriastradh
1970163f9c1Sriastradh mutex_exit(&rwsem->rws_lock);
19877628746Sriastradh
19977628746Sriastradh RWSEM_LOCKED_EX(rwsem);
2000163f9c1Sriastradh }
2010163f9c1Sriastradh
2020163f9c1Sriastradh void
up_write(struct rw_semaphore * rwsem)2030163f9c1Sriastradh up_write(struct rw_semaphore *rwsem)
2040163f9c1Sriastradh {
2050163f9c1Sriastradh
20677628746Sriastradh RWSEM_UNLOCKED_EX(rwsem);
20777628746Sriastradh
2080163f9c1Sriastradh mutex_enter(&rwsem->rws_lock);
2090163f9c1Sriastradh KASSERT(rwsem->rws_writer == curlwp);
2100163f9c1Sriastradh KASSERT(rwsem->rws_readers == 0);
2110163f9c1Sriastradh rwsem->rws_writer = NULL;
2120163f9c1Sriastradh cv_broadcast(&rwsem->rws_cv);
2130163f9c1Sriastradh mutex_exit(&rwsem->rws_lock);
2140163f9c1Sriastradh }
2150163f9c1Sriastradh
2160163f9c1Sriastradh void
downgrade_write(struct rw_semaphore * rwsem)2170163f9c1Sriastradh downgrade_write(struct rw_semaphore *rwsem)
2180163f9c1Sriastradh {
2190163f9c1Sriastradh
22077628746Sriastradh RWSEM_UNLOCKED_EX(rwsem);
22177628746Sriastradh
2220163f9c1Sriastradh mutex_enter(&rwsem->rws_lock);
2230163f9c1Sriastradh KASSERT(rwsem->rws_writer == curlwp);
2240163f9c1Sriastradh KASSERT(rwsem->rws_readers == 0);
2250163f9c1Sriastradh rwsem->rws_writer = NULL;
2260163f9c1Sriastradh rwsem->rws_readers = 1;
2270163f9c1Sriastradh cv_broadcast(&rwsem->rws_cv);
2280163f9c1Sriastradh mutex_exit(&rwsem->rws_lock);
22977628746Sriastradh
23077628746Sriastradh RWSEM_LOCKED_SH(rwsem);
2310163f9c1Sriastradh }
232