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 return_ACPI_STATUS (AE_NO_MEMORY); 413 } 414 415 Info->Parameters = &ThisWalkState->Operands[0]; 416 417 Status = AcpiDsInitAmlWalk (NextWalkState, NULL, MethodNode, 418 ObjDesc->Method.AmlStart, ObjDesc->Method.AmlLength, 419 Info, ACPI_IMODE_EXECUTE); 420 421 ACPI_FREE (Info); 422 if (ACPI_FAILURE (Status)) 423 { 424 goto Cleanup; 425 } 426 427 /* 428 * Delete the operands on the previous walkstate operand stack 429 * (they were copied to new objects) 430 */ 431 for (i = 0; i < ObjDesc->Method.ParamCount; i++) 432 { 433 AcpiUtRemoveReference (ThisWalkState->Operands [i]); 434 ThisWalkState->Operands [i] = NULL; 435 } 436 437 /* Clear the operand stack */ 438 439 ThisWalkState->NumOperands = 0; 440 441 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, 442 "**** Begin nested execution of [%4.4s] **** WalkState=%p\n", 443 MethodNode->Name.Ascii, NextWalkState)); 444 445 /* Invoke an internal method if necessary */ 446 447 if (ObjDesc->Method.InfoFlags & ACPI_METHOD_INTERNAL_ONLY) 448 { 449 Status = ObjDesc->Method.Dispatch.Implementation (NextWalkState); 450 if (Status == AE_OK) 451 { 452 Status = AE_CTRL_TERMINATE; 453 } 454 } 455 456 return_ACPI_STATUS (Status); 457 458 459 Cleanup: 460 461 /* On error, we must terminate the method properly */ 462 463 AcpiDsTerminateControlMethod (ObjDesc, NextWalkState); 464 if (NextWalkState) 465 { 466 AcpiDsDeleteWalkState (NextWalkState); 467 } 468 469 return_ACPI_STATUS (Status); 470 } 471 472 473 /******************************************************************************* 474 * 475 * FUNCTION: AcpiDsRestartControlMethod 476 * 477 * PARAMETERS: WalkState - State for preempted method (caller) 478 * ReturnDesc - Return value from the called method 479 * 480 * RETURN: Status 481 * 482 * DESCRIPTION: Restart a method that was preempted by another (nested) method 483 * invocation. Handle the return value (if any) from the callee. 484 * 485 ******************************************************************************/ 486 487 ACPI_STATUS 488 AcpiDsRestartControlMethod ( 489 ACPI_WALK_STATE *WalkState, 490 ACPI_OPERAND_OBJECT *ReturnDesc) 491 { 492 ACPI_STATUS Status; 493 int SameAsImplicitReturn; 494 495 496 ACPI_FUNCTION_TRACE_PTR (DsRestartControlMethod, WalkState); 497 498 499 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, 500 "****Restart [%4.4s] Op %p ReturnValueFromCallee %p\n", 501 AcpiUtGetNodeName (WalkState->MethodNode), 502 WalkState->MethodCallOp, ReturnDesc)); 503 504 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, 505 " ReturnFromThisMethodUsed?=%X ResStack %p Walk %p\n", 506 WalkState->ReturnUsed, 507 WalkState->Results, WalkState)); 508 509 /* Did the called method return a value? */ 510 511 if (ReturnDesc) 512 { 513 /* Is the implicit return object the same as the return desc? */ 514 515 SameAsImplicitReturn = (WalkState->ImplicitReturnObj == ReturnDesc); 516 517 /* Are we actually going to use the return value? */ 518 519 if (WalkState->ReturnUsed) 520 { 521 /* Save the return value from the previous method */ 522 523 Status = AcpiDsResultPush (ReturnDesc, WalkState); 524 if (ACPI_FAILURE (Status)) 525 { 526 AcpiUtRemoveReference (ReturnDesc); 527 return_ACPI_STATUS (Status); 528 } 529 530 /* 531 * Save as THIS method's return value in case it is returned 532 * immediately to yet another method 533 */ 534 WalkState->ReturnDesc = ReturnDesc; 535 } 536 537 /* 538 * The following code is the optional support for the so-called 539 * "implicit return". Some AML code assumes that the last value of the 540 * method is "implicitly" returned to the caller, in the absence of an 541 * explicit return value. 542 * 543 * Just save the last result of the method as the return value. 544 * 545 * NOTE: this is optional because the ASL language does not actually 546 * support this behavior. 547 */ 548 else if (!AcpiDsDoImplicitReturn (ReturnDesc, WalkState, FALSE) || 549 SameAsImplicitReturn) 550 { 551 /* 552 * Delete the return value if it will not be used by the 553 * calling method or remove one reference if the explicit return 554 * is the same as the implicit return value. 555 */ 556 AcpiUtRemoveReference (ReturnDesc); 557 } 558 } 559 560 return_ACPI_STATUS (AE_OK); 561 } 562 563 564 /******************************************************************************* 565 * 566 * FUNCTION: AcpiDsTerminateControlMethod 567 * 568 * PARAMETERS: MethodDesc - Method object 569 * WalkState - State associated with the method 570 * 571 * RETURN: None 572 * 573 * DESCRIPTION: Terminate a control method. Delete everything that the method 574 * created, delete all locals and arguments, and delete the parse 575 * tree if requested. 576 * 577 * MUTEX: Interpreter is locked 578 * 579 ******************************************************************************/ 580 581 void 582 AcpiDsTerminateControlMethod ( 583 ACPI_OPERAND_OBJECT *MethodDesc, 584 ACPI_WALK_STATE *WalkState) 585 { 586 587 ACPI_FUNCTION_TRACE_PTR (DsTerminateControlMethod, WalkState); 588 589 590 /* MethodDesc is required, WalkState is optional */ 591 592 if (!MethodDesc) 593 { 594 return_VOID; 595 } 596 597 if (WalkState) 598 { 599 /* Delete all arguments and locals */ 600 601 AcpiDsMethodDataDeleteAll (WalkState); 602 603 /* 604 * If method is serialized, release the mutex and restore the 605 * current sync level for this thread 606 */ 607 if (MethodDesc->Method.Mutex) 608 { 609 /* Acquisition Depth handles recursive calls */ 610 611 MethodDesc->Method.Mutex->Mutex.AcquisitionDepth--; 612 if (!MethodDesc->Method.Mutex->Mutex.AcquisitionDepth) 613 { 614 WalkState->Thread->CurrentSyncLevel = 615 MethodDesc->Method.Mutex->Mutex.OriginalSyncLevel; 616 617 AcpiOsReleaseMutex (MethodDesc->Method.Mutex->Mutex.OsMutex); 618 MethodDesc->Method.Mutex->Mutex.ThreadId = 0; 619 } 620 } 621 622 /* 623 * Delete any namespace objects created anywhere within the 624 * namespace by the execution of this method. Unless: 625 * 1) This method is a module-level executable code method, in which 626 * case we want make the objects permanent. 627 * 2) There are other threads executing the method, in which case we 628 * will wait until the last thread has completed. 629 */ 630 if (!(MethodDesc->Method.InfoFlags & ACPI_METHOD_MODULE_LEVEL) && 631 (MethodDesc->Method.ThreadCount == 1)) 632 { 633 /* Delete any direct children of (created by) this method */ 634 635 AcpiNsDeleteNamespaceSubtree (WalkState->MethodNode); 636 637 /* 638 * Delete any objects that were created by this method 639 * elsewhere in the namespace (if any were created). 640 * Use of the ACPI_METHOD_MODIFIED_NAMESPACE optimizes the 641 * deletion such that we don't have to perform an entire 642 * namespace walk for every control method execution. 643 */ 644 if (MethodDesc->Method.InfoFlags & ACPI_METHOD_MODIFIED_NAMESPACE) 645 { 646 AcpiNsDeleteNamespaceByOwner (MethodDesc->Method.OwnerId); 647 MethodDesc->Method.InfoFlags &= ~ACPI_METHOD_MODIFIED_NAMESPACE; 648 } 649 } 650 } 651 652 /* Decrement the thread count on the method */ 653 654 if (MethodDesc->Method.ThreadCount) 655 { 656 MethodDesc->Method.ThreadCount--; 657 } 658 else 659 { 660 ACPI_ERROR ((AE_INFO, 661 "Invalid zero thread count in method")); 662 } 663 664 /* Are there any other threads currently executing this method? */ 665 666 if (MethodDesc->Method.ThreadCount) 667 { 668 /* 669 * Additional threads. Do not release the OwnerId in this case, 670 * we immediately reuse it for the next thread executing this method 671 */ 672 ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, 673 "*** Completed execution of one thread, %u threads remaining\n", 674 MethodDesc->Method.ThreadCount)); 675 } 676 else 677 { 678 /* This is the only executing thread for this method */ 679 680 /* 681 * Support to dynamically change a method from NotSerialized to 682 * Serialized if it appears that the method is incorrectly written and 683 * does not support multiple thread execution. The best example of this 684 * is if such a method creates namespace objects and blocks. A second 685 * thread will fail with an AE_ALREADY_EXISTS exception. 686 * 687 * This code is here because we must wait until the last thread exits 688 * before marking the method as serialized. 689 */ 690 if (MethodDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED_PENDING) 691 { 692 if (WalkState) 693 { 694 ACPI_INFO ((AE_INFO, 695 "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error", 696 WalkState->MethodNode->Name.Ascii)); 697 } 698 699 /* 700 * Method tried to create an object twice and was marked as 701 * "pending serialized". The probable cause is that the method 702 * cannot handle reentrancy. 703 * 704 * The method was created as NotSerialized, but it tried to create 705 * a named object and then blocked, causing the second thread 706 * entrance to begin and then fail. Workaround this problem by 707 * marking the method permanently as Serialized when the last 708 * thread exits here. 709 */ 710 MethodDesc->Method.InfoFlags &= ~ACPI_METHOD_SERIALIZED_PENDING; 711 MethodDesc->Method.InfoFlags |= ACPI_METHOD_SERIALIZED; 712 MethodDesc->Method.SyncLevel = 0; 713 } 714 715 /* No more threads, we can free the OwnerId */ 716 717 if (!(MethodDesc->Method.InfoFlags & ACPI_METHOD_MODULE_LEVEL)) 718 { 719 AcpiUtReleaseOwnerId (&MethodDesc->Method.OwnerId); 720 } 721 } 722 723 return_VOID; 724 } 725 726 727