> On Nov 13, 2015, at 10:53 PM, Michael Zimmermann <[email protected]> > wrote: > > > I think I've made some progress. > Thx to the fact that the only thing stuck is the ARM thread mode and the > timer interrupts are still fired(I verified that my callback is called from > an interrupt, being it a fluke or not) I could easily dump the current PC > of the DxeCore context. >
There is most likely a raise to TPL_HIGH and a TPL restore in that path that causes event dispatch. > This way I discovered that the code is stuck in the delay functions of the > timerLib because they're not thread safe. > Is it one of the edk2 timer libs? If so which one? Thanks, Andrew Fish > The reason is that a hw counter gets reset and started for this and if this > happens from an interrupt(UART uses delay functions) while the thread mode > is whiting for the counter to reach an specific value it will just wait > forever because the counter gets disabled after the delay. > > Code: > usecs = (usecs * 33 + 1000 - 33) / 1000; > > writel(0, GPT_CLEAR); > writel(0, GPT_ENABLE); > while (readl(GPT_COUNT_VAL) != 0) ; > > writel(GPT_ENABLE_EN, GPT_ENABLE); > while (readl(GPT_COUNT_VAL) < usecs) ; > > writel(0, GPT_ENABLE); > writel(0, GPT_CLEAR); > > I guess I need to try keeping the counter running and detect when it rolled > over to make it thread-safe. > > Michael > > On Fri, Nov 13, 2015 at 1:14 AM, Andrew Fish <[email protected]> wrote: > >> >>> On Nov 12, 2015, at 11:21 AM, Michael Zimmermann < >> [email protected]> wrote: >>> >>> thx for this information. >>> I don't have any debug hw but UART. >>> >>> After some careful DEBUG printing and tracing the call stack using >>> '__builtin_return_address(0)' >> >> FYI the edk2 has a portable form of this RETURN_ADDRESS(0) >> >>> I found that the application's dead happens >>> at 'WaitForEvent' >> >> The WaitForEvent() is what happens at the shell prompt when it is waiting >> for keyboard input. So that is functioning as intended. gBS->WaitForEvent() >> is the power efficient way to wait. >> >> If you look at CoreWaitForEvent() it checks all the events, and then calls >> CoreSignalEvent with the gIdleLoopEvent. If the DXE Cpu driver supports >> this feature gIdleLoopEvent will put the CPU in a low power state until the >> next timer tick. Since CoreWaitForEvent() checked all the event state it >> can not change until the next timer tick so this lets the system spend its >> idle time in a lower power mode. >> >> >> https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/Dxe/Event/Event.c#L685 >> for(;;) { >> >> for(Index = 0; Index < NumberOfEvents; Index++) { >> >> Status = CoreCheckEvent (UserEvents[Index]); >> >> // >> // provide index of event that caused problem >> // >> if (Status != EFI_NOT_READY) { >> if (UserIndex != NULL) { >> *UserIndex = Index; >> } >> return Status; >> } >> } >> >> // >> // Signal the Idle event >> // >> CoreSignalEvent (gIdleLoopEvent); >> } >> >> So if you are really stuck here it could imply your Simple Text In is >> hung? Maybe your issue is data corruption vs an event issue? >> >>> and that there aren't any long running events(it returns >>> from CoreDispatchEventNotifies and CoreRestoreTpl everytime). >>> >>> I'm not sure this is always the case though because without the DEBUG's >> the >>> application can even stop when the edk2 shell is printing the device map >>> and WaitForEvent usually is only used for key input. >>> >>> isn't there some GCC option to save the stacktrace so I can dump it after >>> some seconds runtime(when the system died) and just print the whole >>> backtrace?(like the linux kernel does). >>> >> >> Well this is when it gets tricky. For VC++ you can only _ReturnAddress(), >> and that is equivalent of __builtin_return_address(0) on the GCC side. But >> it gets worse... To get a stack trace for X64 with VC++ you need symbols as >> the unwind is in the .PDB file. >> >> For clang, and I think with the right GCC compiler flags, you can walk the >> stack as each routine saves the frame pointer on entry. This is a simple >> X64 C function that just returns 0. The push of the %rbp and saving %rsp >> allow the stack to be unwound in software. >> >> pushq %rbp >> movq %rsp, %rbp >> xorl %eax, %eax >> popq %rbp >> retq >> >> So it is possible to unwind the stack in software. >> >> Simple answer is try RETURN_ADDRESS(1), RETURN_ADDRESS(2), etc. If it is >> supported the compiler will generate the code to unwind for you. >> >> Complex answer is write the stack unwind code, if it is possible? Which >> Processor and I assume you are using GCC? >> >> You can use the GetImageName() function in >> https://github.com/tianocore/edk2/blob/master/ArmPkg/Library/DefaultExceptionHandlerLib/DefaultExceptionHandlerUefi.c#L64 >> to see how you can use the PE/COFF library functions to convert a >> FaultAddress to the ImageBase, and name of the image. If you know the >> offset into the image relative to the start you can load image in gdb from >> the build output directory and see what code it maps to. >> >> The X86 version of all this lives: >> https://github.com/tianocore/edk2/blob/master/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c >> >> Thanks, >> >> Andrew Fish >> >>> On Thu, Nov 12, 2015 at 7:39 PM, Andrew Fish <[email protected]> wrote: >>> >>>> >>>> On Nov 12, 2015, at 9:05 AM, Michael Zimmermann < >> [email protected]> >>>> wrote: >>>> >>>> thx, this perfectly explains my situation(that EDK2 shell stops while >>>> printing sth. like the map or waiting for this 5s startup.nsh timeout). >>>> >>>> So this means that processing the event queue is caused by any API call >>>> which never returns to the DXE phase for some reason. >>>> >>>> >>>> You should also check your code looking for TPL violations. Calling EFI >>>> Services at to high a TPL can lead to undefined behavior. >>>> >>>> If you look at the UEFI 2.5 spec, Section 6.1 Event, Timer, and Task >>>> Priority Services Table 23 lists TPL Restrictions. You can check that >> your >>>> code is not violating any of these restrictions. The natural reaction >> for >>>> some one having performance issues is to cheat and try to elevate their >>>> TPL. >>>> >>>> >>>> ... >>>> >>>> The class of bugs created by calling EFI Services at to hight a TPL are >>>> usually reentrancy related. Basically the locks in the DXE Core raise >> TPL >>>> to protect critical sections, like the protocol data base or event >> queue. >>>> Calling at an illegal TPL can cause corruption of some of these >> structures, >>>> and thus undefined behavior. >>>> >>>> Another possibility is an event TPL deadlock. When your event runs at a >>>> TPL only events of a higher TPL can run. Your event is blocking code at >> <= >>>> current TPL from running. So if your event is waiting on something to >>>> complete that needs to run at <= the current TPL that code is starved >> and >>>> will never get run. The TPL Restriction table is the guide here also. >> For >>>> example if you driver implements Block IO it can be called at TPL <= >>>> TPL_CALLBACK, so if your driver has an event that needs to complete to >> make >>>> forward progress it needs to run at TPL_NOTIFY. >>>> >>>> Since you are producing GOP, there is going to be system code that >>>> implements Simple Text Output on top of GOP so you inherit those TPL >>>> restrictions. >>>> >>>> Is there a special way to debug the event system or do I have to put >> DEBUG >>>> calls all over the place? >>>> >>>> >>>> Actually putting DEBUG prints all over the place can make it worse as >> you >>>> can increase the amount of time spent processing events. >>>> >>>> I'm not sure if you have a debugger, but if you do.... >>>> 1) I'd break in a few times during the hang to try and get an idea of >>>> which event is taking all the time from the back trace >>>> 2) Pay attention to the TPL when you break in, dump gEfiCurrentTpl >>>> 3) You can walk the gEventQueue[] LIST_ENTRY to see what events are >>>> active. >= your current TPL. >>>> 4) As I mentioned I have lldb scripts to dump queues so you can figure >> out >>>> the high frequency timer functions and inspect that code. >>>> >>>> If you don't have a debugger... >>>> 1) Add DEBUG print to CoreCreateEventInternal() and print out info about >>>> timer events that get created. Then you can look at the timer event >>>> functions. >>>> 2) CoreDispatchEventNotifies() calls the event notify functions. >>>> Event->NotifyFunction (Event, Event->NotifyContext); >>>> You can use the TimerLib to try and figure out what event is taking al >> the >>>> time. You can DEBUG print the slowest function, and maybe only do it >> every >>>> Nth time so you don't do it 1,000 times a second. >>>> You could also add code to figure out if the function is running longer >>>> than (or some large percentage of) its period. >>>> >>>> >>>> Caveat Emptor .... >>>> Assume it is your event and figure out if you are doing something slow >> at >>>> an elevated TPL, calling a service at too hight a TPL., or have TPL >>>> related deadlock based on the TPL restrictions. >>>> >>>> Thanks, >>>> >>>> Andrew Fish >>>> >>>> PS Sorry if this is more detail than you needed, but I figured other >> folks >>>> might find this useful. >>>> >>>> >>>> On Thu, Nov 12, 2015 at 5:44 PM, Andrew Fish <[email protected]> wrote: >>>> >>>>> >>>>>> On Nov 12, 2015, at 3:28 AM, Andrew Fish <[email protected]> wrote: >>>>>> >>>>>>> >>>>>>> On Nov 12, 2015, at 3:22 AM, Andrew Fish <[email protected]> wrote: >>>>>>> >>>>>>> >>>>>>>> On Nov 12, 2015, at 12:49 AM, Michael Zimmermann < >>>>> [email protected]> wrote: >>>>>>>> >>>>>>>> Stall was just an example, I can also use DEBUG. My timer interval >> is >>>>>>>> 100ms(I converted it from the 100ns unit). >>>>>>>> >>>>>>>> What I meant with "thread mode code" is the "normal" non IRQ context >>>>> code >>>>>>>> running on the CPU. That means that there are actually two contexts >> in >>>>>>>> EDK2, the exception(i.e. IRQ's like the Timer/Watchdog) and the >>>>> normal mode >>>>>>>> all code is running in. >>>>>>>> >>>>>>> >>>>>>> OK thanks that helps. The timer ISR runs in interrupt context, and >>>>> from an ARM point of view EFI is running the ARM "Thread mode". The >> ISR >>>>> context is an implementation detail and not really defined by the >>>>> specification. >>>>>>> >>>>>>> I'd also point out the Events dispatch independent of the interrupt >>>>> context. gBS->RestoreTpl() can cause events to dispatch. For example a >> lot >>>>> of the EFI Protocol services have locks that raise TPL to prevent >>>>> recursion. When these locks are released and the TPL is restored >> events are >>>>> dispatched. So just calling EFI services can cause events to run. So >>>>> conceptually you can ignore the interrupt context, as that is used to >>>>> implement the timer tick. Every thing else is an event that runs in the >>>>> main EFI context. >>>>>>> >>>>>>> >>>>> >> https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/Dxe/Event/Tpl.c#L126 >>>>>>> >>>>>> >>>>>> As Kinney pointed out the events are cooperative and there is no >>>>> scheduler, so if you are getting stuck some chunk of code is running >> too >>>>> long at an elevated TPL. You may need to performance profile to figure >> out >>>>> the bad code. >>>>>> >>>>> >>>>> The events are managed by queues in the DXE Core. The events are >>>>> described by the IEVENT data structure and linked into queues based on >>>>> state. The state transition to the event is calling a C function >> stored in >>>>> the IEVENT. The event must return, or call an EFI service, to give >> control >>>>> back to the DXE Core. >>>>> >>>>> >>>>> >> https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/Dxe/Event/Event.c >>>>> >>>>> /// >>>>> /// gEventQueueLock - Protects the event queues >>>>> /// >>>>> EFI_LOCK gEventQueueLock = EFI_INITIALIZE_LOCK_VARIABLE >> (TPL_HIGH_LEVEL); >>>>> >>>>> /// >>>>> /// gEventQueue - A list of event's to notify for each priority level >>>>> /// >>>>> LIST_ENTRY gEventQueue[TPL_HIGH_LEVEL + 1]; >>>>> >>>>> /// >>>>> /// gEventPending - A bitmask of the EventQueues that are pending >>>>> /// >>>>> UINTN gEventPending = 0; >>>>> >>>>> /// >>>>> /// gEventSignalQueue - A list of events to signal based on EventGroup >>>>> type >>>>> /// >>>>> LIST_ENTRY gEventSignalQueue = INITIALIZE_LIST_HEAD_VARIABLE >>>>> (gEventSignalQueue); >>>>> >>>>> It is possible to write a debugger script to dump out the info about >> the >>>>> events if you have source level debug available for the DXE Core. >>>>> >>>>> Thanks, >>>>> >>>>> Andrew Fish >>>>> >>>>>> Thanks, >>>>>> >>>>>> Andrew Fish >>>>>> >>>>>> >>>>>>> Thanks, >>>>>>> >>>>>>> Andrew Fish >>>>>>> >>>>>>> >>>>>>>> On Thu, Nov 12, 2015 at 9:27 AM, Andrew Fish <[email protected]> >> wrote: >>>>>>>> >>>>>>>>> >>>>>>>>>> On Nov 11, 2015, at 11:51 PM, Michael Zimmermann < >>>>>>>>> [email protected]> wrote: >>>>>>>>>> >>>>>>>>>> I've started investigating in the timer event problem and I think >> I >>>>> have >>>>>>>>>> some weird problem with my platform drivers(I hope, so it's not a >>>>> EDK2 >>>>>>>>> bug). >>>>>>>>>> >>>>>>>>>> If I create a timer that runs every 100ms which does nothing but a >>>>>>>>>> stall(1), the thread mode code stops after some random >> time(usually >>>>> in >>>>>>>>> edk2 >>>>>>>>>> shell so I guess it's a race condition which needs some cpu load). >>>>>>>>>> >>>>>>>>> >>>>>>>>> Michael, >>>>>>>>> >>>>>>>>> Watch out as Stall() is Microseconds, and SetTimer() is 100ns I've >>>>> seen >>>>>>>>> bugs like that before in code. >>>>>>>>> >>>>>>>>>> When threadmode code is stopped the timer continues getting called >>>>> and >>>>>>>>> even >>>>>>>>>> if I stop the timer afterwards(with CloseEvent) it keeps being >>>>> stopped. >>>>>>>>>> >>>>>>>>>> Is there a way to get the threadmode context from inside a timer >>>>>>>>> callback? >>>>>>>>>> This way I could read the PC to check what's going on. >>>>>>>>>> >>>>>>>>> >>>>>>>>> There are no threads. EFI is an event model. If your code is >> running >>>>> you >>>>>>>>> are blocking every one else's forward progress. Only code running >> at >>>>> a >>>>>>>>> higher TPL can preempt. So when your event is running it is >> blocking >>>>> the >>>>>>>>> main flow and any event trying to run at <= TPL of your event from >>>>> making >>>>>>>>> forward progress. gBS->Stall() does not yield, it is no different >>>>> than >>>>>>>>> running code. >>>>>>>>> >>>>>>>>> So there is only one context, you can print it out any time you >> want. >>>>>>>>> >>>>>>>>> Thanks, >>>>>>>>> >>>>>>>>> Andrew Fish >>>>>>>>> >>>>>>>>> PS When folks yell at us for not having threads in EFI we point >> them >>>>> at: >>>>>>>>> https://urldefense.proofpoint.com/v2/url?u=https-3A__web.stanford.edu_-7Eouster_cgi-2Dbin_papers_threads.pdf&d=BQICAg&c=Hw-EJUFt2_D9PK5csBJ29kRV40HqSDXWTLPyZ6W8u84&r=4sdzHKz0eU1vXqaUySVmyA&m=iyUc8eXmiD7L89LghmMd5hdgVupHEDfyZBXpjyiE-9I&s=60wp7PciYqIO8kDWvG2i0EVI8x_Mt0wPgpxjGCfkSEA&e= >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>>> Michael >>>>>>>>>> >>>>>>>>>> On Wed, Nov 11, 2015 at 8:10 PM, Kinney, Michael D < >>>>>>>>>> [email protected]> wrote: >>>>>>>>>> >>>>>>>>>>> Michael, >>>>>>>>>>> >>>>>>>>>>> A periodic event timer at 30 times a second should not cause >> pauses >>>>>>>>>>> forever, unless the action you are performing in the event >>>>> notification >>>>>>>>>>> function takes more than 1/30 of a second to complete. You >> should >>>>> be >>>>>>>>> able >>>>>>>>>>> to just add a periodic event handler that does nothing, so you >> can >>>>>>>>> measure >>>>>>>>>>> what the overhead is. >>>>>>>>>>> >>>>>>>>>>> Another option is to use the performance counter in the TimerLib >>>>> each >>>>>>>>> time >>>>>>>>>>> a Blt() is called (GetPerformanceCounterProperties() and >>>>>>>>>>> GetPerformanceCounter()). When Blt() is called frequently, the >>>>> amount >>>>>>>>> of >>>>>>>>>>> time since last vsync will have elapsed, and you can go the vsync >>>>> action >>>>>>>>>>> within the Blt() call. If you also set a one shot timer event, >> so >>>>> if >>>>>>>>> the >>>>>>>>>>> last call to Blt() did not do a vsync and there are no more Blt() >>>>> calls, >>>>>>>>>>> 1/30th of a second later, the vsync action can be done. Every >> time >>>>>>>>> Blt() >>>>>>>>>>> is called, the one shot timer can be re-armed. This way, the one >>>>> shot >>>>>>>>>>> timer event is not actually executed very often. >>>>>>>>>>> >>>>>>>>>>> Mike >>>>>>>>>>> >>>>>>>>>>>> -----Original Message----- >>>>>>>>>>>> From: edk2-devel [mailto:[email protected]] On >>>>> Behalf Of >>>>>>>>>>> Michael Zimmermann >>>>>>>>>>>> Sent: Wednesday, November 11, 2015 12:32 AM >>>>>>>>>>>> To: [email protected] >>>>>>>>>>>> Subject: [edk2] EFI GOP with manual vsync trigger >>>>>>>>>>>> >>>>>>>>>>>> Hi, >>>>>>>>>>>> >>>>>>>>>>>> my Graphics HW uses a manual vsync trigger. That means that >> after >>>>>>>>> drawing >>>>>>>>>>>> to the framebuffer I need to manually trigger vsync(you can >>>>> compare it >>>>>>>>> to >>>>>>>>>>>> switching between double buffers). >>>>>>>>>>>> >>>>>>>>>>>> The problem is that UEFI's GraphicsOutputProtocol(GOP) doesn't >>>>> take >>>>>>>>> care >>>>>>>>>>> of >>>>>>>>>>>> HW that needs a flush. >>>>>>>>>>>> While issuing the trigger after every Blt Operation works, this >>>>>>>>> obviously >>>>>>>>>>>> causes extremely slow rendering for applications like the Shell >>>>> which >>>>>>>>> call >>>>>>>>>>>> Blt very often(like for every character). >>>>>>>>>>>> >>>>>>>>>>>> Also I can't use a timer to set the trigger(like 30times a >> second) >>>>>>>>> because >>>>>>>>>>>> it takes too much time and the Timer Interrupt ends up consuming >>>>> too >>>>>>>>> much >>>>>>>>>>>> time and the "normal" code gets paused forever. >>>>>>>>>>>> >>>>>>>>>>>> Do you have any other ideas how to handle this? >>>>>>>>>>>> >>>>>>>>>>>> Thx >>>>>>>>>>>> Michael >>>>>>>>>>>> _______________________________________________ >>>>>>>>>>>> edk2-devel mailing list >>>>>>>>>>>> [email protected] >>>>>>>>>>>> https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_mailman_listinfo_edk2-2Ddevel&d=BQICAg&c=Hw-EJUFt2_D9PK5csBJ29kRV40HqSDXWTLPyZ6W8u84&r=4sdzHKz0eU1vXqaUySVmyA&m=iyUc8eXmiD7L89LghmMd5hdgVupHEDfyZBXpjyiE-9I&s=31tD1YVkelbWB8njOCusaOe0MPbnDWaBzkwrYlfpysw&e= >>>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>> _______________________________________________ >>>>>>>>>> edk2-devel mailing list >>>>>>>>>> [email protected] >>>>>>>>>> https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_mailman_listinfo_edk2-2Ddevel&d=BQICAg&c=Hw-EJUFt2_D9PK5csBJ29kRV40HqSDXWTLPyZ6W8u84&r=4sdzHKz0eU1vXqaUySVmyA&m=iyUc8eXmiD7L89LghmMd5hdgVupHEDfyZBXpjyiE-9I&s=31tD1YVkelbWB8njOCusaOe0MPbnDWaBzkwrYlfpysw&e= >>>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>> _______________________________________________ >>>>>>>> edk2-devel mailing list >>>>>>>> [email protected] >>>>>>>> https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_mailman_listinfo_edk2-2Ddevel&d=BQICAg&c=Hw-EJUFt2_D9PK5csBJ29kRV40HqSDXWTLPyZ6W8u84&r=4sdzHKz0eU1vXqaUySVmyA&m=iyUc8eXmiD7L89LghmMd5hdgVupHEDfyZBXpjyiE-9I&s=31tD1YVkelbWB8njOCusaOe0MPbnDWaBzkwrYlfpysw&e= >>>>>>>> >>>>>>> >>>>>>> _______________________________________________ >>>>>>> edk2-devel mailing list >>>>>>> [email protected] >>>>>>> https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_mailman_listinfo_edk2-2Ddevel&d=BQICAg&c=Hw-EJUFt2_D9PK5csBJ29kRV40HqSDXWTLPyZ6W8u84&r=4sdzHKz0eU1vXqaUySVmyA&m=iyUc8eXmiD7L89LghmMd5hdgVupHEDfyZBXpjyiE-9I&s=31tD1YVkelbWB8njOCusaOe0MPbnDWaBzkwrYlfpysw&e= >>>>>>> >>>>> >>>>> >>>> >>>> >>>> >>> _______________________________________________ >>> edk2-devel mailing list >>> [email protected] >>> https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_mailman_listinfo_edk2-2Ddevel&d=BQICAg&c=Hw-EJUFt2_D9PK5csBJ29kRV40HqSDXWTLPyZ6W8u84&r=4sdzHKz0eU1vXqaUySVmyA&m=iyUc8eXmiD7L89LghmMd5hdgVupHEDfyZBXpjyiE-9I&s=31tD1YVkelbWB8njOCusaOe0MPbnDWaBzkwrYlfpysw&e= >>> >> >> > _______________________________________________ > edk2-devel mailing list > [email protected] > https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_mailman_listinfo_edk2-2Ddevel&d=BQICAg&c=Hw-EJUFt2_D9PK5csBJ29kRV40HqSDXWTLPyZ6W8u84&r=4sdzHKz0eU1vXqaUySVmyA&m=iyUc8eXmiD7L89LghmMd5hdgVupHEDfyZBXpjyiE-9I&s=31tD1YVkelbWB8njOCusaOe0MPbnDWaBzkwrYlfpysw&e= > _______________________________________________ edk2-devel mailing list [email protected] https://lists.01.org/mailman/listinfo/edk2-devel

