aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2014-09-30 16:58:52 -0400
committerMatt Fleming <matt.fleming@intel.com>2014-10-03 13:41:03 -0400
commit6d80dba1c9fe4316ef626980102b92fa30c7845a (patch)
tree7d2d4551430d392af7dab18ff755fab1e422fb7b
parent77e21e87acec8c857df4bfbc647ea21d0a87364d (diff)
efi: Provide a non-blocking SetVariable() operation
There are some circumstances that call for trying to write an EFI variable in a non-blocking way. One such scenario is when writing pstore data in efi_pstore_write() via the pstore_dump() kdump callback. Now that we have an EFI runtime spinlock we need a way of aborting if there is contention instead of spinning, since when writing pstore data from the kdump callback, the runtime lock may already be held by the CPU that's running the callback if we crashed in the middle of an EFI variable operation. The situation is sufficiently special that a new EFI variable operation is warranted. Introduce ->set_variable_nonblocking() for this use case. It is an optional EFI backend operation, and need only be implemented by those backends that usually acquire locks to serialize access to EFI variables, as is the case for virt_efi_set_variable() where we now grab the EFI runtime spinlock. Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ingo Molnar <mingo@kernel.org> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Matthew Garrett <mjg59@srcf.ucam.org> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c19
-rw-r--r--drivers/firmware/efi/vars.c47
-rw-r--r--include/linux/efi.h6
3 files changed, 72 insertions, 0 deletions
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 9694cba665c4..4349206198b2 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -200,6 +200,24 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
200 return status; 200 return status;
201} 201}
202 202
203static efi_status_t
204virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
205 u32 attr, unsigned long data_size,
206 void *data)
207{
208 unsigned long flags;
209 efi_status_t status;
210
211 if (!spin_trylock_irqsave(&efi_runtime_lock, flags))
212 return EFI_NOT_READY;
213
214 status = efi_call_virt(set_variable, name, vendor, attr, data_size,
215 data);
216 spin_unlock_irqrestore(&efi_runtime_lock, flags);
217 return status;
218}
219
220
203static efi_status_t virt_efi_query_variable_info(u32 attr, 221static efi_status_t virt_efi_query_variable_info(u32 attr,
204 u64 *storage_space, 222 u64 *storage_space,
205 u64 *remaining_space, 223 u64 *remaining_space,
@@ -287,6 +305,7 @@ void efi_native_runtime_setup(void)
287 efi.get_variable = virt_efi_get_variable; 305 efi.get_variable = virt_efi_get_variable;
288 efi.get_next_variable = virt_efi_get_next_variable; 306 efi.get_next_variable = virt_efi_get_next_variable;
289 efi.set_variable = virt_efi_set_variable; 307 efi.set_variable = virt_efi_set_variable;
308 efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking;
290 efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; 309 efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count;
291 efi.reset_system = virt_efi_reset_system; 310 efi.reset_system = virt_efi_reset_system;
292 efi.query_variable_info = virt_efi_query_variable_info; 311 efi.query_variable_info = virt_efi_query_variable_info;
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index 1fa724f31b0e..fa3c66bdc1e5 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -595,6 +595,39 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
595} 595}
596EXPORT_SYMBOL_GPL(efivar_entry_set); 596EXPORT_SYMBOL_GPL(efivar_entry_set);
597 597
598/*
599 * efivar_entry_set_nonblocking - call set_variable_nonblocking()
600 *
601 * This function is guaranteed to not block and is suitable for calling
602 * from crash/panic handlers.
603 *
604 * Crucially, this function will not block if it cannot acquire
605 * __efivars->lock. Instead, it returns -EBUSY.
606 */
607static int
608efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor,
609 u32 attributes, unsigned long size, void *data)
610{
611 const struct efivar_operations *ops = __efivars->ops;
612 unsigned long flags;
613 efi_status_t status;
614
615 if (!spin_trylock_irqsave(&__efivars->lock, flags))
616 return -EBUSY;
617
618 status = check_var_size(attributes, size + ucs2_strsize(name, 1024));
619 if (status != EFI_SUCCESS) {
620 spin_unlock_irqrestore(&__efivars->lock, flags);
621 return -ENOSPC;
622 }
623
624 status = ops->set_variable_nonblocking(name, &vendor, attributes,
625 size, data);
626
627 spin_unlock_irqrestore(&__efivars->lock, flags);
628 return efi_status_to_err(status);
629}
630
598/** 631/**
599 * efivar_entry_set_safe - call set_variable() if enough space in firmware 632 * efivar_entry_set_safe - call set_variable() if enough space in firmware
600 * @name: buffer containing the variable name 633 * @name: buffer containing the variable name
@@ -622,6 +655,20 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
622 if (!ops->query_variable_store) 655 if (!ops->query_variable_store)
623 return -ENOSYS; 656 return -ENOSYS;
624 657
658 /*
659 * If the EFI variable backend provides a non-blocking
660 * ->set_variable() operation and we're in a context where we
661 * cannot block, then we need to use it to avoid live-locks,
662 * since the implication is that the regular ->set_variable()
663 * will block.
664 *
665 * If no ->set_variable_nonblocking() is provided then
666 * ->set_variable() is assumed to be non-blocking.
667 */
668 if (!block && ops->set_variable_nonblocking)
669 return efivar_entry_set_nonblocking(name, vendor, attributes,
670 size, data);
671
625 if (!block) { 672 if (!block) {
626 if (!spin_trylock_irqsave(&__efivars->lock, flags)) 673 if (!spin_trylock_irqsave(&__efivars->lock, flags))
627 return -EBUSY; 674 return -EBUSY;
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 78b29b133e14..0949f9c7e872 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -503,6 +503,10 @@ typedef efi_status_t efi_get_next_variable_t (unsigned long *name_size, efi_char
503typedef efi_status_t efi_set_variable_t (efi_char16_t *name, efi_guid_t *vendor, 503typedef efi_status_t efi_set_variable_t (efi_char16_t *name, efi_guid_t *vendor,
504 u32 attr, unsigned long data_size, 504 u32 attr, unsigned long data_size,
505 void *data); 505 void *data);
506typedef efi_status_t
507efi_set_variable_nonblocking_t(efi_char16_t *name, efi_guid_t *vendor,
508 u32 attr, unsigned long data_size, void *data);
509
506typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count); 510typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count);
507typedef void efi_reset_system_t (int reset_type, efi_status_t status, 511typedef void efi_reset_system_t (int reset_type, efi_status_t status,
508 unsigned long data_size, efi_char16_t *data); 512 unsigned long data_size, efi_char16_t *data);
@@ -822,6 +826,7 @@ extern struct efi {
822 efi_get_variable_t *get_variable; 826 efi_get_variable_t *get_variable;
823 efi_get_next_variable_t *get_next_variable; 827 efi_get_next_variable_t *get_next_variable;
824 efi_set_variable_t *set_variable; 828 efi_set_variable_t *set_variable;
829 efi_set_variable_nonblocking_t *set_variable_nonblocking;
825 efi_query_variable_info_t *query_variable_info; 830 efi_query_variable_info_t *query_variable_info;
826 efi_update_capsule_t *update_capsule; 831 efi_update_capsule_t *update_capsule;
827 efi_query_capsule_caps_t *query_capsule_caps; 832 efi_query_capsule_caps_t *query_capsule_caps;
@@ -1042,6 +1047,7 @@ struct efivar_operations {
1042 efi_get_variable_t *get_variable; 1047 efi_get_variable_t *get_variable;
1043 efi_get_next_variable_t *get_next_variable; 1048 efi_get_next_variable_t *get_next_variable;
1044 efi_set_variable_t *set_variable; 1049 efi_set_variable_t *set_variable;
1050 efi_set_variable_nonblocking_t *set_variable_nonblocking;
1045 efi_query_variable_store_t *query_variable_store; 1051 efi_query_variable_store_t *query_variable_store;
1046}; 1052};
1047 1053