Commit 024e4ec1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'please-pull-pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux

Pull pstore patches from Tony Luck:
 "A few fixes to reduce places where pstore might hang a system in the
  crash path.  Plus a new mountpoint (/sys/fs/pstore ...  makes more
  sense then /dev/pstore)."

Fix up trivial conflict in drivers/firmware/efivars.c

* tag 'please-pull-pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux:
  pstore: Create a convenient mount point for pstore
  efi_pstore: Introducing workqueue updating sysfs
  efivars: Disable external interrupt while holding efivars->lock
  efi_pstore: Avoid deadlock in non-blocking paths
  pstore: Avoid deadlock in panic and emergency-restart path
parents 850cb82b fb0af3f2
Where: /dev/pstore/... Where: /sys/fs/pstore/... (or /dev/pstore/...)
Date: March 2011 Date: March 2011
Kernel Version: 2.6.39 Kernel Version: 2.6.39
Contact: tony.luck@intel.com Contact: tony.luck@intel.com
...@@ -11,9 +11,9 @@ Description: Generic interface to platform dependent persistent storage. ...@@ -11,9 +11,9 @@ Description: Generic interface to platform dependent persistent storage.
of the console log is captured, but other interesting of the console log is captured, but other interesting
data can also be saved. data can also be saved.
# mount -t pstore -o kmsg_bytes=8000 - /dev/pstore # mount -t pstore -o kmsg_bytes=8000 - /sys/fs/pstore
$ ls -l /dev/pstore $ ls -l /sys/fs/pstore/
total 0 total 0
-r--r--r-- 1 root root 7896 Nov 30 15:38 dmesg-erst-1 -r--r--r-- 1 root root 7896 Nov 30 15:38 dmesg-erst-1
...@@ -27,9 +27,9 @@ Description: Generic interface to platform dependent persistent storage. ...@@ -27,9 +27,9 @@ Description: Generic interface to platform dependent persistent storage.
the file will signal to the underlying persistent storage the file will signal to the underlying persistent storage
device that it can reclaim the space for later re-use. device that it can reclaim the space for later re-use.
$ rm /dev/pstore/dmesg-erst-1 $ rm /sys/fs/pstore/dmesg-erst-1
The expectation is that all files in /dev/pstore The expectation is that all files in /sys/fs/pstore/
will be saved elsewhere and erased from persistent store will be saved elsewhere and erased from persistent store
soon after boot to free up space ready for the next soon after boot to free up space ready for the next
catastrophe. catastrophe.
......
...@@ -158,6 +158,13 @@ efivar_create_sysfs_entry(struct efivars *efivars, ...@@ -158,6 +158,13 @@ efivar_create_sysfs_entry(struct efivars *efivars,
efi_char16_t *variable_name, efi_char16_t *variable_name,
efi_guid_t *vendor_guid); efi_guid_t *vendor_guid);
/*
* Prototype for workqueue functions updating sysfs entry
*/
static void efivar_update_sysfs_entries(struct work_struct *);
static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries);
/* Return the number of unicode characters in data */ /* Return the number of unicode characters in data */
static unsigned long static unsigned long
utf16_strnlen(efi_char16_t *s, size_t maxlength) utf16_strnlen(efi_char16_t *s, size_t maxlength)
...@@ -405,10 +412,11 @@ static efi_status_t ...@@ -405,10 +412,11 @@ static efi_status_t
get_var_data(struct efivars *efivars, struct efi_variable *var) get_var_data(struct efivars *efivars, struct efi_variable *var)
{ {
efi_status_t status; efi_status_t status;
unsigned long flags;
spin_lock(&efivars->lock); spin_lock_irqsave(&efivars->lock, flags);
status = get_var_data_locked(efivars, var); status = get_var_data_locked(efivars, var);
spin_unlock(&efivars->lock); spin_unlock_irqrestore(&efivars->lock, flags);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
...@@ -537,14 +545,14 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) ...@@ -537,14 +545,14 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
return -EINVAL; return -EINVAL;
} }
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
status = efivars->ops->set_variable(new_var->VariableName, status = efivars->ops->set_variable(new_var->VariableName,
&new_var->VendorGuid, &new_var->VendorGuid,
new_var->Attributes, new_var->Attributes,
new_var->DataSize, new_var->DataSize,
new_var->Data); new_var->Data);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
...@@ -713,7 +721,7 @@ static ssize_t efivarfs_file_write(struct file *file, ...@@ -713,7 +721,7 @@ static ssize_t efivarfs_file_write(struct file *file,
* amounts of memory. Pick a default size of 64K if * amounts of memory. Pick a default size of 64K if
* QueryVariableInfo() isn't supported by the firmware. * QueryVariableInfo() isn't supported by the firmware.
*/ */
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
if (!efivars->ops->query_variable_info) if (!efivars->ops->query_variable_info)
status = EFI_UNSUPPORTED; status = EFI_UNSUPPORTED;
...@@ -723,7 +731,7 @@ static ssize_t efivarfs_file_write(struct file *file, ...@@ -723,7 +731,7 @@ static ssize_t efivarfs_file_write(struct file *file,
&remaining_size, &max_size); &remaining_size, &max_size);
} }
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
if (status != EFI_UNSUPPORTED) if (status != EFI_UNSUPPORTED)
...@@ -754,7 +762,7 @@ static ssize_t efivarfs_file_write(struct file *file, ...@@ -754,7 +762,7 @@ static ssize_t efivarfs_file_write(struct file *file,
* set_variable call, and removal of the variable from the efivars * set_variable call, and removal of the variable from the efivars
* list (in the case of an authenticated delete). * list (in the case of an authenticated delete).
*/ */
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
status = efivars->ops->set_variable(var->var.VariableName, status = efivars->ops->set_variable(var->var.VariableName,
&var->var.VendorGuid, &var->var.VendorGuid,
...@@ -762,7 +770,7 @@ static ssize_t efivarfs_file_write(struct file *file, ...@@ -762,7 +770,7 @@ static ssize_t efivarfs_file_write(struct file *file,
data); data);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
kfree(data); kfree(data);
return efi_status_to_err(status); return efi_status_to_err(status);
...@@ -783,21 +791,21 @@ static ssize_t efivarfs_file_write(struct file *file, ...@@ -783,21 +791,21 @@ static ssize_t efivarfs_file_write(struct file *file,
NULL); NULL);
if (status == EFI_BUFFER_TOO_SMALL) { if (status == EFI_BUFFER_TOO_SMALL) {
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
i_size_write(inode, newdatasize + sizeof(attributes)); i_size_write(inode, newdatasize + sizeof(attributes));
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
} else if (status == EFI_NOT_FOUND) { } else if (status == EFI_NOT_FOUND) {
list_del(&var->list); list_del(&var->list);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
efivar_unregister(var); efivar_unregister(var);
drop_nlink(inode); drop_nlink(inode);
d_delete(file->f_dentry); d_delete(file->f_dentry);
dput(file->f_dentry); dput(file->f_dentry);
} else { } else {
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
pr_warn("efivarfs: inconsistent EFI variable implementation? " pr_warn("efivarfs: inconsistent EFI variable implementation? "
"status = %lx\n", status); "status = %lx\n", status);
} }
...@@ -819,11 +827,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, ...@@ -819,11 +827,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
void *data; void *data;
ssize_t size = 0; ssize_t size = 0;
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
status = efivars->ops->get_variable(var->var.VariableName, status = efivars->ops->get_variable(var->var.VariableName,
&var->var.VendorGuid, &var->var.VendorGuid,
&attributes, &datasize, NULL); &attributes, &datasize, NULL);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
if (status != EFI_BUFFER_TOO_SMALL) if (status != EFI_BUFFER_TOO_SMALL)
return efi_status_to_err(status); return efi_status_to_err(status);
...@@ -833,12 +841,12 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, ...@@ -833,12 +841,12 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
status = efivars->ops->get_variable(var->var.VariableName, status = efivars->ops->get_variable(var->var.VariableName,
&var->var.VendorGuid, &var->var.VendorGuid,
&attributes, &datasize, &attributes, &datasize,
(data + sizeof(attributes))); (data + sizeof(attributes)));
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
size = efi_status_to_err(status); size = efi_status_to_err(status);
...@@ -966,9 +974,9 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, ...@@ -966,9 +974,9 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
goto out; goto out;
kobject_uevent(&var->kobj, KOBJ_ADD); kobject_uevent(&var->kobj, KOBJ_ADD);
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
list_add(&var->list, &efivars->list); list_add(&var->list, &efivars->list);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
dget(dentry); dget(dentry);
out: out:
...@@ -985,7 +993,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -985,7 +993,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
struct efivars *efivars = var->efivars; struct efivars *efivars = var->efivars;
efi_status_t status; efi_status_t status;
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
status = efivars->ops->set_variable(var->var.VariableName, status = efivars->ops->set_variable(var->var.VariableName,
&var->var.VendorGuid, &var->var.VendorGuid,
...@@ -993,14 +1001,14 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -993,14 +1001,14 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) { if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) {
list_del(&var->list); list_del(&var->list);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
efivar_unregister(var); efivar_unregister(var);
drop_nlink(dentry->d_inode); drop_nlink(dentry->d_inode);
dput(dentry); dput(dentry);
return 0; return 0;
} }
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
return -EINVAL; return -EINVAL;
}; };
...@@ -1066,13 +1074,13 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1066,13 +1074,13 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
/* copied by the above to local storage in the dentry. */ /* copied by the above to local storage in the dentry. */
kfree(name); kfree(name);
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
efivars->ops->get_variable(entry->var.VariableName, efivars->ops->get_variable(entry->var.VariableName,
&entry->var.VendorGuid, &entry->var.VendorGuid,
&entry->var.Attributes, &entry->var.Attributes,
&size, &size,
NULL); NULL);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
inode->i_private = entry; inode->i_private = entry;
...@@ -1123,7 +1131,7 @@ static int efi_pstore_open(struct pstore_info *psi) ...@@ -1123,7 +1131,7 @@ static int efi_pstore_open(struct pstore_info *psi)
{ {
struct efivars *efivars = psi->data; struct efivars *efivars = psi->data;
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
efivars->walk_entry = list_first_entry(&efivars->list, efivars->walk_entry = list_first_entry(&efivars->list,
struct efivar_entry, list); struct efivar_entry, list);
return 0; return 0;
...@@ -1133,7 +1141,7 @@ static int efi_pstore_close(struct pstore_info *psi) ...@@ -1133,7 +1141,7 @@ static int efi_pstore_close(struct pstore_info *psi)
{ {
struct efivars *efivars = psi->data; struct efivars *efivars = psi->data;
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
return 0; return 0;
} }
...@@ -1209,8 +1217,18 @@ static int efi_pstore_write(enum pstore_type_id type, ...@@ -1209,8 +1217,18 @@ static int efi_pstore_write(enum pstore_type_id type,
int i, ret = 0; int i, ret = 0;
u64 storage_space, remaining_space, max_variable_size; u64 storage_space, remaining_space, max_variable_size;
efi_status_t status = EFI_NOT_FOUND; efi_status_t status = EFI_NOT_FOUND;
unsigned long flags;
spin_lock(&efivars->lock);
if (pstore_cannot_block_path(reason)) {
/*
* If the lock is taken by another cpu in non-blocking path,
* this driver returns without entering firmware to avoid
* hanging up.
*/
if (!spin_trylock_irqsave(&efivars->lock, flags))
return -EBUSY;
} else
spin_lock_irqsave(&efivars->lock, flags);
/* /*
* Check if there is a space enough to log. * Check if there is a space enough to log.
...@@ -1222,7 +1240,7 @@ static int efi_pstore_write(enum pstore_type_id type, ...@@ -1222,7 +1240,7 @@ static int efi_pstore_write(enum pstore_type_id type,
&remaining_space, &remaining_space,
&max_variable_size); &max_variable_size);
if (status || remaining_space < size + DUMP_NAME_LEN * 2) { if (status || remaining_space < size + DUMP_NAME_LEN * 2) {
spin_unlock(&efivars->lock); spin_unlock_irqrestore(&efivars->lock, flags);
*id = part; *id = part;
return -ENOSPC; return -ENOSPC;
} }
...@@ -1236,13 +1254,10 @@ static int efi_pstore_write(enum pstore_type_id type, ...@@ -1236,13 +1254,10 @@ static int efi_pstore_write(enum pstore_type_id type,
efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES, efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
size, psi->buf); size, psi->buf);
spin_unlock(&efivars->lock); spin_unlock_irqrestore(&efivars->lock, flags);
if (size) if (reason == KMSG_DUMP_OOPS)
ret = efivar_create_sysfs_entry(efivars, schedule_work(&efivar_work);
utf16_strsize(efi_name,
DUMP_NAME_LEN * 2),
efi_name, &vendor);
*id = part; *id = part;
return ret; return ret;
...@@ -1263,7 +1278,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, ...@@ -1263,7 +1278,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count, sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count,
time.tv_sec); time.tv_sec);
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
for (i = 0; i < DUMP_NAME_LEN; i++) for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i]; efi_name[i] = name[i];
...@@ -1307,7 +1322,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, ...@@ -1307,7 +1322,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
if (found) if (found)
list_del(&found->list); list_del(&found->list);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
if (found) if (found)
efivar_unregister(found); efivar_unregister(found);
...@@ -1377,7 +1392,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, ...@@ -1377,7 +1392,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
return -EINVAL; return -EINVAL;
} }
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
/* /*
* Does this variable already exist? * Does this variable already exist?
...@@ -1395,7 +1410,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, ...@@ -1395,7 +1410,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
} }
} }
if (found) { if (found) {
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
return -EINVAL; return -EINVAL;
} }
...@@ -1409,10 +1424,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, ...@@ -1409,10 +1424,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
status); status);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
return -EIO; return -EIO;
} }
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
/* Create the entry in sysfs. Locking is not required here */ /* Create the entry in sysfs. Locking is not required here */
status = efivar_create_sysfs_entry(efivars, status = efivar_create_sysfs_entry(efivars,
...@@ -1440,7 +1455,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, ...@@ -1440,7 +1455,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EACCES; return -EACCES;
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
/* /*
* Does this variable already exist? * Does this variable already exist?
...@@ -1458,7 +1473,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, ...@@ -1458,7 +1473,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
} }
} }
if (!found) { if (!found) {
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
return -EINVAL; return -EINVAL;
} }
/* force the Attributes/DataSize to 0 to ensure deletion */ /* force the Attributes/DataSize to 0 to ensure deletion */
...@@ -1474,18 +1489,87 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, ...@@ -1474,18 +1489,87 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
status); status);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
return -EIO; return -EIO;
} }
list_del(&search_efivar->list); list_del(&search_efivar->list);
/* We need to release this lock before unregistering. */ /* We need to release this lock before unregistering. */
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
efivar_unregister(search_efivar); efivar_unregister(search_efivar);
/* It's dead Jim.... */ /* It's dead Jim.... */
return count; return count;
} }
static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor)
{
struct efivar_entry *entry, *n;
struct efivars *efivars = &__efivars;
unsigned long strsize1, strsize2;
bool found = false;
strsize1 = utf16_strsize(variable_name, 1024);
list_for_each_entry_safe(entry, n, &efivars->list, list) {
strsize2 = utf16_strsize(entry->var.VariableName, 1024);
if (strsize1 == strsize2 &&
!memcmp(variable_name, &(entry->var.VariableName),
strsize2) &&
!efi_guidcmp(entry->var.VendorGuid,
*vendor)) {
found = true;
break;
}
}
return found;
}
static void efivar_update_sysfs_entries(struct work_struct *work)
{
struct efivars *efivars = &__efivars;
efi_guid_t vendor;
efi_char16_t *variable_name;
unsigned long variable_name_size = 1024;
efi_status_t status = EFI_NOT_FOUND;
bool found;
/* Add new sysfs entries */
while (1) {
variable_name = kzalloc(variable_name_size, GFP_KERNEL);
if (!variable_name) {
pr_err("efivars: Memory allocation failed.\n");
return;
}
spin_lock_irq(&efivars->lock);
found = false;
while (1) {
variable_name_size = 1024;
status = efivars->ops->get_next_variable(
&variable_name_size,
variable_name,
&vendor);
if (status != EFI_SUCCESS) {
break;
} else {
if (!variable_is_present(variable_name,
&vendor)) {
found = true;
break;
}
}
}
spin_unlock_irq(&efivars->lock);
if (!found) {
kfree(variable_name);
break;
} else
efivar_create_sysfs_entry(efivars,
variable_name_size,
variable_name, &vendor);
}
}
/* /*
* Let's not leave out systab information that snuck into * Let's not leave out systab information that snuck into
* the efivars driver * the efivars driver
...@@ -1594,9 +1678,9 @@ efivar_create_sysfs_entry(struct efivars *efivars, ...@@ -1594,9 +1678,9 @@ efivar_create_sysfs_entry(struct efivars *efivars,
kfree(short_name); kfree(short_name);
short_name = NULL; short_name = NULL;
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
list_add(&new_efivar->list, &efivars->list); list_add(&new_efivar->list, &efivars->list);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
return 0; return 0;
} }
...@@ -1665,9 +1749,9 @@ void unregister_efivars(struct efivars *efivars) ...@@ -1665,9 +1749,9 @@ void unregister_efivars(struct efivars *efivars)
struct efivar_entry *entry, *n; struct efivar_entry *entry, *n;
list_for_each_entry_safe(entry, n, &efivars->list, list) { list_for_each_entry_safe(entry, n, &efivars->list, list) {
spin_lock(&efivars->lock); spin_lock_irq(&efivars->lock);
list_del(&entry->list); list_del(&entry->list);
spin_unlock(&efivars->lock); spin_unlock_irq(&efivars->lock);
efivar_unregister(entry); efivar_unregister(entry);
} }
if (efivars->new_var) if (efivars->new_var)
...@@ -1823,6 +1907,8 @@ efivars_init(void) ...@@ -1823,6 +1907,8 @@ efivars_init(void)
static void __exit static void __exit
efivars_exit(void) efivars_exit(void)
{ {
cancel_work_sync(&efivar_work);
if (efi_enabled(EFI_RUNTIME_SERVICES)) { if (efi_enabled(EFI_RUNTIME_SERVICES)) {
unregister_efivars(&__efivars); unregister_efivars(&__efivars);
kobject_put(efi_kobj); kobject_put(efi_kobj);
......
...@@ -418,9 +418,25 @@ static struct file_system_type pstore_fs_type = { ...@@ -418,9 +418,25 @@ static struct file_system_type pstore_fs_type = {
.kill_sb = pstore_kill_sb, .kill_sb = pstore_kill_sb,
}; };
static struct kobject *pstore_kobj;
static int __init init_pstore_fs(void) static int __init init_pstore_fs(void)
{ {
return register_filesystem(&pstore_fs_type); int err = 0;
/* Create a convenient mount point for people to access pstore */
pstore_kobj = kobject_create_and_add("pstore", fs_kobj);
if (!pstore_kobj) {
err = -ENOMEM;
goto out;
}
err = register_filesystem(&pstore_fs_type);
if (err < 0)
kobject_put(pstore_kobj);
out:
return err;
} }
module_init(init_pstore_fs) module_init(init_pstore_fs)
......
...@@ -96,6 +96,27 @@ static const char *get_reason_str(enum kmsg_dump_reason reason) ...@@ -96,6 +96,27 @@ static const char *get_reason_str(enum kmsg_dump_reason reason)
} }
} }
bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
{
/*
* In case of NMI path, pstore shouldn't be blocked
* regardless of reason.
*/
if (in_nmi())
return true;
switch (reason) {
/* In panic case, other cpus are stopped by smp_send_stop(). */
case KMSG_DUMP_PANIC:
/* Emergency restart shouldn't be blocked by spin lock. */
case KMSG_DUMP_EMERG:
return true;
default:
return false;
}
}
EXPORT_SYMBOL_GPL(pstore_cannot_block_path);
/* /*
* callback from kmsg_dump. (s2,l2) has the most recently * callback from kmsg_dump. (s2,l2) has the most recently
* written bytes, older bytes are in (s1,l1). Save as much * written bytes, older bytes are in (s1,l1). Save as much
...@@ -114,10 +135,12 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -114,10 +135,12 @@ static void pstore_dump(struct kmsg_dumper *dumper,
why = get_reason_str(reason); why = get_reason_str(reason);
if (in_nmi()) { if (pstore_cannot_block_path(reason)) {
is_locked = spin_trylock(&psinfo->buf_lock); is_locked = spin_trylock_irqsave(&psinfo->buf_lock, flags);
if (!is_locked) if (!is_locked) {
pr_err("pstore dump routine blocked in NMI, may corrupt error record\n"); pr_err("pstore dump routine blocked in %s path, may corrupt error record\n"
, in_nmi() ? "NMI" : why);
}
} else } else
spin_lock_irqsave(&psinfo->buf_lock, flags); spin_lock_irqsave(&psinfo->buf_lock, flags);
oopscount++; oopscount++;
...@@ -143,9 +166,9 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -143,9 +166,9 @@ static void pstore_dump(struct kmsg_dumper *dumper,
total += hsize + len; total += hsize + len;
part++; part++;
} }
if (in_nmi()) { if (pstore_cannot_block_path(reason)) {
if (is_locked) if (is_locked)
spin_unlock(&psinfo->buf_lock); spin_unlock_irqrestore(&psinfo->buf_lock, flags);
} else } else
spin_unlock_irqrestore(&psinfo->buf_lock, flags); spin_unlock_irqrestore(&psinfo->buf_lock, flags);
} }
......
...@@ -740,7 +740,8 @@ struct efivars { ...@@ -740,7 +740,8 @@ struct efivars {
* 1) ->list - adds, removals, reads, writes * 1) ->list - adds, removals, reads, writes
* 2) ops.[gs]et_variable() calls. * 2) ops.[gs]et_variable() calls.
* It must not be held when creating sysfs entries or calling kmalloc. * It must not be held when creating sysfs entries or calling kmalloc.
* ops.get_next_variable() is only called from register_efivars(), * ops.get_next_variable() is only called from register_efivars()
* or efivar_update_sysfs_entries(),
* which is protected by the BKL, so that path is safe. * which is protected by the BKL, so that path is safe.
*/ */
spinlock_t lock; spinlock_t lock;
......
...@@ -68,12 +68,18 @@ struct pstore_info { ...@@ -68,12 +68,18 @@ struct pstore_info {
#ifdef CONFIG_PSTORE #ifdef CONFIG_PSTORE
extern int pstore_register(struct pstore_info *); extern int pstore_register(struct pstore_info *);
extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason);
#else #else
static inline int static inline int
pstore_register(struct pstore_info *psi) pstore_register(struct pstore_info *psi)
{ {
return -ENODEV; return -ENODEV;
} }
static inline bool
pstore_cannot_block_path(enum kmsg_dump_reason reason)
{
return false;
}
#endif #endif
#endif /*_LINUX_PSTORE_H*/ #endif /*_LINUX_PSTORE_H*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment