Home | History | Annotate | Line # | Download | only in linux
linux_rwsem.c revision 1.3
      1 /*	$NetBSD: linux_rwsem.c,v 1.3 2021/12/19 11:21:45 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2021 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: linux_rwsem.c,v 1.3 2021/12/19 11:21:45 riastradh Exp $");
     31 
     32 #include <sys/types.h>
     33 
     34 #include <sys/condvar.h>
     35 #include <sys/lwp.h>
     36 #include <sys/lockdebug.h>
     37 #include <sys/rwlock.h>
     38 
     39 #include <machine/limits.h>
     40 
     41 #include <lib/libkern/libkern.h>
     42 
     43 #include <linux/rwsem.h>
     44 
     45 #define	RWSEM_WANTLOCK(RWSEM)						      \
     46 	LOCKDEBUG_WANTLOCK((RWSEM)->rws_debug, (RWSEM),			      \
     47 	    (uintptr_t)__builtin_return_address(0), 0)
     48 #define	RWSEM_LOCKED_EX(RWSEM)						      \
     49 	LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL,		      \
     50 	    (uintptr_t)__builtin_return_address(0), 0)
     51 #define	RWSEM_LOCKED_SH(RWSEM)						      \
     52 	LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL,		      \
     53 	    (uintptr_t)__builtin_return_address(0), 1)
     54 #define	RWSEM_UNLOCKED_EX(RWSEM)					      \
     55 	LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM),			      \
     56 	    (uintptr_t)__builtin_return_address(0), 0)
     57 #define	RWSEM_UNLOCKED_SH(RWSEM)					      \
     58 	LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM),			      \
     59 	    (uintptr_t)__builtin_return_address(0), 1)
     60 
     61 #ifdef LOCKDEBUG
     62 static void
     63 rwsem_dump(const volatile void *cookie, lockop_printer_t pr)
     64 {
     65 	const volatile struct rw_semaphore *rwsem = cookie;
     66 
     67 	pr("%-13s: %p", "writer", rwsem->rws_writer);
     68 	pr("%-13s: %u", "readers", rwsem->rws_readers);
     69 	pr("%-13s: %s", "writewanted", rwsem->rws_writewanted ? "yes" : "no");
     70 }
     71 
     72 static lockops_t rwsem_lockops = {
     73 	.lo_name = "Linux read/write semaphore",
     74 	.lo_type = LOCKOPS_SLEEP,
     75 	.lo_dump = rwsem_dump,
     76 };
     77 #endif
     78 
     79 void
     80 init_rwsem(struct rw_semaphore *rwsem)
     81 {
     82 
     83 	mutex_init(&rwsem->rws_lock, MUTEX_DEFAULT, IPL_VM);
     84 	cv_init(&rwsem->rws_cv, "lnxrwsem");
     85 	rwsem->rws_writer = NULL;
     86 	rwsem->rws_readers = 0;
     87 
     88 #ifdef LOCKDEBUG
     89 	rwsem->rws_debug = LOCKDEBUG_ALLOC(rwsem, &rwsem_lockops,
     90 	    (uintptr_t)__builtin_return_address(0));
     91 #endif
     92 }
     93 
     94 void
     95 destroy_rwsem(struct rw_semaphore *rwsem)
     96 {
     97 
     98 	KASSERT(rwsem->rws_readers == 0);
     99 	KASSERT(rwsem->rws_writer == NULL);
    100 
    101 #ifdef LOCKDEBUG
    102 	LOCKDEBUG_FREE(rwsem->rws_debug, rwsem);
    103 #endif
    104 
    105 	cv_destroy(&rwsem->rws_cv);
    106 	mutex_destroy(&rwsem->rws_lock);
    107 }
    108 
    109 void
    110 down_read(struct rw_semaphore *rwsem)
    111 {
    112 
    113 	RWSEM_WANTLOCK(rwsem);
    114 
    115 	mutex_enter(&rwsem->rws_lock);
    116 	while (rwsem->rws_writer || rwsem->rws_writewanted)
    117 		cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
    118 	KASSERT(rwsem->rws_readers < UINT_MAX);
    119 	rwsem->rws_readers++;
    120 	mutex_exit(&rwsem->rws_lock);
    121 
    122 	RWSEM_LOCKED_SH(rwsem);
    123 }
    124 
    125 bool
    126 down_read_trylock(struct rw_semaphore *rwsem)
    127 {
    128 	bool ret = false;
    129 
    130 	/*
    131 	 * Note: Linux apparently relies on down_read_trylock to
    132 	 * quietly succeed when the caller already holds a reader lock.
    133 	 * This is why we can't use rwlock(9), which absolutely
    134 	 * prohibits recursive use and crashes immediately under
    135 	 * LOCKDEBUG if you try it.
    136 	 */
    137 
    138 	mutex_enter(&rwsem->rws_lock);
    139 	if (rwsem->rws_writer == NULL && !rwsem->rws_writewanted) {
    140 		KASSERT(rwsem->rws_readers < UINT_MAX);
    141 		rwsem->rws_readers++;
    142 		ret = true;
    143 	}
    144 	mutex_exit(&rwsem->rws_lock);
    145 
    146 	if (ret)
    147 		RWSEM_LOCKED_SH(rwsem);
    148 
    149 	return ret;
    150 }
    151 
    152 void
    153 up_read(struct rw_semaphore *rwsem)
    154 {
    155 
    156 	RWSEM_UNLOCKED_SH(rwsem);
    157 
    158 	mutex_enter(&rwsem->rws_lock);
    159 	KASSERT(rwsem->rws_readers);
    160 	KASSERT(rwsem->rws_writer == NULL);
    161 	if (--rwsem->rws_readers == 0)
    162 		cv_broadcast(&rwsem->rws_cv);
    163 	mutex_exit(&rwsem->rws_lock);
    164 }
    165 
    166 void
    167 down_write(struct rw_semaphore *rwsem)
    168 {
    169 
    170 	RWSEM_WANTLOCK(rwsem);
    171 
    172 	mutex_enter(&rwsem->rws_lock);
    173 
    174 	/* If another writer is waiting, get in the queue.  */
    175 	while (rwsem->rws_writewanted)
    176 		cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
    177 
    178 	/*
    179 	 * No other writers waiting.  Our turn.  Announce our intent to
    180 	 * readers, and wait for the writer or readers to finish.
    181 	 */
    182 	rwsem->rws_writewanted = true;
    183 	while (rwsem->rws_writer || rwsem->rws_readers) {
    184 		KASSERTMSG(rwsem->rws_writer != curlwp,
    185 		    "locking against myself: rwsem=%p lwp=%p", rwsem, curlwp);
    186 		cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
    187 	}
    188 
    189 	/* At last, it is ours!  */
    190 	KASSERT(rwsem->rws_readers == 0);
    191 	KASSERT(rwsem->rws_writer == NULL);
    192 	KASSERT(rwsem->rws_writewanted);
    193 	rwsem->rws_writewanted = false;
    194 	rwsem->rws_writer = curlwp;
    195 
    196 	mutex_exit(&rwsem->rws_lock);
    197 
    198 	RWSEM_LOCKED_EX(rwsem);
    199 }
    200 
    201 void
    202 up_write(struct rw_semaphore *rwsem)
    203 {
    204 
    205 	RWSEM_UNLOCKED_EX(rwsem);
    206 
    207 	mutex_enter(&rwsem->rws_lock);
    208 	KASSERT(rwsem->rws_writer == curlwp);
    209 	KASSERT(rwsem->rws_readers == 0);
    210 	rwsem->rws_writer = NULL;
    211 	cv_broadcast(&rwsem->rws_cv);
    212 	mutex_exit(&rwsem->rws_lock);
    213 }
    214 
    215 void
    216 downgrade_write(struct rw_semaphore *rwsem)
    217 {
    218 
    219 	RWSEM_UNLOCKED_EX(rwsem);
    220 
    221 	mutex_enter(&rwsem->rws_lock);
    222 	KASSERT(rwsem->rws_writer == curlwp);
    223 	KASSERT(rwsem->rws_readers == 0);
    224 	rwsem->rws_writer = NULL;
    225 	rwsem->rws_readers = 1;
    226 	cv_broadcast(&rwsem->rws_cv);
    227 	mutex_exit(&rwsem->rws_lock);
    228 
    229 	RWSEM_LOCKED_SH(rwsem);
    230 }
    231