aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorSeiji Aguchi <seiji.aguchi@hds.com>2013-02-12 15:59:07 -0500
committerTony Luck <tony.luck@intel.com>2013-02-12 15:59:07 -0500
commit81fa4e581d9283f7992a0d8c534bb141eb840a14 (patch)
tree2bc7677534c1cef8dfb5388059777bfb1164e66c /drivers/firmware
parente59310adf5eebce108f78b6c47bb330aae2e1666 (diff)
efivars: Disable external interrupt while holding efivars->lock
[Problem] There is a scenario which efi_pstore fails to log messages in a panic case. - CPUA holds an efi_var->lock in either efivarfs parts or efi_pstore with interrupt enabled. - CPUB panics and sends IPI to CPUA in smp_send_stop(). - CPUA stops with holding the lock. - CPUB kicks efi_pstore_write() via kmsg_dump(KSMG_DUMP_PANIC) but it returns without logging messages. [Patch Description] This patch disables an external interruption while holding efivars->lock as follows. In efi_pstore_write() and get_var_data(), spin_lock/spin_unlock is replaced by spin_lock_irqsave/spin_unlock_irqrestore because they may be called in an interrupt context. In other functions, they are replaced by spin_lock_irq/spin_unlock_irq. because they are all called from a process context. By applying this patch, we can avoid the problem above with a following senario. - CPUA holds an efi_var->lock with interrupt disabled. - CPUB panics and sends IPI to CPUA in smp_send_stop(). - CPUA receives the IPI after releasing the lock because it is disabling interrupt while holding the lock. - CPUB waits for one sec until CPUA releases the lock. - CPUB kicks efi_pstore_write() via kmsg_dump(KSMG_DUMP_PANIC) And it can hold the lock successfully. Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com> Acked-by: Mike Waychison <mikew@google.com> Acked-by: Matt Fleming <matt.fleming@intel.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/efivars.c86
1 files changed, 44 insertions, 42 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index ef5070d86f88..a64fb7bda365 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -405,10 +405,11 @@ static efi_status_t
405get_var_data(struct efivars *efivars, struct efi_variable *var) 405get_var_data(struct efivars *efivars, struct efi_variable *var)
406{ 406{
407 efi_status_t status; 407 efi_status_t status;
408 unsigned long flags;
408 409
409 spin_lock(&efivars->lock); 410 spin_lock_irqsave(&efivars->lock, flags);
410 status = get_var_data_locked(efivars, var); 411 status = get_var_data_locked(efivars, var);
411 spin_unlock(&efivars->lock); 412 spin_unlock_irqrestore(&efivars->lock, flags);
412 413
413 if (status != EFI_SUCCESS) { 414 if (status != EFI_SUCCESS) {
414 printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", 415 printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
@@ -537,14 +538,14 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
537 return -EINVAL; 538 return -EINVAL;
538 } 539 }
539 540
540 spin_lock(&efivars->lock); 541 spin_lock_irq(&efivars->lock);
541 status = efivars->ops->set_variable(new_var->VariableName, 542 status = efivars->ops->set_variable(new_var->VariableName,
542 &new_var->VendorGuid, 543 &new_var->VendorGuid,
543 new_var->Attributes, 544 new_var->Attributes,
544 new_var->DataSize, 545 new_var->DataSize,
545 new_var->Data); 546 new_var->Data);
546 547
547 spin_unlock(&efivars->lock); 548 spin_unlock_irq(&efivars->lock);
548 549
549 if (status != EFI_SUCCESS) { 550 if (status != EFI_SUCCESS) {
550 printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", 551 printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
@@ -713,7 +714,7 @@ static ssize_t efivarfs_file_write(struct file *file,
713 * amounts of memory. Pick a default size of 64K if 714 * amounts of memory. Pick a default size of 64K if
714 * QueryVariableInfo() isn't supported by the firmware. 715 * QueryVariableInfo() isn't supported by the firmware.
715 */ 716 */
716 spin_lock(&efivars->lock); 717 spin_lock_irq(&efivars->lock);
717 718
718 if (!efivars->ops->query_variable_info) 719 if (!efivars->ops->query_variable_info)
719 status = EFI_UNSUPPORTED; 720 status = EFI_UNSUPPORTED;
@@ -723,7 +724,7 @@ static ssize_t efivarfs_file_write(struct file *file,
723 &remaining_size, &max_size); 724 &remaining_size, &max_size);
724 } 725 }
725 726
726 spin_unlock(&efivars->lock); 727 spin_unlock_irq(&efivars->lock);
727 728
728 if (status != EFI_SUCCESS) { 729 if (status != EFI_SUCCESS) {
729 if (status != EFI_UNSUPPORTED) 730 if (status != EFI_UNSUPPORTED)
@@ -754,7 +755,7 @@ static ssize_t efivarfs_file_write(struct file *file,
754 * set_variable call, and removal of the variable from the efivars 755 * set_variable call, and removal of the variable from the efivars
755 * list (in the case of an authenticated delete). 756 * list (in the case of an authenticated delete).
756 */ 757 */
757 spin_lock(&efivars->lock); 758 spin_lock_irq(&efivars->lock);
758 759
759 status = efivars->ops->set_variable(var->var.VariableName, 760 status = efivars->ops->set_variable(var->var.VariableName,
760 &var->var.VendorGuid, 761 &var->var.VendorGuid,
@@ -762,7 +763,7 @@ static ssize_t efivarfs_file_write(struct file *file,
762 data); 763 data);
763 764
764 if (status != EFI_SUCCESS) { 765 if (status != EFI_SUCCESS) {
765 spin_unlock(&efivars->lock); 766 spin_unlock_irq(&efivars->lock);
766 kfree(data); 767 kfree(data);
767 768
768 return efi_status_to_err(status); 769 return efi_status_to_err(status);
@@ -783,20 +784,20 @@ static ssize_t efivarfs_file_write(struct file *file,
783 NULL); 784 NULL);
784 785
785 if (status == EFI_BUFFER_TOO_SMALL) { 786 if (status == EFI_BUFFER_TOO_SMALL) {
786 spin_unlock(&efivars->lock); 787 spin_unlock_irq(&efivars->lock);
787 mutex_lock(&inode->i_mutex); 788 mutex_lock(&inode->i_mutex);
788 i_size_write(inode, newdatasize + sizeof(attributes)); 789 i_size_write(inode, newdatasize + sizeof(attributes));
789 mutex_unlock(&inode->i_mutex); 790 mutex_unlock(&inode->i_mutex);
790 791
791 } else if (status == EFI_NOT_FOUND) { 792 } else if (status == EFI_NOT_FOUND) {
792 list_del(&var->list); 793 list_del(&var->list);
793 spin_unlock(&efivars->lock); 794 spin_unlock_irq(&efivars->lock);
794 efivar_unregister(var); 795 efivar_unregister(var);
795 drop_nlink(inode); 796 drop_nlink(inode);
796 dput(file->f_dentry); 797 dput(file->f_dentry);
797 798
798 } else { 799 } else {
799 spin_unlock(&efivars->lock); 800 spin_unlock_irq(&efivars->lock);
800 pr_warn("efivarfs: inconsistent EFI variable implementation? " 801 pr_warn("efivarfs: inconsistent EFI variable implementation? "
801 "status = %lx\n", status); 802 "status = %lx\n", status);
802 } 803 }
@@ -818,11 +819,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
818 void *data; 819 void *data;
819 ssize_t size = 0; 820 ssize_t size = 0;
820 821
821 spin_lock(&efivars->lock); 822 spin_lock_irq(&efivars->lock);
822 status = efivars->ops->get_variable(var->var.VariableName, 823 status = efivars->ops->get_variable(var->var.VariableName,
823 &var->var.VendorGuid, 824 &var->var.VendorGuid,
824 &attributes, &datasize, NULL); 825 &attributes, &datasize, NULL);
825 spin_unlock(&efivars->lock); 826 spin_unlock_irq(&efivars->lock);
826 827
827 if (status != EFI_BUFFER_TOO_SMALL) 828 if (status != EFI_BUFFER_TOO_SMALL)
828 return efi_status_to_err(status); 829 return efi_status_to_err(status);
@@ -832,12 +833,12 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
832 if (!data) 833 if (!data)
833 return -ENOMEM; 834 return -ENOMEM;
834 835
835 spin_lock(&efivars->lock); 836 spin_lock_irq(&efivars->lock);
836 status = efivars->ops->get_variable(var->var.VariableName, 837 status = efivars->ops->get_variable(var->var.VariableName,
837 &var->var.VendorGuid, 838 &var->var.VendorGuid,
838 &attributes, &datasize, 839 &attributes, &datasize,
839 (data + sizeof(attributes))); 840 (data + sizeof(attributes)));
840 spin_unlock(&efivars->lock); 841 spin_unlock_irq(&efivars->lock);
841 842
842 if (status != EFI_SUCCESS) { 843 if (status != EFI_SUCCESS) {
843 size = efi_status_to_err(status); 844 size = efi_status_to_err(status);
@@ -965,9 +966,9 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
965 goto out; 966 goto out;
966 967
967 kobject_uevent(&var->kobj, KOBJ_ADD); 968 kobject_uevent(&var->kobj, KOBJ_ADD);
968 spin_lock(&efivars->lock); 969 spin_lock_irq(&efivars->lock);
969 list_add(&var->list, &efivars->list); 970 list_add(&var->list, &efivars->list);
970 spin_unlock(&efivars->lock); 971 spin_unlock_irq(&efivars->lock);
971 d_instantiate(dentry, inode); 972 d_instantiate(dentry, inode);
972 dget(dentry); 973 dget(dentry);
973out: 974out:
@@ -984,7 +985,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
984 struct efivars *efivars = var->efivars; 985 struct efivars *efivars = var->efivars;
985 efi_status_t status; 986 efi_status_t status;
986 987
987 spin_lock(&efivars->lock); 988 spin_lock_irq(&efivars->lock);
988 989
989 status = efivars->ops->set_variable(var->var.VariableName, 990 status = efivars->ops->set_variable(var->var.VariableName,
990 &var->var.VendorGuid, 991 &var->var.VendorGuid,
@@ -992,14 +993,14 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
992 993
993 if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) { 994 if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) {
994 list_del(&var->list); 995 list_del(&var->list);
995 spin_unlock(&efivars->lock); 996 spin_unlock_irq(&efivars->lock);
996 efivar_unregister(var); 997 efivar_unregister(var);
997 drop_nlink(dir); 998 drop_nlink(dir);
998 dput(dentry); 999 dput(dentry);
999 return 0; 1000 return 0;
1000 } 1001 }
1001 1002
1002 spin_unlock(&efivars->lock); 1003 spin_unlock_irq(&efivars->lock);
1003 return -EINVAL; 1004 return -EINVAL;
1004}; 1005};
1005 1006
@@ -1065,13 +1066,13 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
1065 /* copied by the above to local storage in the dentry. */ 1066 /* copied by the above to local storage in the dentry. */
1066 kfree(name); 1067 kfree(name);
1067 1068
1068 spin_lock(&efivars->lock); 1069 spin_lock_irq(&efivars->lock);
1069 efivars->ops->get_variable(entry->var.VariableName, 1070 efivars->ops->get_variable(entry->var.VariableName,
1070 &entry->var.VendorGuid, 1071 &entry->var.VendorGuid,
1071 &entry->var.Attributes, 1072 &entry->var.Attributes,
1072 &size, 1073 &size,
1073 NULL); 1074 NULL);
1074 spin_unlock(&efivars->lock); 1075 spin_unlock_irq(&efivars->lock);
1075 1076
1076 mutex_lock(&inode->i_mutex); 1077 mutex_lock(&inode->i_mutex);
1077 inode->i_private = entry; 1078 inode->i_private = entry;
@@ -1122,7 +1123,7 @@ static int efi_pstore_open(struct pstore_info *psi)
1122{ 1123{
1123 struct efivars *efivars = psi->data; 1124 struct efivars *efivars = psi->data;
1124 1125
1125 spin_lock(&efivars->lock); 1126 spin_lock_irq(&efivars->lock);
1126 efivars->walk_entry = list_first_entry(&efivars->list, 1127 efivars->walk_entry = list_first_entry(&efivars->list,
1127 struct efivar_entry, list); 1128 struct efivar_entry, list);
1128 return 0; 1129 return 0;
@@ -1132,7 +1133,7 @@ static int efi_pstore_close(struct pstore_info *psi)
1132{ 1133{
1133 struct efivars *efivars = psi->data; 1134 struct efivars *efivars = psi->data;
1134 1135
1135 spin_unlock(&efivars->lock); 1136 spin_unlock_irq(&efivars->lock);
1136 return 0; 1137 return 0;
1137} 1138}
1138 1139
@@ -1208,6 +1209,7 @@ static int efi_pstore_write(enum pstore_type_id type,
1208 int i, ret = 0; 1209 int i, ret = 0;
1209 u64 storage_space, remaining_space, max_variable_size; 1210 u64 storage_space, remaining_space, max_variable_size;
1210 efi_status_t status = EFI_NOT_FOUND; 1211 efi_status_t status = EFI_NOT_FOUND;
1212 unsigned long flags;
1211 1213
1212 if (pstore_cannot_block_path(reason)) { 1214 if (pstore_cannot_block_path(reason)) {
1213 /* 1215 /*
@@ -1215,10 +1217,10 @@ static int efi_pstore_write(enum pstore_type_id type,
1215 * this driver returns without entering firmware to avoid 1217 * this driver returns without entering firmware to avoid
1216 * hanging up. 1218 * hanging up.
1217 */ 1219 */
1218 if (!spin_trylock(&efivars->lock)) 1220 if (!spin_trylock_irqsave(&efivars->lock, flags))
1219 return -EBUSY; 1221 return -EBUSY;
1220 } else 1222 } else
1221 spin_lock(&efivars->lock); 1223 spin_lock_irqsave(&efivars->lock, flags);
1222 1224
1223 /* 1225 /*
1224 * Check if there is a space enough to log. 1226 * Check if there is a space enough to log.
@@ -1230,7 +1232,7 @@ static int efi_pstore_write(enum pstore_type_id type,
1230 &remaining_space, 1232 &remaining_space,
1231 &max_variable_size); 1233 &max_variable_size);
1232 if (status || remaining_space < size + DUMP_NAME_LEN * 2) { 1234 if (status || remaining_space < size + DUMP_NAME_LEN * 2) {
1233 spin_unlock(&efivars->lock); 1235 spin_unlock_irqrestore(&efivars->lock, flags);
1234 *id = part; 1236 *id = part;
1235 return -ENOSPC; 1237 return -ENOSPC;
1236 } 1238 }
@@ -1244,7 +1246,7 @@ static int efi_pstore_write(enum pstore_type_id type,
1244 efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES, 1246 efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
1245 size, psi->buf); 1247 size, psi->buf);
1246 1248
1247 spin_unlock(&efivars->lock); 1249 spin_unlock_irqrestore(&efivars->lock, flags);
1248 1250
1249 if (size) 1251 if (size)
1250 ret = efivar_create_sysfs_entry(efivars, 1252 ret = efivar_create_sysfs_entry(efivars,
@@ -1271,7 +1273,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
1271 sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count, 1273 sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count,
1272 time.tv_sec); 1274 time.tv_sec);
1273 1275
1274 spin_lock(&efivars->lock); 1276 spin_lock_irq(&efivars->lock);
1275 1277
1276 for (i = 0; i < DUMP_NAME_LEN; i++) 1278 for (i = 0; i < DUMP_NAME_LEN; i++)
1277 efi_name[i] = name[i]; 1279 efi_name[i] = name[i];
@@ -1315,7 +1317,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
1315 if (found) 1317 if (found)
1316 list_del(&found->list); 1318 list_del(&found->list);
1317 1319
1318 spin_unlock(&efivars->lock); 1320 spin_unlock_irq(&efivars->lock);
1319 1321
1320 if (found) 1322 if (found)
1321 efivar_unregister(found); 1323 efivar_unregister(found);
@@ -1385,7 +1387,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
1385 return -EINVAL; 1387 return -EINVAL;
1386 } 1388 }
1387 1389
1388 spin_lock(&efivars->lock); 1390 spin_lock_irq(&efivars->lock);
1389 1391
1390 /* 1392 /*
1391 * Does this variable already exist? 1393 * Does this variable already exist?
@@ -1403,7 +1405,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
1403 } 1405 }
1404 } 1406 }
1405 if (found) { 1407 if (found) {
1406 spin_unlock(&efivars->lock); 1408 spin_unlock_irq(&efivars->lock);
1407 return -EINVAL; 1409 return -EINVAL;
1408 } 1410 }
1409 1411
@@ -1417,10 +1419,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
1417 if (status != EFI_SUCCESS) { 1419 if (status != EFI_SUCCESS) {
1418 printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", 1420 printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
1419 status); 1421 status);
1420 spin_unlock(&efivars->lock); 1422 spin_unlock_irq(&efivars->lock);
1421 return -EIO; 1423 return -EIO;
1422 } 1424 }
1423 spin_unlock(&efivars->lock); 1425 spin_unlock_irq(&efivars->lock);
1424 1426
1425 /* Create the entry in sysfs. Locking is not required here */ 1427 /* Create the entry in sysfs. Locking is not required here */
1426 status = efivar_create_sysfs_entry(efivars, 1428 status = efivar_create_sysfs_entry(efivars,
@@ -1448,7 +1450,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
1448 if (!capable(CAP_SYS_ADMIN)) 1450 if (!capable(CAP_SYS_ADMIN))
1449 return -EACCES; 1451 return -EACCES;
1450 1452
1451 spin_lock(&efivars->lock); 1453 spin_lock_irq(&efivars->lock);
1452 1454
1453 /* 1455 /*
1454 * Does this variable already exist? 1456 * Does this variable already exist?
@@ -1466,7 +1468,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
1466 } 1468 }
1467 } 1469 }
1468 if (!found) { 1470 if (!found) {
1469 spin_unlock(&efivars->lock); 1471 spin_unlock_irq(&efivars->lock);
1470 return -EINVAL; 1472 return -EINVAL;
1471 } 1473 }
1472 /* force the Attributes/DataSize to 0 to ensure deletion */ 1474 /* force the Attributes/DataSize to 0 to ensure deletion */
@@ -1482,12 +1484,12 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
1482 if (status != EFI_SUCCESS) { 1484 if (status != EFI_SUCCESS) {
1483 printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", 1485 printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
1484 status); 1486 status);
1485 spin_unlock(&efivars->lock); 1487 spin_unlock_irq(&efivars->lock);
1486 return -EIO; 1488 return -EIO;
1487 } 1489 }
1488 list_del(&search_efivar->list); 1490 list_del(&search_efivar->list);
1489 /* We need to release this lock before unregistering. */ 1491 /* We need to release this lock before unregistering. */
1490 spin_unlock(&efivars->lock); 1492 spin_unlock_irq(&efivars->lock);
1491 efivar_unregister(search_efivar); 1493 efivar_unregister(search_efivar);
1492 1494
1493 /* It's dead Jim.... */ 1495 /* It's dead Jim.... */
@@ -1602,9 +1604,9 @@ efivar_create_sysfs_entry(struct efivars *efivars,
1602 kfree(short_name); 1604 kfree(short_name);
1603 short_name = NULL; 1605 short_name = NULL;
1604 1606
1605 spin_lock(&efivars->lock); 1607 spin_lock_irq(&efivars->lock);
1606 list_add(&new_efivar->list, &efivars->list); 1608 list_add(&new_efivar->list, &efivars->list);
1607 spin_unlock(&efivars->lock); 1609 spin_unlock_irq(&efivars->lock);
1608 1610
1609 return 0; 1611 return 0;
1610} 1612}
@@ -1673,9 +1675,9 @@ void unregister_efivars(struct efivars *efivars)
1673 struct efivar_entry *entry, *n; 1675 struct efivar_entry *entry, *n;
1674 1676
1675 list_for_each_entry_safe(entry, n, &efivars->list, list) { 1677 list_for_each_entry_safe(entry, n, &efivars->list, list) {
1676 spin_lock(&efivars->lock); 1678 spin_lock_irq(&efivars->lock);
1677 list_del(&entry->list); 1679 list_del(&entry->list);
1678 spin_unlock(&efivars->lock); 1680 spin_unlock_irq(&efivars->lock);
1679 efivar_unregister(entry); 1681 efivar_unregister(entry);
1680 } 1682 }
1681 if (efivars->new_var) 1683 if (efivars->new_var)