Skip to content

Commit a93bc0c

Browse files
Seiji Aguchiaegl
Seiji Aguchi
authored andcommitted
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 <[email protected]> Acked-by: Matt Fleming <[email protected]> Signed-off-by: Tony Luck <[email protected]>
1 parent 81fa4e5 commit a93bc0c

File tree

2 files changed

+82
-6
lines changed

2 files changed

+82
-6
lines changed

drivers/firmware/efivars.c

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ efivar_create_sysfs_entry(struct efivars *efivars,
158158
efi_char16_t *variable_name,
159159
efi_guid_t *vendor_guid);
160160

161+
/*
162+
* Prototype for workqueue functions updating sysfs entry
163+
*/
164+
165+
static void efivar_update_sysfs_entries(struct work_struct *);
166+
static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries);
167+
161168
/* Return the number of unicode characters in data */
162169
static unsigned long
163170
utf16_strnlen(efi_char16_t *s, size_t maxlength)
@@ -1248,11 +1255,8 @@ static int efi_pstore_write(enum pstore_type_id type,
12481255

12491256
spin_unlock_irqrestore(&efivars->lock, flags);
12501257

1251-
if (size)
1252-
ret = efivar_create_sysfs_entry(efivars,
1253-
utf16_strsize(efi_name,
1254-
DUMP_NAME_LEN * 2),
1255-
efi_name, &vendor);
1258+
if (reason == KMSG_DUMP_OOPS)
1259+
schedule_work(&efivar_work);
12561260

12571261
*id = part;
12581262
return ret;
@@ -1496,6 +1500,75 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
14961500
return count;
14971501
}
14981502

1503+
static 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+
1525+
static 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+
14991572
/*
15001573
* Let's not leave out systab information that snuck into
15011574
* the efivars driver
@@ -1833,6 +1906,8 @@ efivars_init(void)
18331906
static void __exit
18341907
efivars_exit(void)
18351908
{
1909+
cancel_work_sync(&efivar_work);
1910+
18361911
if (efi_enabled) {
18371912
unregister_efivars(&__efivars);
18381913
kobject_put(efi_kobj);

include/linux/efi.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,8 @@ struct efivars {
728728
* 1) ->list - adds, removals, reads, writes
729729
* 2) ops.[gs]et_variable() calls.
730730
* It must not be held when creating sysfs entries or calling kmalloc.
731-
* ops.get_next_variable() is only called from register_efivars(),
731+
* ops.get_next_variable() is only called from register_efivars()
732+
* or efivar_update_sysfs_entries(),
732733
* which is protected by the BKL, so that path is safe.
733734
*/
734735
spinlock_t lock;

0 commit comments

Comments
 (0)