diff options
author | Sylvain Chouleur <sylvain.chouleur@intel.com> | 2016-07-15 15:36:30 -0400 |
---|---|---|
committer | Matt Fleming <matt@codeblueprint.co.uk> | 2016-09-09 11:08:42 -0400 |
commit | 21b3ddd39feecd2f4d6c52bcd30f0a4fa14f125a (patch) | |
tree | 40773635e6415ce23293c67eda0327dddec90ae7 | |
parent | 217b27d4671a0a3f34147f1b341683d36b7457db (diff) |
efi: Don't use spinlocks for efi vars
All efivars operations are protected by a spinlock which prevents
interruptions and preemption. This is too restricted, we just need a
lock preventing concurrency.
The idea is to use a semaphore of count 1 and to have two ways of
locking, depending on the context:
- In interrupt context, we call down_trylock(), if it fails we return
an error
- In normal context, we call down_interruptible()
We don't use a mutex here because the mutex_trylock() function must not
be called from interrupt context, whereas the down_trylock() can.
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>
-rw-r--r-- | drivers/firmware/efi/efi-pstore.c | 36 | ||||
-rw-r--r-- | drivers/firmware/efi/efivars.c | 22 | ||||
-rw-r--r-- | drivers/firmware/efi/vars.c | 137 | ||||
-rw-r--r-- | fs/efivarfs/inode.c | 5 | ||||
-rw-r--r-- | fs/efivarfs/super.c | 9 | ||||
-rw-r--r-- | include/linux/efi.h | 6 |
6 files changed, 139 insertions, 76 deletions
diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 30a24d09ea6c..1c33d7469e4a 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c | |||
@@ -125,16 +125,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos, | |||
125 | * @entry: deleting entry | 125 | * @entry: deleting entry |
126 | * @turn_off_scanning: Check if a scanning flag should be turned off | 126 | * @turn_off_scanning: Check if a scanning flag should be turned off |
127 | */ | 127 | */ |
128 | static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, | 128 | static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, |
129 | bool turn_off_scanning) | 129 | bool turn_off_scanning) |
130 | { | 130 | { |
131 | if (entry->deleting) { | 131 | if (entry->deleting) { |
132 | list_del(&entry->list); | 132 | list_del(&entry->list); |
133 | efivar_entry_iter_end(); | 133 | efivar_entry_iter_end(); |
134 | efivar_unregister(entry); | 134 | efivar_unregister(entry); |
135 | efivar_entry_iter_begin(); | 135 | if (efivar_entry_iter_begin()) |
136 | return -EINTR; | ||
136 | } else if (turn_off_scanning) | 137 | } else if (turn_off_scanning) |
137 | entry->scanning = false; | 138 | entry->scanning = false; |
139 | |||
140 | return 0; | ||
138 | } | 141 | } |
139 | 142 | ||
140 | /** | 143 | /** |
@@ -144,13 +147,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, | |||
144 | * @head: list head | 147 | * @head: list head |
145 | * @stop: a flag checking if scanning will stop | 148 | * @stop: a flag checking if scanning will stop |
146 | */ | 149 | */ |
147 | static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, | 150 | static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, |
148 | struct efivar_entry *next, | 151 | struct efivar_entry *next, |
149 | struct list_head *head, bool stop) | 152 | struct list_head *head, bool stop) |
150 | { | 153 | { |
151 | __efi_pstore_scan_sysfs_exit(pos, true); | 154 | int ret = __efi_pstore_scan_sysfs_exit(pos, true); |
155 | |||
156 | if (ret) | ||
157 | return ret; | ||
158 | |||
152 | if (stop) | 159 | if (stop) |
153 | __efi_pstore_scan_sysfs_exit(next, &next->list != head); | 160 | ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head); |
161 | return ret; | ||
154 | } | 162 | } |
155 | 163 | ||
156 | /** | 164 | /** |
@@ -172,13 +180,17 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) | |||
172 | struct efivar_entry *entry, *n; | 180 | struct efivar_entry *entry, *n; |
173 | struct list_head *head = &efivar_sysfs_list; | 181 | struct list_head *head = &efivar_sysfs_list; |
174 | int size = 0; | 182 | int size = 0; |
183 | int ret; | ||
175 | 184 | ||
176 | if (!*pos) { | 185 | if (!*pos) { |
177 | list_for_each_entry_safe(entry, n, head, list) { | 186 | list_for_each_entry_safe(entry, n, head, list) { |
178 | efi_pstore_scan_sysfs_enter(entry, n, head); | 187 | efi_pstore_scan_sysfs_enter(entry, n, head); |
179 | 188 | ||
180 | size = efi_pstore_read_func(entry, data); | 189 | size = efi_pstore_read_func(entry, data); |
181 | efi_pstore_scan_sysfs_exit(entry, n, head, size < 0); | 190 | ret = efi_pstore_scan_sysfs_exit(entry, n, head, |
191 | size < 0); | ||
192 | if (ret) | ||
193 | return ret; | ||
182 | if (size) | 194 | if (size) |
183 | break; | 195 | break; |
184 | } | 196 | } |
@@ -190,7 +202,9 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) | |||
190 | efi_pstore_scan_sysfs_enter((*pos), n, head); | 202 | efi_pstore_scan_sysfs_enter((*pos), n, head); |
191 | 203 | ||
192 | size = efi_pstore_read_func((*pos), data); | 204 | size = efi_pstore_read_func((*pos), data); |
193 | efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); | 205 | ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); |
206 | if (ret) | ||
207 | return ret; | ||
194 | if (size) | 208 | if (size) |
195 | break; | 209 | break; |
196 | } | 210 | } |
@@ -232,7 +246,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, | |||
232 | if (!*data.buf) | 246 | if (!*data.buf) |
233 | return -ENOMEM; | 247 | return -ENOMEM; |
234 | 248 | ||
235 | efivar_entry_iter_begin(); | 249 | if (efivar_entry_iter_begin()) { |
250 | kfree(*data.buf); | ||
251 | return -EINTR; | ||
252 | } | ||
236 | size = efi_pstore_sysfs_entry_iter(&data, | 253 | size = efi_pstore_sysfs_entry_iter(&data, |
237 | (struct efivar_entry **)&psi->data); | 254 | (struct efivar_entry **)&psi->data); |
238 | efivar_entry_iter_end(); | 255 | efivar_entry_iter_end(); |
@@ -347,7 +364,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, | |||
347 | edata.time = time; | 364 | edata.time = time; |
348 | edata.name = efi_name; | 365 | edata.name = efi_name; |
349 | 366 | ||
350 | efivar_entry_iter_begin(); | 367 | if (efivar_entry_iter_begin()) |
368 | return -EINTR; | ||
351 | found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry); | 369 | found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry); |
352 | 370 | ||
353 | if (found && !entry->scanning) { | 371 | if (found && !entry->scanning) { |
diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 116b244dee68..3e626fd9bd4e 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c | |||
@@ -510,7 +510,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, | |||
510 | vendor = del_var->VendorGuid; | 510 | vendor = del_var->VendorGuid; |
511 | } | 511 | } |
512 | 512 | ||
513 | efivar_entry_iter_begin(); | 513 | if (efivar_entry_iter_begin()) |
514 | return -EINTR; | ||
514 | entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true); | 515 | entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true); |
515 | if (!entry) | 516 | if (!entry) |
516 | err = -EINVAL; | 517 | err = -EINVAL; |
@@ -575,7 +576,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) | |||
575 | return ret; | 576 | return ret; |
576 | 577 | ||
577 | kobject_uevent(&new_var->kobj, KOBJ_ADD); | 578 | kobject_uevent(&new_var->kobj, KOBJ_ADD); |
578 | efivar_entry_add(new_var, &efivar_sysfs_list); | 579 | if (efivar_entry_add(new_var, &efivar_sysfs_list)) { |
580 | efivar_unregister(new_var); | ||
581 | return -EINTR; | ||
582 | } | ||
579 | 583 | ||
580 | return 0; | 584 | return 0; |
581 | } | 585 | } |
@@ -690,7 +694,10 @@ static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor, | |||
690 | 694 | ||
691 | static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) | 695 | static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) |
692 | { | 696 | { |
693 | efivar_entry_remove(entry); | 697 | int err = efivar_entry_remove(entry); |
698 | |||
699 | if (err) | ||
700 | return err; | ||
694 | efivar_unregister(entry); | 701 | efivar_unregister(entry); |
695 | return 0; | 702 | return 0; |
696 | } | 703 | } |
@@ -698,7 +705,14 @@ static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) | |||
698 | static void efivars_sysfs_exit(void) | 705 | static void efivars_sysfs_exit(void) |
699 | { | 706 | { |
700 | /* Remove all entries and destroy */ | 707 | /* Remove all entries and destroy */ |
701 | __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, NULL, NULL); | 708 | int err; |
709 | |||
710 | err = __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, | ||
711 | NULL, NULL); | ||
712 | if (err) { | ||
713 | pr_err("efivars: Failed to destroy sysfs entries\n"); | ||
714 | return; | ||
715 | } | ||
702 | 716 | ||
703 | if (efivars_new_var) | 717 | if (efivars_new_var) |
704 | sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var); | 718 | sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var); |
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index d0d807e1287e..9336ffdf6e2c 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c | |||
@@ -43,7 +43,7 @@ static struct efivars *__efivars; | |||
43 | * 2) ->ops calls | 43 | * 2) ->ops calls |
44 | * 3) (un)registration of __efivars | 44 | * 3) (un)registration of __efivars |
45 | */ | 45 | */ |
46 | static DEFINE_SPINLOCK(efivars_lock); | 46 | static DEFINE_SEMAPHORE(efivars_lock); |
47 | 47 | ||
48 | static bool efivar_wq_enabled = true; | 48 | static bool efivar_wq_enabled = true; |
49 | DECLARE_WORK(efivar_work, NULL); | 49 | DECLARE_WORK(efivar_work, NULL); |
@@ -442,7 +442,10 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), | |||
442 | return -ENOMEM; | 442 | return -ENOMEM; |
443 | } | 443 | } |
444 | 444 | ||
445 | spin_lock_irq(&efivars_lock); | 445 | if (down_interruptible(&efivars_lock)) { |
446 | err = -EINTR; | ||
447 | goto free; | ||
448 | } | ||
446 | 449 | ||
447 | /* | 450 | /* |
448 | * Per EFI spec, the maximum storage allocated for both | 451 | * Per EFI spec, the maximum storage allocated for both |
@@ -458,7 +461,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), | |||
458 | switch (status) { | 461 | switch (status) { |
459 | case EFI_SUCCESS: | 462 | case EFI_SUCCESS: |
460 | if (duplicates) | 463 | if (duplicates) |
461 | spin_unlock_irq(&efivars_lock); | 464 | up(&efivars_lock); |
462 | 465 | ||
463 | variable_name_size = var_name_strnsize(variable_name, | 466 | variable_name_size = var_name_strnsize(variable_name, |
464 | variable_name_size); | 467 | variable_name_size); |
@@ -484,8 +487,12 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), | |||
484 | status = EFI_NOT_FOUND; | 487 | status = EFI_NOT_FOUND; |
485 | } | 488 | } |
486 | 489 | ||
487 | if (duplicates) | 490 | if (duplicates) { |
488 | spin_lock_irq(&efivars_lock); | 491 | if (down_interruptible(&efivars_lock)) { |
492 | err = -EINTR; | ||
493 | goto free; | ||
494 | } | ||
495 | } | ||
489 | 496 | ||
490 | break; | 497 | break; |
491 | case EFI_NOT_FOUND: | 498 | case EFI_NOT_FOUND: |
@@ -499,8 +506,8 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), | |||
499 | 506 | ||
500 | } while (status != EFI_NOT_FOUND); | 507 | } while (status != EFI_NOT_FOUND); |
501 | 508 | ||
502 | spin_unlock_irq(&efivars_lock); | 509 | up(&efivars_lock); |
503 | 510 | free: | |
504 | kfree(variable_name); | 511 | kfree(variable_name); |
505 | 512 | ||
506 | return err; | 513 | return err; |
@@ -511,24 +518,34 @@ EXPORT_SYMBOL_GPL(efivar_init); | |||
511 | * efivar_entry_add - add entry to variable list | 518 | * efivar_entry_add - add entry to variable list |
512 | * @entry: entry to add to list | 519 | * @entry: entry to add to list |
513 | * @head: list head | 520 | * @head: list head |
521 | * | ||
522 | * Returns 0 on success, or a kernel error code on failure. | ||
514 | */ | 523 | */ |
515 | void efivar_entry_add(struct efivar_entry *entry, struct list_head *head) | 524 | int efivar_entry_add(struct efivar_entry *entry, struct list_head *head) |
516 | { | 525 | { |
517 | spin_lock_irq(&efivars_lock); | 526 | if (down_interruptible(&efivars_lock)) |
527 | return -EINTR; | ||
518 | list_add(&entry->list, head); | 528 | list_add(&entry->list, head); |
519 | spin_unlock_irq(&efivars_lock); | 529 | up(&efivars_lock); |
530 | |||
531 | return 0; | ||
520 | } | 532 | } |
521 | EXPORT_SYMBOL_GPL(efivar_entry_add); | 533 | EXPORT_SYMBOL_GPL(efivar_entry_add); |
522 | 534 | ||
523 | /** | 535 | /** |
524 | * efivar_entry_remove - remove entry from variable list | 536 | * efivar_entry_remove - remove entry from variable list |
525 | * @entry: entry to remove from list | 537 | * @entry: entry to remove from list |
538 | * | ||
539 | * Returns 0 on success, or a kernel error code on failure. | ||
526 | */ | 540 | */ |
527 | void efivar_entry_remove(struct efivar_entry *entry) | 541 | int efivar_entry_remove(struct efivar_entry *entry) |
528 | { | 542 | { |
529 | spin_lock_irq(&efivars_lock); | 543 | if (down_interruptible(&efivars_lock)) |
544 | return -EINTR; | ||
530 | list_del(&entry->list); | 545 | list_del(&entry->list); |
531 | spin_unlock_irq(&efivars_lock); | 546 | up(&efivars_lock); |
547 | |||
548 | return 0; | ||
532 | } | 549 | } |
533 | EXPORT_SYMBOL_GPL(efivar_entry_remove); | 550 | EXPORT_SYMBOL_GPL(efivar_entry_remove); |
534 | 551 | ||
@@ -545,10 +562,8 @@ EXPORT_SYMBOL_GPL(efivar_entry_remove); | |||
545 | */ | 562 | */ |
546 | static void efivar_entry_list_del_unlock(struct efivar_entry *entry) | 563 | static void efivar_entry_list_del_unlock(struct efivar_entry *entry) |
547 | { | 564 | { |
548 | lockdep_assert_held(&efivars_lock); | ||
549 | |||
550 | list_del(&entry->list); | 565 | list_del(&entry->list); |
551 | spin_unlock_irq(&efivars_lock); | 566 | up(&efivars_lock); |
552 | } | 567 | } |
553 | 568 | ||
554 | /** | 569 | /** |
@@ -571,8 +586,6 @@ int __efivar_entry_delete(struct efivar_entry *entry) | |||
571 | const struct efivar_operations *ops = __efivars->ops; | 586 | const struct efivar_operations *ops = __efivars->ops; |
572 | efi_status_t status; | 587 | efi_status_t status; |
573 | 588 | ||
574 | lockdep_assert_held(&efivars_lock); | ||
575 | |||
576 | status = ops->set_variable(entry->var.VariableName, | 589 | status = ops->set_variable(entry->var.VariableName, |
577 | &entry->var.VendorGuid, | 590 | &entry->var.VendorGuid, |
578 | 0, 0, NULL); | 591 | 0, 0, NULL); |
@@ -589,20 +602,22 @@ EXPORT_SYMBOL_GPL(__efivar_entry_delete); | |||
589 | * variable list. It is the caller's responsibility to free @entry | 602 | * variable list. It is the caller's responsibility to free @entry |
590 | * once we return. | 603 | * once we return. |
591 | * | 604 | * |
592 | * Returns 0 on success, or a converted EFI status code if | 605 | * Returns 0 on success, -EINTR if we can't grab the semaphore, |
593 | * set_variable() fails. | 606 | * converted EFI status code if set_variable() fails. |
594 | */ | 607 | */ |
595 | int efivar_entry_delete(struct efivar_entry *entry) | 608 | int efivar_entry_delete(struct efivar_entry *entry) |
596 | { | 609 | { |
597 | const struct efivar_operations *ops = __efivars->ops; | 610 | const struct efivar_operations *ops = __efivars->ops; |
598 | efi_status_t status; | 611 | efi_status_t status; |
599 | 612 | ||
600 | spin_lock_irq(&efivars_lock); | 613 | if (down_interruptible(&efivars_lock)) |
614 | return -EINTR; | ||
615 | |||
601 | status = ops->set_variable(entry->var.VariableName, | 616 | status = ops->set_variable(entry->var.VariableName, |
602 | &entry->var.VendorGuid, | 617 | &entry->var.VendorGuid, |
603 | 0, 0, NULL); | 618 | 0, 0, NULL); |
604 | if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) { | 619 | if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) { |
605 | spin_unlock_irq(&efivars_lock); | 620 | up(&efivars_lock); |
606 | return efi_status_to_err(status); | 621 | return efi_status_to_err(status); |
607 | } | 622 | } |
608 | 623 | ||
@@ -628,9 +643,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_delete); | |||
628 | * If @head is not NULL a lookup is performed to determine whether | 643 | * If @head is not NULL a lookup is performed to determine whether |
629 | * the entry is already on the list. | 644 | * the entry is already on the list. |
630 | * | 645 | * |
631 | * Returns 0 on success, -EEXIST if a lookup is performed and the entry | 646 | * Returns 0 on success, -EINTR if we can't grab the semaphore, |
632 | * already exists on the list, or a converted EFI status code if | 647 | * -EEXIST if a lookup is performed and the entry already exists on |
633 | * set_variable() fails. | 648 | * the list, or a converted EFI status code if set_variable() fails. |
634 | */ | 649 | */ |
635 | int efivar_entry_set(struct efivar_entry *entry, u32 attributes, | 650 | int efivar_entry_set(struct efivar_entry *entry, u32 attributes, |
636 | unsigned long size, void *data, struct list_head *head) | 651 | unsigned long size, void *data, struct list_head *head) |
@@ -640,10 +655,10 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes, | |||
640 | efi_char16_t *name = entry->var.VariableName; | 655 | efi_char16_t *name = entry->var.VariableName; |
641 | efi_guid_t vendor = entry->var.VendorGuid; | 656 | efi_guid_t vendor = entry->var.VendorGuid; |
642 | 657 | ||
643 | spin_lock_irq(&efivars_lock); | 658 | if (down_interruptible(&efivars_lock)) |
644 | 659 | return -EINTR; | |
645 | if (head && efivar_entry_find(name, vendor, head, false)) { | 660 | if (head && efivar_entry_find(name, vendor, head, false)) { |
646 | spin_unlock_irq(&efivars_lock); | 661 | up(&efivars_lock); |
647 | return -EEXIST; | 662 | return -EEXIST; |
648 | } | 663 | } |
649 | 664 | ||
@@ -652,7 +667,7 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes, | |||
652 | status = ops->set_variable(name, &vendor, | 667 | status = ops->set_variable(name, &vendor, |
653 | attributes, size, data); | 668 | attributes, size, data); |
654 | 669 | ||
655 | spin_unlock_irq(&efivars_lock); | 670 | up(&efivars_lock); |
656 | 671 | ||
657 | return efi_status_to_err(status); | 672 | return efi_status_to_err(status); |
658 | 673 | ||
@@ -673,23 +688,22 @@ efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, | |||
673 | u32 attributes, unsigned long size, void *data) | 688 | u32 attributes, unsigned long size, void *data) |
674 | { | 689 | { |
675 | const struct efivar_operations *ops = __efivars->ops; | 690 | const struct efivar_operations *ops = __efivars->ops; |
676 | unsigned long flags; | ||
677 | efi_status_t status; | 691 | efi_status_t status; |
678 | 692 | ||
679 | if (!spin_trylock_irqsave(&efivars_lock, flags)) | 693 | if (down_trylock(&efivars_lock)) |
680 | return -EBUSY; | 694 | return -EBUSY; |
681 | 695 | ||
682 | status = check_var_size_nonblocking(attributes, | 696 | status = check_var_size_nonblocking(attributes, |
683 | size + ucs2_strsize(name, 1024)); | 697 | size + ucs2_strsize(name, 1024)); |
684 | if (status != EFI_SUCCESS) { | 698 | if (status != EFI_SUCCESS) { |
685 | spin_unlock_irqrestore(&efivars_lock, flags); | 699 | up(&efivars_lock); |
686 | return -ENOSPC; | 700 | return -ENOSPC; |
687 | } | 701 | } |
688 | 702 | ||
689 | status = ops->set_variable_nonblocking(name, &vendor, attributes, | 703 | status = ops->set_variable_nonblocking(name, &vendor, attributes, |
690 | size, data); | 704 | size, data); |
691 | 705 | ||
692 | spin_unlock_irqrestore(&efivars_lock, flags); | 706 | up(&efivars_lock); |
693 | return efi_status_to_err(status); | 707 | return efi_status_to_err(status); |
694 | } | 708 | } |
695 | 709 | ||
@@ -714,7 +728,6 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, | |||
714 | bool block, unsigned long size, void *data) | 728 | bool block, unsigned long size, void *data) |
715 | { | 729 | { |
716 | const struct efivar_operations *ops = __efivars->ops; | 730 | const struct efivar_operations *ops = __efivars->ops; |
717 | unsigned long flags; | ||
718 | efi_status_t status; | 731 | efi_status_t status; |
719 | 732 | ||
720 | if (!ops->query_variable_store) | 733 | if (!ops->query_variable_store) |
@@ -735,21 +748,22 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, | |||
735 | size, data); | 748 | size, data); |
736 | 749 | ||
737 | if (!block) { | 750 | if (!block) { |
738 | if (!spin_trylock_irqsave(&efivars_lock, flags)) | 751 | if (down_trylock(&efivars_lock)) |
739 | return -EBUSY; | 752 | return -EBUSY; |
740 | } else { | 753 | } else { |
741 | spin_lock_irqsave(&efivars_lock, flags); | 754 | if (down_interruptible(&efivars_lock)) |
755 | return -EINTR; | ||
742 | } | 756 | } |
743 | 757 | ||
744 | status = check_var_size(attributes, size + ucs2_strsize(name, 1024)); | 758 | status = check_var_size(attributes, size + ucs2_strsize(name, 1024)); |
745 | if (status != EFI_SUCCESS) { | 759 | if (status != EFI_SUCCESS) { |
746 | spin_unlock_irqrestore(&efivars_lock, flags); | 760 | up(&efivars_lock); |
747 | return -ENOSPC; | 761 | return -ENOSPC; |
748 | } | 762 | } |
749 | 763 | ||
750 | status = ops->set_variable(name, &vendor, attributes, size, data); | 764 | status = ops->set_variable(name, &vendor, attributes, size, data); |
751 | 765 | ||
752 | spin_unlock_irqrestore(&efivars_lock, flags); | 766 | up(&efivars_lock); |
753 | 767 | ||
754 | return efi_status_to_err(status); | 768 | return efi_status_to_err(status); |
755 | } | 769 | } |
@@ -779,8 +793,6 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid, | |||
779 | int strsize1, strsize2; | 793 | int strsize1, strsize2; |
780 | bool found = false; | 794 | bool found = false; |
781 | 795 | ||
782 | lockdep_assert_held(&efivars_lock); | ||
783 | |||
784 | list_for_each_entry_safe(entry, n, head, list) { | 796 | list_for_each_entry_safe(entry, n, head, list) { |
785 | strsize1 = ucs2_strsize(name, 1024); | 797 | strsize1 = ucs2_strsize(name, 1024); |
786 | strsize2 = ucs2_strsize(entry->var.VariableName, 1024); | 798 | strsize2 = ucs2_strsize(entry->var.VariableName, 1024); |
@@ -822,10 +834,11 @@ int efivar_entry_size(struct efivar_entry *entry, unsigned long *size) | |||
822 | 834 | ||
823 | *size = 0; | 835 | *size = 0; |
824 | 836 | ||
825 | spin_lock_irq(&efivars_lock); | 837 | if (down_interruptible(&efivars_lock)) |
838 | return -EINTR; | ||
826 | status = ops->get_variable(entry->var.VariableName, | 839 | status = ops->get_variable(entry->var.VariableName, |
827 | &entry->var.VendorGuid, NULL, size, NULL); | 840 | &entry->var.VendorGuid, NULL, size, NULL); |
828 | spin_unlock_irq(&efivars_lock); | 841 | up(&efivars_lock); |
829 | 842 | ||
830 | if (status != EFI_BUFFER_TOO_SMALL) | 843 | if (status != EFI_BUFFER_TOO_SMALL) |
831 | return efi_status_to_err(status); | 844 | return efi_status_to_err(status); |
@@ -851,8 +864,6 @@ int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes, | |||
851 | const struct efivar_operations *ops = __efivars->ops; | 864 | const struct efivar_operations *ops = __efivars->ops; |
852 | efi_status_t status; | 865 | efi_status_t status; |
853 | 866 | ||
854 | lockdep_assert_held(&efivars_lock); | ||
855 | |||
856 | status = ops->get_variable(entry->var.VariableName, | 867 | status = ops->get_variable(entry->var.VariableName, |
857 | &entry->var.VendorGuid, | 868 | &entry->var.VendorGuid, |
858 | attributes, size, data); | 869 | attributes, size, data); |
@@ -874,11 +885,12 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes, | |||
874 | const struct efivar_operations *ops = __efivars->ops; | 885 | const struct efivar_operations *ops = __efivars->ops; |
875 | efi_status_t status; | 886 | efi_status_t status; |
876 | 887 | ||
877 | spin_lock_irq(&efivars_lock); | 888 | if (down_interruptible(&efivars_lock)) |
889 | return -EINTR; | ||
878 | status = ops->get_variable(entry->var.VariableName, | 890 | status = ops->get_variable(entry->var.VariableName, |
879 | &entry->var.VendorGuid, | 891 | &entry->var.VendorGuid, |
880 | attributes, size, data); | 892 | attributes, size, data); |
881 | spin_unlock_irq(&efivars_lock); | 893 | up(&efivars_lock); |
882 | 894 | ||
883 | return efi_status_to_err(status); | 895 | return efi_status_to_err(status); |
884 | } | 896 | } |
@@ -925,7 +937,8 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, | |||
925 | * set_variable call, and removal of the variable from the efivars | 937 | * set_variable call, and removal of the variable from the efivars |
926 | * list (in the case of an authenticated delete). | 938 | * list (in the case of an authenticated delete). |
927 | */ | 939 | */ |
928 | spin_lock_irq(&efivars_lock); | 940 | if (down_interruptible(&efivars_lock)) |
941 | return -EINTR; | ||
929 | 942 | ||
930 | /* | 943 | /* |
931 | * Ensure that the available space hasn't shrunk below the safe level | 944 | * Ensure that the available space hasn't shrunk below the safe level |
@@ -965,7 +978,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, | |||
965 | if (status == EFI_NOT_FOUND) | 978 | if (status == EFI_NOT_FOUND) |
966 | efivar_entry_list_del_unlock(entry); | 979 | efivar_entry_list_del_unlock(entry); |
967 | else | 980 | else |
968 | spin_unlock_irq(&efivars_lock); | 981 | up(&efivars_lock); |
969 | 982 | ||
970 | if (status && status != EFI_BUFFER_TOO_SMALL) | 983 | if (status && status != EFI_BUFFER_TOO_SMALL) |
971 | return efi_status_to_err(status); | 984 | return efi_status_to_err(status); |
@@ -973,7 +986,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, | |||
973 | return 0; | 986 | return 0; |
974 | 987 | ||
975 | out: | 988 | out: |
976 | spin_unlock_irq(&efivars_lock); | 989 | up(&efivars_lock); |
977 | return err; | 990 | return err; |
978 | 991 | ||
979 | } | 992 | } |
@@ -986,9 +999,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_set_get_size); | |||
986 | * efivar_entry_iter_end() is called. This function is usually used in | 999 | * efivar_entry_iter_end() is called. This function is usually used in |
987 | * conjunction with __efivar_entry_iter() or efivar_entry_iter(). | 1000 | * conjunction with __efivar_entry_iter() or efivar_entry_iter(). |
988 | */ | 1001 | */ |
989 | void efivar_entry_iter_begin(void) | 1002 | int efivar_entry_iter_begin(void) |
990 | { | 1003 | { |
991 | spin_lock_irq(&efivars_lock); | 1004 | return down_interruptible(&efivars_lock); |
992 | } | 1005 | } |
993 | EXPORT_SYMBOL_GPL(efivar_entry_iter_begin); | 1006 | EXPORT_SYMBOL_GPL(efivar_entry_iter_begin); |
994 | 1007 | ||
@@ -999,7 +1012,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_iter_begin); | |||
999 | */ | 1012 | */ |
1000 | void efivar_entry_iter_end(void) | 1013 | void efivar_entry_iter_end(void) |
1001 | { | 1014 | { |
1002 | spin_unlock_irq(&efivars_lock); | 1015 | up(&efivars_lock); |
1003 | } | 1016 | } |
1004 | EXPORT_SYMBOL_GPL(efivar_entry_iter_end); | 1017 | EXPORT_SYMBOL_GPL(efivar_entry_iter_end); |
1005 | 1018 | ||
@@ -1075,7 +1088,9 @@ int efivar_entry_iter(int (*func)(struct efivar_entry *, void *), | |||
1075 | { | 1088 | { |
1076 | int err = 0; | 1089 | int err = 0; |
1077 | 1090 | ||
1078 | efivar_entry_iter_begin(); | 1091 | err = efivar_entry_iter_begin(); |
1092 | if (err) | ||
1093 | return err; | ||
1079 | err = __efivar_entry_iter(func, head, data, NULL); | 1094 | err = __efivar_entry_iter(func, head, data, NULL); |
1080 | efivar_entry_iter_end(); | 1095 | efivar_entry_iter_end(); |
1081 | 1096 | ||
@@ -1120,12 +1135,17 @@ int efivars_register(struct efivars *efivars, | |||
1120 | const struct efivar_operations *ops, | 1135 | const struct efivar_operations *ops, |
1121 | struct kobject *kobject) | 1136 | struct kobject *kobject) |
1122 | { | 1137 | { |
1123 | spin_lock_irq(&efivars_lock); | 1138 | if (down_interruptible(&efivars_lock)) |
1139 | return -EINTR; | ||
1140 | |||
1124 | efivars->ops = ops; | 1141 | efivars->ops = ops; |
1125 | efivars->kobject = kobject; | 1142 | efivars->kobject = kobject; |
1126 | 1143 | ||
1127 | __efivars = efivars; | 1144 | __efivars = efivars; |
1128 | spin_unlock_irq(&efivars_lock); | 1145 | |
1146 | pr_info("Registered efivars operations\n"); | ||
1147 | |||
1148 | up(&efivars_lock); | ||
1129 | 1149 | ||
1130 | return 0; | 1150 | return 0; |
1131 | } | 1151 | } |
@@ -1142,7 +1162,9 @@ int efivars_unregister(struct efivars *efivars) | |||
1142 | { | 1162 | { |
1143 | int rv; | 1163 | int rv; |
1144 | 1164 | ||
1145 | spin_lock_irq(&efivars_lock); | 1165 | if (down_interruptible(&efivars_lock)) |
1166 | return -EINTR; | ||
1167 | |||
1146 | if (!__efivars) { | 1168 | if (!__efivars) { |
1147 | printk(KERN_ERR "efivars not registered\n"); | 1169 | printk(KERN_ERR "efivars not registered\n"); |
1148 | rv = -EINVAL; | 1170 | rv = -EINVAL; |
@@ -1154,11 +1176,12 @@ int efivars_unregister(struct efivars *efivars) | |||
1154 | goto out; | 1176 | goto out; |
1155 | } | 1177 | } |
1156 | 1178 | ||
1179 | pr_info("Unregistered efivars operations\n"); | ||
1157 | __efivars = NULL; | 1180 | __efivars = NULL; |
1158 | 1181 | ||
1159 | rv = 0; | 1182 | rv = 0; |
1160 | out: | 1183 | out: |
1161 | spin_unlock_irq(&efivars_lock); | 1184 | up(&efivars_lock); |
1162 | return rv; | 1185 | return rv; |
1163 | } | 1186 | } |
1164 | EXPORT_SYMBOL_GPL(efivars_unregister); | 1187 | EXPORT_SYMBOL_GPL(efivars_unregister); |
diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c index 1d73fc6dba13..cbb50cadcffc 100644 --- a/fs/efivarfs/inode.c +++ b/fs/efivarfs/inode.c | |||
@@ -105,7 +105,10 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, | |||
105 | 105 | ||
106 | inode->i_private = var; | 106 | inode->i_private = var; |
107 | 107 | ||
108 | efivar_entry_add(var, &efivarfs_list); | 108 | err = efivar_entry_add(var, &efivarfs_list); |
109 | if (err) | ||
110 | goto out; | ||
111 | |||
109 | d_instantiate(dentry, inode); | 112 | d_instantiate(dentry, inode); |
110 | dget(dentry); | 113 | dget(dentry); |
111 | out: | 114 | out: |
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 688ccc16b702..01e3d6e53944 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c | |||
@@ -161,7 +161,9 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, | |||
161 | kfree(name); | 161 | kfree(name); |
162 | 162 | ||
163 | efivar_entry_size(entry, &size); | 163 | efivar_entry_size(entry, &size); |
164 | efivar_entry_add(entry, &efivarfs_list); | 164 | err = efivar_entry_add(entry, &efivarfs_list); |
165 | if (err) | ||
166 | goto fail_inode; | ||
165 | 167 | ||
166 | inode_lock(inode); | 168 | inode_lock(inode); |
167 | inode->i_private = entry; | 169 | inode->i_private = entry; |
@@ -182,7 +184,10 @@ fail: | |||
182 | 184 | ||
183 | static int efivarfs_destroy(struct efivar_entry *entry, void *data) | 185 | static int efivarfs_destroy(struct efivar_entry *entry, void *data) |
184 | { | 186 | { |
185 | efivar_entry_remove(entry); | 187 | int err = efivar_entry_remove(entry); |
188 | |||
189 | if (err) | ||
190 | return err; | ||
186 | kfree(entry); | 191 | kfree(entry); |
187 | return 0; | 192 | return 0; |
188 | } | 193 | } |
diff --git a/include/linux/efi.h b/include/linux/efi.h index deecb2902715..4d6da7b66c19 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
@@ -1297,8 +1297,8 @@ struct kobject *efivars_kobject(void); | |||
1297 | int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), | 1297 | int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), |
1298 | void *data, bool duplicates, struct list_head *head); | 1298 | void *data, bool duplicates, struct list_head *head); |
1299 | 1299 | ||
1300 | void efivar_entry_add(struct efivar_entry *entry, struct list_head *head); | 1300 | int efivar_entry_add(struct efivar_entry *entry, struct list_head *head); |
1301 | void efivar_entry_remove(struct efivar_entry *entry); | 1301 | int efivar_entry_remove(struct efivar_entry *entry); |
1302 | 1302 | ||
1303 | int __efivar_entry_delete(struct efivar_entry *entry); | 1303 | int __efivar_entry_delete(struct efivar_entry *entry); |
1304 | int efivar_entry_delete(struct efivar_entry *entry); | 1304 | int efivar_entry_delete(struct efivar_entry *entry); |
@@ -1315,7 +1315,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, | |||
1315 | int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, | 1315 | int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, |
1316 | bool block, unsigned long size, void *data); | 1316 | bool block, unsigned long size, void *data); |
1317 | 1317 | ||
1318 | void efivar_entry_iter_begin(void); | 1318 | int efivar_entry_iter_begin(void); |
1319 | void efivar_entry_iter_end(void); | 1319 | void efivar_entry_iter_end(void); |
1320 | 1320 | ||
1321 | int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *), | 1321 | int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *), |