From: Anton Vorontsov <cbouatmailru-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Matthew Garrett <mjg59-1xO5oi07KQx4cg9Nei1l7Q@public.gmane.org>,
Tony Luck <tony.luck-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>,
Kees Cook <keescook-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>,
Matt Fleming
<matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Subject: Re: EFI pstore: BUG: scheduling while atomic, and possible circular locking dependency
Date: Thu, 22 Nov 2012 02:07:19 -0800 [thread overview]
Message-ID: <20121122100719.GA1687@lizard> (raw)
In-Reply-To: <50ADD509.2060800-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
On Thu, Nov 22, 2012 at 03:32:25PM +0800, Lingzhu Xiang wrote:
[...]
> efi_pstore_read stops trying to kmalloc(GFP_KERNEL), but some others still do.
>
> [ 38.185217] BUG: sleeping function called from invalid context at mm/slub.c:930
> [ 38.186584] in_atomic(): 1, irqs_disabled(): 0, pid: 852, name: mount
> [ 38.187749] 3 locks held by mount/852:
> [ 38.188457] #0: (&type->s_umount_key#38/1){+.+.+.}, at: [<ffffffff811d0cce>] sget+0x3ae/0x670
> [ 38.190208] #1: (&psinfo->read_mutex){+.+.+.}, at: [<ffffffff812c060b>] pstore_get_records+0x3b/0x130
> [ 38.191956] #2: (&(&efivars->lock)->rlock){+.+.+.}, at: [<ffffffff8154e55d>] efi_pstore_open+0x1d/0x40
Ugh. It really should not leave spinlocks locked after returning from
open(). That's because pstore itself does sleeping stuff after ->open().
So, I guess efivars's pstore part needs a complete rework.
In it current design, the read routine has to use rcu lock-less technique,
and we need a really ugly hack in the sysfs routine to make write actually
work. This is because the efi's sysfs routine is responsible for adding
variables to a list, not just for adding variables to sysfs hierarchy.
The down below is a patch to give an idea. It might happen to work on
adding and reading the dumps, but it surely won't work on removing things.
I didn't test it.
Anyway, it's not pstore's core issue, it's purely EFI which makes things
messy, so EFI maintainers will need to continue this, as I really have no
time currently.
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index d10c987..7327a6d 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -144,7 +144,8 @@ static int
efivar_create_sysfs_entry(struct efivars *efivars,
unsigned long variable_name_size,
efi_char16_t *variable_name,
- efi_guid_t *vendor_guid);
+ efi_guid_t *vendor_guid,
+ gfp_t gfp);
/* Return the number of unicode characters in data */
static unsigned long
@@ -643,17 +644,20 @@ static int efi_pstore_open(struct pstore_info *psi)
{
struct efivars *efivars = psi->data;
- spin_lock(&efivars->lock);
- efivars->walk_entry = list_first_entry(&efivars->list,
- struct efivar_entry, list);
+ rcu_read_lock();
+ efivars->walk_entry = list_first_or_null_rcu(&efivars->list,
+ struct efivar_entry,
+ list);
+ if (!efivars->walk_entry) {
+ rcu_read_unlock();
+ return -ENODATA;
+ }
return 0;
}
static int efi_pstore_close(struct pstore_info *psi)
{
- struct efivars *efivars = psi->data;
-
- spin_unlock(&efivars->lock);
+ rcu_read_unlock();
return 0;
}
@@ -661,38 +665,43 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
struct timespec *timespec,
char **buf, struct pstore_info *psi)
{
- efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
struct efivars *efivars = psi->data;
- char name[DUMP_NAME_LEN];
- int i;
- unsigned int part, size;
- unsigned long time;
-
- while (&efivars->walk_entry->list != &efivars->list) {
- if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
- vendor)) {
- for (i = 0; i < DUMP_NAME_LEN; i++) {
- name[i] = efivars->walk_entry->var.VariableName[i];
- }
- if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
- *id = part;
- timespec->tv_sec = time;
- timespec->tv_nsec = 0;
- get_var_data_locked(efivars, &efivars->walk_entry->var);
- size = efivars->walk_entry->var.DataSize;
- *buf = kmalloc(size, GFP_KERNEL);
- if (*buf == NULL)
- return -ENOMEM;
- memcpy(*buf, efivars->walk_entry->var.Data,
- size);
- efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
- struct efivar_entry, list);
- return size;
- }
- }
- efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
- struct efivar_entry, list);
+ struct efivar_entry *entry = efivars->walk_entry;
+
+ list_for_each_entry_continue_rcu(entry, &efivars->list, list) {
+ efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+ char name[DUMP_NAME_LEN];
+ int i;
+ unsigned int part;
+ unsigned int size;
+ unsigned long time;
+
+ if (efi_guidcmp(entry->var.VendorGuid, vendor))
+ continue;
+
+ for (i = 0; i < DUMP_NAME_LEN; i++)
+ name[i] = entry->var.VariableName[i];
+
+ if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) != 3)
+ continue;
+
+ *id = part;
+ timespec->tv_sec = time;
+ timespec->tv_nsec = 0;
+
+ get_var_data_locked(efivars, &entry->var);
+ size = entry->var.DataSize;
+ if (!size)
+ return -ENODATA;
+
+ *buf = kmalloc(size, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+
+ memcpy(*buf, entry->var.Data, size);
+ return size;
}
+
return 0;
}
@@ -741,7 +750,7 @@ static int efi_pstore_write(enum pstore_type_id type,
}
if (found)
- list_del(&found->list);
+ list_del_rcu(&found->list);
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i];
@@ -753,12 +762,11 @@ static int efi_pstore_write(enum pstore_type_id type,
if (found)
efivar_unregister(found);
-
if (size)
ret = efivar_create_sysfs_entry(efivars,
utf16_strsize(efi_name,
DUMP_NAME_LEN * 2),
- efi_name, &vendor);
+ efi_name, &vendor, GFP_ATOMIC);
*id = part;
return ret;
@@ -875,7 +883,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
utf16_strsize(new_var->VariableName,
1024),
new_var->VariableName,
- &new_var->VendorGuid);
+ &new_var->VendorGuid,
+ GFP_KERNEL);
if (status) {
printk(KERN_WARNING "efivars: variable created, but sysfs entry wasn't.\n");
}
@@ -933,7 +942,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
spin_unlock(&efivars->lock);
return -EIO;
}
- list_del(&search_efivar->list);
+ list_del_rcu(&search_efivar->list);
/* We need to release this lock before unregistering. */
spin_unlock(&efivars->lock);
efivar_unregister(search_efivar);
@@ -999,14 +1008,15 @@ static int
efivar_create_sysfs_entry(struct efivars *efivars,
unsigned long variable_name_size,
efi_char16_t *variable_name,
- efi_guid_t *vendor_guid)
+ efi_guid_t *vendor_guid,
+ gfp_t gfp)
{
int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38;
char *short_name;
struct efivar_entry *new_efivar;
- short_name = kzalloc(short_name_size + 1, GFP_KERNEL);
- new_efivar = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
+ short_name = kzalloc(short_name_size + 1, gfp);
+ new_efivar = kzalloc(sizeof(struct efivar_entry), gfp);
if (!short_name || !new_efivar) {
kfree(short_name);
@@ -1018,6 +1028,8 @@ efivar_create_sysfs_entry(struct efivars *efivars,
memcpy(new_efivar->var.VariableName, variable_name,
variable_name_size);
memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t));
+ if (gfp == GFP_ATOMIC)
+ goto just_add;
/* Convert Unicode to normal chars (assume top bits are 0),
ala UTF-8 */
@@ -1040,11 +1052,12 @@ efivar_create_sysfs_entry(struct efivars *efivars,
}
kobject_uevent(&new_efivar->kobj, KOBJ_ADD);
+just_add:
kfree(short_name);
short_name = NULL;
spin_lock(&efivars->lock);
- list_add(&new_efivar->list, &efivars->list);
+ list_add_tail_rcu(&new_efivar->list, &efivars->list);
spin_unlock(&efivars->lock);
return 0;
@@ -1115,7 +1128,7 @@ void unregister_efivars(struct efivars *efivars)
list_for_each_entry_safe(entry, n, &efivars->list, list) {
spin_lock(&efivars->lock);
- list_del(&entry->list);
+ list_del_rcu(&entry->list);
spin_unlock(&efivars->lock);
efivar_unregister(entry);
}
@@ -1172,7 +1185,8 @@ int register_efivars(struct efivars *efivars,
efivar_create_sysfs_entry(efivars,
variable_name_size,
variable_name,
- &vendor_guid);
+ &vendor_guid,
+ GFP_KERNEL);
break;
case EFI_NOT_FOUND:
break;
next prev parent reply other threads:[~2012-11-22 10:07 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-11-22 2:57 EFI pstore: BUG: scheduling while atomic, and possible circular locking dependency Lingzhu Xiang
[not found] ` <50AD94A4.4030100-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2012-11-22 4:12 ` Anton Vorontsov
[not found] ` <20121122041239.GA24623-SAfYLu58TvsVgZ49a2IoEzcLetGT9WKNKwcig+XE9tjR7s880joybQ@public.gmane.org>
2012-11-22 7:32 ` Lingzhu Xiang
[not found] ` <50ADD509.2060800-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2012-11-22 10:07 ` Anton Vorontsov [this message]
2012-11-26 17:06 ` Seiji Aguchi
[not found] ` <A5ED84D3BB3A384992CBB9C77DEDA4D4149FA32A-ohthHghroY0jroPwUH3sq+6wyyQG6/Uh@public.gmane.org>
2012-11-26 17:50 ` Matt Fleming
2013-04-12 11:54 ` Lingzhu Xiang
[not found] ` <5167F5DE.8070804-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-04-13 14:40 ` Seiji Aguchi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20121122100719.GA1687@lizard \
--to=cbouatmailru-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
--cc=keescook-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org \
--cc=linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org \
--cc=matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org \
--cc=mjg59-1xO5oi07KQx4cg9Nei1l7Q@public.gmane.org \
--cc=tony.luck-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox