Skip to content

Commit 0d22f33

Browse files
Sylvain Chouleurmfleming
authored andcommitted
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>
1 parent 544f629 commit 0d22f33

File tree

6 files changed

+139
-76
lines changed

6 files changed

+139
-76
lines changed

drivers/firmware/efi/efi-pstore.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
121121
* @entry: deleting entry
122122
* @turn_off_scanning: Check if a scanning flag should be turned off
123123
*/
124-
static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
124+
static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
125125
bool turn_off_scanning)
126126
{
127127
if (entry->deleting) {
128128
list_del(&entry->list);
129129
efivar_entry_iter_end();
130130
efivar_unregister(entry);
131-
efivar_entry_iter_begin();
131+
if (efivar_entry_iter_begin())
132+
return -EINTR;
132133
} else if (turn_off_scanning)
133134
entry->scanning = false;
135+
136+
return 0;
134137
}
135138

136139
/**
@@ -140,13 +143,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
140143
* @head: list head
141144
* @stop: a flag checking if scanning will stop
142145
*/
143-
static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
146+
static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
144147
struct efivar_entry *next,
145148
struct list_head *head, bool stop)
146149
{
147-
__efi_pstore_scan_sysfs_exit(pos, true);
150+
int ret = __efi_pstore_scan_sysfs_exit(pos, true);
151+
152+
if (ret)
153+
return ret;
154+
148155
if (stop)
149-
__efi_pstore_scan_sysfs_exit(next, &next->list != head);
156+
ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head);
157+
return ret;
150158
}
151159

152160
/**
@@ -168,13 +176,17 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
168176
struct efivar_entry *entry, *n;
169177
struct list_head *head = &efivar_sysfs_list;
170178
int size = 0;
179+
int ret;
171180

172181
if (!*pos) {
173182
list_for_each_entry_safe(entry, n, head, list) {
174183
efi_pstore_scan_sysfs_enter(entry, n, head);
175184

176185
size = efi_pstore_read_func(entry, data);
177-
efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
186+
ret = efi_pstore_scan_sysfs_exit(entry, n, head,
187+
size < 0);
188+
if (ret)
189+
return ret;
178190
if (size)
179191
break;
180192
}
@@ -186,7 +198,9 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
186198
efi_pstore_scan_sysfs_enter((*pos), n, head);
187199

188200
size = efi_pstore_read_func((*pos), data);
189-
efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
201+
ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
202+
if (ret)
203+
return ret;
190204
if (size)
191205
break;
192206
}
@@ -226,7 +240,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
226240
if (!*data.buf)
227241
return -ENOMEM;
228242

229-
efivar_entry_iter_begin();
243+
if (efivar_entry_iter_begin()) {
244+
kfree(*data.buf);
245+
return -EINTR;
246+
}
230247
size = efi_pstore_sysfs_entry_iter(&data,
231248
(struct efivar_entry **)&psi->data);
232249
efivar_entry_iter_end();
@@ -341,7 +358,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
341358
edata.time = time;
342359
edata.name = efi_name;
343360

344-
efivar_entry_iter_begin();
361+
if (efivar_entry_iter_begin())
362+
return -EINTR;
345363
found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
346364

347365
if (found && !entry->scanning) {

drivers/firmware/efi/efivars.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
510510
vendor = del_var->VendorGuid;
511511
}
512512

513-
efivar_entry_iter_begin();
513+
if (efivar_entry_iter_begin())
514+
return -EINTR;
514515
entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true);
515516
if (!entry)
516517
err = -EINVAL;
@@ -575,7 +576,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
575576
return ret;
576577

577578
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+
}
579583

580584
return 0;
581585
}
@@ -690,15 +694,25 @@ static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor,
690694

691695
static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
692696
{
693-
efivar_entry_remove(entry);
697+
int err = efivar_entry_remove(entry);
698+
699+
if (err)
700+
return err;
694701
efivar_unregister(entry);
695702
return 0;
696703
}
697704

698705
static void efivars_sysfs_exit(void)
699706
{
700707
/* 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+
}
702716

703717
if (efivars_new_var)
704718
sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var);

0 commit comments

Comments
 (0)