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.6 2005/06/09 20:47:37 swildner 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 #include <sys/thread2.h> 42 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 43 #include <sys/lock.h> 44 #include <sys/mutex.h> 45 #endif 46 47 #define _COMPONENT ACPI_OS_SERVICES 48 ACPI_MODULE_NAME("SYNCH") 49 50 static MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 51 52 #if defined(__DragonFly__) 53 # define AS_LOCK(as) crit_enter() 54 # define AS_UNLOCK(as) crit_exit() 55 # define AS_LOCK_DECL 56 # define msleep(a, b, c, d, e) tsleep(a, c, d, e) 57 #elif __FreeBSD_version < 500000 58 # define AS_LOCK(as) s = splhigh() 59 # define AS_UNLOCK(as) splx(s) 60 # define AS_LOCK_DECL int s 61 # define msleep(a, b, c, d, e) tsleep(a, c, d, e) 62 #else 63 # define AS_LOCK(as) mtx_lock(&(as)->as_mtx) 64 # define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx) 65 # define AS_LOCK_DECL 66 #endif 67 68 /* 69 * Simple counting semaphore implemented using a mutex. (Subsequently used 70 * in the OSI code to implement a mutex. Go figure.) 71 */ 72 struct acpi_semaphore { 73 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 74 struct mtx as_mtx; 75 #endif 76 UINT32 as_units; 77 UINT32 as_maxunits; 78 UINT32 as_pendings; 79 UINT32 as_resetting; 80 UINT32 as_timeouts; 81 }; 82 83 #ifndef ACPI_NO_SEMAPHORES 84 #ifndef ACPI_SEMAPHORES_MAX_PENDING 85 #define ACPI_SEMAPHORES_MAX_PENDING 4 86 #endif 87 static int acpi_semaphore_debug = 0; 88 TUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 89 SYSCTL_INT(_debug, OID_AUTO, acpi_semaphore_debug, CTLFLAG_RW, 90 &acpi_semaphore_debug, 0, ""); 91 #endif 92 93 ACPI_STATUS 94 AcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle) 95 { 96 #ifndef ACPI_NO_SEMAPHORES 97 struct acpi_semaphore *as; 98 99 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 100 101 if (OutHandle == NULL) 102 return(AE_BAD_PARAMETER); 103 if (InitialUnits > MaxUnits) 104 return_ACPI_STATUS(AE_BAD_PARAMETER); 105 106 as = malloc(sizeof(*as), M_ACPISEM, M_INTWAIT | M_ZERO); 107 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 108 mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 109 #endif 110 as->as_units = InitialUnits; 111 as->as_maxunits = MaxUnits; 112 as->as_pendings = as->as_resetting = as->as_timeouts = 0; 113 114 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 115 "created semaphore %p max %d, initial %d\n", 116 as, InitialUnits, MaxUnits)); 117 118 *OutHandle = (ACPI_HANDLE)as; 119 return_ACPI_STATUS(AE_OK); 120 #else 121 *OutHandle = (ACPI_HANDLE)OutHandle; 122 return(AE_OK); 123 #endif 124 } 125 126 ACPI_STATUS 127 AcpiOsDeleteSemaphore (ACPI_HANDLE Handle) 128 { 129 #ifndef ACPI_NO_SEMAPHORES 130 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 131 struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 132 #endif 133 134 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 135 136 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 137 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 138 mtx_destroy(&as->as_mtx); 139 #endif 140 free(Handle, M_ACPISEM); 141 return_ACPI_STATUS(AE_OK); 142 #else 143 return(AE_OK); 144 #endif 145 } 146 147 /* 148 * This implementation has a bug, in that it has to stall for the entire 149 * timeout before it will return AE_TIME. A better implementation would 150 * use getmicrotime() to correctly adjust the timeout after being woken up. 151 */ 152 ACPI_STATUS 153 AcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout) 154 { 155 #ifndef ACPI_NO_SEMAPHORES 156 struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 157 ACPI_STATUS result; 158 int rv, tmo; 159 struct timeval timeouttv, currenttv, timelefttv; 160 AS_LOCK_DECL; 161 162 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 163 164 if (as == NULL) 165 return_ACPI_STATUS(AE_BAD_PARAMETER); 166 167 if (cold) 168 return_ACPI_STATUS(AE_OK); 169 170 #if 0 171 if (as->as_units < Units && as->as_timeouts > 10) { 172 printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 173 AS_LOCK(as); 174 as->as_units = as->as_maxunits; 175 if (as->as_pendings) 176 as->as_resetting = 1; 177 as->as_timeouts = 0; 178 wakeup(as); 179 AS_UNLOCK(as); 180 return_ACPI_STATUS(AE_TIME); 181 } 182 183 if (as->as_resetting) { 184 return_ACPI_STATUS(AE_TIME); 185 } 186 #endif 187 188 /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 189 if (Timeout == ACPI_WAIT_FOREVER) { 190 tmo = 0; 191 timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 192 timeouttv.tv_usec = 0; 193 } else { 194 /* compute timeout using microseconds per tick */ 195 tmo = (Timeout * 1000) / (1000000 / hz); 196 if (tmo <= 0) 197 tmo = 1; 198 timeouttv.tv_sec = Timeout / 1000; 199 timeouttv.tv_usec = (Timeout % 1000) * 1000; 200 } 201 202 /* calculate timeout value in timeval */ 203 getmicrotime(¤ttv); 204 timevaladd(&timeouttv, ¤ttv); 205 206 AS_LOCK(as); 207 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 208 "get %d units from semaphore %p (has %d), timeout %d\n", 209 Units, as, as->as_units, Timeout)); 210 for (;;) { 211 if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 212 result = AE_OK; 213 break; 214 } 215 if (as->as_units >= Units) { 216 as->as_units -= Units; 217 result = AE_OK; 218 break; 219 } 220 221 /* limit number of pending treads */ 222 if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 223 result = AE_TIME; 224 break; 225 } 226 227 /* if timeout values of zero is specified, return immediately */ 228 if (Timeout == 0) { 229 result = AE_TIME; 230 break; 231 } 232 233 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 234 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 235 "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 236 as, &as->as_mtx, PCATCH, tmo)); 237 #endif 238 239 as->as_pendings++; 240 241 if (acpi_semaphore_debug) { 242 printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 243 __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 244 } 245 246 rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 247 248 as->as_pendings--; 249 250 #if 0 251 if (as->as_resetting) { 252 /* semaphore reset, return immediately */ 253 if (as->as_pendings == 0) { 254 as->as_resetting = 0; 255 } 256 result = AE_TIME; 257 break; 258 } 259 #endif 260 261 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 262 if (rv == EWOULDBLOCK) { 263 result = AE_TIME; 264 break; 265 } 266 267 /* check if we already awaited enough */ 268 timelefttv = timeouttv; 269 getmicrotime(¤ttv); 270 timevalsub(&timelefttv, ¤ttv); 271 if (timelefttv.tv_sec < 0) { 272 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as)); 273 result = AE_TIME; 274 break; 275 } 276 277 /* adjust timeout for the next sleep */ 278 tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz); 279 if (tmo <= 0) 280 tmo = 1; 281 282 if (acpi_semaphore_debug) { 283 printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n", 284 __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId()); 285 } 286 } 287 288 if (acpi_semaphore_debug) { 289 if (result == AE_TIME && Timeout > 0) { 290 printf("%s: Timeout %d, pending %d, semaphore %p\n", 291 __func__, Timeout, as->as_pendings, as); 292 } 293 if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 294 printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n", 295 __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 296 } 297 } 298 299 if (result == AE_TIME) { 300 as->as_timeouts++; 301 } else { 302 as->as_timeouts = 0; 303 } 304 305 AS_UNLOCK(as); 306 307 return_ACPI_STATUS(result); 308 #else 309 return(AE_OK); 310 #endif 311 } 312 313 ACPI_STATUS 314 AcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units) 315 { 316 #ifndef ACPI_NO_SEMAPHORES 317 struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 318 AS_LOCK_DECL; 319 320 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 321 322 if (as == NULL) 323 return_ACPI_STATUS(AE_BAD_PARAMETER); 324 325 AS_LOCK(as); 326 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 327 "return %d units to semaphore %p (has %d)\n", 328 Units, as, as->as_units)); 329 if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 330 as->as_units += Units; 331 if (as->as_units > as->as_maxunits) 332 as->as_units = as->as_maxunits; 333 } 334 335 if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 336 printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 337 __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 338 } 339 340 wakeup(as); 341 AS_UNLOCK(as); 342 return_ACPI_STATUS(AE_OK); 343 #else 344 return(AE_OK); 345 #endif 346 } 347