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