xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_rwsem.c (revision 246be0cd584889acd481c85c6f6934743094ed15)
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