Skip to content

Commit 81fa4e5

Browse files
Seiji Aguchiaegl
Seiji Aguchi
authored andcommitted
efivars: Disable external interrupt while holding efivars->lock
[Problem] There is a scenario which efi_pstore fails to log messages in a panic case. - CPUA holds an efi_var->lock in either efivarfs parts or efi_pstore with interrupt enabled. - CPUB panics and sends IPI to CPUA in smp_send_stop(). - CPUA stops with holding the lock. - CPUB kicks efi_pstore_write() via kmsg_dump(KSMG_DUMP_PANIC) but it returns without logging messages. [Patch Description] This patch disables an external interruption while holding efivars->lock as follows. In efi_pstore_write() and get_var_data(), spin_lock/spin_unlock is replaced by spin_lock_irqsave/spin_unlock_irqrestore because they may be called in an interrupt context. In other functions, they are replaced by spin_lock_irq/spin_unlock_irq. because they are all called from a process context. By applying this patch, we can avoid the problem above with a following senario. - CPUA holds an efi_var->lock with interrupt disabled. - CPUB panics and sends IPI to CPUA in smp_send_stop(). - CPUA receives the IPI after releasing the lock because it is disabling interrupt while holding the lock. - CPUB waits for one sec until CPUA releases the lock. - CPUB kicks efi_pstore_write() via kmsg_dump(KSMG_DUMP_PANIC) And it can hold the lock successfully. Signed-off-by: Seiji Aguchi <[email protected]> Acked-by: Mike Waychison <[email protected]> Acked-by: Matt Fleming <[email protected]> Signed-off-by: Tony Luck <[email protected]>
1 parent e59310a commit 81fa4e5

File tree

1 file changed

+44
-42
lines changed

1 file changed

+44
-42
lines changed

