Skip to content

Commit

Permalink
efi: Use a file local lock for efivars
Browse files Browse the repository at this point in the history
This patch replaces the spinlock in the efivars struct with a single lock
for the whole vars.c file.  The goal of this lock is to protect concurrent
calls to efi variable services, registering and unregistering. This allows
us to register new efivars operations without having in-progress call.

Signed-off-by: Sylvain Chouleur <sylvain.chouleur@intel.com>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Leif Lindholm <leif.lindholm@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Sylvain Chouleur <sylvain.chouleur@gmail.com>
Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
  • Loading branch information
Sylvain Chouleur authored and Matt Fleming committed Sep 9, 2016
1 parent 2ead308 commit 217b27d
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 42 deletions.
83 changes: 47 additions & 36 deletions drivers/firmware/efi/vars.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
/* Private pointer to registered efivars */
static struct efivars *__efivars;

/*
* efivars_lock protects three things:
* 1) efivarfs_list and efivars_sysfs_list
* 2) ->ops calls
* 3) (un)registration of __efivars
*/
static DEFINE_SPINLOCK(efivars_lock);

static bool efivar_wq_enabled = true;
DECLARE_WORK(efivar_work, NULL);
EXPORT_SYMBOL_GPL(efivar_work);
Expand Down Expand Up @@ -434,7 +442,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
return -ENOMEM;
}

spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);

/*
* Per EFI spec, the maximum storage allocated for both
Expand All @@ -450,7 +458,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
switch (status) {
case EFI_SUCCESS:
if (duplicates)
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);

variable_name_size = var_name_strnsize(variable_name,
variable_name_size);
Expand All @@ -477,7 +485,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
}

if (duplicates)
spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);

break;
case EFI_NOT_FOUND:
Expand All @@ -491,7 +499,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),

} while (status != EFI_NOT_FOUND);

spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);

kfree(variable_name);

Expand All @@ -506,9 +514,9 @@ EXPORT_SYMBOL_GPL(efivar_init);
*/
void efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
{
spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);
list_add(&entry->list, head);
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);
}
EXPORT_SYMBOL_GPL(efivar_entry_add);

Expand All @@ -518,9 +526,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_add);
*/
void efivar_entry_remove(struct efivar_entry *entry)
{
spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);
list_del(&entry->list);
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);
}
EXPORT_SYMBOL_GPL(efivar_entry_remove);

Expand All @@ -537,10 +545,10 @@ EXPORT_SYMBOL_GPL(efivar_entry_remove);
*/
static void efivar_entry_list_del_unlock(struct efivar_entry *entry)
{
lockdep_assert_held(&__efivars->lock);
lockdep_assert_held(&efivars_lock);

list_del(&entry->list);
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);
}

/**
Expand All @@ -563,7 +571,7 @@ int __efivar_entry_delete(struct efivar_entry *entry)
const struct efivar_operations *ops = __efivars->ops;
efi_status_t status;

lockdep_assert_held(&__efivars->lock);
lockdep_assert_held(&efivars_lock);

status = ops->set_variable(entry->var.VariableName,
&entry->var.VendorGuid,
Expand All @@ -589,12 +597,12 @@ int efivar_entry_delete(struct efivar_entry *entry)
const struct efivar_operations *ops = __efivars->ops;
efi_status_t status;

spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);
status = ops->set_variable(entry->var.VariableName,
&entry->var.VendorGuid,
0, 0, NULL);
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);
return efi_status_to_err(status);
}

Expand Down Expand Up @@ -632,10 +640,10 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
efi_char16_t *name = entry->var.VariableName;
efi_guid_t vendor = entry->var.VendorGuid;

spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);

if (head && efivar_entry_find(name, vendor, head, false)) {
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);
return -EEXIST;
}

Expand All @@ -644,7 +652,7 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
status = ops->set_variable(name, &vendor,
attributes, size, data);

spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);

return efi_status_to_err(status);

Expand All @@ -658,7 +666,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_set);
* from crash/panic handlers.
*
* Crucially, this function will not block if it cannot acquire
* __efivars->lock. Instead, it returns -EBUSY.
* efivars_lock. Instead, it returns -EBUSY.
*/
static int
efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor,
Expand All @@ -668,20 +676,20 @@ efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor,
unsigned long flags;
efi_status_t status;

if (!spin_trylock_irqsave(&__efivars->lock, flags))
if (!spin_trylock_irqsave(&efivars_lock, flags))
return -EBUSY;

status = check_var_size_nonblocking(attributes,
size + ucs2_strsize(name, 1024));
if (status != EFI_SUCCESS) {
spin_unlock_irqrestore(&__efivars->lock, flags);
spin_unlock_irqrestore(&efivars_lock, flags);
return -ENOSPC;
}

status = ops->set_variable_nonblocking(name, &vendor, attributes,
size, data);

