Skip to content

Commit

Permalink
efi: Don't use spinlocks for efi vars
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Cc: Leif Lindholm <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Sylvain Chouleur <[email protected]>
Signed-off-by: Matt Fleming <[email protected]>
  • Loading branch information
Sylvain Chouleur authored and mfleming committed Sep 9, 2016
1 parent 217b27d commit 21b3ddd
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 76 deletions.
36 changes: 27 additions & 9 deletions drivers/firmware/efi/efi-pstore.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
* @entry: deleting entry
* @turn_off_scanning: Check if a scanning flag should be turned off
*/
static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
bool turn_off_scanning)
{
if (entry->deleting) {
list_del(&entry->list);
efivar_entry_iter_end();
efivar_unregister(entry);
efivar_entry_iter_begin();
if (efivar_entry_iter_begin())
return -EINTR;
} else if (turn_off_scanning)
entry->scanning = false;

return 0;
}

/**
Expand All @@ -144,13 +147,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
* @head: list head
* @stop: a flag checking if scanning will stop
*/
static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
struct efivar_entry *next,
struct list_head *head, bool stop)
{
__efi_pstore_scan_sysfs_exit(pos, true);
int ret = __efi_pstore_scan_sysfs_exit(pos, true);

if (ret)
return ret;

if (stop)
__efi_pstore_scan_sysfs_exit(next, &next->list != head);
ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head);
return ret;
}

/**
Expand All @@ -172,13 +180,17 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
struct efivar_entry *entry, *n;
struct list_head *head = &efivar_sysfs_list;
int size = 0;
int ret;

if (!*pos) {
list_for_each_entry_safe(entry, n, head, list) {
efi_pstore_scan_sysfs_enter(entry, n, head);

size = efi_pstore_read_func(entry, data);
efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
ret = efi_pstore_scan_sysfs_exit(entry, n, head,
size < 0);
if (ret)
return ret;
if (size)
break;
}
Expand All @@ -190,7 +202,9 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
efi_pstore_scan_sysfs_enter((*pos), n, head);

size = efi_pstore_read_func((*pos), data);
efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
if (ret)
return ret;
if (size)
break;
}
Expand Down Expand Up @@ -232,7 +246,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
if (!*data.buf)
return -ENOMEM;

efivar_entry_iter_begin();
if (efivar_entry_iter_begin()) {
kfree(*data.buf);
return -EINTR;
}
size = efi_pstore_sysfs_entry_iter(&data,
(struct efivar_entry **)&psi->data);
efivar_entry_iter_end();
Expand Down Expand Up @@ -347,7 +364,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
edata.time = time;
edata.name = efi_name;

efivar_entry_iter_begin();
if (efivar_entry_iter_begin())
return -EINTR;
found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);

if (found && !entry->scanning) {
Expand Down
22 changes: 18 additions & 4 deletions drivers/firmware/efi/efivars.c
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
vendor = del_var->VendorGuid;
}

efivar_entry_iter_begin();
if (efivar_entry_iter_begin())
return -EINTR;
entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true);
if (!entry)
err = -EINVAL;
Expand Down Expand Up @@ -575,7 +576,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
return ret;

kobject_uevent(&new_var->kobj, KOBJ_ADD);
efivar_entry_add(new_var, &efivar_sysfs_list);
if (efivar_entry_add(new_var, &efivar_sysfs_list)) {
efivar_unregister(new_var);
return -EINTR;
}

return 0;
}
Expand Down Expand Up @@ -690,15 +694,25 @@ static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor,

static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
{
efivar_entry_remove(entry);
int err = efivar_entry_remove(entry);

if (err)
return err;
efivar_unregister(entry);
return 0;
}

static void efivars_sysfs_exit(void)
{
/* Remove all entries and destroy */
__efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, NULL, NULL);
int err;

err = __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list,
NULL, NULL);
if (err) {
pr_err("efivars: Failed to destroy sysfs entries\n");
return;
}

if (efivars_new_var)
sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var);
Expand Down
Loading

0 comments on commit 21b3ddd

Please sign in to comment.