From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753870AbcIIPXc (ORCPT ); Fri, 9 Sep 2016 11:23:32 -0400 Received: from mail-wm0-f45.google.com ([74.125.82.45]:37866 "EHLO mail-wm0-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753284AbcIIPTM (ORCPT ); Fri, 9 Sep 2016 11:19:12 -0400 From: Matt Fleming To: Ingo Molnar , Thomas Gleixner , "H . Peter Anvin" Cc: Sylvain Chouleur , Ard Biesheuvel , linux-kernel@vger.kernel.org, linux-efi@vger.kernel.org, Matt Fleming , Leif Lindholm , Mark Rutland , Sylvain Chouleur Subject: [PATCH 14/29] efi: Use a file local lock for efivars Date: Fri, 9 Sep 2016 16:18:36 +0100 Message-Id: <20160909151851.27577-15-matt@codeblueprint.co.uk> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20160909151851.27577-1-matt@codeblueprint.co.uk> References: <20160909151851.27577-1-matt@codeblueprint.co.uk> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Sylvain Chouleur 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 Signed-off-by: Ard Biesheuvel Cc: Leif Lindholm Cc: Mark Rutland Cc: Sylvain Chouleur Signed-off-by: Matt Fleming --- drivers/firmware/efi/vars.c | 83 +++++++++++++++++++++++++-------------------- include/linux/efi.h | 6 ---- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index d3b751383286..d0d807e1287e 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -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); @@ -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 @@ -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); @@ -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: @@ -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); @@ -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); @@ -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); @@ -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); } /** @@ -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, @@ -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); } @@ -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; } @@ -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); @@ -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, @@ -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); } @@ -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); } @@ -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); @@ -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); @@ -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, @@ -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); } @@ -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 @@ -957,7 +965,7 @@ 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); @@ -965,7 +973,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, return 0; out: - spin_unlock_irq(&__efivars->lock); + spin_unlock_irq(&efivars_lock); return err; } @@ -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); @@ -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); @@ -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; } @@ -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; @@ -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); diff --git a/include/linux/efi.h b/include/linux/efi.h index d8b555db81c7..deecb2902715 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -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; -- 2.9.3