spin_unlock_irqrestore(&__efivars->lock, flags);
spin_unlock_irqrestore(&efivars_lock, flags);
return efi_status_to_err(status);
}

Expand Down Expand Up @@ -727,21 +735,21 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
size, data);

if (!block) {
if (!spin_trylock_irqsave(&__efivars->lock, flags))
if (!spin_trylock_irqsave(&efivars_lock, flags))
return -EBUSY;
} else {
spin_lock_irqsave(&__efivars->lock, flags);
spin_lock_irqsave(&efivars_lock, flags);
}

status = check_var_size(attributes, size + ucs2_strsize(name, 1024));
if (status != EFI_SUCCESS) {
spin_unlock_irqrestore(&__efivars->lock, flags);
spin_unlock_irqrestore(&efivars_lock, flags);
return -ENOSPC;
}

status = ops->set_variable(name, &vendor, attributes, size, data);

spin_unlock_irqrestore(&__efivars->lock, flags);
spin_unlock_irqrestore(&efivars_lock, flags);

return efi_status_to_err(status);
}
Expand Down Expand Up @@ -771,7 +779,7 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
int strsize1, strsize2;
bool found = false;

lockdep_assert_held(&__efivars->lock);
lockdep_assert_held(&efivars_lock);

list_for_each_entry_safe(entry, n, head, list) {
strsize1 = ucs2_strsize(name, 1024);
Expand Down Expand Up @@ -814,10 +822,10 @@ int efivar_entry_size(struct efivar_entry *entry, unsigned long *size)

*size = 0;

spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);
status = ops->get_variable(entry->var.VariableName,
&entry->var.VendorGuid, NULL, size, NULL);
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);

if (status != EFI_BUFFER_TOO_SMALL)
return efi_status_to_err(status);
Expand All @@ -843,7 +851,7 @@ int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
const struct efivar_operations *ops = __efivars->ops;
efi_status_t status;

lockdep_assert_held(&__efivars->lock);
lockdep_assert_held(&efivars_lock);

status = ops->get_variable(entry->var.VariableName,
&entry->var.VendorGuid,
Expand All @@ -866,11 +874,11 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
const struct efivar_operations *ops = __efivars->ops;
efi_status_t status;

spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);
status = ops->get_variable(entry->var.VariableName,
&entry->var.VendorGuid,
attributes, size, data);
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);

return efi_status_to_err(status);
}
Expand Down Expand Up @@ -917,7 +925,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
* set_variable call, and removal of the variable from the efivars
* list (in the case of an authenticated delete).
*/
spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);

/*
* Ensure that the available space hasn't shrunk below the safe level
Expand Down Expand Up @@ -957,15 +965,15 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
if (status == EFI_NOT_FOUND)
efivar_entry_list_del_unlock(entry);
else
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);

if (status && status != EFI_BUFFER_TOO_SMALL)
return efi_status_to_err(status);

return 0;

out:
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);
return err;

}
Expand All @@ -980,7 +988,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_set_get_size);
*/
void efivar_entry_iter_begin(void)
{
spin_lock_irq(&__efivars->lock);
spin_lock_irq(&efivars_lock);
}
EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);

Expand All @@ -991,7 +999,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
*/
void efivar_entry_iter_end(void)
{
spin_unlock_irq(&__efivars->lock);
spin_unlock_irq(&efivars_lock);
}
EXPORT_SYMBOL_GPL(efivar_entry_iter_end);

Expand Down Expand Up @@ -1112,11 +1120,12 @@ int efivars_register(struct efivars *efivars,
const struct efivar_operations *ops,
struct kobject *kobject)
{
spin_lock_init(&efivars->lock);
spin_lock_irq(&efivars_lock);
efivars->ops = ops;
efivars->kobject = kobject;

__efivars = efivars;
spin_unlock_irq(&efivars_lock);

return 0;
}
Expand All @@ -1133,6 +1142,7 @@ int efivars_unregister(struct efivars *efivars)
{
int rv;

spin_lock_irq(&efivars_lock);
if (!__efivars) {
printk(KERN_ERR "efivars not registered\n");
rv = -EINVAL;
Expand All @@ -1148,6 +1158,7 @@ int efivars_unregister(struct efivars *efivars)

rv = 0;
out:
spin_unlock_irq(&efivars_lock);
return rv;
}
EXPORT_SYMBOL_GPL(efivars_unregister);
6 changes: 0 additions & 6 deletions include/linux/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1157,12 +1157,6 @@ struct efivar_operations {
};

struct efivars {
/*
* ->lock protects two things:
* 1) efivarfs_list and efivars_sysfs_list
* 2) ->ops calls
*/
spinlock_t lock;
struct kset *kset;
struct kobject *kobject;
const struct efivar_operations *ops;
Expand Down

0 comments on commit 217b27d

Please sign in to comment.