diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-21 12:38:18 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-21 12:38:18 -0500 |
| commit | 024e4ec1856d57bb78c06ec903d29dcf716f5f47 (patch) | |
| tree | 72c89ea735f2aad2f63f64444ec056d7da8e06ff | |
| parent | 850cb82b754c931c570c9cb7f10c9f2181bac617 (diff) | |
| parent | fb0af3f2b1b613e5ea75426d454c7e5b1d1eef49 (diff) | |
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
| -rw-r--r-- | Documentation/ABI/testing/pstore | 10 | ||||
| -rw-r--r-- | drivers/firmware/efivars.c | 180 | ||||
| -rw-r--r-- | fs/pstore/inode.c | 18 | ||||
| -rw-r--r-- | fs/pstore/platform.c | 35 | ||||
| -rw-r--r-- | include/linux/efi.h | 3 | ||||
| -rw-r--r-- | include/linux/pstore.h | 6 |
6 files changed, 192 insertions, 60 deletions
diff --git a/Documentation/ABI/testing/pstore b/Documentation/ABI/testing/pstore index ff1df4e3b059..5fca9f5e10a3 100644 --- a/Documentation/ABI/testing/pstore +++ b/Documentation/ABI/testing/pstore | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | Where: /dev/pstore/... | 1 | Where: /sys/fs/pstore/... (or /dev/pstore/...) |
| 2 | Date: March 2011 | 2 | Date: March 2011 |
| 3 | Kernel Version: 2.6.39 | 3 | Kernel Version: 2.6.39 |
| 4 | Contact: tony.luck@intel.com | 4 | Contact: tony.luck@intel.com |
| @@ -11,9 +11,9 @@ Description: Generic interface to platform dependent persistent storage. | |||
| 11 | of the console log is captured, but other interesting | 11 | of the console log is captured, but other interesting |
| 12 | data can also be saved. | 12 | data can also be saved. |
| 13 | 13 | ||
| 14 | # mount -t pstore -o kmsg_bytes=8000 - /dev/pstore | 14 | # mount -t pstore -o kmsg_bytes=8000 - /sys/fs/pstore |
| 15 | 15 | ||
| 16 | $ ls -l /dev/pstore | 16 | $ ls -l /sys/fs/pstore/ |
| 17 | total 0 | 17 | total 0 |
| 18 | -r--r--r-- 1 root root 7896 Nov 30 15:38 dmesg-erst-1 | 18 | -r--r--r-- 1 root root 7896 Nov 30 15:38 dmesg-erst-1 |
| 19 | 19 | ||
| @@ -27,9 +27,9 @@ Description: Generic interface to platform dependent persistent storage. | |||
| 27 | the file will signal to the underlying persistent storage | 27 | the file will signal to the underlying persistent storage |
| 28 | device that it can reclaim the space for later re-use. | 28 | device that it can reclaim the space for later re-use. |
| 29 | 29 | ||
| 30 | $ rm /dev/pstore/dmesg-erst-1 | 30 | $ rm /sys/fs/pstore/dmesg-erst-1 |
| 31 | 31 | ||
| 32 | The expectation is that all files in /dev/pstore | 32 | The expectation is that all files in /sys/fs/pstore/ |
| 33 | will be saved elsewhere and erased from persistent store | 33 | will be saved elsewhere and erased from persistent store |
| 34 | soon after boot to free up space ready for the next | 34 | soon after boot to free up space ready for the next |
| 35 | catastrophe. | 35 | catastrophe. |
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index f5596db0cf58..fed08b661711 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
| @@ -158,6 +158,13 @@ efivar_create_sysfs_entry(struct efivars *efivars, | |||
| 158 | efi_char16_t *variable_name, | 158 | efi_char16_t *variable_name, |
| 159 | efi_guid_t *vendor_guid); | 159 | efi_guid_t *vendor_guid); |
| 160 | 160 | ||
| 161 | /* | ||
| 162 | * Prototype for workqueue functions updating sysfs entry | ||
| 163 | */ | ||
| 164 | |||
| 165 | static void efivar_update_sysfs_entries(struct work_struct *); | ||
| 166 | static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); | ||
| 167 | |||
| 161 | /* Return the number of unicode characters in data */ | 168 | /* Return the number of unicode characters in data */ |
| 162 | static unsigned long | 169 | static unsigned long |
| 163 | utf16_strnlen(efi_char16_t *s, size_t maxlength) | 170 | utf16_strnlen(efi_char16_t *s, size_t maxlength) |
| @@ -405,10 +412,11 @@ static efi_status_t | |||
| 405 | get_var_data(struct efivars *efivars, struct efi_variable *var) | 412 | get_var_data(struct efivars *efivars, struct efi_variable *var) |
| 406 | { | 413 | { |
| 407 | efi_status_t status; | 414 | efi_status_t status; |
| 415 | unsigned long flags; | ||
| 408 | 416 | ||
| 409 | spin_lock(&efivars->lock); | 417 | spin_lock_irqsave(&efivars->lock, flags); |
| 410 | status = get_var_data_locked(efivars, var); | 418 | status = get_var_data_locked(efivars, var); |
| 411 | spin_unlock(&efivars->lock); | 419 | spin_unlock_irqrestore(&efivars->lock, flags); |
| 412 | 420 | ||
| 413 | if (status != EFI_SUCCESS) { | 421 | if (status != EFI_SUCCESS) { |
| 414 | printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", | 422 | 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 | return -EINVAL; | 545 | return -EINVAL; |
| 538 | } | 546 | } |
| 539 | 547 | ||
| 540 | spin_lock(&efivars->lock); | 548 | spin_lock_irq(&efivars->lock); |
| 541 | status = efivars->ops->set_variable(new_var->VariableName, | 549 | status = efivars->ops->set_variable(new_var->VariableName, |
| 542 | &new_var->VendorGuid, | 550 | &new_var->VendorGuid, |
| 543 | new_var->Attributes, | 551 | new_var->Attributes, |
| 544 | new_var->DataSize, | 552 | new_var->DataSize, |
| 545 | new_var->Data); | 553 | new_var->Data); |
| 546 | 554 | ||
| 547 | spin_unlock(&efivars->lock); | 555 | spin_unlock_irq(&efivars->lock); |
| 548 | 556 | ||
| 549 | if (status != EFI_SUCCESS) { | 557 | if (status != EFI_SUCCESS) { |
| 550 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | 558 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", |
| @@ -713,7 +721,7 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
| 713 | * amounts of memory. Pick a default size of 64K if | 721 | * amounts of memory. Pick a default size of 64K if |
| 714 | * QueryVariableInfo() isn't supported by the firmware. | 722 | * QueryVariableInfo() isn't supported by the firmware. |
| 715 | */ | 723 | */ |
| 716 | spin_lock(&efivars->lock); | 724 | spin_lock_irq(&efivars->lock); |
| 717 | 725 | ||
| 718 | if (!efivars->ops->query_variable_info) | 726 | if (!efivars->ops->query_variable_info) |
| 719 | status = EFI_UNSUPPORTED; | 727 | status = EFI_UNSUPPORTED; |
| @@ -723,7 +731,7 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
| 723 | &remaining_size, &max_size); | 731 | &remaining_size, &max_size); |
| 724 | } | 732 | } |
| 725 | 733 | ||
| 726 | spin_unlock(&efivars->lock); | 734 | spin_unlock_irq(&efivars->lock); |
| 727 | 735 | ||
| 728 | if (status != EFI_SUCCESS) { | 736 | if (status != EFI_SUCCESS) { |
| 729 | if (status != EFI_UNSUPPORTED) | 737 | if (status != EFI_UNSUPPORTED) |
| @@ -754,7 +762,7 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
| 754 | * set_variable call, and removal of the variable from the efivars | 762 | * set_variable call, and removal of the variable from the efivars |
| 755 | * list (in the case of an authenticated delete). | 763 | * list (in the case of an authenticated delete). |
| 756 | */ | 764 | */ |
| 757 | spin_lock(&efivars->lock); | 765 | spin_lock_irq(&efivars->lock); |
| 758 | 766 | ||
| 759 | status = efivars->ops->set_variable(var->var.VariableName, | 767 | status = efivars->ops->set_variable(var->var.VariableName, |
| 760 | &var->var.VendorGuid, | 768 | &var->var.VendorGuid, |
| @@ -762,7 +770,7 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
| 762 | data); | 770 | data); |
| 763 | 771 | ||
| 764 | if (status != EFI_SUCCESS) { | 772 | if (status != EFI_SUCCESS) { |
| 765 | spin_unlock(&efivars->lock); | 773 | spin_unlock_irq(&efivars->lock); |
| 766 | kfree(data); | 774 | kfree(data); |
| 767 | 775 | ||
| 768 | return efi_status_to_err(status); | 776 | return efi_status_to_err(status); |
| @@ -783,21 +791,21 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
| 783 | NULL); | 791 | NULL); |
| 784 | 792 | ||
| 785 | if (status == EFI_BUFFER_TOO_SMALL) { | 793 | if (status == EFI_BUFFER_TOO_SMALL) { |
| 786 | spin_unlock(&efivars->lock); | 794 | spin_unlock_irq(&efivars->lock); |
| 787 | mutex_lock(&inode->i_mutex); | 795 | mutex_lock(&inode->i_mutex); |
| 788 | i_size_write(inode, newdatasize + sizeof(attributes)); | 796 | i_size_write(inode, newdatasize + sizeof(attributes)); |
| 789 | mutex_unlock(&inode->i_mutex); | 797 | mutex_unlock(&inode->i_mutex); |
| 790 | 798 | ||
| 791 | } else if (status == EFI_NOT_FOUND) { | 799 | } else if (status == EFI_NOT_FOUND) { |
| 792 | list_del(&var->list); | 800 | list_del(&var->list); |
| 793 | spin_unlock(&efivars->lock); | 801 | spin_unlock_irq(&efivars->lock); |
| 794 | efivar_unregister(var); | 802 | efivar_unregister(var); |
| 795 | drop_nlink(inode); | 803 | drop_nlink(inode); |
| 796 | d_delete(file->f_dentry); | 804 | d_delete(file->f_dentry); |
| 797 | dput(file->f_dentry); | 805 | dput(file->f_dentry); |
| 798 | 806 | ||
| 799 | } else { | 807 | } else { |
| 800 | spin_unlock(&efivars->lock); | 808 | spin_unlock_irq(&efivars->lock); |
| 801 | pr_warn("efivarfs: inconsistent EFI variable implementation? " | 809 | pr_warn("efivarfs: inconsistent EFI variable implementation? " |
| 802 | "status = %lx\n", status); | 810 | "status = %lx\n", status); |
| 803 | } | 811 | } |
| @@ -819,11 +827,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, | |||
| 819 | void *data; | 827 | void *data; |
| 820 | ssize_t size = 0; | 828 | ssize_t size = 0; |
| 821 | 829 | ||
| 822 | spin_lock(&efivars->lock); | 830 | spin_lock_irq(&efivars->lock); |
| 823 | status = efivars->ops->get_variable(var->var.VariableName, | 831 | status = efivars->ops->get_variable(var->var.VariableName, |
| 824 | &var->var.VendorGuid, | 832 | &var->var.VendorGuid, |
| 825 | &attributes, &datasize, NULL); | 833 | &attributes, &datasize, NULL); |
| 826 | spin_unlock(&efivars->lock); | 834 | spin_unlock_irq(&efivars->lock); |
| 827 | 835 | ||
| 828 | if (status != EFI_BUFFER_TOO_SMALL) | 836 | if (status != EFI_BUFFER_TOO_SMALL) |
| 829 | return efi_status_to_err(status); | 837 | return efi_status_to_err(status); |
| @@ -833,12 +841,12 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, | |||
| 833 | if (!data) | 841 | if (!data) |
| 834 | return -ENOMEM; | 842 | return -ENOMEM; |
| 835 | 843 | ||
| 836 | spin_lock(&efivars->lock); | 844 | spin_lock_irq(&efivars->lock); |
| 837 | status = efivars->ops->get_variable(var->var.VariableName, | 845 | status = efivars->ops->get_variable(var->var.VariableName, |
| 838 | &var->var.VendorGuid, | 846 | &var->var.VendorGuid, |
| 839 | &attributes, &datasize, | 847 | &attributes, &datasize, |
| 840 | (data + sizeof(attributes))); | 848 | (data + sizeof(attributes))); |
| 841 | spin_unlock(&efivars->lock); | 849 | spin_unlock_irq(&efivars->lock); |
| 842 | 850 | ||
| 843 | if (status != EFI_SUCCESS) { | 851 | if (status != EFI_SUCCESS) { |
| 844 | size = efi_status_to_err(status); | 852 | size = efi_status_to_err(status); |
| @@ -966,9 +974,9 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, | |||
| 966 | goto out; | 974 | goto out; |
| 967 | 975 | ||
| 968 | kobject_uevent(&var->kobj, KOBJ_ADD); | 976 | kobject_uevent(&var->kobj, KOBJ_ADD); |
| 969 | spin_lock(&efivars->lock); | 977 | spin_lock_irq(&efivars->lock); |
| 970 | list_add(&var->list, &efivars->list); | 978 | list_add(&var->list, &efivars->list); |
| 971 | spin_unlock(&efivars->lock); | 979 | spin_unlock_irq(&efivars->lock); |
| 972 | d_instantiate(dentry, inode); | 980 | d_instantiate(dentry, inode); |
| 973 | dget(dentry); | 981 | dget(dentry); |
| 974 | out: | 982 | out: |
| @@ -985,7 +993,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) | |||
| 985 | struct efivars *efivars = var->efivars; | 993 | struct efivars *efivars = var->efivars; |
| 986 | efi_status_t status; | 994 | efi_status_t status; |
| 987 | 995 | ||
| 988 | spin_lock(&efivars->lock); | 996 | spin_lock_irq(&efivars->lock); |
| 989 | 997 | ||
| 990 | status = efivars->ops->set_variable(var->var.VariableName, | 998 | status = efivars->ops->set_variable(var->var.VariableName, |
| 991 | &var->var.VendorGuid, | 999 | &var->var.VendorGuid, |
| @@ -993,14 +1001,14 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) | |||
| 993 | 1001 | ||
| 994 | if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) { | 1002 | if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) { |
| 995 | list_del(&var->list); | 1003 | list_del(&var->list); |
| 996 | spin_unlock(&efivars->lock); | 1004 | spin_unlock_irq(&efivars->lock); |
| 997 | efivar_unregister(var); | 1005 | efivar_unregister(var); |
| 998 | drop_nlink(dentry->d_inode); | 1006 | drop_nlink(dentry->d_inode); |
| 999 | dput(dentry); | 1007 | dput(dentry); |
| 1000 | return 0; | 1008 | return 0; |
| 1001 | } | 1009 | } |
| 1002 | 1010 | ||
| 1003 | spin_unlock(&efivars->lock); | 1011 | spin_unlock_irq(&efivars->lock); |
| 1004 | return -EINVAL; | 1012 | return -EINVAL; |
| 1005 | }; | 1013 | }; |
| 1006 | 1014 | ||
| @@ -1066,13 +1074,13 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 1066 | /* copied by the above to local storage in the dentry. */ | 1074 | /* copied by the above to local storage in the dentry. */ |
| 1067 | kfree(name); | 1075 | kfree(name); |
| 1068 | 1076 | ||
| 1069 | spin_lock(&efivars->lock); | 1077 | spin_lock_irq(&efivars->lock); |
| 1070 | efivars->ops->get_variable(entry->var.VariableName, | 1078 | efivars->ops->get_variable(entry->var.VariableName, |
| 1071 | &entry->var.VendorGuid, | 1079 | &entry->var.VendorGuid, |
| 1072 | &entry->var.Attributes, | 1080 | &entry->var.Attributes, |
| 1073 | &size, | 1081 | &size, |
| 1074 | NULL); | 1082 | NULL); |
| 1075 | spin_unlock(&efivars->lock); | 1083 | spin_unlock_irq(&efivars->lock); |
| 1076 | 1084 | ||
| 1077 | mutex_lock(&inode->i_mutex); | 1085 | mutex_lock(&inode->i_mutex); |
| 1078 | inode->i_private = entry; | 1086 | inode->i_private = entry; |
| @@ -1123,7 +1131,7 @@ static int efi_pstore_open(struct pstore_info *psi) | |||
| 1123 | { | 1131 | { |
| 1124 | struct efivars *efivars = psi->data; | 1132 | struct efivars *efivars = psi->data; |
| 1125 | 1133 | ||
| 1126 | spin_lock(&efivars->lock); | 1134 | spin_lock_irq(&efivars->lock); |
| 1127 | efivars->walk_entry = list_first_entry(&efivars->list, | 1135 | efivars->walk_entry = list_first_entry(&efivars->list, |
| 1128 | struct efivar_entry, list); | 1136 | struct efivar_entry, list); |
| 1129 | return 0; | 1137 | return 0; |
| @@ -1133,7 +1141,7 @@ static int efi_pstore_close(struct pstore_info *psi) | |||
| 1133 | { | 1141 | { |
| 1134 | struct efivars *efivars = psi->data; | 1142 | struct efivars *efivars = psi->data; |
| 1135 | 1143 | ||
| 1136 | spin_unlock(&efivars->lock); | 1144 | spin_unlock_irq(&efivars->lock); |
| 1137 | return 0; | 1145 | return 0; |
| 1138 | } | 1146 | } |
| 1139 | 1147 | ||
| @@ -1209,8 +1217,18 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
| 1209 | int i, ret = 0; | 1217 | int i, ret = 0; |
| 1210 | u64 storage_space, remaining_space, max_variable_size; | 1218 | u64 storage_space, remaining_space, max_variable_size; |
| 1211 | efi_status_t status = EFI_NOT_FOUND; | 1219 | efi_status_t status = EFI_NOT_FOUND; |
| 1212 | 1220 | unsigned long flags; | |
| 1213 | spin_lock(&efivars->lock); | 1221 | |
| 1222 | if (pstore_cannot_block_path(reason)) { | ||
| 1223 | /* | ||
| 1224 | * If the lock is taken by another cpu in non-blocking path, | ||
| 1225 | * this driver returns without entering firmware to avoid | ||
| 1226 | * hanging up. | ||
| 1227 | */ | ||
| 1228 | if (!spin_trylock_irqsave(&efivars->lock, flags)) | ||
| 1229 | return -EBUSY; | ||
| 1230 | } else | ||
| 1231 | spin_lock_irqsave(&efivars->lock, flags); | ||
| 1214 | 1232 | ||
| 1215 | /* | 1233 | /* |
| 1216 | * Check if there is a space enough to log. | 1234 | * Check if there is a space enough to log. |
| @@ -1222,7 +1240,7 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
| 1222 | &remaining_space, | 1240 | &remaining_space, |
| 1223 | &max_variable_size); | 1241 | &max_variable_size); |
| 1224 | if (status || remaining_space < size + DUMP_NAME_LEN * 2) { | 1242 | if (status || remaining_space < size + DUMP_NAME_LEN * 2) { |
| 1225 | spin_unlock(&efivars->lock); | 1243 | spin_unlock_irqrestore(&efivars->lock, flags); |
| 1226 | *id = part; | 1244 | *id = part; |
| 1227 | return -ENOSPC; | 1245 | return -ENOSPC; |
| 1228 | } | 1246 | } |
| @@ -1236,13 +1254,10 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
| 1236 | efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES, | 1254 | efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES, |
| 1237 | size, psi->buf); | 1255 | size, psi->buf); |
| 1238 | 1256 | ||
| 1239 | spin_unlock(&efivars->lock); | 1257 | spin_unlock_irqrestore(&efivars->lock, flags); |
| 1240 | 1258 | ||
| 1241 | if (size) | 1259 | if (reason == KMSG_DUMP_OOPS) |
| 1242 | ret = efivar_create_sysfs_entry(efivars, | 1260 | schedule_work(&efivar_work); |
| 1243 | utf16_strsize(efi_name, | ||
| 1244 | DUMP_NAME_LEN * 2), | ||
| 1245 | efi_name, &vendor); | ||
| 1246 | 1261 | ||
| 1247 | *id = part; | 1262 | *id = part; |
| 1248 | return ret; | 1263 | return ret; |
| @@ -1263,7 +1278,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, | |||
| 1263 | sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count, | 1278 | sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count, |
| 1264 | time.tv_sec); | 1279 | time.tv_sec); |
| 1265 | 1280 | ||
| 1266 | spin_lock(&efivars->lock); | 1281 | spin_lock_irq(&efivars->lock); |
| 1267 | 1282 | ||
| 1268 | for (i = 0; i < DUMP_NAME_LEN; i++) | 1283 | for (i = 0; i < DUMP_NAME_LEN; i++) |
| 1269 | efi_name[i] = name[i]; | 1284 | efi_name[i] = name[i]; |
| @@ -1307,7 +1322,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, | |||
| 1307 | if (found) | 1322 | if (found) |
| 1308 | list_del(&found->list); | 1323 | list_del(&found->list); |
| 1309 | 1324 | ||
| 1310 | spin_unlock(&efivars->lock); | 1325 | spin_unlock_irq(&efivars->lock); |
| 1311 | 1326 | ||
| 1312 | if (found) | 1327 | if (found) |
| 1313 | efivar_unregister(found); | 1328 | efivar_unregister(found); |
| @@ -1377,7 +1392,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | |||
| 1377 | return -EINVAL; | 1392 | return -EINVAL; |
| 1378 | } | 1393 | } |
| 1379 | 1394 | ||
| 1380 | spin_lock(&efivars->lock); | 1395 | spin_lock_irq(&efivars->lock); |
| 1381 | 1396 | ||
| 1382 | /* | 1397 | /* |
| 1383 | * Does this variable already exist? | 1398 | * Does this variable already exist? |
| @@ -1395,7 +1410,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | |||
| 1395 | } | 1410 | } |
| 1396 | } | 1411 | } |
| 1397 | if (found) { | 1412 | if (found) { |
| 1398 | spin_unlock(&efivars->lock); | 1413 | spin_unlock_irq(&efivars->lock); |
| 1399 | return -EINVAL; | 1414 | return -EINVAL; |
| 1400 | } | 1415 | } |
| 1401 | 1416 | ||
| @@ -1409,10 +1424,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | |||
| 1409 | if (status != EFI_SUCCESS) { | 1424 | if (status != EFI_SUCCESS) { |
| 1410 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | 1425 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", |
| 1411 | status); | 1426 | status); |
| 1412 | spin_unlock(&efivars->lock); | 1427 | spin_unlock_irq(&efivars->lock); |
| 1413 | return -EIO; | 1428 | return -EIO; |
| 1414 | } | 1429 | } |
| 1415 | spin_unlock(&efivars->lock); | 1430 | spin_unlock_irq(&efivars->lock); |
| 1416 | 1431 | ||
| 1417 | /* Create the entry in sysfs. Locking is not required here */ | 1432 | /* Create the entry in sysfs. Locking is not required here */ |
| 1418 | status = efivar_create_sysfs_entry(efivars, | 1433 | status = efivar_create_sysfs_entry(efivars, |
| @@ -1440,7 +1455,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, | |||
| 1440 | if (!capable(CAP_SYS_ADMIN)) | 1455 | if (!capable(CAP_SYS_ADMIN)) |
| 1441 | return -EACCES; | 1456 | return -EACCES; |
| 1442 | 1457 | ||
| 1443 | spin_lock(&efivars->lock); | 1458 | spin_lock_irq(&efivars->lock); |
| 1444 | 1459 | ||
| 1445 | /* | 1460 | /* |
| 1446 | * Does this variable already exist? | 1461 | * Does this variable already exist? |
| @@ -1458,7 +1473,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, | |||
| 1458 | } | 1473 | } |
| 1459 | } | 1474 | } |
| 1460 | if (!found) { | 1475 | if (!found) { |
| 1461 | spin_unlock(&efivars->lock); | 1476 | spin_unlock_irq(&efivars->lock); |
| 1462 | return -EINVAL; | 1477 | return -EINVAL; |
| 1463 | } | 1478 | } |
| 1464 | /* force the Attributes/DataSize to 0 to ensure deletion */ | 1479 | /* 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 | if (status != EFI_SUCCESS) { | 1489 | if (status != EFI_SUCCESS) { |
| 1475 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | 1490 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", |
| 1476 | status); | 1491 | status); |
| 1477 | spin_unlock(&efivars->lock); | 1492 | spin_unlock_irq(&efivars->lock); |
| 1478 | return -EIO; | 1493 | return -EIO; |
| 1479 | } | 1494 | } |
| 1480 | list_del(&search_efivar->list); | 1495 | list_del(&search_efivar->list); |
| 1481 | /* We need to release this lock before unregistering. */ | 1496 | /* We need to release this lock before unregistering. */ |
| 1482 | spin_unlock(&efivars->lock); | 1497 | spin_unlock_irq(&efivars->lock); |
| 1483 | efivar_unregister(search_efivar); | 1498 | efivar_unregister(search_efivar); |
| 1484 | 1499 | ||
| 1485 | /* It's dead Jim.... */ | 1500 | /* It's dead Jim.... */ |
| 1486 | return count; | 1501 | return count; |
| 1487 | } | 1502 | } |
| 1488 | 1503 | ||
| 1504 | static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor) | ||
| 1505 | { | ||
| 1506 | struct efivar_entry *entry, *n; | ||
| 1507 | struct efivars *efivars = &__efivars; | ||
| 1508 | unsigned long strsize1, strsize2; | ||
| 1509 | bool found = false; | ||
| 1510 | |||
| 1511 | strsize1 = utf16_strsize(variable_name, 1024); | ||
| 1512 | list_for_each_entry_safe(entry, n, &efivars->list, list) { | ||
| 1513 | strsize2 = utf16_strsize(entry->var.VariableName, 1024); | ||
| 1514 | if (strsize1 == strsize2 && | ||
| 1515 | !memcmp(variable_name, &(entry->var.VariableName), | ||
| 1516 | strsize2) && | ||
| 1517 | !efi_guidcmp(entry->var.VendorGuid, | ||
| 1518 | *vendor)) { | ||
| 1519 | found = true; | ||
| 1520 | break; | ||
| 1521 | } | ||
| 1522 | } | ||
| 1523 | return found; | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | static void efivar_update_sysfs_entries(struct work_struct *work) | ||
| 1527 | { | ||
| 1528 | struct efivars *efivars = &__efivars; | ||
| 1529 | efi_guid_t vendor; | ||
| 1530 | efi_char16_t *variable_name; | ||
| 1531 | unsigned long variable_name_size = 1024; | ||
| 1532 | efi_status_t status = EFI_NOT_FOUND; | ||
| 1533 | bool found; | ||
| 1534 | |||
| 1535 | /* Add new sysfs entries */ | ||
| 1536 | while (1) { | ||
| 1537 | variable_name = kzalloc(variable_name_size, GFP_KERNEL); | ||
| 1538 | if (!variable_name) { | ||
| 1539 | pr_err("efivars: Memory allocation failed.\n"); | ||
| 1540 | return; | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | spin_lock_irq(&efivars->lock); | ||
| 1544 | found = false; | ||
| 1545 | while (1) { | ||
| 1546 | variable_name_size = 1024; | ||
| 1547 | status = efivars->ops->get_next_variable( | ||
| 1548 | &variable_name_size, | ||
| 1549 | variable_name, | ||
| 1550 | &vendor); | ||
| 1551 | if (status != EFI_SUCCESS) { | ||
| 1552 | break; | ||
| 1553 | } else { | ||
| 1554 | if (!variable_is_present(variable_name, | ||
| 1555 | &vendor)) { | ||
| 1556 | found = true; | ||
| 1557 | break; | ||
| 1558 | } | ||
| 1559 | } | ||
| 1560 | } | ||
| 1561 | spin_unlock_irq(&efivars->lock); | ||
| 1562 | |||
| 1563 | if (!found) { | ||
| 1564 | kfree(variable_name); | ||
| 1565 | break; | ||
| 1566 | } else | ||
| 1567 | efivar_create_sysfs_entry(efivars, | ||
| 1568 | variable_name_size, | ||
| 1569 | variable_name, &vendor); | ||
| 1570 | } | ||
| 1571 | } | ||
| 1572 | |||
| 1489 | /* | 1573 | /* |
| 1490 | * Let's not leave out systab information that snuck into | 1574 | * Let's not leave out systab information that snuck into |
| 1491 | * the efivars driver | 1575 | * the efivars driver |
| @@ -1594,9 +1678,9 @@ efivar_create_sysfs_entry(struct efivars *efivars, | |||
| 1594 | kfree(short_name); | 1678 | kfree(short_name); |
| 1595 | short_name = NULL; | 1679 | short_name = NULL; |
| 1596 | 1680 | ||
| 1597 | spin_lock(&efivars->lock); | 1681 | spin_lock_irq(&efivars->lock); |
| 1598 | list_add(&new_efivar->list, &efivars->list); | 1682 | list_add(&new_efivar->list, &efivars->list); |
| 1599 | spin_unlock(&efivars->lock); | 1683 | spin_unlock_irq(&efivars->lock); |
| 1600 | 1684 | ||
| 1601 | return 0; | 1685 | return 0; |
| 1602 | } | 1686 | } |
| @@ -1665,9 +1749,9 @@ void unregister_efivars(struct efivars *efivars) | |||
| 1665 | struct efivar_entry *entry, *n; | 1749 | struct efivar_entry *entry, *n; |
| 1666 | 1750 | ||
| 1667 | list_for_each_entry_safe(entry, n, &efivars->list, list) { | 1751 | list_for_each_entry_safe(entry, n, &efivars->list, list) { |
| 1668 | spin_lock(&efivars->lock); | 1752 | spin_lock_irq(&efivars->lock); |
| 1669 | list_del(&entry->list); | 1753 | list_del(&entry->list); |
| 1670 | spin_unlock(&efivars->lock); | 1754 | spin_unlock_irq(&efivars->lock); |
| 1671 | efivar_unregister(entry); | 1755 | efivar_unregister(entry); |
| 1672 | } | 1756 | } |
| 1673 | if (efivars->new_var) | 1757 | if (efivars->new_var) |
| @@ -1823,6 +1907,8 @@ err_put: | |||
| 1823 | static void __exit | 1907 | static void __exit |
| 1824 | efivars_exit(void) | 1908 | efivars_exit(void) |
| 1825 | { | 1909 | { |
| 1910 | cancel_work_sync(&efivar_work); | ||
| 1911 | |||
| 1826 | if (efi_enabled(EFI_RUNTIME_SERVICES)) { | 1912 | if (efi_enabled(EFI_RUNTIME_SERVICES)) { |
| 1827 | unregister_efivars(&__efivars); | 1913 | unregister_efivars(&__efivars); |
| 1828 | kobject_put(efi_kobj); | 1914 | kobject_put(efi_kobj); |
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 67de74ca85f4..e4bcb2cf055a 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c | |||
| @@ -418,9 +418,25 @@ static struct file_system_type pstore_fs_type = { | |||
| 418 | .kill_sb = pstore_kill_sb, | 418 | .kill_sb = pstore_kill_sb, |
| 419 | }; | 419 | }; |
| 420 | 420 | ||
| 421 | static struct kobject *pstore_kobj; | ||
| 422 | |||
| 421 | static int __init init_pstore_fs(void) | 423 | static int __init init_pstore_fs(void) |
| 422 | { | 424 | { |
| 423 | return register_filesystem(&pstore_fs_type); | 425 | int err = 0; |
| 426 | |||
| 427 | /* Create a convenient mount point for people to access pstore */ | ||
| 428 | pstore_kobj = kobject_create_and_add("pstore", fs_kobj); | ||
| 429 | if (!pstore_kobj) { | ||
| 430 | err = -ENOMEM; | ||
| 431 | goto out; | ||
| 432 | } | ||
| 433 | |||
| 434 | err = register_filesystem(&pstore_fs_type); | ||
| 435 | if (err < 0) | ||
| 436 | kobject_put(pstore_kobj); | ||
| 437 | |||
| 438 | out: | ||
| 439 | return err; | ||
| 424 | } | 440 | } |
| 425 | module_init(init_pstore_fs) | 441 | module_init(init_pstore_fs) |
| 426 | 442 | ||
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 5ea2e77ff023..86d1038b5a12 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c | |||
| @@ -96,6 +96,27 @@ static const char *get_reason_str(enum kmsg_dump_reason reason) | |||
| 96 | } | 96 | } |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | bool pstore_cannot_block_path(enum kmsg_dump_reason reason) | ||
| 100 | { | ||
| 101 | /* | ||
| 102 | * In case of NMI path, pstore shouldn't be blocked | ||
| 103 | * regardless of reason. | ||
| 104 | */ | ||
| 105 | if (in_nmi()) | ||
| 106 | return true; | ||
| 107 | |||
| 108 | switch (reason) { | ||
| 109 | /* In panic case, other cpus are stopped by smp_send_stop(). */ | ||
| 110 | case KMSG_DUMP_PANIC: | ||
| 111 | /* Emergency restart shouldn't be blocked by spin lock. */ | ||
| 112 | case KMSG_DUMP_EMERG: | ||
| 113 | return true; | ||
| 114 | default: | ||
| 115 | return false; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | EXPORT_SYMBOL_GPL(pstore_cannot_block_path); | ||
| 119 | |||
| 99 | /* | 120 | /* |
| 100 | * callback from kmsg_dump. (s2,l2) has the most recently | 121 | * callback from kmsg_dump. (s2,l2) has the most recently |
| 101 | * written bytes, older bytes are in (s1,l1). Save as much | 122 | * written bytes, older bytes are in (s1,l1). Save as much |
| @@ -114,10 +135,12 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
| 114 | 135 | ||
| 115 | why = get_reason_str(reason); | 136 | why = get_reason_str(reason); |
| 116 | 137 | ||
| 117 | if (in_nmi()) { | 138 | if (pstore_cannot_block_path(reason)) { |
| 118 | is_locked = spin_trylock(&psinfo->buf_lock); | 139 | is_locked = spin_trylock_irqsave(&psinfo->buf_lock, flags); |
| 119 | if (!is_locked) | 140 | if (!is_locked) { |
| 120 | pr_err("pstore dump routine blocked in NMI, may corrupt error record\n"); | 141 | pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" |
| 142 | , in_nmi() ? "NMI" : why); | ||
| 143 | } | ||
| 121 | } else | 144 | } else |
| 122 | spin_lock_irqsave(&psinfo->buf_lock, flags); | 145 | spin_lock_irqsave(&psinfo->buf_lock, flags); |
| 123 | oopscount++; | 146 | oopscount++; |
| @@ -143,9 +166,9 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
| 143 | total += hsize + len; | 166 | total += hsize + len; |
| 144 | part++; | 167 | part++; |
| 145 | } | 168 | } |
| 146 | if (in_nmi()) { | 169 | if (pstore_cannot_block_path(reason)) { |
| 147 | if (is_locked) | 170 | if (is_locked) |
| 148 | spin_unlock(&psinfo->buf_lock); | 171 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); |
| 149 | } else | 172 | } else |
| 150 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); | 173 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); |
| 151 | } | 174 | } |
diff --git a/include/linux/efi.h b/include/linux/efi.h index 7a9498ab3c2d..9bf2f1fcae27 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
| @@ -740,7 +740,8 @@ struct efivars { | |||
| 740 | * 1) ->list - adds, removals, reads, writes | 740 | * 1) ->list - adds, removals, reads, writes |
| 741 | * 2) ops.[gs]et_variable() calls. | 741 | * 2) ops.[gs]et_variable() calls. |
| 742 | * It must not be held when creating sysfs entries or calling kmalloc. | 742 | * It must not be held when creating sysfs entries or calling kmalloc. |
| 743 | * ops.get_next_variable() is only called from register_efivars(), | 743 | * ops.get_next_variable() is only called from register_efivars() |
| 744 | * or efivar_update_sysfs_entries(), | ||
| 744 | * which is protected by the BKL, so that path is safe. | 745 | * which is protected by the BKL, so that path is safe. |
| 745 | */ | 746 | */ |
| 746 | spinlock_t lock; | 747 | spinlock_t lock; |
diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 1788909d9a99..75d01760c911 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h | |||
| @@ -68,12 +68,18 @@ struct pstore_info { | |||
| 68 | 68 | ||
| 69 | #ifdef CONFIG_PSTORE | 69 | #ifdef CONFIG_PSTORE |
| 70 | extern int pstore_register(struct pstore_info *); | 70 | extern int pstore_register(struct pstore_info *); |
| 71 | extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason); | ||
| 71 | #else | 72 | #else |
| 72 | static inline int | 73 | static inline int |
| 73 | pstore_register(struct pstore_info *psi) | 74 | pstore_register(struct pstore_info *psi) |
| 74 | { | 75 | { |
| 75 | return -ENODEV; | 76 | return -ENODEV; |
| 76 | } | 77 | } |
| 78 | static inline bool | ||
| 79 | pstore_cannot_block_path(enum kmsg_dump_reason reason) | ||
| 80 | { | ||
| 81 | return false; | ||
| 82 | } | ||
| 77 | #endif | 83 | #endif |
| 78 | 84 | ||
| 79 | #endif /*_LINUX_PSTORE_H*/ | 85 | #endif /*_LINUX_PSTORE_H*/ |
