1 1.5 riastrad /* $NetBSD: linux_wait_bit.c,v 1.5 2021/12/19 12:36:09 riastradh Exp $ */ 2 1.1 riastrad 3 1.1 riastrad /*- 4 1.1 riastrad * Copyright (c) 2018 The NetBSD Foundation, Inc. 5 1.1 riastrad * All rights reserved. 6 1.1 riastrad * 7 1.1 riastrad * This code is derived from software contributed to The NetBSD Foundation 8 1.1 riastrad * by Taylor R. Campbell. 9 1.1 riastrad * 10 1.1 riastrad * Redistribution and use in source and binary forms, with or without 11 1.1 riastrad * modification, are permitted provided that the following conditions 12 1.1 riastrad * are met: 13 1.1 riastrad * 1. Redistributions of source code must retain the above copyright 14 1.1 riastrad * notice, this list of conditions and the following disclaimer. 15 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 riastrad * notice, this list of conditions and the following disclaimer in the 17 1.1 riastrad * documentation and/or other materials provided with the distribution. 18 1.1 riastrad * 19 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE. 30 1.1 riastrad */ 31 1.1 riastrad 32 1.1 riastrad #include <sys/cdefs.h> 33 1.5 riastrad __KERNEL_RCSID(0, "$NetBSD: linux_wait_bit.c,v 1.5 2021/12/19 12:36:09 riastradh Exp $"); 34 1.1 riastrad 35 1.1 riastrad #include <sys/param.h> 36 1.1 riastrad #include <sys/types.h> 37 1.1 riastrad #include <sys/bitops.h> 38 1.1 riastrad #include <sys/condvar.h> 39 1.1 riastrad #include <sys/mutex.h> 40 1.1 riastrad #include <sys/systm.h> 41 1.1 riastrad 42 1.1 riastrad #include <linux/bitops.h> 43 1.1 riastrad #include <linux/sched.h> 44 1.1 riastrad #include <linux/wait_bit.h> 45 1.1 riastrad 46 1.1 riastrad static struct { 47 1.1 riastrad struct waitbitentry { 48 1.1 riastrad kmutex_t lock; 49 1.1 riastrad kcondvar_t cv; 50 1.1 riastrad } ent; 51 1.1 riastrad char pad[CACHE_LINE_SIZE - sizeof(struct waitbitentry)]; 52 1.1 riastrad } waitbittab[PAGE_SIZE/CACHE_LINE_SIZE] __cacheline_aligned; 53 1.1 riastrad CTASSERT(sizeof(waitbittab) == PAGE_SIZE); 54 1.1 riastrad CTASSERT(sizeof(waitbittab[0]) == CACHE_LINE_SIZE); 55 1.1 riastrad 56 1.1 riastrad int 57 1.1 riastrad linux_wait_bit_init(void) 58 1.1 riastrad { 59 1.1 riastrad size_t i; 60 1.1 riastrad 61 1.1 riastrad for (i = 0; i < __arraycount(waitbittab); i++) { 62 1.1 riastrad mutex_init(&waitbittab[i].ent.lock, MUTEX_DEFAULT, IPL_VM); 63 1.1 riastrad cv_init(&waitbittab[i].ent.cv, "waitbit"); 64 1.1 riastrad } 65 1.1 riastrad 66 1.1 riastrad return 0; 67 1.1 riastrad } 68 1.1 riastrad 69 1.1 riastrad void 70 1.1 riastrad linux_wait_bit_fini(void) 71 1.1 riastrad { 72 1.1 riastrad size_t i; 73 1.1 riastrad 74 1.1 riastrad for (i = 0; i < __arraycount(waitbittab); i++) { 75 1.1 riastrad cv_destroy(&waitbittab[i].ent.cv); 76 1.1 riastrad mutex_destroy(&waitbittab[i].ent.lock); 77 1.1 riastrad } 78 1.1 riastrad } 79 1.1 riastrad 80 1.1 riastrad static inline size_t 81 1.1 riastrad wait_bit_hash(const volatile unsigned long *bitmap, unsigned bit) 82 1.1 riastrad { 83 1.1 riastrad /* Try to avoid cache line collisions. */ 84 1.1 riastrad const volatile unsigned long *word = bitmap + bit/(NBBY*sizeof(*word)); 85 1.1 riastrad 86 1.1 riastrad return ((uintptr_t)word >> ilog2(CACHE_LINE_SIZE)) % 87 1.1 riastrad __arraycount(waitbittab); 88 1.1 riastrad } 89 1.1 riastrad 90 1.1 riastrad static struct waitbitentry * 91 1.1 riastrad wait_bit_enter(const volatile unsigned long *bitmap, unsigned bit) 92 1.1 riastrad { 93 1.1 riastrad struct waitbitentry *wbe = &waitbittab[wait_bit_hash(bitmap, bit)].ent; 94 1.1 riastrad 95 1.1 riastrad mutex_enter(&wbe->lock); 96 1.1 riastrad 97 1.1 riastrad return wbe; 98 1.1 riastrad } 99 1.1 riastrad 100 1.1 riastrad static void 101 1.1 riastrad wait_bit_exit(struct waitbitentry *wbe) 102 1.1 riastrad { 103 1.1 riastrad 104 1.1 riastrad mutex_exit(&wbe->lock); 105 1.1 riastrad } 106 1.1 riastrad 107 1.5 riastrad /* 108 1.5 riastrad * clear_and_wake_up_bit(bit, bitmap) 109 1.5 riastrad * 110 1.5 riastrad * Clear the specified bit in the bitmap and wake any waiters in 111 1.5 riastrad * wait_on_bit or wait_on_bit_timeout that were waiting for it to 112 1.5 riastrad * clear. 113 1.5 riastrad */ 114 1.4 riastrad void 115 1.4 riastrad clear_and_wake_up_bit(int bit, volatile unsigned long *bitmap) 116 1.4 riastrad { 117 1.4 riastrad struct waitbitentry *wbe; 118 1.4 riastrad 119 1.4 riastrad wbe = wait_bit_enter(bitmap, bit); 120 1.4 riastrad clear_bit(bit, bitmap); 121 1.4 riastrad cv_broadcast(&wbe->cv); 122 1.4 riastrad wait_bit_exit(wbe); 123 1.4 riastrad } 124 1.4 riastrad 125 1.5 riastrad /* 126 1.5 riastrad * wait_on_bit(bitmap, bit, flags) 127 1.5 riastrad * 128 1.5 riastrad * Wait for the specified bit in bitmap to be cleared. Returns 0 129 1.5 riastrad * on success, -EINTR on signal, unless flags has 130 1.5 riastrad * TASK_UNINTERRUPTIBLE set. 131 1.5 riastrad */ 132 1.1 riastrad int 133 1.3 riastrad wait_on_bit(const volatile unsigned long *bitmap, unsigned bit, int flags) 134 1.3 riastrad { 135 1.3 riastrad struct waitbitentry *wbe; 136 1.3 riastrad int error, ret; 137 1.3 riastrad 138 1.5 riastrad if (test_bit(bit, bitmap) == 0) 139 1.3 riastrad return 0; 140 1.3 riastrad 141 1.3 riastrad wbe = wait_bit_enter(bitmap, bit); 142 1.3 riastrad 143 1.5 riastrad while (test_bit(bit, bitmap)) { 144 1.3 riastrad if (flags & TASK_UNINTERRUPTIBLE) { 145 1.3 riastrad cv_wait(&wbe->cv, &wbe->lock); 146 1.3 riastrad } else { 147 1.3 riastrad error = cv_wait_sig(&wbe->cv, &wbe->lock); 148 1.5 riastrad if (error) { 149 1.5 riastrad /* cv_wait_sig can only fail on signal. */ 150 1.5 riastrad KASSERTMSG(error == EINTR || error == ERESTART, 151 1.5 riastrad "error=%d", error); 152 1.5 riastrad ret = -EINTR; 153 1.3 riastrad goto out; 154 1.5 riastrad } 155 1.3 riastrad } 156 1.3 riastrad } 157 1.3 riastrad 158 1.5 riastrad /* Bit is clear. Return zero on success. */ 159 1.5 riastrad KASSERT(test_bit(bit, bitmap) == 0); 160 1.3 riastrad ret = 0; 161 1.3 riastrad 162 1.5 riastrad out: KASSERT(test_bit(bit, bitmap) == 0 || ret != 0); 163 1.5 riastrad wait_bit_exit(wbe); 164 1.3 riastrad return ret; 165 1.3 riastrad } 166 1.3 riastrad 167 1.5 riastrad /* 168 1.5 riastrad * wait_on_bit_timeout(bitmap, bit, flags, timeout) 169 1.5 riastrad * 170 1.5 riastrad * Wait for the specified bit in bitmap to be cleared. Returns 0 171 1.5 riastrad * on success, -EINTR on signal unless flags has 172 1.5 riastrad * TASK_UNINTERRUPTIBLE set, or -EAGAIN on timeout. 173 1.5 riastrad */ 174 1.3 riastrad int 175 1.1 riastrad wait_on_bit_timeout(const volatile unsigned long *bitmap, unsigned bit, 176 1.1 riastrad int flags, unsigned long timeout) 177 1.1 riastrad { 178 1.1 riastrad struct waitbitentry *wbe; 179 1.1 riastrad int error, ret; 180 1.1 riastrad 181 1.5 riastrad if (test_bit(bit, bitmap) == 0) 182 1.5 riastrad return 0; 183 1.1 riastrad 184 1.1 riastrad wbe = wait_bit_enter(bitmap, bit); 185 1.1 riastrad 186 1.5 riastrad while (test_bit(bit, bitmap)) { 187 1.1 riastrad unsigned starttime, endtime; 188 1.1 riastrad 189 1.5 riastrad if (timeout == 0) { 190 1.5 riastrad ret = -EAGAIN; 191 1.5 riastrad goto out; 192 1.5 riastrad } 193 1.5 riastrad 194 1.5 riastrad starttime = getticks(); 195 1.1 riastrad if (flags & TASK_UNINTERRUPTIBLE) { 196 1.1 riastrad error = cv_timedwait(&wbe->cv, &wbe->lock, 197 1.5 riastrad MIN(timeout, INT_MAX/2)); 198 1.1 riastrad } else { 199 1.1 riastrad error = cv_timedwait_sig(&wbe->cv, &wbe->lock, 200 1.5 riastrad MIN(timeout, INT_MAX/2)); 201 1.1 riastrad } 202 1.5 riastrad endtime = getticks(); 203 1.1 riastrad 204 1.5 riastrad /* 205 1.5 riastrad * If we were interrupted or timed out, massage the 206 1.5 riastrad * error return and stop here. 207 1.5 riastrad */ 208 1.5 riastrad if (error) { 209 1.5 riastrad KASSERTMSG((error == EINTR || error == ERESTART || 210 1.5 riastrad error == EWOULDBLOCK), "error=%d", error); 211 1.5 riastrad if (error == EINTR || error == ERESTART) { 212 1.5 riastrad ret = -EINTR; 213 1.5 riastrad } else if (error == EWOULDBLOCK) { 214 1.5 riastrad ret = -EAGAIN; 215 1.5 riastrad } else { 216 1.5 riastrad panic("invalid error=%d", error); 217 1.5 riastrad } 218 1.1 riastrad goto out; 219 1.1 riastrad } 220 1.1 riastrad 221 1.1 riastrad /* Otherwise, debit the time spent. */ 222 1.5 riastrad timeout -= MIN(timeout, (endtime - starttime)); 223 1.1 riastrad } 224 1.5 riastrad 225 1.5 riastrad /* Bit is clear. Return zero on success. */ 226 1.5 riastrad KASSERT(test_bit(bit, bitmap) == 0); 227 1.1 riastrad ret = timeout; 228 1.1 riastrad 229 1.5 riastrad out: KASSERT(test_bit(bit, bitmap) == 0 || ret != 0); 230 1.5 riastrad wait_bit_exit(wbe); 231 1.1 riastrad return ret; 232 1.1 riastrad } 233