Skip to content

Commit 21b3ddd

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 217b27d commit 21b3ddd

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
@@ -125,16 +125,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
125125
* @entry: deleting entry
126126
* @turn_off_scanning: Check if a scanning flag should be turned off
127127
*/
128-
static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
128+
static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
129129
bool turn_off_scanning)
130130
{
131131
if (entry->deleting) {
132132
list_del(&entry->list);
133133
efivar_entry_iter_end();
134134
efivar_unregister(entry);
135-
efivar_entry_iter_begin();
135+
if (efivar_entry_iter_begin())
136+
return -EINTR;
136137
} else if (turn_off_scanning)
137138
entry->scanning = false;
139+
140+
return 0;
138141
}
139142

140143
/**
@@ -144,13 +147,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
144147
* @head: list head
145148
* @stop: a flag checking if scanning will stop
146149
*/
147-
static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
150+
static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
148151
struct efivar_entry *next,
149152
struct list_head *head, bool stop)
150153
{
151-
__efi_pstore_scan_sysfs_exit(pos, true);
154+
int ret = __efi_pstore_scan_sysfs_exit(pos, true);
155+
156+
if (ret)
157+
return ret;
158+
152159
if (stop)
153-
__efi_pstore_scan_sysfs_exit(next, &next->list != head);
160+
ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head);
161+
return ret;
154162
}
155163

156164
/**
@@ -172,13 +180,17 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
172180
struct efivar_entry *entry, *n;
173181
struct list_head *head = &efivar_sysfs_list;
174182
int size = 0;
183+
int ret;
175184

176185
if (!*pos) {
177186
list_for_each_entry_safe(entry, n, head, list) {
178187
efi_pstore_scan_sysfs_enter(entry, n, head);
179188

180189
size = efi_pstore_read_func(entry, data);
181-
efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
190+
ret = efi_pstore_scan_sysfs_exit(entry, n, head,
191+
size < 0);
192+
if (ret)
193+
return ret;
182194
if (size)
183195
break;
184196
}
@@ -190,7 +202,9 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
190202
efi_pstore_scan_sysfs_enter((*pos), n, head);
191203

192204
size = efi_pstore_read_func((*pos), data);
193-
efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
205+
ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
206+
if (ret)
207+
return ret;
194208
if (size)
195209
break;
196210
}
@@ -232,7 +246,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
232246
if (!*data.buf)
233247
return -ENOMEM;
234248

235-
efivar_entry_iter_begin();
249+
if (efivar_entry_iter_begin()) {
250+
kfree(*data.buf);
251+
return -EINTR;
252+
}
236253
size = efi_pstore_sysfs_entry_iter(&data,
237254
(struct efivar_entry **)&psi->data);
238255
efivar_entry_iter_end();
@@ -347,7 +364,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
347364
edata.time = time;
348365
edata.name = efi_name;
349366

350-
efivar_entry_iter_begin();
367+
if (efivar_entry_iter_begin())
368+
return -EINTR;
351369
found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
352370

353371
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)