1/******************************************************************************
2 *
3 * Module Name: dsmethod - Parser/Interpreter interface - control method parsing
4 *
5 *****************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2011, 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
59static ACPI_STATUS
60AcpiDsCreateMethodMutex (
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
80ACPI_STATUS
81AcpiDsMethodError (
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
142static ACPI_STATUS
143AcpiDsCreateMethodMutex (
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
192ACPI_STATUS
193AcpiDsBeginMethodExecution (
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
319Cleanup:
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
344ACPI_STATUS
345AcpiDsCallControlMethod (
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
459Cleanup:
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
487ACPI_STATUS
488AcpiDsRestartControlMethod (
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
581void
582AcpiDsTerminateControlMethod (
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