1 /*- 2 * Copyright (c) 2000 Michael Smith 3 * Copyright (c) 2000 BSDi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: src/sys/dev/acpica/Osd/OsdSynch.c,v 1.17.2.1 2003/08/22 20:49:21 jhb Exp $ 28 * $DragonFly: src/sys/dev/acpica/Osd/Attic/OsdSynch.c,v 1.3 2004/05/05 22:18:09 dillon Exp $ 29 */ 30 31 /* 32 * 6.1 : Mutual Exclusion and Synchronisation 33 */ 34 35 #include "acpi.h" 36 37 #include "opt_acpi.h" 38 #include <sys/kernel.h> 39 #include <sys/malloc.h> 40 #include <sys/sysctl.h> 41 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 42 #include <sys/lock.h> 43 #include <sys/mutex.h> 44 #endif 45 46 #define _COMPONENT ACPI_OS_SERVICES 47 ACPI_MODULE_NAME("SYNCH") 48 49 static MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 50 51 #if defined(__DragonFly__) || __FreeBSD_version < 500000 52 # define AS_LOCK(as) s = splhigh() 53 # define AS_UNLOCK(as) splx(s) 54 # define AS_LOCK_DECL int s 55 # define msleep(a, b, c, d, e) tsleep(a, c, d, e) 56 #else 57 # define AS_LOCK(as) mtx_lock(&(as)->as_mtx) 58 # define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx) 59 # define AS_LOCK_DECL 60 #endif 61 62 /* 63 * Simple counting semaphore implemented using a mutex. (Subsequently used 64 * in the OSI code to implement a mutex. Go figure.) 65 */ 66 struct acpi_semaphore { 67 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 68 struct mtx as_mtx; 69 #endif 70 UINT32 as_units; 71 UINT32 as_maxunits; 72 UINT32 as_pendings; 73 UINT32 as_resetting; 74 UINT32 as_timeouts; 75 }; 76 77 #ifndef ACPI_NO_SEMAPHORES 78 #ifndef ACPI_SEMAPHORES_MAX_PENDING 79 #define ACPI_SEMAPHORES_MAX_PENDING 4 80 #endif 81 static int acpi_semaphore_debug = 0; 82 TUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 83 SYSCTL_INT(_debug, OID_AUTO, acpi_semaphore_debug, CTLFLAG_RW, 84 &acpi_semaphore_debug, 0, ""); 85 #endif 86 87 ACPI_STATUS 88 AcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle) 89 { 90 #ifndef ACPI_NO_SEMAPHORES 91 struct acpi_semaphore *as; 92 93 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 94 95 if (OutHandle == NULL) 96 return(AE_BAD_PARAMETER); 97 if (InitialUnits > MaxUnits) 98 return_ACPI_STATUS(AE_BAD_PARAMETER); 99 100 as = malloc(sizeof(*as), M_ACPISEM, M_INTWAIT | M_ZERO); 101 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 102 mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 103 #endif 104 as->as_units = InitialUnits; 105 as->as_maxunits = MaxUnits; 106 as->as_pendings = as->as_resetting = as->as_timeouts = 0; 107 108 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 109 "created semaphore %p max %d, initial %d\n", 110 as, InitialUnits, MaxUnits)); 111 112 *OutHandle = (ACPI_HANDLE)as; 113 return_ACPI_STATUS(AE_OK); 114 #else 115 *OutHandle = (ACPI_HANDLE)OutHandle; 116 return(AE_OK); 117 #endif 118 } 119 120 ACPI_STATUS 121 AcpiOsDeleteSemaphore (ACPI_HANDLE Handle) 122 { 123 #ifndef ACPI_NO_SEMAPHORES 124 struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 125 126 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 127 128 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 129 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 130 mtx_destroy(&as->as_mtx); 131 #endif 132 free(Handle, M_ACPISEM); 133 return_ACPI_STATUS(AE_OK); 134 #else 135 return(AE_OK); 136 #endif 137 } 138 139 /* 140 * This implementation has a bug, in that it has to stall for the entire 141 * timeout before it will return AE_TIME. A better implementation would 142 * use getmicrotime() to correctly adjust the timeout after being woken up. 143 */ 144 ACPI_STATUS 145 AcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout) 146 { 147 #ifndef ACPI_NO_SEMAPHORES 148 struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 149 ACPI_STATUS result; 150 int rv, tmo; 151 struct timeval timeouttv, currenttv, timelefttv; 152 AS_LOCK_DECL; 153 154 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 155 156 if (as == NULL) 157 return_ACPI_STATUS(AE_BAD_PARAMETER); 158 159 if (cold) 160 return_ACPI_STATUS(AE_OK); 161 162 #if 0 163 if (as->as_units < Units && as->as_timeouts > 10) { 164 printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 165 AS_LOCK(as); 166 as->as_units = as->as_maxunits; 167 if (as->as_pendings) 168 as->as_resetting = 1; 169 as->as_timeouts = 0; 170 wakeup(as); 171 AS_UNLOCK(as); 172 return_ACPI_STATUS(AE_TIME); 173 } 174 175 if (as->as_resetting) { 176 return_ACPI_STATUS(AE_TIME); 177 } 178 #endif 179 180 /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 181 if (Timeout == ACPI_WAIT_FOREVER) { 182 tmo = 0; 183 timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 184 timeouttv.tv_usec = 0; 185 } else { 186 /* compute timeout using microseconds per tick */ 187 tmo = (Timeout * 1000) / (1000000 / hz); 188 if (tmo <= 0) 189 tmo = 1; 190 timeouttv.tv_sec = Timeout / 1000; 191 timeouttv.tv_usec = (Timeout % 1000) * 1000; 192 } 193 194 /* calculate timeout value in timeval */ 195 getmicrotime(¤ttv); 196 timevaladd(&timeouttv, ¤ttv); 197 198 AS_LOCK(as); 199 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 200 "get %d units from semaphore %p (has %d), timeout %d\n", 201 Units, as, as->as_units, Timeout)); 202 for (;;) { 203 if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 204 result = AE_OK; 205 break; 206 } 207 if (as->as_units >= Units) { 208 as->as_units -= Units; 209 result = AE_OK; 210 break; 211 } 212 213 /* limit number of pending treads */ 214 if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 215 result = AE_TIME; 216 break; 217 } 218 219 /* if timeout values of zero is specified, return immediately */ 220 if (Timeout == 0) { 221 result = AE_TIME; 222 break; 223 } 224 225 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 226 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 227 "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 228 as, &as->as_mtx, PCATCH, tmo)); 229 #endif 230 231 as->as_pendings++; 232 233 if (acpi_semaphore_debug) { 234 printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 235 __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 236 } 237 238 rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 239 240 as->as_pendings--; 241 242 #if 0 243 if (as->as_resetting) { 244 /* semaphore reset, return immediately */ 245 if (as->as_pendings == 0) { 246 as->as_resetting = 0; 247 } 248 result = AE_TIME; 249 break; 250 } 251 #endif 252 253 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 254 if (rv == EWOULDBLOCK) { 255 result = AE_TIME; 256 break; 257 } 258 259 /* check if we already awaited enough */ 260 timelefttv = timeouttv; 261 getmicrotime(¤ttv); 262 timevalsub(&timelefttv, ¤ttv); 263 if (timelefttv.tv_sec < 0) { 264 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as)); 265 result = AE_TIME; 266 break; 267 } 268 269 /* adjust timeout for the next sleep */ 270 tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz); 271 if (tmo <= 0) 272 tmo = 1; 273 274 if (acpi_semaphore_debug) { 275 printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n", 276 __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId()); 277 } 278 } 279 280 if (acpi_semaphore_debug) { 281 if (result == AE_TIME && Timeout > 0) { 282 printf("%s: Timeout %d, pending %d, semaphore %p\n", 283 __func__, Timeout, as->as_pendings, as); 284 } 285 if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 286 printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n", 287 __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 288 } 289 } 290 291 if (result == AE_TIME) { 292 as->as_timeouts++; 293 } else { 294 as->as_timeouts = 0; 295 } 296 297 AS_UNLOCK(as); 298 299 return_ACPI_STATUS(result); 300 #else 301 return(AE_OK); 302 #endif 303 } 304 305 ACPI_STATUS 306 AcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units) 307 { 308 #ifndef ACPI_NO_SEMAPHORES 309 struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 310 AS_LOCK_DECL; 311 312 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 313 314 if (as == NULL) 315 return_ACPI_STATUS(AE_BAD_PARAMETER); 316 317 AS_LOCK(as); 318 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 319 "return %d units to semaphore %p (has %d)\n", 320 Units, as, as->as_units)); 321 if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 322 as->as_units += Units; 323 if (as->as_units > as->as_maxunits) 324 as->as_units = as->as_maxunits; 325 } 326 327 if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 328 printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 329 __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 330 } 331 332 wakeup(as); 333 AS_UNLOCK(as); 334 return_ACPI_STATUS(AE_OK); 335 #else 336 return(AE_OK); 337 #endif 338 } 339