summaryrefslogtreecommitdiff
path: root/drivers/firmware/efi/vars.c
diff options
context:
space:
mode:
authorArd Biesheuvel <ardb@kernel.org>2022-06-20 18:04:32 +0300
committerArd Biesheuvel <ardb@kernel.org>2022-06-24 21:40:18 +0300
commitec3507b2ca51286de6ecd85fdac8e722219cdef8 (patch)
tree134b3046bb3b859a73aa44c132e0c072b15fbb20 /drivers/firmware/efi/vars.c
parent8ca869b24538a7b5501af368e87e4a59b0c04117 (diff)
downloadlinux-ec3507b2ca51286de6ecd85fdac8e722219cdef8.tar.xz
efi: vars: Don't drop lock in the middle of efivar_init()
Even though the efivars_lock lock is documented as protecting the efivars->ops pointer (among other things), efivar_init() happily releases and reacquires the lock for every EFI variable that it enumerates. This used to be needed because the lock was originally a spinlock, which prevented the callback that is invoked for every variable from being able to sleep. However, releasing the lock could potentially invalidate the ops pointer, but more importantly, it might allow a SetVariable() runtime service call to take place concurrently, and the UEFI spec does not define how this affects an enumeration that is running in parallel using the GetNextVariable() runtime service, which is what efivar_init() uses. In the meantime, the lock has been converted into a semaphore, and the only reason we need to drop the lock is because the efivarfs pseudo filesystem driver will otherwise deadlock when it invokes the efivars API from the callback to create the efivar_entry items and insert them into the linked list. (EFI pstore is affected in a similar way) So let's switch to helpers that can be used while the lock is already taken. This way, we can hold on to the lock throughout the enumeration. Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Diffstat (limited to 'drivers/firmware/efi/vars.c')
-rw-r--r--drivers/firmware/efi/vars.c22
1 files changed, 11 insertions, 11 deletions
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index cae590bd08f2..146360e2f1cb 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -450,9 +450,6 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
&vendor_guid);
switch (status) {
case EFI_SUCCESS:
- if (duplicates)
- up(&efivars_lock);
-
variable_name_size = var_name_strnsize(variable_name,
variable_name_size);
@@ -476,14 +473,6 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
if (err)
status = EFI_NOT_FOUND;
}
-
- if (duplicates) {
- if (down_interruptible(&efivars_lock)) {
- err = -EINTR;
- goto free;
- }
- }
-
break;
case EFI_UNSUPPORTED:
err = -EOPNOTSUPP;
@@ -527,6 +516,17 @@ int efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
EXPORT_SYMBOL_GPL(efivar_entry_add);
/**
+ * __efivar_entry_add - add entry to variable list
+ * @entry: entry to add to list
+ * @head: list head
+ */
+void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
+{
+ list_add(&entry->list, head);
+}
+EXPORT_SYMBOL_GPL(__efivar_entry_add);
+
+/**
* efivar_entry_remove - remove entry from variable list
* @entry: entry to remove from list
*