drivers/firmware/efivars.c

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -405,10 +405,11 @@ static efi_status_t
405405
get_var_data(struct efivars *efivars, struct efi_variable *var)
406406
{
407407
efi_status_t status;
408+
unsigned long flags;
408409

409-
spin_lock(&efivars->lock);
410+
spin_lock_irqsave(&efivars->lock, flags);
410411
status = get_var_data_locked(efivars, var);
411-
spin_unlock(&efivars->lock);
412+
spin_unlock_irqrestore(&efivars->lock, flags);
412413

413414
if (status != EFI_SUCCESS) {
414415
printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
@@ -537,14 +538,14 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
537538
return -EINVAL;
538539
}
539540

540-
spin_lock(&efivars->lock);
541+
spin_lock_irq(&efivars->lock);
541542
status = efivars->ops->set_variable(new_var->VariableName,
542543
&new_var->VendorGuid,
543544
new_var->Attributes,
544545
new_var->DataSize,
545546
new_var->Data);
546547

547-
spin_unlock(&efivars->lock);
548+
spin_unlock_irq(&efivars->lock);
548549

549550
if (status != EFI_SUCCESS) {
550551
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
@@ -713,7 +714,7 @@ static ssize_t efivarfs_file_write(struct file *file,
713714
* amounts of memory. Pick a default size of 64K if
714715
* QueryVariableInfo() isn't supported by the firmware.
715716
*/
716-
spin_lock(&efivars->lock);
717+
spin_lock_irq(&efivars->lock);
717718

718719
if (!efivars->ops->query_variable_info)
719720
status = EFI_UNSUPPORTED;
@@ -723,7 +724,7 @@ static ssize_t efivarfs_file_write(struct file *file,
723724
&remaining_size, &max_size);
724725
}
725726

726-
spin_unlock(&efivars->lock);
727+
spin_unlock_irq(&efivars->lock);
727728

728729
if (status != EFI_SUCCESS) {
729730
if (status != EFI_UNSUPPORTED)
@@ -754,15 +755,15 @@ static ssize_t efivarfs_file_write(struct file *file,
754755
* set_variable call, and removal of the variable from the efivars
755756
* list (in the case of an authenticated delete).
756757
*/
757-
spin_lock(&efivars->lock);
758+
spin_lock_irq(&efivars->lock);
758759

759760
status = efivars->ops->set_variable(var->var.VariableName,
760761
&var->var.VendorGuid,
761762
attributes, datasize,
762763
data);
763764

764765
if (status != EFI_SUCCESS) {
765-
spin_unlock(&efivars->lock);
766+
spin_unlock_irq(&efivars->lock);
766767
kfree(data);
767768

768769
return efi_status_to_err(status);
@@ -783,20 +784,20 @@ static ssize_t efivarfs_file_write(struct file *file,
783784
NULL);
784785

785786
if (status == EFI_BUFFER_TOO_SMALL) {
786-
spin_unlock(&efivars->lock);
787+
spin_unlock_irq(&efivars->lock);
787788
mutex_lock(&inode->i_mutex);
788789
i_size_write(inode, newdatasize + sizeof(attributes));
789790
mutex_unlock(&inode->i_mutex);
790791

791792
} else if (status == EFI_NOT_FOUND) {
792793
list_del(&var->list);
793-
spin_unlock(&efivars->lock);
794+
spin_unlock_irq(&efivars->lock);
794795
efivar_unregister(var);
795796
drop_nlink(inode);
796797
dput(file->f_dentry);
797798

798799
} else {
799-
spin_unlock(&efivars->lock);
800+
spin_unlock_irq(&efivars->lock);
800801
pr_warn("efivarfs: inconsistent EFI variable implementation? "
801802
"status = %lx\n", status);
802803
}
@@ -818,11 +819,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
818819
void *data;
819820
ssize_t size = 0;
820821

821-
spin_lock(&efivars->lock);
822+
spin_lock_irq(&efivars->lock);
822823
status = efivars->ops->get_variable(var->var.VariableName,
823824
&var->var.VendorGuid,
824825
&attributes, &datasize, NULL);
825-
spin_unlock(&efivars->lock);
826+
spin_unlock_irq(&efivars->lock);
826827

827828
if (status != EFI_BUFFER_TOO_SMALL)
828829
return efi_status_to_err(status);
@@ -832,12 +833,12 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
832833
if (!data)
833834
return -ENOMEM;
834835

835-
spin_lock(&efivars->lock);
836+
spin_lock_irq(&efivars->lock);
836837
status = efivars->ops->get_variable(var->var.VariableName,
837838
&var->var.VendorGuid,
838839
&attributes, &datasize,
839840
(data + sizeof(attributes)));
840-
spin_unlock(&efivars->lock);
841+
spin_unlock_irq(&efivars->lock);
841842

842843
if (status != EFI_SUCCESS) {
843844
size = efi_status_to_err(status);
@@ -965,9 +966,9 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
965966
goto out;
966967

967968
kobject_uevent(&var->kobj, KOBJ_ADD);
968-
spin_lock(&efivars->lock);
969+
spin_lock_irq(&efivars->lock);
969970
list_add(&var->list, &efivars->list);
970-
spin_unlock(&efivars->lock);
971+
spin_unlock_irq(&efivars->lock);
971972
d_instantiate(dentry, inode);
972973
dget(dentry);
973974
out:
@@ -984,22 +985,22 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
984985
struct efivars *efivars = var->efivars;
985986
efi_status_t status;
986987

987-
spin_lock(&efivars->lock);
988+
spin_lock_irq(&efivars->lock);
988989

989990
status = efivars->ops->set_variable(var->var.VariableName,
990991
&var->var.VendorGuid,
991992
0, 0, NULL);
992993

993994
if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) {
994995
list_del(&var->list);
995-
spin_unlock(&efivars->lock);
996+
spin_unlock_irq(&efivars->lock);
996997
efivar_unregister(var);
997998
drop_nlink(dir);
998999
dput(dentry);
9991000
return 0;
10001001
}
10011002

1002-
spin_unlock(&efivars->lock);
1003+
spin_unlock_irq(&efivars->lock);
10031004
return -EINVAL;
10041005
};
10051006

@@ -1065,13 +1066,13 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
10651066
/* copied by the above to local storage in the dentry. */
10661067
kfree(name);
10671068

1068-
spin_lock(&efivars->lock);
1069+
spin_lock_irq(&efivars->lock);
10691070
efivars->ops->get_variable(entry->var.VariableName,
10701071
&entry->var.VendorGuid,
10711072
&entry->var.Attributes,
10721073
&size,
10731074
NULL);
1074-
spin_unlock(&efivars->lock);
1075+
spin_unlock_irq(&efivars->lock);
10751076

10761077
mutex_lock(&inode->i_mutex);
10771078
inode->i_private = entry;
@@ -1122,7 +1123,7 @@ static int efi_pstore_open(struct pstore_info *psi)
11221123
{
11231124
struct efivars *efivars = psi->data;
11241125

1125-
spin_lock(&efivars->lock);
1126+
spin_lock_irq(&efivars->lock);
11261127
efivars->walk_entry = list_first_entry(&efivars->list,
11271128
struct efivar_entry, list);
11281129
return 0;
@@ -1132,7 +1133,7 @@ static int efi_pstore_close(struct pstore_info *psi)
11321133
{
11331134
struct efivars *efivars = psi->data;
11341135

1135-
spin_unlock(&efivars->lock);
1136+
spin_unlock_irq(&efivars->lock);
11361137
return 0;
11371138
}
11381139

@@ -1208,17 +1209,18 @@ static int efi_pstore_write(enum pstore_type_id type,
12081209
int i, ret = 0;
12091210
u64 storage_space, remaining_space, max_variable_size;
12101211
efi_status_t status = EFI_NOT_FOUND;
1212+
unsigned long flags;
12111213

12121214
if (pstore_cannot_block_path(reason)) {
12131215
/*
12141216
* If the lock is taken by another cpu in non-blocking path,
12151217
* this driver returns without entering firmware to avoid
12161218
* hanging up.
12171219
*/
1218-
if (!spin_trylock(&efivars->lock))
1220+
if (!spin_trylock_irqsave(&efivars->lock, flags))
12191221
return -EBUSY;
12201222
} else
1221-
spin_lock(&efivars->lock);
1223+
spin_lock_irqsave(&efivars->lock, flags);
12221224

12231225
/*
12241226
* Check if there is a space enough to log.
@@ -1230,7 +1232,7 @@ static int efi_pstore_write(enum pstore_type_id type,
12301232
&remaining_space,
12311233
&max_variable_size);
12321234
if (status || remaining_space < size + DUMP_NAME_LEN * 2) {
1233-
spin_unlock(&efivars->lock);
1235+
spin_unlock_irqrestore(&efivars->lock, flags);
12341236
*id = part;
12351237
return -ENOSPC;
12361238
}
@@ -1244,7 +1246,7 @@ static int efi_pstore_write(enum pstore_type_id type,
12441246
efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
12451247
size, psi->buf);
12461248

1247-
spin_unlock(&efivars->lock);
1249+
spin_unlock_irqrestore(&efivars->lock, flags);
12481250

12491251
if (size)
12501252
ret = efivar_create_sysfs_entry(efivars,
@@ -1271,7 +1273,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
12711273
sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count,
12721274
time.tv_sec);
12731275

1274-
spin_lock(&efivars->lock);
1276+
spin_lock_irq(&efivars->lock);
12751277

12761278
for (i = 0; i < DUMP_NAME_LEN; i++)
12771279
efi_name[i] = name[i];
@@ -1315,7 +1317,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
13151317
if (found)
13161318
list_del(&found->list);
13171319

1318-
spin_unlock(&efivars->lock);
1320+
spin_unlock_irq(&efivars->lock);
13191321

13201322
if (found)
13211323
efivar_unregister(found);
@@ -1385,7 +1387,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
13851387
return -EINVAL;
13861388
}
13871389

1388-
spin_lock(&efivars->lock);
1390+
spin_lock_irq(&efivars->lock);
13891391

13901392
/*
13911393
* Does this variable already exist?
@@ -1403,7 +1405,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
14031405
}
14041406
}
14051407
if (found) {
1406-
spin_unlock(&efivars->lock);
1408+
spin_unlock_irq(&efivars->lock);
14071409
return -EINVAL;
14081410
}
14091411

@@ -1417,10 +1419,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
14171419
if (status != EFI_SUCCESS) {
14181420
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
14191421
status);
1420-
spin_unlock(&efivars->lock);
1422+
spin_unlock_irq(&efivars->lock);
14211423
return -EIO;
14221424
}
1423-
spin_unlock(&efivars->lock);
1425+
spin_unlock_irq(&efivars->lock);
14241426

14251427
/* Create the entry in sysfs. Locking is not required here */
14261428
status = efivar_create_sysfs_entry(efivars,
@@ -1448,7 +1450,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
14481450
if (!capable(CAP_SYS_ADMIN))
14491451
return -EACCES;
14501452

1451-
spin_lock(&efivars->lock);
1453+
spin_lock_irq(&efivars->lock);
14521454

14531455
/*
14541456
* Does this variable already exist?
@@ -1466,7 +1468,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
14661468
}
14671469
}
14681470
if (!found) {
1469-
spin_unlock(&efivars->lock);
1471+
spin_unlock_irq(&efivars->lock);
14701472
return -EINVAL;
14711473
}
14721474
/* force the Attributes/DataSize to 0 to ensure deletion */
@@ -1482,12 +1484,12 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
14821484
if (status != EFI_SUCCESS) {
14831485
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
14841486
status);
1485-
spin_unlock(&efivars->lock);
1487+
spin_unlock_irq(&efivars->lock);
14861488
return -EIO;
14871489
}
14881490
list_del(&search_efivar->list);
14891491
/* We need to release this lock before unregistering. */
1490-
spin_unlock(&efivars->lock);
1492+
spin_unlock_irq(&efivars->lock);
14911493
efivar_unregister(search_efivar);
14921494

14931495
/* It's dead Jim.... */
@@ -1602,9 +1604,9 @@ efivar_create_sysfs_entry(struct efivars *efivars,
16021604
kfree(short_name);
16031605
short_name = NULL;
16041606

1605-
spin_lock(&efivars->lock);
1607+
spin_lock_irq(&efivars->lock);
16061608
list_add(&new_efivar->list, &efivars->list);
1607-
spin_unlock(&efivars->lock);
1609+
spin_unlock_irq(&efivars->lock);
16081610

16091611
return 0;
16101612
}
@@ -1673,9 +1675,9 @@ void unregister_efivars(struct efivars *efivars)
16731675
struct efivar_entry *entry, *n;
16741676

16751677
list_for_each_entry_safe(entry, n, &efivars->list, list) {
1676-
spin_lock(&efivars->lock);
1678+
spin_lock_irq(&efivars->lock);
16771679
list_del(&entry->list);
1678-
spin_unlock(&efivars->lock);
1680+
spin_unlock_irq(&efivars->lock);
16791681
efivar_unregister(entry);
16801682
}
16811683
if (efivars->new_var)

0 commit comments

Comments
 (0)