1 /****************************************************************************** 2 * 3 * Module Name: dsmethod - Parser/Interpreter interface - control method parsing 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2012, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 #define __DSMETHOD_C__ 45 46 #include <contrib/dev/acpica/include/acpi.h> 47 #include <contrib/dev/acpica/include/accommon.h> 48 #include <contrib/dev/acpica/include/acdispat.h> 49 #include <contrib/dev/acpica/include/acinterp.h> 50 #include <contrib/dev/acpica/include/acnamesp.h> 51 #include <contrib/dev/acpica/include/acdisasm.h> 52 53 54 #define _COMPONENT ACPI_DISPATCHER 55 ACPI_MODULE_NAME ("dsmethod") 56 57 /* Local prototypes */ 58 59 static ACPI_STATUS 60 AcpiDsCreateMethodMutex ( 61 ACPI_OPERAND_OBJECT *MethodDesc); 62 63 64 /******************************************************************************* 65 * 66 * FUNCTION: AcpiDsMethodError 67 * 68 * PARAMETERS: Status - Execution status 69 * WalkState - Current state 70 * 71 * RETURN: Status 72 * 73 * DESCRIPTION: Called on method error. Invoke the global exception handler if 74 * present, dump the method data if the disassembler is configured 75 * 76 * Note: Allows the exception handler to change the status code 77 * 78 ******************************************************************************/ 79 80 ACPI_STATUS 81 AcpiDsMethodError ( 82 ACPI_STATUS Status, 83 ACPI_WALK_STATE *WalkState) 84 { 85 ACPI_FUNCTION_ENTRY (); 86 87 88 /* Ignore AE_OK and control exception codes */ 89 90 if (ACPI_SUCCESS (Status) || 91 (Status & AE_CODE_CONTROL)) 92 { 93 return (Status); 94 } 95 96 /* Invoke the global exception handler */ 97 98 if (AcpiGbl_ExceptionHandler) 99 { 100 /* Exit the interpreter, allow handler to execute methods */ 101 102 AcpiExExitInterpreter (); 103 104 /* 105 * Handler can map the exception code to anything it wants, including 106 * AE_OK, in which case the executing method will not be aborted. 107 */ 108 Status = AcpiGbl_ExceptionHandler (Status, 109 WalkState->MethodNode ? 110 WalkState->MethodNode->Name.Integer : 0, 111 WalkState->Opcode, WalkState->AmlOffset, NULL); 112 AcpiExEnterInterpreter (); 113 } 114 115 AcpiDsClearImplicitReturn (WalkState); 116 117 #ifdef ACPI_DISASSEMBLER 118 if (ACPI_FAILURE (Status)) 119 { 120 /* Display method locals/args if disassembler is present */ 121 122 AcpiDmDumpMethodInfo (Status, WalkState, WalkState->Op); 123 } 124 #endif 125 126 return (Status); 127 } 128 129 130 /******************************************************************************* 131 * 132 * FUNCTION: AcpiDsCreateMethodMutex 133 * 134 * PARAMETERS: ObjDesc - The method object 135 * 136 * RETURN: Status 137 * 138 * DESCRIPTION: Create a mutex object for a serialized control method 139 * 140 ******************************************************************************/ 141 142 static ACPI_STATUS 143 AcpiDsCreateMethodMutex ( 144 ACPI_OPERAND_OBJECT *MethodDesc) 145 { 146 ACPI_OPERAND_OBJECT *MutexDesc; 147 ACPI_STATUS Status; 148 149 150 ACPI_FUNCTION_TRACE (DsCreateMethodMutex); 151 152 153 /* Create the new mutex object */ 154 155 MutexDesc = AcpiUtCreateInternalObject (ACPI_TYPE_MUTEX); 156 if (!MutexDesc) 157 { 158 return_ACPI_STATUS (AE_NO_MEMORY); 159 } 160 161 /* Create the actual OS Mutex */ 162 163 Status = AcpiOsCreateMutex (&MutexDesc->Mutex.OsMutex); 164 if (ACPI_FAILURE (Status)) 165 { 166 return_ACPI_STATUS (Status); 167 } 168 169 MutexDesc->Mutex.SyncLevel = MethodDesc->Method.SyncLevel; 170 MethodDesc->Method.Mutex = MutexDesc; 171 return_ACPI_STATUS (AE_OK); 172 } 173 174 175 /******************************************************************************* 176 * 177 * FUNCTION: AcpiDsBeginMethodExecution 178 * 179 * PARAMETERS: MethodNode - Node of the method 180 * ObjDesc - The method object 181 * WalkState - current state, NULL if not yet executing 182 * a method. 183 * 184 * RETURN: Status 185 * 186 * DESCRIPTION: Prepare a method for execution. Parses the method if necessary, 187 * increments the thread count, and waits at the method semaphore 188 * for clearance to execute. 189 * 190 ******************************************************************************/ 191 192 ACPI_STATUS 193 AcpiDsBeginMethodExecution ( 194 ACPI_NAMESPACE_NODE *MethodNode, 195 ACPI_OPERAND_OBJECT *ObjDesc, 196 ACPI_WALK_STATE *WalkState) 197 { 198 ACPI_STATUS Status = AE_OK; 199 200 201 ACPI_FUNCTION_TRACE_PTR (DsBeginMethodExecution, MethodNode); 202 203 204 if (!MethodNode) 205 { 206 return_ACPI_STATUS (AE_NULL_ENTRY); 207 } 208 209 /* Prevent wraparound of thread count */ 210 211 if (ObjDesc->Method.ThreadCount == ACPI_UINT8_MAX) 212 { 213 ACPI_ERROR ((AE_INFO, 214 "Method reached maximum reentrancy limit (255)")); 215 return_ACPI_STATUS (AE_AML_METHOD_LIMIT); 216 } 217 218 /* 219 * If this method is serialized, we need to acquire the method mutex. 220 */ 221 if (ObjDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED) 222 { 223 /* 224 * Create a mutex for the method if it is defined to be Serialized 225 * and a mutex has not already been created. We defer the mutex creation 226 * until a method is actually executed, to minimize the object count 227 */ 228 if (!ObjDesc->Method.Mutex) 229 { 230 Status = AcpiDsCreateMethodMutex (ObjDesc); 231 if (ACPI_FAILURE (Status)) 232 { 233 return_ACPI_STATUS (Status); 234 } 235 } 236 237 /* 238 * The CurrentSyncLevel (per-thread) must be less than or equal to 239 * the sync level of the method. This mechanism provides some 240 * deadlock prevention 241 * 242 * Top-level method invocation has no walk state at this point 243 */ 244 if (WalkState && 245 (WalkState->Thread->CurrentSyncLevel > ObjDesc->Method.Mutex->Mutex.SyncLevel)) 246 { 247 ACPI_ERROR ((AE_INFO, 248 "Cannot acquire Mutex for method [%4.4s], current SyncLevel is too large (%u)", 249 AcpiUtGetNodeName (MethodNode), 250 WalkState->Thread->CurrentSyncLevel)); 251 252 return_ACPI_STATUS (AE_AML_MUTEX_ORDER); 253 } 254 255 /* 256 * Obtain the method mutex if necessary. Do not acquire mutex for a 257 * recursive call. 258 */ 259 if (!WalkState || 260 !ObjDesc->Method.Mutex->Mutex.ThreadId || 261 (WalkState->Thread->ThreadId != ObjDesc->Method.Mutex->Mutex.ThreadId)) 262 { 263 /* 264 * Acquire the method mutex. This releases the interpreter if we 265 * block (and reacquires it before it returns) 266 */ 267 Status = AcpiExSystemWaitMutex (ObjDesc->Method.Mutex->Mutex.OsMutex, 268 ACPI_WAIT_FOREVER); 269 if (ACPI_FAILURE (Status)) 270 { 271 return_ACPI_STATUS (Status); 272 } 273 274 /* Update the mutex and walk info and save the original SyncLevel */ 275 276 if (WalkState) 277 { 278 ObjDesc->Method.Mutex->Mutex.OriginalSyncLevel = 279 WalkState->Thread->CurrentSyncLevel; 280 281 ObjDesc->Method.Mutex->Mutex.ThreadId = WalkState->Thread->ThreadId; 282 WalkState->Thread->CurrentSyncLevel = ObjDesc->Method.SyncLevel; 283 } 284 else 285 { 286 ObjDesc->Method.Mutex->Mutex.OriginalSyncLevel = 287 ObjDesc->Method.Mutex->Mutex.SyncLevel; 288 } 289 } 290 291 /* Always increase acquisition depth */ 292 293 ObjDesc->Method.Mutex->Mutex.AcquisitionDepth++; 294 } 295 296 /* 297 * Allocate an Owner ID for this method, only if this is the first thread 298 * to begin concurrent execution. We only need one OwnerId, even if the 299 * method is invoked recursively. 300 */ 301 if (!ObjDesc->Method.OwnerId) 302 { 303 Status = AcpiUtAllocateOwnerId (&ObjDesc->Method.OwnerId); 304 if (ACPI_FAILURE (Status)) 305 { 306 goto Cleanup; 307 } 308 } 309 310 /* 311 * Increment the method parse tree thread count since it has been 312 * reentered one more time (even if it is the same thread) 313 */ 314 ObjDesc->Method.ThreadCount++; 315 AcpiMethodCount++; 316 return_ACPI_STATUS (Status); 317 318 319 Cleanup: 320 /* On error, must release the method mutex (if present) */ 321 322 if (ObjDesc->Method.Mutex) 323 { 324 AcpiOsReleaseMutex (ObjDesc->Method.Mutex->Mutex.OsMutex); 325 } 326 return_ACPI_STATUS (Status); 327 } 328 329 330 /******************************************************************************* 331 * 332 * FUNCTION: AcpiDsCallControlMethod 333 * 334 * PARAMETERS: Thread - Info for this thread 335 * ThisWalkState - Current walk state 336 * Op - Current Op to be walked 337 * 338 * RETURN: Status 339 * 340 * DESCRIPTION: Transfer execution to a called control method 341 * 342 ******************************************************************************/ 343 344 ACPI_STATUS 345 AcpiDsCallControlMethod ( 346 ACPI_THREAD_STATE *Thread, 347 ACPI_WALK_STATE *ThisWalkState, 348 ACPI_PARSE_OBJECT *Op) 349 { 350 ACPI_STATUS Status; 351 ACPI_NAMESPACE_NODE *MethodNode; 352 ACPI_WALK_STATE *NextWalkState = NULL; 353 ACPI_OPERAND_OBJECT *ObjDesc; 354 ACPI_EVALUATE_INFO *Info; 355 UINT32 i; 356 357 358 ACPI_FUNCTION_TRACE_PTR (DsCallControlMethod, ThisWalkState); 359 360 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Calling method %p, currentstate=%p\n", 361 ThisWalkState->PrevOp, ThisWalkState)); 362 363 /* 364 * Get the namespace entry for the control method we are about to call 365 */ 366 MethodNode = ThisWalkState->MethodCallNode; 367 if (!MethodNode) 368 { 369 return_ACPI_STATUS (AE_NULL_ENTRY); 370 } 371 372 ObjDesc = AcpiNsGetAttachedObject (MethodNode); 373 if (!ObjDesc) 374 { 375 return_ACPI_STATUS (AE_NULL_OBJECT); 376 } 377 378 /* Init for new method, possibly wait on method mutex */ 379 380 Status = AcpiDsBeginMethodExecution (MethodNode, ObjDesc, 381 ThisWalkState); 382 if (ACPI_FAILURE (Status)) 383 { 384 return_ACPI_STATUS (Status); 385 } 386 387 /* Begin method parse/execution. Create a new walk state */ 388 389 NextWalkState = AcpiDsCreateWalkState (ObjDesc->Method.OwnerId, 390 NULL, ObjDesc, Thread); 391 if (!NextWalkState) 392 { 393 Status = AE_NO_MEMORY; 394 goto Cleanup; 395 } 396 397 /* 398 * The resolved arguments were put on the previous walk state's operand 399 * stack. Operands on the previous walk state stack always 400 * start at index 0. Also, null terminate the list of arguments 401 */ 402 ThisWalkState->Operands [ThisWalkState->NumOperands] = NULL; 403 404 /* 405 * Allocate and initialize the evaluation information block 406 * TBD: this is somewhat inefficient, should change interface to 407 * DsInitAmlWalk. For now, keeps this struct off the CPU stack 408 */ 409 Info = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO)); 410 if (!Info) 411 { 412 Status = AE_NO_MEMORY; 413 goto Cleanup; 414 } 415 416 Info->Parameters = &ThisWalkState->Operands[0]; 417 418 Status = AcpiDsInitAmlWalk (NextWalkState, NULL, MethodNode, 419 ObjDesc->Method.AmlStart, ObjDesc->Method.AmlLength, 420 Info, ACPI_IMODE_EXECUTE); 421 422 ACPI_FREE (Info); 423 if (ACPI_FAILURE (Status)) 424 { 425 goto Cleanup; 426 } 427 428 /* 429 * Delete the operands on the previous walkstate operand stack 430 * (they were copied to new objects) 431 */ 432 for (i = 0; i < ObjDesc->Method.ParamCount; i++) 433 { 434 AcpiUtRemoveReference (ThisWalkState->Operands [i]); 435 ThisWalkState->Operands [i] = NULL; 436 } 437 438 /* Clear the operand stack */ 439 440 ThisWalkState->NumOperands = 0; 441 442 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, 443 "**** Begin nested execution of [%4.4s] **** WalkState=%p\n", 444 MethodNode->Name.Ascii, NextWalkState)); 445 446 /* Invoke an internal method if necessary */ 447 448 if (ObjDesc->Method.InfoFlags & ACPI_METHOD_INTERNAL_ONLY) 449 { 450 Status = ObjDesc->Method.Dispatch.Implementation (NextWalkState); 451 if (Status == AE_OK) 452 { 453 Status = AE_CTRL_TERMINATE; 454 } 455 } 456 457 return_ACPI_STATUS (Status); 458 459 460 Cleanup: 461 462 /* On error, we must terminate the method properly */ 463 464 AcpiDsTerminateControlMethod (ObjDesc, NextWalkState); 465 if (NextWalkState) 466 { 467 AcpiDsDeleteWalkState (NextWalkState); 468 } 469 470 return_ACPI_STATUS (Status); 471 } 472 473 474 /******************************************************************************* 475 * 476 * FUNCTION: AcpiDsRestartControlMethod 477 * 478 * PARAMETERS: WalkState - State for preempted method (caller) 479 * ReturnDesc - Return value from the called method 480 * 481 * RETURN: Status 482 * 483 * DESCRIPTION: Restart a method that was preempted by another (nested) method 484 * invocation. Handle the return value (if any) from the callee. 485 * 486 ******************************************************************************/ 487 488 ACPI_STATUS 489 AcpiDsRestartControlMethod ( 490 ACPI_WALK_STATE *WalkState, 491 ACPI_OPERAND_OBJECT *ReturnDesc) 492 { 493 ACPI_STATUS Status; 494 int SameAsImplicitReturn; 495 496 497 ACPI_FUNCTION_TRACE_PTR (DsRestartControlMethod, WalkState); 498 499 500 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, 501 "****Restart [%4.4s] Op %p ReturnValueFromCallee %p\n", 502 AcpiUtGetNodeName (WalkState->MethodNode), 503 WalkState->MethodCallOp, ReturnDesc)); 504 505 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, 506 " ReturnFromThisMethodUsed?=%X ResStack %p Walk %p\n", 507 WalkState->ReturnUsed, 508 WalkState->Results, WalkState)); 509 510 /* Did the called method return a value? */ 511 512 if (ReturnDesc) 513 { 514 /* Is the implicit return object the same as the return desc? */ 515 516 SameAsImplicitReturn = (WalkState->ImplicitReturnObj == ReturnDesc); 517 518 /* Are we actually going to use the return value? */ 519 520 if (WalkState->ReturnUsed) 521 { 522 /* Save the return value from the previous method */ 523 524 Status = AcpiDsResultPush (ReturnDesc, WalkState); 525 if (ACPI_FAILURE (Status)) 526 { 527 AcpiUtRemoveReference (ReturnDesc); 528 return_ACPI_STATUS (Status); 529 } 530 531 /* 532 * Save as THIS method's return value in case it is returned 533 * immediately to yet another method 534 */ 535 WalkState->ReturnDesc = ReturnDesc; 536 } 537 538 /* 539 * The following code is the optional support for the so-called 540 * "implicit return". Some AML code assumes that the last value of the 541 * method is "implicitly" returned to the caller, in the absence of an 542 * explicit return value. 543 * 544 * Just save the last result of the method as the return value. 545 * 546 * NOTE: this is optional because the ASL language does not actually 547 * support this behavior. 548 */ 549 else if (!AcpiDsDoImplicitReturn (ReturnDesc, WalkState, FALSE) || 550 SameAsImplicitReturn) 551 { 552 /* 553 * Delete the return value if it will not be used by the 554 * calling method or remove one reference if the explicit return 555 * is the same as the implicit return value. 556 */ 557 AcpiUtRemoveReference (ReturnDesc); 558 } 559 } 560 561 return_ACPI_STATUS (AE_OK); 562 } 563 564 565 /******************************************************************************* 566 * 567 * FUNCTION: AcpiDsTerminateControlMethod 568 * 569 * PARAMETERS: MethodDesc - Method object 570 * WalkState - State associated with the method 571 * 572 * RETURN: None 573 * 574 * DESCRIPTION: Terminate a control method. Delete everything that the method 575 * created, delete all locals and arguments, and delete the parse 576 * tree if requested. 577 * 578 * MUTEX: Interpreter is locked 579 * 580 ******************************************************************************/ 581 582 void 583 AcpiDsTerminateControlMethod ( 584 ACPI_OPERAND_OBJECT *MethodDesc, 585 ACPI_WALK_STATE *WalkState) 586 { 587 588 ACPI_FUNCTION_TRACE_PTR (DsTerminateControlMethod, WalkState); 589 590 591 /* MethodDesc is required, WalkState is optional */ 592 593 if (!MethodDesc) 594 { 595 return_VOID; 596 } 597 598 if (WalkState) 599 { 600 /* Delete all arguments and locals */ 601 602 AcpiDsMethodDataDeleteAll (WalkState); 603 604 /* 605 * If method is serialized, release the mutex and restore the 606 * current sync level for this thread 607 */ 608 if (MethodDesc->Method.Mutex) 609 { 610 /* Acquisition Depth handles recursive calls */ 611 612 MethodDesc->Method.Mutex->Mutex.AcquisitionDepth--; 613 if (!MethodDesc->Method.Mutex->Mutex.AcquisitionDepth) 614 { 615 WalkState->Thread->CurrentSyncLevel = 616 MethodDesc->Method.Mutex->Mutex.OriginalSyncLevel; 617 618 AcpiOsReleaseMutex (MethodDesc->Method.Mutex->Mutex.OsMutex); 619 MethodDesc->Method.Mutex->Mutex.ThreadId = 0; 620 } 621 } 622 623 /* 624 * Delete any namespace objects created anywhere within the 625 * namespace by the execution of this method. Unless: 626 * 1) This method is a module-level executable code method, in which 627 * case we want make the objects permanent. 628 * 2) There are other threads executing the method, in which case we 629 * will wait until the last thread has completed. 630 */ 631 if (!(MethodDesc->Method.InfoFlags & ACPI_METHOD_MODULE_LEVEL) && 632 (MethodDesc->Method.ThreadCount == 1)) 633 { 634 /* Delete any direct children of (created by) this method */ 635 636 AcpiNsDeleteNamespaceSubtree (WalkState->MethodNode); 637 638 /* 639 * Delete any objects that were created by this method 640 * elsewhere in the namespace (if any were created). 641 * Use of the ACPI_METHOD_MODIFIED_NAMESPACE optimizes the 642 * deletion such that we don't have to perform an entire 643 * namespace walk for every control method execution. 644 */ 645 if (MethodDesc->Method.InfoFlags & ACPI_METHOD_MODIFIED_NAMESPACE) 646 { 647 AcpiNsDeleteNamespaceByOwner (MethodDesc->Method.OwnerId); 648 MethodDesc->Method.InfoFlags &= ~ACPI_METHOD_MODIFIED_NAMESPACE; 649 } 650 } 651 } 652 653 /* Decrement the thread count on the method */ 654 655 if (MethodDesc->Method.ThreadCount) 656 { 657 MethodDesc->Method.ThreadCount--; 658 } 659 else 660 { 661 ACPI_ERROR ((AE_INFO, 662 "Invalid zero thread count in method")); 663 } 664 665 /* Are there any other threads currently executing this method? */ 666 667 if (MethodDesc->Method.ThreadCount) 668 { 669 /* 670 * Additional threads. Do not release the OwnerId in this case, 671 * we immediately reuse it for the next thread executing this method 672 */ 673 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, 674 "*** Completed execution of one thread, %u threads remaining\n", 675 MethodDesc->Method.ThreadCount)); 676 } 677 else 678 { 679 /* This is the only executing thread for this method */ 680 681 /* 682 * Support to dynamically change a method from NotSerialized to 683 * Serialized if it appears that the method is incorrectly written and 684 * does not support multiple thread execution. The best example of this 685 * is if such a method creates namespace objects and blocks. A second 686 * thread will fail with an AE_ALREADY_EXISTS exception. 687 * 688 * This code is here because we must wait until the last thread exits 689 * before marking the method as serialized. 690 */ 691 if (MethodDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED_PENDING) 692 { 693 if (WalkState) 694 { 695 ACPI_INFO ((AE_INFO, 696 "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error", 697 WalkState->MethodNode->Name.Ascii)); 698 } 699 700 /* 701 * Method tried to create an object twice and was marked as 702 * "pending serialized". The probable cause is that the method 703 * cannot handle reentrancy. 704 * 705 * The method was created as NotSerialized, but it tried to create 706 * a named object and then blocked, causing the second thread 707 * entrance to begin and then fail. Workaround this problem by 708 * marking the method permanently as Serialized when the last 709 * thread exits here. 710 */ 711 MethodDesc->Method.InfoFlags &= ~ACPI_METHOD_SERIALIZED_PENDING; 712 MethodDesc->Method.InfoFlags |= ACPI_METHOD_SERIALIZED; 713 MethodDesc->Method.SyncLevel = 0; 714 } 715 716 /* No more threads, we can free the OwnerId */ 717 718 if (!(MethodDesc->Method.InfoFlags & ACPI_METHOD_MODULE_LEVEL)) 719 { 720 AcpiUtReleaseOwnerId (&MethodDesc->Method.OwnerId); 721 } 722 } 723 724 return_VOID; 725 } 726