[edk2-devel] purpose of EFI_LOCK

Laszlo Ersek lersek at redhat.com
Fri May 5 19:59:49 UTC 2023


Hi Andrew,

thank you! The background / paragraph on the naming helps!

Cheers,
Laszlo

On 5/4/23 20:01, Andrew (EFI) Fish wrote:
> Laszlo,
> 
> Hope you are doing well! Sorry to top post but I’m just commenting on
> the big picture, not answering your specific questions yet.
> 
> The Wiki definition of lock  is something like: In computer science
> locks are a mechanism that enforces limits on access to a resource when
> there is the possibility for contention between threads of execution. I
> think the key point here is “threads of execution” is not threads….
> 
> UEFI is single threaded with a cooperative event model. There is no
> scheduler and an event blocks all forward progress of any event at the
> same or lower TPL. The protocol services have defined TPL Restrictions
> [1] so that is possible to implement locking. In the context of EFI
> raising the TPL blocks any “threads of execution” (events) that could
> preempt the running code from contending with a critical section. 
> 
> Lets thing about what would happen if you use an atomic primitive as a
> lock in EFI. Let’s say the app is installing a protocol so the DXE Core
> has the protocol database locked. Any event that fired in that window
> would not be able to call any UEFI Boot Service that was related to
> protocols and expect it to succeed. If the event blocked on the lock,
> the system is dead locked. If the the lock was tested and failed that
> basically means it would be normal for any UEFI service to fail in
> events and event code needed to coded to deal with that. Basically the
> even could we need to defer to a future time the event gets signaled. I
> think think this quickly devolves in the event code having to implement
> a simple scheduler for its set. Thus making locks raise the TPL is just
> better for everyone. 
> 
> Not that we have always been good at naming things, but in the context
> of EFI a Lock is best implemented as raising TPL so we made an up level
> look API to make that clear to people, and to help educate people how
> locks should be implemented in EFI.
> 
> This is old, but it is a good sumarry of why we did not want BIOS
> programmers dealing with threads 20+ years ago when we designed EFI. 
> 
> 
> preview.png
> threads <https://web.stanford.edu/~ouster/cgi-bin/papers/threads.pdf>
> PDF Document · 107 KB
> <https://web.stanford.edu/~ouster/cgi-bin/papers/threads.pdf>
> 
> <https://web.stanford.edu/~ouster/cgi-bin/papers/threads.pdf>
> 
> 
> [1]
> https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#tpl-restrictions <https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#tpl-restrictions>
> 
> Thanks,
> 
> Andrew Fish
> 
>> On May 4, 2023, at 1:45 AM, Laszlo Ersek <lersek at redhat.com> wrote:
>>
>> Hi,
>>
>> what benefit does EFI_LOCK add, over direct gBS->RaiseTPL() and
>> gBS->RestoreTPL() calls?
>>
>> Considering just the two APIs EfiAcquireLock() and EfiReleaseLock():
>>
>> - The "Lock" field (effectively, lock status field) is useless; it is
>> only written to, never read (outside of ASSERT()s)
>>
>> - The "OwnerTpl" and "Tpl" fields are just convenience storage for the
>> new TPL we raise to, and the old TPL we restore.
>>
>> Considering the EfiAcquireLockOrFail() API as well:
>>
>> - This does read the "Lock" (lock status) field, and if it is
>> EfiLockAcquired, the RaiseTPL() call is refused. So the idea here seems
>> to be to ensure a *strict* rise in the TPL. Namely, the RaiseTPL() in
>> EfiAcquireLockOrFail() would still succeed after the RaiseTPL() in
>> EfiAcquireLock() -- it is valid to "raise" the TPL to the current TPL
>> --, but the lock status check prevents that.
>>
>> - However (#1), this same "strict" raise would be possible by just
>> calling RaiseTPL() again, and comparing the returned old TPL against the
>> TPL we've just set as new. If the old TPL is strictly lower, then we've
>> just "acquired the lock", otherwise we've "already been holding" the
>> lock. So, from this perspective, EfiAcquireLockOrFail() doesn't add much.
>>
>> - Furthermore (#2), if another agent raised the TPL already, but didn't
>> use our particlar EFI_LOCK object to do that, then the status stored in
>> "EFI_LOCK.Lock" will not be able to track anything.
>>
>> So what is EFI_LOCK good for?
>>
>> Effectively I'm disturbed by the *name* "EFI_LOCK". A lock (or mutex) is
>> supposed to protect some shared resource. If two agents access the
>> resource without first acquiring the lock, there's trouble. Therefore
>> the resource itself becomes qualified as "requiring protection by the
>> lock, at all times". However, the "current TPL" is *not* a resource like
>> that. It's a UEFI spec level concept; the RaiseTPL and RestoreTPL boot
>> services are available to any agents; those agents are *not* required to
>> go through any kind of extra mutual exclusion.
>>
>> I've also considered that maybe EFI_LOCK could provide some protection
>> against inadvertently *lowering* the TPL via RaiseTPL() (which is
>> undefined behavior, per spec). But I don't see how --
>> EfiAcquireLockOrFail() will happily lower the TPL if the Lock field is
>> EfiLockReleased, but another agent has meanwhile raised the TPL strictly
>> above the Tpl field.
>>
>> Please explain. :)
>>
>> Thanks!
>> Laszlo
>>
> 



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#104159): https://edk2.groups.io/g/devel/message/104159
Mute This Topic: https://groups.io/mt/98680041/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/3943202/1813853/130120423/xyzzy [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-




More information about the edk2-devel-archive mailing list