aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorSeiji Aguchi <seiji.aguchi@hds.com>2013-02-12 16:04:41 -0500
committerTony Luck <tony.luck@intel.com>2013-02-12 16:04:41 -0500
commita93bc0c6e07ed9bac44700280e65e2945d864fd4 (patch)
treed14543068b4734356ba2c8eb637b1c87dab378f3 /drivers/firmware
parent81fa4e581d9283f7992a0d8c534bb141eb840a14 (diff)
efi_pstore: Introducing workqueue updating sysfs
[Problem] efi_pstore creates sysfs entries, which enable users to access to NVRAM, in a write callback. If a kernel panic happens in an interrupt context, it may fail because it could sleep due to dynamic memory allocations during creating sysfs entries. [Patch Description] This patch removes sysfs operations from a write callback by introducing a workqueue updating sysfs entries which is scheduled after the write callback is called. Also, the workqueue is kicked in a just oops case. A system will go down in other cases such as panic, clean shutdown and emergency restart. And we don't need to create sysfs entries because there is no chance for users to access to them. efi_pstore will be robust against a kernel panic in an interrupt context with this patch. Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.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.c85
1 files changed, 80 insertions, 5 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index a64fb7bda365..69225115304d 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -158,6 +158,13 @@ efivar_create_sysfs_entry(struct efivars *efivars,
158 efi_char16_t *variable_name, 158 efi_char16_t *variable_name,
159 efi_guid_t *vendor_guid); 159 efi_guid_t *vendor_guid);
160 160
161/*
162 * Prototype for workqueue functions updating sysfs entry
163 */
164
165static void efivar_update_sysfs_entries(struct work_struct *);
166static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries);
167
161/* Return the number of unicode characters in data */ 168/* Return the number of unicode characters in data */
162static unsigned long 169static unsigned long
163utf16_strnlen(efi_char16_t *s, size_t maxlength) 170utf16_strnlen(efi_char16_t *s, size_t maxlength)
@@ -1248,11 +1255,8 @@ static int efi_pstore_write(enum pstore_type_id type,
1248 1255
1249 spin_unlock_irqrestore(&efivars->lock, flags); 1256 spin_unlock_irqrestore(&efivars->lock, flags);
1250 1257
1251 if (size) 1258 if (reason == KMSG_DUMP_OOPS)
1252 ret = efivar_create_sysfs_entry(efivars, 1259 schedule_work(&efivar_work);
1253 utf16_strsize(efi_name,
1254 DUMP_NAME_LEN * 2),
1255 efi_name, &vendor);
1256 1260
1257 *id = part; 1261 *id = part;
1258 return ret; 1262 return ret;
@@ -1496,6 +1500,75 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
1496 return count; 1500 return count;
1497} 1501}
1498 1502
1503static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor)
1504{
1505 struct efivar_entry *entry, *n;
1506 struct efivars *efivars = &__efivars;
1507 unsigned long strsize1, strsize2;
1508 bool found = false;
1509
1510 strsize1 = utf16_strsize(variable_name, 1024);
1511 list_for_each_entry_safe(entry, n, &efivars->list, list) {
1512 strsize2 = utf16_strsize(entry->var.VariableName, 1024);
1513 if (strsize1 == strsize2 &&
1514 !memcmp(variable_name, &(entry->var.VariableName),
1515 strsize2) &&
1516 !efi_guidcmp(entry->var.VendorGuid,
1517 *vendor)) {
1518 found = true;
1519 break;
1520 }
1521 }
1522 return found;
1523}
1524
1525static void efivar_update_sysfs_entries(struct work_struct *work)
1526{
1527 struct efivars *efivars = &__efivars;
1528 efi_guid_t vendor;
1529 efi_char16_t *variable_name;
1530 unsigned long variable_name_size = 1024;
1531 efi_status_t status = EFI_NOT_FOUND;
1532 bool found;
1533
1534 /* Add new sysfs entries */
1535 while (1) {
1536 variable_name = kzalloc(variable_name_size, GFP_KERNEL);
1537 if (!variable_name) {
1538 pr_err("efivars: Memory allocation failed.\n");
1539 return;
1540 }
1541
1542 spin_lock_irq(&efivars->lock);
1543 found = false;
1544 while (1) {
1545 variable_name_size = 1024;
1546 status = efivars->ops->get_next_variable(
1547 &variable_name_size,
1548 variable_name,
1549 &vendor);
1550 if (status != EFI_SUCCESS) {
1551 break;
1552 } else {
1553 if (!variable_is_present(variable_name,
1554 &vendor)) {
1555 found = true;
1556 break;
1557 }
1558 }
1559 }
1560 spin_unlock_irq(&efivars->lock);
1561
1562 if (!found) {
1563 kfree(variable_name);
1564 break;
1565 } else
1566 efivar_create_sysfs_entry(efivars,
1567 variable_name_size,
1568 variable_name, &vendor);
1569 }
1570}
1571
1499/* 1572/*
1500 * Let's not leave out systab information that snuck into 1573 * Let's not leave out systab information that snuck into
1501 * the efivars driver 1574 * the efivars driver
@@ -1833,6 +1906,8 @@ err_put:
1833static void __exit 1906static void __exit
1834efivars_exit(void) 1907efivars_exit(void)
1835{ 1908{
1909 cancel_work_sync(&efivar_work);
1910
1836 if (efi_enabled) { 1911 if (efi_enabled) {
1837 unregister_efivars(&__efivars); 1912 unregister_efivars(&__efivars);
1838 kobject_put(efi_kobj); 1913 kobject_put(efi_kobj);