linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
@ 2013-10-11 18:29 Seiji Aguchi
  2013-10-17  5:54 ` Madper Xie
  2013-10-17 13:18 ` Matt Fleming
  0 siblings, 2 replies; 10+ messages in thread
From: Seiji Aguchi @ 2013-10-11 18:29 UTC (permalink / raw)
  To: linux-kernel, linux-efi, matt.fleming, tony.luck
  Cc: tomoki.sekiyama, dle-develop

Change from v2:
- Move dynamic memory allocation to efi_pstore_read() before holding
  efivars->lock to protect entry->var.Data.
- Access to entry->scanning while holding efivars->lock.
- Move a comment about a returned value from efi_pstore_read_func() to
  efi_pstore_read() because "size < 0" case may happen in efi_pstore_read().

Currently, when mounting pstore file system, a read callback of efi_pstore
driver runs mutiple times as below.

- In the first read callback, scan efivar_sysfs_list from head and pass
  a kmsg buffer of a entry to an upper pstore layer.
- In the second read callback, rescan efivar_sysfs_list from the entry and pass
  another kmsg buffer to it.
- Repeat the scan and pass until the end of efivar_sysfs_list.

In this process, an entry is read across the multiple read function calls.
To avoid race between the read and erasion, the whole process above is
protected by a spinlock, holding in open() and releasing in close().

At the same time, kmemdup() is called to pass the buffer to pstore filesystem
during it.
And then, it causes a following lockdep warning.

To make the dynamic memory allocation runnable without taking spinlock,
holding off a deletion of sysfs entry if it happens while scanning it
via efi_pstore, and deleting it after the scan is completed.

To implement it, this patch introduces two flags, scanning and deleting,
to efivar_entry.

[    1.143710] ------------[ cut here ]------------
[    1.144058] WARNING: CPU: 1 PID: 1 at kernel/lockdep.c:2740
lockdep_trace_alloc+0x104/0x110()
[    1.144058] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
[    1.144058] Modules linked in:

[    1.144058] CPU: 1 PID: 1 Comm: systemd Not tainted 3.11.0-rc5 #2
[    1.144058]  0000000000000009 ffff8800797e9ae0 ffffffff816614a5
ffff8800797e9b28
[    1.144058]  ffff8800797e9b18 ffffffff8105510d 0000000000000080
0000000000000046
[    1.144058]  00000000000000d0 00000000000003af ffffffff81ccd0c0
ffff8800797e9b78
[    1.144058] Call Trace:
[    1.144058]  [<ffffffff816614a5>] dump_stack+0x54/0x74
[    1.144058]  [<ffffffff8105510d>] warn_slowpath_common+0x7d/0xa0
[    1.144058]  [<ffffffff8105517c>] warn_slowpath_fmt+0x4c/0x50
[    1.144058]  [<ffffffff8131290f>] ? vsscanf+0x57f/0x7b0
[    1.144058]  [<ffffffff810bbd74>] lockdep_trace_alloc+0x104/0x110
[    1.144058]  [<ffffffff81192da0>] __kmalloc_track_caller+0x50/0x280
[    1.144058]  [<ffffffff815147bb>] ?
efi_pstore_read_func.part.1+0x12b/0x170
[    1.144058]  [<ffffffff8115b260>] kmemdup+0x20/0x50
[    1.144058]  [<ffffffff815147bb>] efi_pstore_read_func.part.1+0x12b/0x170
[    1.144058]  [<ffffffff81514800>] ?
efi_pstore_read_func.part.1+0x170/0x170
[    1.144058]  [<ffffffff815148b4>] efi_pstore_read_func+0xb4/0xe0
[    1.144058]  [<ffffffff81512b7b>] __efivar_entry_iter+0xfb/0x120
[    1.144058]  [<ffffffff8151428f>] efi_pstore_read+0x3f/0x50
[    1.144058]  [<ffffffff8128d7ba>] pstore_get_records+0x9a/0x150
[    1.158207]  [<ffffffff812af25c>] ? selinux_d_instantiate+0x1c/0x20
[    1.158207]  [<ffffffff8128ce30>] ? parse_options+0x80/0x80
[    1.158207]  [<ffffffff8128ced5>] pstore_fill_super+0xa5/0xc0
[    1.158207]  [<ffffffff811ae7d2>] mount_single+0xa2/0xd0
[    1.158207]  [<ffffffff8128ccf8>] pstore_mount+0x18/0x20
[    1.158207]  [<ffffffff811ae8b9>] mount_fs+0x39/0x1b0
[    1.158207]  [<ffffffff81160550>] ? __alloc_percpu+0x10/0x20
[    1.158207]  [<ffffffff811c9493>] vfs_kern_mount+0x63/0xf0
[    1.158207]  [<ffffffff811cbb0e>] do_mount+0x23e/0xa20
[    1.158207]  [<ffffffff8115b51b>] ? strndup_user+0x4b/0xf0
[    1.158207]  [<ffffffff811cc373>] SyS_mount+0x83/0xc0
[    1.158207]  [<ffffffff81673cc2>] system_call_fastpath+0x16/0x1b
[    1.158207] ---[ end trace 61981bc62de9f6f4 ]---

Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
---
 drivers/firmware/efi/efi-pstore.c | 143 +++++++++++++++++++++++++++++++++++---
 drivers/firmware/efi/efivars.c    |  12 ++--
 drivers/firmware/efi/vars.c       |  12 +++-
 include/linux/efi.h               |   2 +
 4 files changed, 153 insertions(+), 16 deletions(-)

diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
index 5002d50..ebd5dbc 100644
--- a/drivers/firmware/efi/efi-pstore.c
+++ b/drivers/firmware/efi/efi-pstore.c
@@ -18,14 +18,12 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
 
 static int efi_pstore_open(struct pstore_info *psi)
 {
-	efivar_entry_iter_begin();
 	psi->data = NULL;
 	return 0;
 }
 
 static int efi_pstore_close(struct pstore_info *psi)
 {
-	efivar_entry_iter_end();
 	psi->data = NULL;
 	return 0;
 }
@@ -91,19 +89,125 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
 	__efivar_entry_get(entry, &entry->var.Attributes,
 			   &entry->var.DataSize, entry->var.Data);
 	size = entry->var.DataSize;
+	memcpy(*cb_data->buf, entry->var.Data, (size_t)min_t(unsigned long,
+							     1024, size));
 
-	*cb_data->buf = kmemdup(entry->var.Data, size, GFP_KERNEL);
-	if (*cb_data->buf == NULL)
-		return -ENOMEM;
 	return size;
 }
 
+/**
+ * efi_pstore_scan_sysfs_enter
+ * @entry: scanning entry
+ * @next: next entry
+ * @head: list head
+ */
+static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
+					struct efivar_entry *next,
+					struct list_head *head)
+{
+	pos->scanning = true;
+	if (&next->list != head)
+		next->scanning = true;
+}
+
+/**
+ * __efi_pstore_scan_sysfs_exit
+ * @entry: deleting entry
+ * @turn_off_scanning: Check if a scanning flag should be turned off
+ */
+static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
+						bool turn_off_scanning)
+{
+	if (entry->deleting) {
+		list_del(&entry->list);
+		efivar_entry_iter_end();
+		efivar_unregister(entry);
+		efivar_entry_iter_begin();
+	} else if (turn_off_scanning)
+		entry->scanning = false;
+}
+
+/**
+ * efi_pstore_scan_sysfs_exit
+ * @pos: scanning entry
+ * @next: next entry
+ * @head: list head
+ * @stop: a flag checking if scanning will stop
+ */
+static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
+				       struct efivar_entry *next,
+				       struct list_head *head, bool stop)
+{
+	__efi_pstore_scan_sysfs_exit(pos, true);
+	if (stop)
+		__efi_pstore_scan_sysfs_exit(next, &next->list != head);
+}
+
+/**
+ * efi_pstore_sysfs_entry_iter
+ *
+ * @data: function-specific data to pass to callback
+ * @pos: entry to begin iterating from
+ *
+ * You MUST call efivar_enter_iter_begin() before this function, and
+ * efivar_entry_iter_end() afterwards.
+ *
+ * It is possible to begin iteration from an arbitrary entry within
+ * the list by passing @pos. @pos is updated on return to point to
+ * the next entry of the last one passed to efi_pstore_read_func().
+ * To begin iterating from the beginning of the list @pos must be %NULL.
+ */
+static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
+{
+	struct efivar_entry *entry, *n;
+	struct list_head *head = &efivar_sysfs_list;
+	int size = 0;
+
+	if (!*pos) {
+		list_for_each_entry_safe(entry, n, head, list) {
+			efi_pstore_scan_sysfs_enter(entry, n, head);
+
+			size = efi_pstore_read_func(entry, data);
+			efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
+			if (size)
+				break;
+		}
+		*pos = n;
+		return size;
+	}
+
+	list_for_each_entry_safe_from((*pos), n, head, list) {
+		efi_pstore_scan_sysfs_enter((*pos), n, head);
+
+		size = efi_pstore_read_func((*pos), data);
+		efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
+		if (size)
+			break;
+	}
+	*pos = n;
+	return size;
+}
+
+/**
+ * efi_pstore_read
+ *
+ * This function returns a size of NVRAM entry logged via efi_pstore_write().
+ * The meaning and behavior of efi_pstore/pstore are as below.
+ *
+ * size > 0: Got data of an entry logged via efi_pstore_write() successfully,
+ *           and pstore filesystem will continue reading subsequent entries.
+ * size == 0: Entry was not logged via efi_pstore_write(),
+ *            and efi_pstore driver will continue reading subsequent entries.
+ * size < 0: Failed to get data of entry logging via efi_pstore_write(),
+ *           and pstore will stop reading entry.
+ */
 static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
 			       int *count, struct timespec *timespec,
 			       char **buf, bool *compressed,
 			       struct pstore_info *psi)
 {
 	struct pstore_read_data data;
+	ssize_t size;
 
 	data.id = id;
 	data.type = type;
@@ -112,8 +216,17 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
 	data.compressed = compressed;
 	data.buf = buf;
 
-	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
-				   (struct efivar_entry **)&psi->data);
+	*data.buf = kzalloc(1024, GFP_KERNEL);
+	if (!*data.buf)
+		return -ENOMEM;
+
+	efivar_entry_iter_begin();
+	size = efi_pstore_sysfs_entry_iter(&data,
+					   (struct efivar_entry **)&psi->data);
+	efivar_entry_iter_end();
+	if (size <= 0)
+		kfree(*data.buf);
+	return size;
 }
 
 static int efi_pstore_write(enum pstore_type_id type,
@@ -184,9 +297,17 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
 			return 0;
 	}
 
+	if (entry->scanning) {
+		/*
+		 * Skip deletion because this entry will be deleted
+		 * after scanning is completed.
+		 */
+		entry->deleting = true;
+	} else
+		list_del(&entry->list);
+
 	/* found */
 	__efivar_entry_delete(entry);
-	list_del(&entry->list);
 
 	return 1;
 }
@@ -214,10 +335,12 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
 
 	efivar_entry_iter_begin();
 	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
-	efivar_entry_iter_end();
 
-	if (found)
+	if (found && !entry->scanning) {
+		efivar_entry_iter_end();
 		efivar_unregister(entry);
+	} else
+		efivar_entry_iter_end();
 
 	return 0;
 }
diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
index 8a7432a..8c5a61a 100644
--- a/drivers/firmware/efi/efivars.c
+++ b/drivers/firmware/efi/efivars.c
@@ -383,12 +383,16 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
 	else if (__efivar_entry_delete(entry))
 		err = -EIO;
 
-	efivar_entry_iter_end();
-
-	if (err)
+	if (err) {
+		efivar_entry_iter_end();
 		return err;
+	}
 
-	efivar_unregister(entry);
+	if (!entry->scanning) {
+		efivar_entry_iter_end();
+		efivar_unregister(entry);
+	} else
+		efivar_entry_iter_end();
 
 	/* It's dead Jim.... */
 	return count;
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index 391c67b..b22659c 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -683,8 +683,16 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
 	if (!found)
 		return NULL;
 
-	if (remove)
-		list_del(&entry->list);
+	if (remove) {
+		if (entry->scanning) {
+			/*
+			 * The entry will be deleted
+			 * after scanning is completed.
+			 */
+			entry->deleting = true;
+		} else
+			list_del(&entry->list);
+	}
 
 	return entry;
 }
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 5f8f176..04088fb 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -782,6 +782,8 @@ struct efivar_entry {
 	struct efi_variable var;
 	struct list_head list;
 	struct kobject kobj;
+	bool scanning;
+	bool deleting;
 };
 
 extern struct list_head efivar_sysfs_list;
-- 
1.8.2.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-11 18:29 [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed Seiji Aguchi
@ 2013-10-17  5:54 ` Madper Xie
  2013-10-17 13:07   ` Seiji Aguchi
  2013-10-17 13:18 ` Matt Fleming
  1 sibling, 1 reply; 10+ messages in thread
From: Madper Xie @ 2013-10-17  5:54 UTC (permalink / raw)
  To: Seiji Aguchi
  Cc: linux-kernel, linux-efi, matt.fleming, tony.luck,
	tomoki.sekiyama, dle-develop

Howdy Seiji,
  I failed appily this patch to both 3.12-rc2 and 3.12-rc4. Could you
  please let me know which is the right tree for this patch?

  Thanks,
  Madper.
seiji.aguchi@hds.com writes:

> Change from v2:
> - Move dynamic memory allocation to efi_pstore_read() before holding
>   efivars->lock to protect entry->var.Data.
> - Access to entry->scanning while holding efivars->lock.
> - Move a comment about a returned value from efi_pstore_read_func() to
>   efi_pstore_read() because "size < 0" case may happen in efi_pstore_read().
>
> Currently, when mounting pstore file system, a read callback of efi_pstore
> driver runs mutiple times as below.
>
> - In the first read callback, scan efivar_sysfs_list from head and pass
>   a kmsg buffer of a entry to an upper pstore layer.
> - In the second read callback, rescan efivar_sysfs_list from the entry and pass
>   another kmsg buffer to it.
> - Repeat the scan and pass until the end of efivar_sysfs_list.
>
> In this process, an entry is read across the multiple read function calls.
> To avoid race between the read and erasion, the whole process above is
> protected by a spinlock, holding in open() and releasing in close().
>
> At the same time, kmemdup() is called to pass the buffer to pstore filesystem
> during it.
> And then, it causes a following lockdep warning.
>
> To make the dynamic memory allocation runnable without taking spinlock,
> holding off a deletion of sysfs entry if it happens while scanning it
> via efi_pstore, and deleting it after the scan is completed.
>
> To implement it, this patch introduces two flags, scanning and deleting,
> to efivar_entry.
>
> [    1.143710] ------------[ cut here ]------------
> [    1.144058] WARNING: CPU: 1 PID: 1 at kernel/lockdep.c:2740
> lockdep_trace_alloc+0x104/0x110()
> [    1.144058] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
> [    1.144058] Modules linked in:
>
> [    1.144058] CPU: 1 PID: 1 Comm: systemd Not tainted 3.11.0-rc5 #2
> [    1.144058]  0000000000000009 ffff8800797e9ae0 ffffffff816614a5
> ffff8800797e9b28
> [    1.144058]  ffff8800797e9b18 ffffffff8105510d 0000000000000080
> 0000000000000046
> [    1.144058]  00000000000000d0 00000000000003af ffffffff81ccd0c0
> ffff8800797e9b78
> [    1.144058] Call Trace:
> [    1.144058]  [<ffffffff816614a5>] dump_stack+0x54/0x74
> [    1.144058]  [<ffffffff8105510d>] warn_slowpath_common+0x7d/0xa0
> [    1.144058]  [<ffffffff8105517c>] warn_slowpath_fmt+0x4c/0x50
> [    1.144058]  [<ffffffff8131290f>] ? vsscanf+0x57f/0x7b0
> [    1.144058]  [<ffffffff810bbd74>] lockdep_trace_alloc+0x104/0x110
> [    1.144058]  [<ffffffff81192da0>] __kmalloc_track_caller+0x50/0x280
> [    1.144058]  [<ffffffff815147bb>] ?
> efi_pstore_read_func.part.1+0x12b/0x170
> [    1.144058]  [<ffffffff8115b260>] kmemdup+0x20/0x50
> [    1.144058]  [<ffffffff815147bb>] efi_pstore_read_func.part.1+0x12b/0x170
> [    1.144058]  [<ffffffff81514800>] ?
> efi_pstore_read_func.part.1+0x170/0x170
> [    1.144058]  [<ffffffff815148b4>] efi_pstore_read_func+0xb4/0xe0
> [    1.144058]  [<ffffffff81512b7b>] __efivar_entry_iter+0xfb/0x120
> [    1.144058]  [<ffffffff8151428f>] efi_pstore_read+0x3f/0x50
> [    1.144058]  [<ffffffff8128d7ba>] pstore_get_records+0x9a/0x150
> [    1.158207]  [<ffffffff812af25c>] ? selinux_d_instantiate+0x1c/0x20
> [    1.158207]  [<ffffffff8128ce30>] ? parse_options+0x80/0x80
> [    1.158207]  [<ffffffff8128ced5>] pstore_fill_super+0xa5/0xc0
> [    1.158207]  [<ffffffff811ae7d2>] mount_single+0xa2/0xd0
> [    1.158207]  [<ffffffff8128ccf8>] pstore_mount+0x18/0x20
> [    1.158207]  [<ffffffff811ae8b9>] mount_fs+0x39/0x1b0
> [    1.158207]  [<ffffffff81160550>] ? __alloc_percpu+0x10/0x20
> [    1.158207]  [<ffffffff811c9493>] vfs_kern_mount+0x63/0xf0
> [    1.158207]  [<ffffffff811cbb0e>] do_mount+0x23e/0xa20
> [    1.158207]  [<ffffffff8115b51b>] ? strndup_user+0x4b/0xf0
> [    1.158207]  [<ffffffff811cc373>] SyS_mount+0x83/0xc0
> [    1.158207]  [<ffffffff81673cc2>] system_call_fastpath+0x16/0x1b
> [    1.158207] ---[ end trace 61981bc62de9f6f4 ]---
>
> Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
> ---
>  drivers/firmware/efi/efi-pstore.c | 143 +++++++++++++++++++++++++++++++++++---
>  drivers/firmware/efi/efivars.c    |  12 ++--
>  drivers/firmware/efi/vars.c       |  12 +++-
>  include/linux/efi.h               |   2 +
>  4 files changed, 153 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
> index 5002d50..ebd5dbc 100644
> --- a/drivers/firmware/efi/efi-pstore.c
> +++ b/drivers/firmware/efi/efi-pstore.c
> @@ -18,14 +18,12 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
>  
>  static int efi_pstore_open(struct pstore_info *psi)
>  {
> -	efivar_entry_iter_begin();
>  	psi->data = NULL;
>  	return 0;
>  }
>  
>  static int efi_pstore_close(struct pstore_info *psi)
>  {
> -	efivar_entry_iter_end();
>  	psi->data = NULL;
>  	return 0;
>  }
> @@ -91,19 +89,125 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
>  	__efivar_entry_get(entry, &entry->var.Attributes,
>  			   &entry->var.DataSize, entry->var.Data);
>  	size = entry->var.DataSize;
> +	memcpy(*cb_data->buf, entry->var.Data, (size_t)min_t(unsigned long,
> +							     1024, size));
>  
> -	*cb_data->buf = kmemdup(entry->var.Data, size, GFP_KERNEL);
> -	if (*cb_data->buf == NULL)
> -		return -ENOMEM;
>  	return size;
>  }
>  
> +/**
> + * efi_pstore_scan_sysfs_enter
> + * @entry: scanning entry
> + * @next: next entry
> + * @head: list head
> + */
> +static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
> +					struct efivar_entry *next,
> +					struct list_head *head)
> +{
> +	pos->scanning = true;
> +	if (&next->list != head)
> +		next->scanning = true;
> +}
> +
> +/**
> + * __efi_pstore_scan_sysfs_exit
> + * @entry: deleting entry
> + * @turn_off_scanning: Check if a scanning flag should be turned off
> + */
> +static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
> +						bool turn_off_scanning)
> +{
> +	if (entry->deleting) {
> +		list_del(&entry->list);
> +		efivar_entry_iter_end();
> +		efivar_unregister(entry);
> +		efivar_entry_iter_begin();
> +	} else if (turn_off_scanning)
> +		entry->scanning = false;
> +}
> +
> +/**
> + * efi_pstore_scan_sysfs_exit
> + * @pos: scanning entry
> + * @next: next entry
> + * @head: list head
> + * @stop: a flag checking if scanning will stop
> + */
> +static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
> +				       struct efivar_entry *next,
> +				       struct list_head *head, bool stop)
> +{
> +	__efi_pstore_scan_sysfs_exit(pos, true);
> +	if (stop)
> +		__efi_pstore_scan_sysfs_exit(next, &next->list != head);
> +}
> +
> +/**
> + * efi_pstore_sysfs_entry_iter
> + *
> + * @data: function-specific data to pass to callback
> + * @pos: entry to begin iterating from
> + *
> + * You MUST call efivar_enter_iter_begin() before this function, and
> + * efivar_entry_iter_end() afterwards.
> + *
> + * It is possible to begin iteration from an arbitrary entry within
> + * the list by passing @pos. @pos is updated on return to point to
> + * the next entry of the last one passed to efi_pstore_read_func().
> + * To begin iterating from the beginning of the list @pos must be %NULL.
> + */
> +static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
> +{
> +	struct efivar_entry *entry, *n;
> +	struct list_head *head = &efivar_sysfs_list;
> +	int size = 0;
> +
> +	if (!*pos) {
> +		list_for_each_entry_safe(entry, n, head, list) {
> +			efi_pstore_scan_sysfs_enter(entry, n, head);
> +
> +			size = efi_pstore_read_func(entry, data);
> +			efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
> +			if (size)
> +				break;
> +		}
> +		*pos = n;
> +		return size;
> +	}
> +
> +	list_for_each_entry_safe_from((*pos), n, head, list) {
> +		efi_pstore_scan_sysfs_enter((*pos), n, head);
> +
> +		size = efi_pstore_read_func((*pos), data);
> +		efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
> +		if (size)
> +			break;
> +	}
> +	*pos = n;
> +	return size;
> +}
> +
> +/**
> + * efi_pstore_read
> + *
> + * This function returns a size of NVRAM entry logged via efi_pstore_write().
> + * The meaning and behavior of efi_pstore/pstore are as below.
> + *
> + * size > 0: Got data of an entry logged via efi_pstore_write() successfully,
> + *           and pstore filesystem will continue reading subsequent entries.
> + * size == 0: Entry was not logged via efi_pstore_write(),
> + *            and efi_pstore driver will continue reading subsequent entries.
> + * size < 0: Failed to get data of entry logging via efi_pstore_write(),
> + *           and pstore will stop reading entry.
> + */
>  static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
>  			       int *count, struct timespec *timespec,
>  			       char **buf, bool *compressed,
>  			       struct pstore_info *psi)
>  {
>  	struct pstore_read_data data;
> +	ssize_t size;
>  
>  	data.id = id;
>  	data.type = type;
> @@ -112,8 +216,17 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
>  	data.compressed = compressed;
>  	data.buf = buf;
>  
> -	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
> -				   (struct efivar_entry **)&psi->data);
> +	*data.buf = kzalloc(1024, GFP_KERNEL);
> +	if (!*data.buf)
> +		return -ENOMEM;
> +
> +	efivar_entry_iter_begin();
> +	size = efi_pstore_sysfs_entry_iter(&data,
> +					   (struct efivar_entry **)&psi->data);
> +	efivar_entry_iter_end();
> +	if (size <= 0)
> +		kfree(*data.buf);
> +	return size;
>  }
>  
>  static int efi_pstore_write(enum pstore_type_id type,
> @@ -184,9 +297,17 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
>  			return 0;
>  	}
>  
> +	if (entry->scanning) {
> +		/*
> +		 * Skip deletion because this entry will be deleted
> +		 * after scanning is completed.
> +		 */
> +		entry->deleting = true;
> +	} else
> +		list_del(&entry->list);
> +
>  	/* found */
>  	__efivar_entry_delete(entry);
> -	list_del(&entry->list);
>  
>  	return 1;
>  }
> @@ -214,10 +335,12 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
>  
>  	efivar_entry_iter_begin();
>  	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
> -	efivar_entry_iter_end();
>  
> -	if (found)
> +	if (found && !entry->scanning) {
> +		efivar_entry_iter_end();
>  		efivar_unregister(entry);
> +	} else
> +		efivar_entry_iter_end();
>  
>  	return 0;
>  }
> diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
> index 8a7432a..8c5a61a 100644
> --- a/drivers/firmware/efi/efivars.c
> +++ b/drivers/firmware/efi/efivars.c
> @@ -383,12 +383,16 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
>  	else if (__efivar_entry_delete(entry))
>  		err = -EIO;
>  
> -	efivar_entry_iter_end();
> -
> -	if (err)
> +	if (err) {
> +		efivar_entry_iter_end();
>  		return err;
> +	}
>  
> -	efivar_unregister(entry);
> +	if (!entry->scanning) {
> +		efivar_entry_iter_end();
> +		efivar_unregister(entry);
> +	} else
> +		efivar_entry_iter_end();
>  
>  	/* It's dead Jim.... */
>  	return count;
> diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
> index 391c67b..b22659c 100644
> --- a/drivers/firmware/efi/vars.c
> +++ b/drivers/firmware/efi/vars.c
> @@ -683,8 +683,16 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
>  	if (!found)
>  		return NULL;
>  
> -	if (remove)
> -		list_del(&entry->list);
> +	if (remove) {
> +		if (entry->scanning) {
> +			/*
> +			 * The entry will be deleted
> +			 * after scanning is completed.
> +			 */
> +			entry->deleting = true;
> +		} else
> +			list_del(&entry->list);
> +	}
>  
>  	return entry;
>  }
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 5f8f176..04088fb 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -782,6 +782,8 @@ struct efivar_entry {
>  	struct efi_variable var;
>  	struct list_head list;
>  	struct kobject kobj;
> +	bool scanning;
> +	bool deleting;
>  };
>  
>  extern struct list_head efivar_sysfs_list;


-- 
Best,
Madper Xie.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-17  5:54 ` Madper Xie
@ 2013-10-17 13:07   ` Seiji Aguchi
  2013-10-17 14:30     ` Madper Xie
  0 siblings, 1 reply; 10+ messages in thread
From: Seiji Aguchi @ 2013-10-17 13:07 UTC (permalink / raw)
  To: Madper Xie
  Cc: linux-kernel, linux-efi, matt.fleming, tony.luck,
	Tomoki Sekiyama, dle-develop

Hi Madper,

I tested this patch on 3.12-rc4.
Could you please send me the log when you failed to apply?

Seiji

> -----Original Message-----
> From: Madper Xie [mailto:cxie@redhat.com]
> Sent: Thursday, October 17, 2013 1:54 AM
> To: Seiji Aguchi
> Cc: linux-kernel@vger.kernel.org; linux-efi@vger.kernel.org; matt.fleming@intel.com; tony.luck@intel.com; Tomoki Sekiyama; dle-
> develop@lists.sourceforge.net
> Subject: Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
> 
> Howdy Seiji,
>   I failed appily this patch to both 3.12-rc2 and 3.12-rc4. Could you
>   please let me know which is the right tree for this patch?
> 
>   Thanks,
>   Madper.
> seiji.aguchi@hds.com writes:
> 
> > Change from v2:
> > - Move dynamic memory allocation to efi_pstore_read() before holding
> >   efivars->lock to protect entry->var.Data.
> > - Access to entry->scanning while holding efivars->lock.
> > - Move a comment about a returned value from efi_pstore_read_func() to
> >   efi_pstore_read() because "size < 0" case may happen in efi_pstore_read().
> >
> > Currently, when mounting pstore file system, a read callback of efi_pstore
> > driver runs mutiple times as below.
> >
> > - In the first read callback, scan efivar_sysfs_list from head and pass
> >   a kmsg buffer of a entry to an upper pstore layer.
> > - In the second read callback, rescan efivar_sysfs_list from the entry and pass
> >   another kmsg buffer to it.
> > - Repeat the scan and pass until the end of efivar_sysfs_list.
> >
> > In this process, an entry is read across the multiple read function calls.
> > To avoid race between the read and erasion, the whole process above is
> > protected by a spinlock, holding in open() and releasing in close().
> >
> > At the same time, kmemdup() is called to pass the buffer to pstore filesystem
> > during it.
> > And then, it causes a following lockdep warning.
> >
> > To make the dynamic memory allocation runnable without taking spinlock,
> > holding off a deletion of sysfs entry if it happens while scanning it
> > via efi_pstore, and deleting it after the scan is completed.
> >
> > To implement it, this patch introduces two flags, scanning and deleting,
> > to efivar_entry.
> >
> > [    1.143710] ------------[ cut here ]------------
> > [    1.144058] WARNING: CPU: 1 PID: 1 at kernel/lockdep.c:2740
> > lockdep_trace_alloc+0x104/0x110()
> > [    1.144058] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
> > [    1.144058] Modules linked in:
> >
> > [    1.144058] CPU: 1 PID: 1 Comm: systemd Not tainted 3.11.0-rc5 #2
> > [    1.144058]  0000000000000009 ffff8800797e9ae0 ffffffff816614a5
> > ffff8800797e9b28
> > [    1.144058]  ffff8800797e9b18 ffffffff8105510d 0000000000000080
> > 0000000000000046
> > [    1.144058]  00000000000000d0 00000000000003af ffffffff81ccd0c0
> > ffff8800797e9b78
> > [    1.144058] Call Trace:
> > [    1.144058]  [<ffffffff816614a5>] dump_stack+0x54/0x74
> > [    1.144058]  [<ffffffff8105510d>] warn_slowpath_common+0x7d/0xa0
> > [    1.144058]  [<ffffffff8105517c>] warn_slowpath_fmt+0x4c/0x50
> > [    1.144058]  [<ffffffff8131290f>] ? vsscanf+0x57f/0x7b0
> > [    1.144058]  [<ffffffff810bbd74>] lockdep_trace_alloc+0x104/0x110
> > [    1.144058]  [<ffffffff81192da0>] __kmalloc_track_caller+0x50/0x280
> > [    1.144058]  [<ffffffff815147bb>] ?
> > efi_pstore_read_func.part.1+0x12b/0x170
> > [    1.144058]  [<ffffffff8115b260>] kmemdup+0x20/0x50
> > [    1.144058]  [<ffffffff815147bb>] efi_pstore_read_func.part.1+0x12b/0x170
> > [    1.144058]  [<ffffffff81514800>] ?
> > efi_pstore_read_func.part.1+0x170/0x170
> > [    1.144058]  [<ffffffff815148b4>] efi_pstore_read_func+0xb4/0xe0
> > [    1.144058]  [<ffffffff81512b7b>] __efivar_entry_iter+0xfb/0x120
> > [    1.144058]  [<ffffffff8151428f>] efi_pstore_read+0x3f/0x50
> > [    1.144058]  [<ffffffff8128d7ba>] pstore_get_records+0x9a/0x150
> > [    1.158207]  [<ffffffff812af25c>] ? selinux_d_instantiate+0x1c/0x20
> > [    1.158207]  [<ffffffff8128ce30>] ? parse_options+0x80/0x80
> > [    1.158207]  [<ffffffff8128ced5>] pstore_fill_super+0xa5/0xc0
> > [    1.158207]  [<ffffffff811ae7d2>] mount_single+0xa2/0xd0
> > [    1.158207]  [<ffffffff8128ccf8>] pstore_mount+0x18/0x20
> > [    1.158207]  [<ffffffff811ae8b9>] mount_fs+0x39/0x1b0
> > [    1.158207]  [<ffffffff81160550>] ? __alloc_percpu+0x10/0x20
> > [    1.158207]  [<ffffffff811c9493>] vfs_kern_mount+0x63/0xf0
> > [    1.158207]  [<ffffffff811cbb0e>] do_mount+0x23e/0xa20
> > [    1.158207]  [<ffffffff8115b51b>] ? strndup_user+0x4b/0xf0
> > [    1.158207]  [<ffffffff811cc373>] SyS_mount+0x83/0xc0
> > [    1.158207]  [<ffffffff81673cc2>] system_call_fastpath+0x16/0x1b
> > [    1.158207] ---[ end trace 61981bc62de9f6f4 ]---
> >
> > Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
> > ---
> >  drivers/firmware/efi/efi-pstore.c | 143 +++++++++++++++++++++++++++++++++++---
> >  drivers/firmware/efi/efivars.c    |  12 ++--
> >  drivers/firmware/efi/vars.c       |  12 +++-
> >  include/linux/efi.h               |   2 +
> >  4 files changed, 153 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
> > index 5002d50..ebd5dbc 100644
> > --- a/drivers/firmware/efi/efi-pstore.c
> > +++ b/drivers/firmware/efi/efi-pstore.c
> > @@ -18,14 +18,12 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
> >
> >  static int efi_pstore_open(struct pstore_info *psi)
> >  {
> > -	efivar_entry_iter_begin();
> >  	psi->data = NULL;
> >  	return 0;
> >  }
> >
> >  static int efi_pstore_close(struct pstore_info *psi)
> >  {
> > -	efivar_entry_iter_end();
> >  	psi->data = NULL;
> >  	return 0;
> >  }
> > @@ -91,19 +89,125 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
> >  	__efivar_entry_get(entry, &entry->var.Attributes,
> >  			   &entry->var.DataSize, entry->var.Data);
> >  	size = entry->var.DataSize;
> > +	memcpy(*cb_data->buf, entry->var.Data, (size_t)min_t(unsigned long,
> > +							     1024, size));
> >
> > -	*cb_data->buf = kmemdup(entry->var.Data, size, GFP_KERNEL);
> > -	if (*cb_data->buf == NULL)
> > -		return -ENOMEM;
> >  	return size;
> >  }
> >
> > +/**
> > + * efi_pstore_scan_sysfs_enter
> > + * @entry: scanning entry
> > + * @next: next entry
> > + * @head: list head
> > + */
> > +static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
> > +					struct efivar_entry *next,
> > +					struct list_head *head)
> > +{
> > +	pos->scanning = true;
> > +	if (&next->list != head)
> > +		next->scanning = true;
> > +}
> > +
> > +/**
> > + * __efi_pstore_scan_sysfs_exit
> > + * @entry: deleting entry
> > + * @turn_off_scanning: Check if a scanning flag should be turned off
> > + */
> > +static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
> > +						bool turn_off_scanning)
> > +{
> > +	if (entry->deleting) {
> > +		list_del(&entry->list);
> > +		efivar_entry_iter_end();
> > +		efivar_unregister(entry);
> > +		efivar_entry_iter_begin();
> > +	} else if (turn_off_scanning)
> > +		entry->scanning = false;
> > +}
> > +
> > +/**
> > + * efi_pstore_scan_sysfs_exit
> > + * @pos: scanning entry
> > + * @next: next entry
> > + * @head: list head
> > + * @stop: a flag checking if scanning will stop
> > + */
> > +static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
> > +				       struct efivar_entry *next,
> > +				       struct list_head *head, bool stop)
> > +{
> > +	__efi_pstore_scan_sysfs_exit(pos, true);
> > +	if (stop)
> > +		__efi_pstore_scan_sysfs_exit(next, &next->list != head);
> > +}
> > +
> > +/**
> > + * efi_pstore_sysfs_entry_iter
> > + *
> > + * @data: function-specific data to pass to callback
> > + * @pos: entry to begin iterating from
> > + *
> > + * You MUST call efivar_enter_iter_begin() before this function, and
> > + * efivar_entry_iter_end() afterwards.
> > + *
> > + * It is possible to begin iteration from an arbitrary entry within
> > + * the list by passing @pos. @pos is updated on return to point to
> > + * the next entry of the last one passed to efi_pstore_read_func().
> > + * To begin iterating from the beginning of the list @pos must be %NULL.
> > + */
> > +static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
> > +{
> > +	struct efivar_entry *entry, *n;
> > +	struct list_head *head = &efivar_sysfs_list;
> > +	int size = 0;
> > +
> > +	if (!*pos) {
> > +		list_for_each_entry_safe(entry, n, head, list) {
> > +			efi_pstore_scan_sysfs_enter(entry, n, head);
> > +
> > +			size = efi_pstore_read_func(entry, data);
> > +			efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
> > +			if (size)
> > +				break;
> > +		}
> > +		*pos = n;
> > +		return size;
> > +	}
> > +
> > +	list_for_each_entry_safe_from((*pos), n, head, list) {
> > +		efi_pstore_scan_sysfs_enter((*pos), n, head);
> > +
> > +		size = efi_pstore_read_func((*pos), data);
> > +		efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
> > +		if (size)
> > +			break;
> > +	}
> > +	*pos = n;
> > +	return size;
> > +}
> > +
> > +/**
> > + * efi_pstore_read
> > + *
> > + * This function returns a size of NVRAM entry logged via efi_pstore_write().
> > + * The meaning and behavior of efi_pstore/pstore are as below.
> > + *
> > + * size > 0: Got data of an entry logged via efi_pstore_write() successfully,
> > + *           and pstore filesystem will continue reading subsequent entries.
> > + * size == 0: Entry was not logged via efi_pstore_write(),
> > + *            and efi_pstore driver will continue reading subsequent entries.
> > + * size < 0: Failed to get data of entry logging via efi_pstore_write(),
> > + *           and pstore will stop reading entry.
> > + */
> >  static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
> >  			       int *count, struct timespec *timespec,
> >  			       char **buf, bool *compressed,
> >  			       struct pstore_info *psi)
> >  {
> >  	struct pstore_read_data data;
> > +	ssize_t size;
> >
> >  	data.id = id;
> >  	data.type = type;
> > @@ -112,8 +216,17 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
> >  	data.compressed = compressed;
> >  	data.buf = buf;
> >
> > -	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
> > -				   (struct efivar_entry **)&psi->data);
> > +	*data.buf = kzalloc(1024, GFP_KERNEL);
> > +	if (!*data.buf)
> > +		return -ENOMEM;
> > +
> > +	efivar_entry_iter_begin();
> > +	size = efi_pstore_sysfs_entry_iter(&data,
> > +					   (struct efivar_entry **)&psi->data);
> > +	efivar_entry_iter_end();
> > +	if (size <= 0)
> > +		kfree(*data.buf);
> > +	return size;
> >  }
> >
> >  static int efi_pstore_write(enum pstore_type_id type,
> > @@ -184,9 +297,17 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
> >  			return 0;
> >  	}
> >
> > +	if (entry->scanning) {
> > +		/*
> > +		 * Skip deletion because this entry will be deleted
> > +		 * after scanning is completed.
> > +		 */
> > +		entry->deleting = true;
> > +	} else
> > +		list_del(&entry->list);
> > +
> >  	/* found */
> >  	__efivar_entry_delete(entry);
> > -	list_del(&entry->list);
> >
> >  	return 1;
> >  }
> > @@ -214,10 +335,12 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
> >
> >  	efivar_entry_iter_begin();
> >  	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
> > -	efivar_entry_iter_end();
> >
> > -	if (found)
> > +	if (found && !entry->scanning) {
> > +		efivar_entry_iter_end();
> >  		efivar_unregister(entry);
> > +	} else
> > +		efivar_entry_iter_end();
> >
> >  	return 0;
> >  }
> > diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
> > index 8a7432a..8c5a61a 100644
> > --- a/drivers/firmware/efi/efivars.c
> > +++ b/drivers/firmware/efi/efivars.c
> > @@ -383,12 +383,16 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
> >  	else if (__efivar_entry_delete(entry))
> >  		err = -EIO;
> >
> > -	efivar_entry_iter_end();
> > -
> > -	if (err)
> > +	if (err) {
> > +		efivar_entry_iter_end();
> >  		return err;
> > +	}
> >
> > -	efivar_unregister(entry);
> > +	if (!entry->scanning) {
> > +		efivar_entry_iter_end();
> > +		efivar_unregister(entry);
> > +	} else
> > +		efivar_entry_iter_end();
> >
> >  	/* It's dead Jim.... */
> >  	return count;
> > diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
> > index 391c67b..b22659c 100644
> > --- a/drivers/firmware/efi/vars.c
> > +++ b/drivers/firmware/efi/vars.c
> > @@ -683,8 +683,16 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
> >  	if (!found)
> >  		return NULL;
> >
> > -	if (remove)
> > -		list_del(&entry->list);
> > +	if (remove) {
> > +		if (entry->scanning) {
> > +			/*
> > +			 * The entry will be deleted
> > +			 * after scanning is completed.
> > +			 */
> > +			entry->deleting = true;
> > +		} else
> > +			list_del(&entry->list);
> > +	}
> >
> >  	return entry;
> >  }
> > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > index 5f8f176..04088fb 100644
> > --- a/include/linux/efi.h
> > +++ b/include/linux/efi.h
> > @@ -782,6 +782,8 @@ struct efivar_entry {
> >  	struct efi_variable var;
> >  	struct list_head list;
> >  	struct kobject kobj;
> > +	bool scanning;
> > +	bool deleting;
> >  };
> >
> >  extern struct list_head efivar_sysfs_list;
> 
> 
> --
> Best,
> Madper Xie.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-11 18:29 [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed Seiji Aguchi
  2013-10-17  5:54 ` Madper Xie
@ 2013-10-17 13:18 ` Matt Fleming
  2013-10-18 22:30   ` Seiji Aguchi
  1 sibling, 1 reply; 10+ messages in thread
From: Matt Fleming @ 2013-10-17 13:18 UTC (permalink / raw)
  To: Seiji Aguchi
  Cc: linux-kernel, linux-efi, matt.fleming, tony.luck,
	tomoki.sekiyama, dle-develop

On Fri, 11 Oct, at 02:29:07PM, Seiji Aguchi wrote:
> Change from v2:
> - Move dynamic memory allocation to efi_pstore_read() before holding
>   efivars->lock to protect entry->var.Data.
> - Access to entry->scanning while holding efivars->lock.
> - Move a comment about a returned value from efi_pstore_read_func() to
>   efi_pstore_read() because "size < 0" case may happen in efi_pstore_read().

It seems to me that because you're no longer dropping __efivars->lock
when reading from the EFI variable store, you actually don't need all
the ->scanning and ->deleting logic because anything that sets those
flags runs to completion while holding the lock.

Can't the patch be simplified to just allocating data.buf at the
beginning of efi_pstore_read()? Also, it would be a good idea to
introduce a #define for the 1024 magic number, e.g.

#define EFIVARS_DATA_SIZE_MAX	1024

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-17 13:07   ` Seiji Aguchi
@ 2013-10-17 14:30     ` Madper Xie
  2013-10-17 15:07       ` Madper Xie
  0 siblings, 1 reply; 10+ messages in thread
From: Madper Xie @ 2013-10-17 14:30 UTC (permalink / raw)
  To: Seiji Aguchi
  Cc: Madper Xie, linux-kernel, linux-efi, matt.fleming, tony.luck,
	Tomoki Sekiyama, dle-develop

Oops, It seems my mu4e(a email client for emacs)'s auto-indent breaks the
patch... I apologize for this... 

seiji.aguchi@hds.com writes:

> Hi Madper,
>
> I tested this patch on 3.12-rc4.
> Could you please send me the log when you failed to apply?
>
> Seiji
>
>> -----Original Message-----
>> From: Madper Xie [mailto:cxie@redhat.com]
>> Sent: Thursday, October 17, 2013 1:54 AM
>> To: Seiji Aguchi
>> Cc: linux-kernel@vger.kernel.org; linux-efi@vger.kernel.org; matt.fleming@intel.com; tony.luck@intel.com; Tomoki Sekiyama; dle-
>> develop@lists.sourceforge.net
>> Subject: Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
>> 
>> Howdy Seiji,
>>   I failed appily this patch to both 3.12-rc2 and 3.12-rc4. Could you
>>   please let me know which is the right tree for this patch?
>> 
>>   Thanks,
>>   Madper.
>> seiji.aguchi@hds.com writes:
>> 
>> > Change from v2:
>> > - Move dynamic memory allocation to efi_pstore_read() before holding
>> >   efivars->lock to protect entry->var.Data.
>> > - Access to entry->scanning while holding efivars->lock.
>> > - Move a comment about a returned value from efi_pstore_read_func() to
>> >   efi_pstore_read() because "size < 0" case may happen in efi_pstore_read().
>> >
>> > Currently, when mounting pstore file system, a read callback of efi_pstore
>> > driver runs mutiple times as below.
>> >
>> > - In the first read callback, scan efivar_sysfs_list from head and pass
>> >   a kmsg buffer of a entry to an upper pstore layer.
>> > - In the second read callback, rescan efivar_sysfs_list from the entry and pass
>> >   another kmsg buffer to it.
>> > - Repeat the scan and pass until the end of efivar_sysfs_list.
>> >
>> > In this process, an entry is read across the multiple read function calls.
>> > To avoid race between the read and erasion, the whole process above is
>> > protected by a spinlock, holding in open() and releasing in close().
>> >
>> > At the same time, kmemdup() is called to pass the buffer to pstore filesystem
>> > during it.
>> > And then, it causes a following lockdep warning.
>> >
>> > To make the dynamic memory allocation runnable without taking spinlock,
>> > holding off a deletion of sysfs entry if it happens while scanning it
>> > via efi_pstore, and deleting it after the scan is completed.
>> >
>> > To implement it, this patch introduces two flags, scanning and deleting,
>> > to efivar_entry.
>> >
>> > [    1.143710] ------------[ cut here ]------------
>> > [    1.144058] WARNING: CPU: 1 PID: 1 at kernel/lockdep.c:2740
>> > lockdep_trace_alloc+0x104/0x110()
>> > [    1.144058] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
>> > [    1.144058] Modules linked in:
>> >
>> > [    1.144058] CPU: 1 PID: 1 Comm: systemd Not tainted 3.11.0-rc5 #2
>> > [    1.144058]  0000000000000009 ffff8800797e9ae0 ffffffff816614a5
>> > ffff8800797e9b28
>> > [    1.144058]  ffff8800797e9b18 ffffffff8105510d 0000000000000080
>> > 0000000000000046
>> > [    1.144058]  00000000000000d0 00000000000003af ffffffff81ccd0c0
>> > ffff8800797e9b78
>> > [    1.144058] Call Trace:
>> > [    1.144058]  [<ffffffff816614a5>] dump_stack+0x54/0x74
>> > [    1.144058]  [<ffffffff8105510d>] warn_slowpath_common+0x7d/0xa0
>> > [    1.144058]  [<ffffffff8105517c>] warn_slowpath_fmt+0x4c/0x50
>> > [    1.144058]  [<ffffffff8131290f>] ? vsscanf+0x57f/0x7b0
>> > [    1.144058]  [<ffffffff810bbd74>] lockdep_trace_alloc+0x104/0x110
>> > [    1.144058]  [<ffffffff81192da0>] __kmalloc_track_caller+0x50/0x280
>> > [    1.144058]  [<ffffffff815147bb>] ?
>> > efi_pstore_read_func.part.1+0x12b/0x170
>> > [    1.144058]  [<ffffffff8115b260>] kmemdup+0x20/0x50
>> > [    1.144058]  [<ffffffff815147bb>] efi_pstore_read_func.part.1+0x12b/0x170
>> > [    1.144058]  [<ffffffff81514800>] ?
>> > efi_pstore_read_func.part.1+0x170/0x170
>> > [    1.144058]  [<ffffffff815148b4>] efi_pstore_read_func+0xb4/0xe0
>> > [    1.144058]  [<ffffffff81512b7b>] __efivar_entry_iter+0xfb/0x120
>> > [    1.144058]  [<ffffffff8151428f>] efi_pstore_read+0x3f/0x50
>> > [    1.144058]  [<ffffffff8128d7ba>] pstore_get_records+0x9a/0x150
>> > [    1.158207]  [<ffffffff812af25c>] ? selinux_d_instantiate+0x1c/0x20
>> > [    1.158207]  [<ffffffff8128ce30>] ? parse_options+0x80/0x80
>> > [    1.158207]  [<ffffffff8128ced5>] pstore_fill_super+0xa5/0xc0
>> > [    1.158207]  [<ffffffff811ae7d2>] mount_single+0xa2/0xd0
>> > [    1.158207]  [<ffffffff8128ccf8>] pstore_mount+0x18/0x20
>> > [    1.158207]  [<ffffffff811ae8b9>] mount_fs+0x39/0x1b0
>> > [    1.158207]  [<ffffffff81160550>] ? __alloc_percpu+0x10/0x20
>> > [    1.158207]  [<ffffffff811c9493>] vfs_kern_mount+0x63/0xf0
>> > [    1.158207]  [<ffffffff811cbb0e>] do_mount+0x23e/0xa20
>> > [    1.158207]  [<ffffffff8115b51b>] ? strndup_user+0x4b/0xf0
>> > [    1.158207]  [<ffffffff811cc373>] SyS_mount+0x83/0xc0
>> > [    1.158207]  [<ffffffff81673cc2>] system_call_fastpath+0x16/0x1b
>> > [    1.158207] ---[ end trace 61981bc62de9f6f4 ]---
>> >
>> > Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
>> > ---
>> >  drivers/firmware/efi/efi-pstore.c | 143 +++++++++++++++++++++++++++++++++++---
>> >  drivers/firmware/efi/efivars.c    |  12 ++--
>> >  drivers/firmware/efi/vars.c       |  12 +++-
>> >  include/linux/efi.h               |   2 +
>> >  4 files changed, 153 insertions(+), 16 deletions(-)
>> >
>> > diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
>> > index 5002d50..ebd5dbc 100644
>> > --- a/drivers/firmware/efi/efi-pstore.c
>> > +++ b/drivers/firmware/efi/efi-pstore.c
>> > @@ -18,14 +18,12 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
>> >
>> >  static int efi_pstore_open(struct pstore_info *psi)
>> >  {
>> > -	efivar_entry_iter_begin();
>> >  	psi->data = NULL;
>> >  	return 0;
>> >  }
>> >
>> >  static int efi_pstore_close(struct pstore_info *psi)
>> >  {
>> > -	efivar_entry_iter_end();
>> >  	psi->data = NULL;
>> >  	return 0;
>> >  }
>> > @@ -91,19 +89,125 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
>> >  	__efivar_entry_get(entry, &entry->var.Attributes,
>> >  			   &entry->var.DataSize, entry->var.Data);
>> >  	size = entry->var.DataSize;
>> > +	memcpy(*cb_data->buf, entry->var.Data, (size_t)min_t(unsigned long,
>> > +							     1024, size));
>> >
>> > -	*cb_data->buf = kmemdup(entry->var.Data, size, GFP_KERNEL);
>> > -	if (*cb_data->buf == NULL)
>> > -		return -ENOMEM;
>> >  	return size;
>> >  }
>> >
>> > +/**
>> > + * efi_pstore_scan_sysfs_enter
>> > + * @entry: scanning entry
>> > + * @next: next entry
>> > + * @head: list head
>> > + */
>> > +static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
>> > +					struct efivar_entry *next,
>> > +					struct list_head *head)
>> > +{
>> > +	pos->scanning = true;
>> > +	if (&next->list != head)
>> > +		next->scanning = true;
>> > +}
>> > +
>> > +/**
>> > + * __efi_pstore_scan_sysfs_exit
>> > + * @entry: deleting entry
>> > + * @turn_off_scanning: Check if a scanning flag should be turned off
>> > + */
>> > +static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
>> > +						bool turn_off_scanning)
>> > +{
>> > +	if (entry->deleting) {
>> > +		list_del(&entry->list);
>> > +		efivar_entry_iter_end();
>> > +		efivar_unregister(entry);
>> > +		efivar_entry_iter_begin();
>> > +	} else if (turn_off_scanning)
>> > +		entry->scanning = false;
>> > +}
>> > +
>> > +/**
>> > + * efi_pstore_scan_sysfs_exit
>> > + * @pos: scanning entry
>> > + * @next: next entry
>> > + * @head: list head
>> > + * @stop: a flag checking if scanning will stop
>> > + */
>> > +static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
>> > +				       struct efivar_entry *next,
>> > +				       struct list_head *head, bool stop)
>> > +{
>> > +	__efi_pstore_scan_sysfs_exit(pos, true);
>> > +	if (stop)
>> > +		__efi_pstore_scan_sysfs_exit(next, &next->list != head);
>> > +}
>> > +
>> > +/**
>> > + * efi_pstore_sysfs_entry_iter
>> > + *
>> > + * @data: function-specific data to pass to callback
>> > + * @pos: entry to begin iterating from
>> > + *
>> > + * You MUST call efivar_enter_iter_begin() before this function, and
>> > + * efivar_entry_iter_end() afterwards.
>> > + *
>> > + * It is possible to begin iteration from an arbitrary entry within
>> > + * the list by passing @pos. @pos is updated on return to point to
>> > + * the next entry of the last one passed to efi_pstore_read_func().
>> > + * To begin iterating from the beginning of the list @pos must be %NULL.
>> > + */
>> > +static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
>> > +{
>> > +	struct efivar_entry *entry, *n;
>> > +	struct list_head *head = &efivar_sysfs_list;
>> > +	int size = 0;
>> > +
>> > +	if (!*pos) {
>> > +		list_for_each_entry_safe(entry, n, head, list) {
>> > +			efi_pstore_scan_sysfs_enter(entry, n, head);
>> > +
>> > +			size = efi_pstore_read_func(entry, data);
>> > +			efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
>> > +			if (size)
>> > +				break;
>> > +		}
>> > +		*pos = n;
>> > +		return size;
>> > +	}
>> > +
>> > +	list_for_each_entry_safe_from((*pos), n, head, list) {
>> > +		efi_pstore_scan_sysfs_enter((*pos), n, head);
>> > +
>> > +		size = efi_pstore_read_func((*pos), data);
>> > +		efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
>> > +		if (size)
>> > +			break;
>> > +	}
>> > +	*pos = n;
>> > +	return size;
>> > +}
>> > +
>> > +/**
>> > + * efi_pstore_read
>> > + *
>> > + * This function returns a size of NVRAM entry logged via efi_pstore_write().
>> > + * The meaning and behavior of efi_pstore/pstore are as below.
>> > + *
>> > + * size > 0: Got data of an entry logged via efi_pstore_write() successfully,
>> > + *           and pstore filesystem will continue reading subsequent entries.
>> > + * size == 0: Entry was not logged via efi_pstore_write(),
>> > + *            and efi_pstore driver will continue reading subsequent entries.
>> > + * size < 0: Failed to get data of entry logging via efi_pstore_write(),
>> > + *           and pstore will stop reading entry.
>> > + */
>> >  static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
>> >  			       int *count, struct timespec *timespec,
>> >  			       char **buf, bool *compressed,
>> >  			       struct pstore_info *psi)
>> >  {
>> >  	struct pstore_read_data data;
>> > +	ssize_t size;
>> >
>> >  	data.id = id;
>> >  	data.type = type;
>> > @@ -112,8 +216,17 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
>> >  	data.compressed = compressed;
>> >  	data.buf = buf;
>> >
>> > -	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
>> > -				   (struct efivar_entry **)&psi->data);
>> > +	*data.buf = kzalloc(1024, GFP_KERNEL);
>> > +	if (!*data.buf)
>> > +		return -ENOMEM;
>> > +
>> > +	efivar_entry_iter_begin();
>> > +	size = efi_pstore_sysfs_entry_iter(&data,
>> > +					   (struct efivar_entry **)&psi->data);
>> > +	efivar_entry_iter_end();
>> > +	if (size <= 0)
>> > +		kfree(*data.buf);
>> > +	return size;
>> >  }
>> >
>> >  static int efi_pstore_write(enum pstore_type_id type,
>> > @@ -184,9 +297,17 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
>> >  			return 0;
>> >  	}
>> >
>> > +	if (entry->scanning) {
>> > +		/*
>> > +		 * Skip deletion because this entry will be deleted
>> > +		 * after scanning is completed.
>> > +		 */
>> > +		entry->deleting = true;
>> > +	} else
>> > +		list_del(&entry->list);
>> > +
>> >  	/* found */
>> >  	__efivar_entry_delete(entry);
>> > -	list_del(&entry->list);
>> >
>> >  	return 1;
>> >  }
>> > @@ -214,10 +335,12 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
>> >
>> >  	efivar_entry_iter_begin();
>> >  	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
>> > -	efivar_entry_iter_end();
>> >
>> > -	if (found)
>> > +	if (found && !entry->scanning) {
>> > +		efivar_entry_iter_end();
>> >  		efivar_unregister(entry);
>> > +	} else
>> > +		efivar_entry_iter_end();
>> >
>> >  	return 0;
>> >  }
>> > diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
>> > index 8a7432a..8c5a61a 100644
>> > --- a/drivers/firmware/efi/efivars.c
>> > +++ b/drivers/firmware/efi/efivars.c
>> > @@ -383,12 +383,16 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
>> >  	else if (__efivar_entry_delete(entry))
>> >  		err = -EIO;
>> >
>> > -	efivar_entry_iter_end();
>> > -
>> > -	if (err)
>> > +	if (err) {
>> > +		efivar_entry_iter_end();
>> >  		return err;
>> > +	}
>> >
>> > -	efivar_unregister(entry);
>> > +	if (!entry->scanning) {
>> > +		efivar_entry_iter_end();
>> > +		efivar_unregister(entry);
>> > +	} else
>> > +		efivar_entry_iter_end();
>> >
>> >  	/* It's dead Jim.... */
>> >  	return count;
>> > diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
>> > index 391c67b..b22659c 100644
>> > --- a/drivers/firmware/efi/vars.c
>> > +++ b/drivers/firmware/efi/vars.c
>> > @@ -683,8 +683,16 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
>> >  	if (!found)
>> >  		return NULL;
>> >
>> > -	if (remove)
>> > -		list_del(&entry->list);
>> > +	if (remove) {
>> > +		if (entry->scanning) {
>> > +			/*
>> > +			 * The entry will be deleted
>> > +			 * after scanning is completed.
>> > +			 */
>> > +			entry->deleting = true;
>> > +		} else
>> > +			list_del(&entry->list);
>> > +	}
>> >
>> >  	return entry;
>> >  }
>> > diff --git a/include/linux/efi.h b/include/linux/efi.h
>> > index 5f8f176..04088fb 100644
>> > --- a/include/linux/efi.h
>> > +++ b/include/linux/efi.h
>> > @@ -782,6 +782,8 @@ struct efivar_entry {
>> >  	struct efi_variable var;
>> >  	struct list_head list;
>> >  	struct kobject kobj;
>> > +	bool scanning;
>> > +	bool deleting;
>> >  };
>> >
>> >  extern struct list_head efivar_sysfs_list;
>> 
>> 
>> --
>> Best,
>> Madper Xie.


-- 
Best,
Madper Xie.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-17 14:30     ` Madper Xie
@ 2013-10-17 15:07       ` Madper Xie
  2013-10-17 15:11         ` Seiji Aguchi
  0 siblings, 1 reply; 10+ messages in thread
From: Madper Xie @ 2013-10-17 15:07 UTC (permalink / raw)
  To: Madper Xie
  Cc: Seiji Aguchi, linux-kernel, linux-efi, matt.fleming, tony.luck,
	Tomoki Sekiyama, dle-develop

Hi folks,
   I tested it on my DELL XPS desktop. And it won't show any warnings
   when I mounting pstore and deleting pstore items after this patch
   applied.

   Tested-by: Madper Xie
cxie@redhat.com writes:

> Oops, It seems my mu4e(a email client for emacs)'s auto-indent breaks the
> patch... I apologize for this... 
>
> seiji.aguchi@hds.com writes:
>
>> Hi Madper,
>>
>> I tested this patch on 3.12-rc4.
>> Could you please send me the log when you failed to apply?
>>
>> Seiji
>>
>>> -----Original Message-----
>>> From: Madper Xie [mailto:cxie@redhat.com]
>>> Sent: Thursday, October 17, 2013 1:54 AM
>>> To: Seiji Aguchi
>>> Cc: linux-kernel@vger.kernel.org; linux-efi@vger.kernel.org; matt.fleming@intel.com; tony.luck@intel.com; Tomoki Sekiyama; dle-
>>> develop@lists.sourceforge.net
>>> Subject: Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
>>> 
>>> Howdy Seiji,
>>>   I failed appily this patch to both 3.12-rc2 and 3.12-rc4. Could you
>>>   please let me know which is the right tree for this patch?
>>> 
>>>   Thanks,
>>>   Madper.
>>> seiji.aguchi@hds.com writes:
>>> 
>>> > Change from v2:
>>> > - Move dynamic memory allocation to efi_pstore_read() before holding
>>> >   efivars->lock to protect entry->var.Data.
>>> > - Access to entry->scanning while holding efivars->lock.
>>> > - Move a comment about a returned value from efi_pstore_read_func() to
>>> >   efi_pstore_read() because "size < 0" case may happen in efi_pstore_read().
>>> >
>>> > Currently, when mounting pstore file system, a read callback of efi_pstore
>>> > driver runs mutiple times as below.
>>> >
>>> > - In the first read callback, scan efivar_sysfs_list from head and pass
>>> >   a kmsg buffer of a entry to an upper pstore layer.
>>> > - In the second read callback, rescan efivar_sysfs_list from the entry and pass
>>> >   another kmsg buffer to it.
>>> > - Repeat the scan and pass until the end of efivar_sysfs_list.
>>> >
>>> > In this process, an entry is read across the multiple read function calls.
>>> > To avoid race between the read and erasion, the whole process above is
>>> > protected by a spinlock, holding in open() and releasing in close().
>>> >
>>> > At the same time, kmemdup() is called to pass the buffer to pstore filesystem
>>> > during it.
>>> > And then, it causes a following lockdep warning.
>>> >
>>> > To make the dynamic memory allocation runnable without taking spinlock,
>>> > holding off a deletion of sysfs entry if it happens while scanning it
>>> > via efi_pstore, and deleting it after the scan is completed.
>>> >
>>> > To implement it, this patch introduces two flags, scanning and deleting,
>>> > to efivar_entry.
>>> >
>>> > [    1.143710] ------------[ cut here ]------------
>>> > [    1.144058] WARNING: CPU: 1 PID: 1 at kernel/lockdep.c:2740
>>> > lockdep_trace_alloc+0x104/0x110()
>>> > [    1.144058] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
>>> > [    1.144058] Modules linked in:
>>> >
>>> > [    1.144058] CPU: 1 PID: 1 Comm: systemd Not tainted 3.11.0-rc5 #2
>>> > [    1.144058]  0000000000000009 ffff8800797e9ae0 ffffffff816614a5
>>> > ffff8800797e9b28
>>> > [    1.144058]  ffff8800797e9b18 ffffffff8105510d 0000000000000080
>>> > 0000000000000046
>>> > [    1.144058]  00000000000000d0 00000000000003af ffffffff81ccd0c0
>>> > ffff8800797e9b78
>>> > [    1.144058] Call Trace:
>>> > [    1.144058]  [<ffffffff816614a5>] dump_stack+0x54/0x74
>>> > [    1.144058]  [<ffffffff8105510d>] warn_slowpath_common+0x7d/0xa0
>>> > [    1.144058]  [<ffffffff8105517c>] warn_slowpath_fmt+0x4c/0x50
>>> > [    1.144058]  [<ffffffff8131290f>] ? vsscanf+0x57f/0x7b0
>>> > [    1.144058]  [<ffffffff810bbd74>] lockdep_trace_alloc+0x104/0x110
>>> > [    1.144058]  [<ffffffff81192da0>] __kmalloc_track_caller+0x50/0x280
>>> > [    1.144058]  [<ffffffff815147bb>] ?
>>> > efi_pstore_read_func.part.1+0x12b/0x170
>>> > [    1.144058]  [<ffffffff8115b260>] kmemdup+0x20/0x50
>>> > [    1.144058]  [<ffffffff815147bb>] efi_pstore_read_func.part.1+0x12b/0x170
>>> > [    1.144058]  [<ffffffff81514800>] ?
>>> > efi_pstore_read_func.part.1+0x170/0x170
>>> > [    1.144058]  [<ffffffff815148b4>] efi_pstore_read_func+0xb4/0xe0
>>> > [    1.144058]  [<ffffffff81512b7b>] __efivar_entry_iter+0xfb/0x120
>>> > [    1.144058]  [<ffffffff8151428f>] efi_pstore_read+0x3f/0x50
>>> > [    1.144058]  [<ffffffff8128d7ba>] pstore_get_records+0x9a/0x150
>>> > [    1.158207]  [<ffffffff812af25c>] ? selinux_d_instantiate+0x1c/0x20
>>> > [    1.158207]  [<ffffffff8128ce30>] ? parse_options+0x80/0x80
>>> > [    1.158207]  [<ffffffff8128ced5>] pstore_fill_super+0xa5/0xc0
>>> > [    1.158207]  [<ffffffff811ae7d2>] mount_single+0xa2/0xd0
>>> > [    1.158207]  [<ffffffff8128ccf8>] pstore_mount+0x18/0x20
>>> > [    1.158207]  [<ffffffff811ae8b9>] mount_fs+0x39/0x1b0
>>> > [    1.158207]  [<ffffffff81160550>] ? __alloc_percpu+0x10/0x20
>>> > [    1.158207]  [<ffffffff811c9493>] vfs_kern_mount+0x63/0xf0
>>> > [    1.158207]  [<ffffffff811cbb0e>] do_mount+0x23e/0xa20
>>> > [    1.158207]  [<ffffffff8115b51b>] ? strndup_user+0x4b/0xf0
>>> > [    1.158207]  [<ffffffff811cc373>] SyS_mount+0x83/0xc0
>>> > [    1.158207]  [<ffffffff81673cc2>] system_call_fastpath+0x16/0x1b
>>> > [    1.158207] ---[ end trace 61981bc62de9f6f4 ]---
>>> >
>>> > Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
>>> > ---
>>> >  drivers/firmware/efi/efi-pstore.c | 143 +++++++++++++++++++++++++++++++++++---
>>> >  drivers/firmware/efi/efivars.c    |  12 ++--
>>> >  drivers/firmware/efi/vars.c       |  12 +++-
>>> >  include/linux/efi.h               |   2 +
>>> >  4 files changed, 153 insertions(+), 16 deletions(-)
>>> >
>>> > diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
>>> > index 5002d50..ebd5dbc 100644
>>> > --- a/drivers/firmware/efi/efi-pstore.c
>>> > +++ b/drivers/firmware/efi/efi-pstore.c
>>> > @@ -18,14 +18,12 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
>>> >
>>> >  static int efi_pstore_open(struct pstore_info *psi)
>>> >  {
>>> > -	efivar_entry_iter_begin();
>>> >  	psi->data = NULL;
>>> >  	return 0;
>>> >  }
>>> >
>>> >  static int efi_pstore_close(struct pstore_info *psi)
>>> >  {
>>> > -	efivar_entry_iter_end();
>>> >  	psi->data = NULL;
>>> >  	return 0;
>>> >  }
>>> > @@ -91,19 +89,125 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
>>> >  	__efivar_entry_get(entry, &entry->var.Attributes,
>>> >  			   &entry->var.DataSize, entry->var.Data);
>>> >  	size = entry->var.DataSize;
>>> > +	memcpy(*cb_data->buf, entry->var.Data, (size_t)min_t(unsigned long,
>>> > +							     1024, size));
>>> >
>>> > -	*cb_data->buf = kmemdup(entry->var.Data, size, GFP_KERNEL);
>>> > -	if (*cb_data->buf == NULL)
>>> > -		return -ENOMEM;
>>> >  	return size;
>>> >  }
>>> >
>>> > +/**
>>> > + * efi_pstore_scan_sysfs_enter
>>> > + * @entry: scanning entry
>>> > + * @next: next entry
>>> > + * @head: list head
>>> > + */
>>> > +static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
>>> > +					struct efivar_entry *next,
>>> > +					struct list_head *head)
>>> > +{
>>> > +	pos->scanning = true;
>>> > +	if (&next->list != head)
>>> > +		next->scanning = true;
>>> > +}
>>> > +
>>> > +/**
>>> > + * __efi_pstore_scan_sysfs_exit
>>> > + * @entry: deleting entry
>>> > + * @turn_off_scanning: Check if a scanning flag should be turned off
>>> > + */
>>> > +static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
>>> > +						bool turn_off_scanning)
>>> > +{
>>> > +	if (entry->deleting) {
>>> > +		list_del(&entry->list);
>>> > +		efivar_entry_iter_end();
>>> > +		efivar_unregister(entry);
>>> > +		efivar_entry_iter_begin();
>>> > +	} else if (turn_off_scanning)
>>> > +		entry->scanning = false;
>>> > +}
>>> > +
>>> > +/**
>>> > + * efi_pstore_scan_sysfs_exit
>>> > + * @pos: scanning entry
>>> > + * @next: next entry
>>> > + * @head: list head
>>> > + * @stop: a flag checking if scanning will stop
>>> > + */
>>> > +static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
>>> > +				       struct efivar_entry *next,
>>> > +				       struct list_head *head, bool stop)
>>> > +{
>>> > +	__efi_pstore_scan_sysfs_exit(pos, true);
>>> > +	if (stop)
>>> > +		__efi_pstore_scan_sysfs_exit(next, &next->list != head);
>>> > +}
>>> > +
>>> > +/**
>>> > + * efi_pstore_sysfs_entry_iter
>>> > + *
>>> > + * @data: function-specific data to pass to callback
>>> > + * @pos: entry to begin iterating from
>>> > + *
>>> > + * You MUST call efivar_enter_iter_begin() before this function, and
>>> > + * efivar_entry_iter_end() afterwards.
>>> > + *
>>> > + * It is possible to begin iteration from an arbitrary entry within
>>> > + * the list by passing @pos. @pos is updated on return to point to
>>> > + * the next entry of the last one passed to efi_pstore_read_func().
>>> > + * To begin iterating from the beginning of the list @pos must be %NULL.
>>> > + */
>>> > +static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
>>> > +{
>>> > +	struct efivar_entry *entry, *n;
>>> > +	struct list_head *head = &efivar_sysfs_list;
>>> > +	int size = 0;
>>> > +
>>> > +	if (!*pos) {
>>> > +		list_for_each_entry_safe(entry, n, head, list) {
>>> > +			efi_pstore_scan_sysfs_enter(entry, n, head);
>>> > +
>>> > +			size = efi_pstore_read_func(entry, data);
>>> > +			efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
>>> > +			if (size)
>>> > +				break;
>>> > +		}
>>> > +		*pos = n;
>>> > +		return size;
>>> > +	}
>>> > +
>>> > +	list_for_each_entry_safe_from((*pos), n, head, list) {
>>> > +		efi_pstore_scan_sysfs_enter((*pos), n, head);
>>> > +
>>> > +		size = efi_pstore_read_func((*pos), data);
>>> > +		efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
>>> > +		if (size)
>>> > +			break;
>>> > +	}
>>> > +	*pos = n;
>>> > +	return size;
>>> > +}
>>> > +
>>> > +/**
>>> > + * efi_pstore_read
>>> > + *
>>> > + * This function returns a size of NVRAM entry logged via efi_pstore_write().
>>> > + * The meaning and behavior of efi_pstore/pstore are as below.
>>> > + *
>>> > + * size > 0: Got data of an entry logged via efi_pstore_write() successfully,
>>> > + *           and pstore filesystem will continue reading subsequent entries.
>>> > + * size == 0: Entry was not logged via efi_pstore_write(),
>>> > + *            and efi_pstore driver will continue reading subsequent entries.
>>> > + * size < 0: Failed to get data of entry logging via efi_pstore_write(),
>>> > + *           and pstore will stop reading entry.
>>> > + */
>>> >  static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
>>> >  			       int *count, struct timespec *timespec,
>>> >  			       char **buf, bool *compressed,
>>> >  			       struct pstore_info *psi)
>>> >  {
>>> >  	struct pstore_read_data data;
>>> > +	ssize_t size;
>>> >
>>> >  	data.id = id;
>>> >  	data.type = type;
>>> > @@ -112,8 +216,17 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
>>> >  	data.compressed = compressed;
>>> >  	data.buf = buf;
>>> >
>>> > -	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
>>> > -				   (struct efivar_entry **)&psi->data);
>>> > +	*data.buf = kzalloc(1024, GFP_KERNEL);
>>> > +	if (!*data.buf)
>>> > +		return -ENOMEM;
>>> > +
>>> > +	efivar_entry_iter_begin();
>>> > +	size = efi_pstore_sysfs_entry_iter(&data,
>>> > +					   (struct efivar_entry **)&psi->data);
>>> > +	efivar_entry_iter_end();
>>> > +	if (size <= 0)
>>> > +		kfree(*data.buf);
>>> > +	return size;
>>> >  }
>>> >
>>> >  static int efi_pstore_write(enum pstore_type_id type,
>>> > @@ -184,9 +297,17 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
>>> >  			return 0;
>>> >  	}
>>> >
>>> > +	if (entry->scanning) {
>>> > +		/*
>>> > +		 * Skip deletion because this entry will be deleted
>>> > +		 * after scanning is completed.
>>> > +		 */
>>> > +		entry->deleting = true;
>>> > +	} else
>>> > +		list_del(&entry->list);
>>> > +
>>> >  	/* found */
>>> >  	__efivar_entry_delete(entry);
>>> > -	list_del(&entry->list);
>>> >
>>> >  	return 1;
>>> >  }
>>> > @@ -214,10 +335,12 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
>>> >
>>> >  	efivar_entry_iter_begin();
>>> >  	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
>>> > -	efivar_entry_iter_end();
>>> >
>>> > -	if (found)
>>> > +	if (found && !entry->scanning) {
>>> > +		efivar_entry_iter_end();
>>> >  		efivar_unregister(entry);
>>> > +	} else
>>> > +		efivar_entry_iter_end();
>>> >
>>> >  	return 0;
>>> >  }
>>> > diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
>>> > index 8a7432a..8c5a61a 100644
>>> > --- a/drivers/firmware/efi/efivars.c
>>> > +++ b/drivers/firmware/efi/efivars.c
>>> > @@ -383,12 +383,16 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
>>> >  	else if (__efivar_entry_delete(entry))
>>> >  		err = -EIO;
>>> >
>>> > -	efivar_entry_iter_end();
>>> > -
>>> > -	if (err)
>>> > +	if (err) {
>>> > +		efivar_entry_iter_end();
>>> >  		return err;
>>> > +	}
>>> >
>>> > -	efivar_unregister(entry);
>>> > +	if (!entry->scanning) {
>>> > +		efivar_entry_iter_end();
>>> > +		efivar_unregister(entry);
>>> > +	} else
>>> > +		efivar_entry_iter_end();
>>> >
>>> >  	/* It's dead Jim.... */
>>> >  	return count;
>>> > diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
>>> > index 391c67b..b22659c 100644
>>> > --- a/drivers/firmware/efi/vars.c
>>> > +++ b/drivers/firmware/efi/vars.c
>>> > @@ -683,8 +683,16 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
>>> >  	if (!found)
>>> >  		return NULL;
>>> >
>>> > -	if (remove)
>>> > -		list_del(&entry->list);
>>> > +	if (remove) {
>>> > +		if (entry->scanning) {
>>> > +			/*
>>> > +			 * The entry will be deleted
>>> > +			 * after scanning is completed.
>>> > +			 */
>>> > +			entry->deleting = true;
>>> > +		} else
>>> > +			list_del(&entry->list);
>>> > +	}
>>> >
>>> >  	return entry;
>>> >  }
>>> > diff --git a/include/linux/efi.h b/include/linux/efi.h
>>> > index 5f8f176..04088fb 100644
>>> > --- a/include/linux/efi.h
>>> > +++ b/include/linux/efi.h
>>> > @@ -782,6 +782,8 @@ struct efivar_entry {
>>> >  	struct efi_variable var;
>>> >  	struct list_head list;
>>> >  	struct kobject kobj;
>>> > +	bool scanning;
>>> > +	bool deleting;
>>> >  };
>>> >
>>> >  extern struct list_head efivar_sysfs_list;
>>> 
>>> 
>>> --
>>> Best,
>>> Madper Xie.


-- 
Best,
Madper Xie.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-17 15:07       ` Madper Xie
@ 2013-10-17 15:11         ` Seiji Aguchi
  0 siblings, 0 replies; 10+ messages in thread
From: Seiji Aguchi @ 2013-10-17 15:11 UTC (permalink / raw)
  To: Madper Xie
  Cc: linux-kernel, linux-efi, matt.fleming, tony.luck,
	Tomoki Sekiyama, dle-develop

Hi Madper,

Thank you for assisting me.

But, I need to discuss the implementation with Matt more.
After the discussion, I will post v4 and ask you to test it.

Please wait for a while.

Seiji

> -----Original Message-----
> From: Madper Xie [mailto:cxie@redhat.com]
> Sent: Thursday, October 17, 2013 11:07 AM
> To: Madper Xie
> Cc: Seiji Aguchi; linux-kernel@vger.kernel.org; linux-efi@vger.kernel.org; matt.fleming@intel.com; tony.luck@intel.com; Tomoki
> Sekiyama; dle-develop@lists.sourceforge.net
> Subject: Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
> 
> Hi folks,
>    I tested it on my DELL XPS desktop. And it won't show any warnings
>    when I mounting pstore and deleting pstore items after this patch
>    applied.
> 
>    Tested-by: Madper Xie
> cxie@redhat.com writes:
> 
> > Oops, It seems my mu4e(a email client for emacs)'s auto-indent breaks the
> > patch... I apologize for this...
> >
> > seiji.aguchi@hds.com writes:
> >
> >> Hi Madper,
> >>
> >> I tested this patch on 3.12-rc4.
> >> Could you please send me the log when you failed to apply?
> >>
> >> Seiji
> >>
> >>> -----Original Message-----
> >>> From: Madper Xie [mailto:cxie@redhat.com]
> >>> Sent: Thursday, October 17, 2013 1:54 AM
> >>> To: Seiji Aguchi
> >>> Cc: linux-kernel@vger.kernel.org; linux-efi@vger.kernel.org; matt.fleming@intel.com; tony.luck@intel.com; Tomoki Sekiyama;
> dle-
> >>> develop@lists.sourceforge.net
> >>> Subject: Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
> >>>
> >>> Howdy Seiji,
> >>>   I failed appily this patch to both 3.12-rc2 and 3.12-rc4. Could you
> >>>   please let me know which is the right tree for this patch?
> >>>
> >>>   Thanks,
> >>>   Madper.
> >>> seiji.aguchi@hds.com writes:
> >>>
> >>> > Change from v2:
> >>> > - Move dynamic memory allocation to efi_pstore_read() before holding
> >>> >   efivars->lock to protect entry->var.Data.
> >>> > - Access to entry->scanning while holding efivars->lock.
> >>> > - Move a comment about a returned value from efi_pstore_read_func() to
> >>> >   efi_pstore_read() because "size < 0" case may happen in efi_pstore_read().
> >>> >
> >>> > Currently, when mounting pstore file system, a read callback of efi_pstore
> >>> > driver runs mutiple times as below.
> >>> >
> >>> > - In the first read callback, scan efivar_sysfs_list from head and pass
> >>> >   a kmsg buffer of a entry to an upper pstore layer.
> >>> > - In the second read callback, rescan efivar_sysfs_list from the entry and pass
> >>> >   another kmsg buffer to it.
> >>> > - Repeat the scan and pass until the end of efivar_sysfs_list.
> >>> >
> >>> > In this process, an entry is read across the multiple read function calls.
> >>> > To avoid race between the read and erasion, the whole process above is
> >>> > protected by a spinlock, holding in open() and releasing in close().
> >>> >
> >>> > At the same time, kmemdup() is called to pass the buffer to pstore filesystem
> >>> > during it.
> >>> > And then, it causes a following lockdep warning.
> >>> >
> >>> > To make the dynamic memory allocation runnable without taking spinlock,
> >>> > holding off a deletion of sysfs entry if it happens while scanning it
> >>> > via efi_pstore, and deleting it after the scan is completed.
> >>> >
> >>> > To implement it, this patch introduces two flags, scanning and deleting,
> >>> > to efivar_entry.
> >>> >
> >>> > [    1.143710] ------------[ cut here ]------------
> >>> > [    1.144058] WARNING: CPU: 1 PID: 1 at kernel/lockdep.c:2740
> >>> > lockdep_trace_alloc+0x104/0x110()
> >>> > [    1.144058] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
> >>> > [    1.144058] Modules linked in:
> >>> >
> >>> > [    1.144058] CPU: 1 PID: 1 Comm: systemd Not tainted 3.11.0-rc5 #2
> >>> > [    1.144058]  0000000000000009 ffff8800797e9ae0 ffffffff816614a5
> >>> > ffff8800797e9b28
> >>> > [    1.144058]  ffff8800797e9b18 ffffffff8105510d 0000000000000080
> >>> > 0000000000000046
> >>> > [    1.144058]  00000000000000d0 00000000000003af ffffffff81ccd0c0
> >>> > ffff8800797e9b78
> >>> > [    1.144058] Call Trace:
> >>> > [    1.144058]  [<ffffffff816614a5>] dump_stack+0x54/0x74
> >>> > [    1.144058]  [<ffffffff8105510d>] warn_slowpath_common+0x7d/0xa0
> >>> > [    1.144058]  [<ffffffff8105517c>] warn_slowpath_fmt+0x4c/0x50
> >>> > [    1.144058]  [<ffffffff8131290f>] ? vsscanf+0x57f/0x7b0
> >>> > [    1.144058]  [<ffffffff810bbd74>] lockdep_trace_alloc+0x104/0x110
> >>> > [    1.144058]  [<ffffffff81192da0>] __kmalloc_track_caller+0x50/0x280
> >>> > [    1.144058]  [<ffffffff815147bb>] ?
> >>> > efi_pstore_read_func.part.1+0x12b/0x170
> >>> > [    1.144058]  [<ffffffff8115b260>] kmemdup+0x20/0x50
> >>> > [    1.144058]  [<ffffffff815147bb>] efi_pstore_read_func.part.1+0x12b/0x170
> >>> > [    1.144058]  [<ffffffff81514800>] ?
> >>> > efi_pstore_read_func.part.1+0x170/0x170
> >>> > [    1.144058]  [<ffffffff815148b4>] efi_pstore_read_func+0xb4/0xe0
> >>> > [    1.144058]  [<ffffffff81512b7b>] __efivar_entry_iter+0xfb/0x120
> >>> > [    1.144058]  [<ffffffff8151428f>] efi_pstore_read+0x3f/0x50
> >>> > [    1.144058]  [<ffffffff8128d7ba>] pstore_get_records+0x9a/0x150
> >>> > [    1.158207]  [<ffffffff812af25c>] ? selinux_d_instantiate+0x1c/0x20
> >>> > [    1.158207]  [<ffffffff8128ce30>] ? parse_options+0x80/0x80
> >>> > [    1.158207]  [<ffffffff8128ced5>] pstore_fill_super+0xa5/0xc0
> >>> > [    1.158207]  [<ffffffff811ae7d2>] mount_single+0xa2/0xd0
> >>> > [    1.158207]  [<ffffffff8128ccf8>] pstore_mount+0x18/0x20
> >>> > [    1.158207]  [<ffffffff811ae8b9>] mount_fs+0x39/0x1b0
> >>> > [    1.158207]  [<ffffffff81160550>] ? __alloc_percpu+0x10/0x20
> >>> > [    1.158207]  [<ffffffff811c9493>] vfs_kern_mount+0x63/0xf0
> >>> > [    1.158207]  [<ffffffff811cbb0e>] do_mount+0x23e/0xa20
> >>> > [    1.158207]  [<ffffffff8115b51b>] ? strndup_user+0x4b/0xf0
> >>> > [    1.158207]  [<ffffffff811cc373>] SyS_mount+0x83/0xc0
> >>> > [    1.158207]  [<ffffffff81673cc2>] system_call_fastpath+0x16/0x1b
> >>> > [    1.158207] ---[ end trace 61981bc62de9f6f4 ]---
> >>> >
> >>> > Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
> >>> > ---
> >>> >  drivers/firmware/efi/efi-pstore.c | 143 +++++++++++++++++++++++++++++++++++---
> >>> >  drivers/firmware/efi/efivars.c    |  12 ++--
> >>> >  drivers/firmware/efi/vars.c       |  12 +++-
> >>> >  include/linux/efi.h               |   2 +
> >>> >  4 files changed, 153 insertions(+), 16 deletions(-)
> >>> >
> >>> > diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
> >>> > index 5002d50..ebd5dbc 100644
> >>> > --- a/drivers/firmware/efi/efi-pstore.c
> >>> > +++ b/drivers/firmware/efi/efi-pstore.c
> >>> > @@ -18,14 +18,12 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
> >>> >
> >>> >  static int efi_pstore_open(struct pstore_info *psi)
> >>> >  {
> >>> > -	efivar_entry_iter_begin();
> >>> >  	psi->data = NULL;
> >>> >  	return 0;
> >>> >  }
> >>> >
> >>> >  static int efi_pstore_close(struct pstore_info *psi)
> >>> >  {
> >>> > -	efivar_entry_iter_end();
> >>> >  	psi->data = NULL;
> >>> >  	return 0;
> >>> >  }
> >>> > @@ -91,19 +89,125 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
> >>> >  	__efivar_entry_get(entry, &entry->var.Attributes,
> >>> >  			   &entry->var.DataSize, entry->var.Data);
> >>> >  	size = entry->var.DataSize;
> >>> > +	memcpy(*cb_data->buf, entry->var.Data, (size_t)min_t(unsigned long,
> >>> > +							     1024, size));
> >>> >
> >>> > -	*cb_data->buf = kmemdup(entry->var.Data, size, GFP_KERNEL);
> >>> > -	if (*cb_data->buf == NULL)
> >>> > -		return -ENOMEM;
> >>> >  	return size;
> >>> >  }
> >>> >
> >>> > +/**
> >>> > + * efi_pstore_scan_sysfs_enter
> >>> > + * @entry: scanning entry
> >>> > + * @next: next entry
> >>> > + * @head: list head
> >>> > + */
> >>> > +static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
> >>> > +					struct efivar_entry *next,
> >>> > +					struct list_head *head)
> >>> > +{
> >>> > +	pos->scanning = true;
> >>> > +	if (&next->list != head)
> >>> > +		next->scanning = true;
> >>> > +}
> >>> > +
> >>> > +/**
> >>> > + * __efi_pstore_scan_sysfs_exit
> >>> > + * @entry: deleting entry
> >>> > + * @turn_off_scanning: Check if a scanning flag should be turned off
> >>> > + */
> >>> > +static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
> >>> > +						bool turn_off_scanning)
> >>> > +{
> >>> > +	if (entry->deleting) {
> >>> > +		list_del(&entry->list);
> >>> > +		efivar_entry_iter_end();
> >>> > +		efivar_unregister(entry);
> >>> > +		efivar_entry_iter_begin();
> >>> > +	} else if (turn_off_scanning)
> >>> > +		entry->scanning = false;
> >>> > +}
> >>> > +
> >>> > +/**
> >>> > + * efi_pstore_scan_sysfs_exit
> >>> > + * @pos: scanning entry
> >>> > + * @next: next entry
> >>> > + * @head: list head
> >>> > + * @stop: a flag checking if scanning will stop
> >>> > + */
> >>> > +static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
> >>> > +				       struct efivar_entry *next,
> >>> > +				       struct list_head *head, bool stop)
> >>> > +{
> >>> > +	__efi_pstore_scan_sysfs_exit(pos, true);
> >>> > +	if (stop)
> >>> > +		__efi_pstore_scan_sysfs_exit(next, &next->list != head);
> >>> > +}
> >>> > +
> >>> > +/**
> >>> > + * efi_pstore_sysfs_entry_iter
> >>> > + *
> >>> > + * @data: function-specific data to pass to callback
> >>> > + * @pos: entry to begin iterating from
> >>> > + *
> >>> > + * You MUST call efivar_enter_iter_begin() before this function, and
> >>> > + * efivar_entry_iter_end() afterwards.
> >>> > + *
> >>> > + * It is possible to begin iteration from an arbitrary entry within
> >>> > + * the list by passing @pos. @pos is updated on return to point to
> >>> > + * the next entry of the last one passed to efi_pstore_read_func().
> >>> > + * To begin iterating from the beginning of the list @pos must be %NULL.
> >>> > + */
> >>> > +static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
> >>> > +{
> >>> > +	struct efivar_entry *entry, *n;
> >>> > +	struct list_head *head = &efivar_sysfs_list;
> >>> > +	int size = 0;
> >>> > +
> >>> > +	if (!*pos) {
> >>> > +		list_for_each_entry_safe(entry, n, head, list) {
> >>> > +			efi_pstore_scan_sysfs_enter(entry, n, head);
> >>> > +
> >>> > +			size = efi_pstore_read_func(entry, data);
> >>> > +			efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
> >>> > +			if (size)
> >>> > +				break;
> >>> > +		}
> >>> > +		*pos = n;
> >>> > +		return size;
> >>> > +	}
> >>> > +
> >>> > +	list_for_each_entry_safe_from((*pos), n, head, list) {
> >>> > +		efi_pstore_scan_sysfs_enter((*pos), n, head);
> >>> > +
> >>> > +		size = efi_pstore_read_func((*pos), data);
> >>> > +		efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
> >>> > +		if (size)
> >>> > +			break;
> >>> > +	}
> >>> > +	*pos = n;
> >>> > +	return size;
> >>> > +}
> >>> > +
> >>> > +/**
> >>> > + * efi_pstore_read
> >>> > + *
> >>> > + * This function returns a size of NVRAM entry logged via efi_pstore_write().
> >>> > + * The meaning and behavior of efi_pstore/pstore are as below.
> >>> > + *
> >>> > + * size > 0: Got data of an entry logged via efi_pstore_write() successfully,
> >>> > + *           and pstore filesystem will continue reading subsequent entries.
> >>> > + * size == 0: Entry was not logged via efi_pstore_write(),
> >>> > + *            and efi_pstore driver will continue reading subsequent entries.
> >>> > + * size < 0: Failed to get data of entry logging via efi_pstore_write(),
> >>> > + *           and pstore will stop reading entry.
> >>> > + */
> >>> >  static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
> >>> >  			       int *count, struct timespec *timespec,
> >>> >  			       char **buf, bool *compressed,
> >>> >  			       struct pstore_info *psi)
> >>> >  {
> >>> >  	struct pstore_read_data data;
> >>> > +	ssize_t size;
> >>> >
> >>> >  	data.id = id;
> >>> >  	data.type = type;
> >>> > @@ -112,8 +216,17 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
> >>> >  	data.compressed = compressed;
> >>> >  	data.buf = buf;
> >>> >
> >>> > -	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
> >>> > -				   (struct efivar_entry **)&psi->data);
> >>> > +	*data.buf = kzalloc(1024, GFP_KERNEL);
> >>> > +	if (!*data.buf)
> >>> > +		return -ENOMEM;
> >>> > +
> >>> > +	efivar_entry_iter_begin();
> >>> > +	size = efi_pstore_sysfs_entry_iter(&data,
> >>> > +					   (struct efivar_entry **)&psi->data);
> >>> > +	efivar_entry_iter_end();
> >>> > +	if (size <= 0)
> >>> > +		kfree(*data.buf);
> >>> > +	return size;
> >>> >  }
> >>> >
> >>> >  static int efi_pstore_write(enum pstore_type_id type,
> >>> > @@ -184,9 +297,17 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
> >>> >  			return 0;
> >>> >  	}
> >>> >
> >>> > +	if (entry->scanning) {
> >>> > +		/*
> >>> > +		 * Skip deletion because this entry will be deleted
> >>> > +		 * after scanning is completed.
> >>> > +		 */
> >>> > +		entry->deleting = true;
> >>> > +	} else
> >>> > +		list_del(&entry->list);
> >>> > +
> >>> >  	/* found */
> >>> >  	__efivar_entry_delete(entry);
> >>> > -	list_del(&entry->list);
> >>> >
> >>> >  	return 1;
> >>> >  }
> >>> > @@ -214,10 +335,12 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
> >>> >
> >>> >  	efivar_entry_iter_begin();
> >>> >  	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
> >>> > -	efivar_entry_iter_end();
> >>> >
> >>> > -	if (found)
> >>> > +	if (found && !entry->scanning) {
> >>> > +		efivar_entry_iter_end();
> >>> >  		efivar_unregister(entry);
> >>> > +	} else
> >>> > +		efivar_entry_iter_end();
> >>> >
> >>> >  	return 0;
> >>> >  }
> >>> > diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
> >>> > index 8a7432a..8c5a61a 100644
> >>> > --- a/drivers/firmware/efi/efivars.c
> >>> > +++ b/drivers/firmware/efi/efivars.c
> >>> > @@ -383,12 +383,16 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
> >>> >  	else if (__efivar_entry_delete(entry))
> >>> >  		err = -EIO;
> >>> >
> >>> > -	efivar_entry_iter_end();
> >>> > -
> >>> > -	if (err)
> >>> > +	if (err) {
> >>> > +		efivar_entry_iter_end();
> >>> >  		return err;
> >>> > +	}
> >>> >
> >>> > -	efivar_unregister(entry);
> >>> > +	if (!entry->scanning) {
> >>> > +		efivar_entry_iter_end();
> >>> > +		efivar_unregister(entry);
> >>> > +	} else
> >>> > +		efivar_entry_iter_end();
> >>> >
> >>> >  	/* It's dead Jim.... */
> >>> >  	return count;
> >>> > diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
> >>> > index 391c67b..b22659c 100644
> >>> > --- a/drivers/firmware/efi/vars.c
> >>> > +++ b/drivers/firmware/efi/vars.c
> >>> > @@ -683,8 +683,16 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
> >>> >  	if (!found)
> >>> >  		return NULL;
> >>> >
> >>> > -	if (remove)
> >>> > -		list_del(&entry->list);
> >>> > +	if (remove) {
> >>> > +		if (entry->scanning) {
> >>> > +			/*
> >>> > +			 * The entry will be deleted
> >>> > +			 * after scanning is completed.
> >>> > +			 */
> >>> > +			entry->deleting = true;
> >>> > +		} else
> >>> > +			list_del(&entry->list);
> >>> > +	}
> >>> >
> >>> >  	return entry;
> >>> >  }
> >>> > diff --git a/include/linux/efi.h b/include/linux/efi.h
> >>> > index 5f8f176..04088fb 100644
> >>> > --- a/include/linux/efi.h
> >>> > +++ b/include/linux/efi.h
> >>> > @@ -782,6 +782,8 @@ struct efivar_entry {
> >>> >  	struct efi_variable var;
> >>> >  	struct list_head list;
> >>> >  	struct kobject kobj;
> >>> > +	bool scanning;
> >>> > +	bool deleting;
> >>> >  };
> >>> >
> >>> >  extern struct list_head efivar_sysfs_list;
> >>>
> >>>
> >>> --
> >>> Best,
> >>> Madper Xie.
> 
> 
> --
> Best,
> Madper Xie.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-17 13:18 ` Matt Fleming
@ 2013-10-18 22:30   ` Seiji Aguchi
  2013-10-30 14:35     ` Matt Fleming
  0 siblings, 1 reply; 10+ messages in thread
From: Seiji Aguchi @ 2013-10-18 22:30 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-kernel, linux-efi, matt.fleming, tony.luck,
	Tomoki Sekiyama, dle-develop

Matt,

> It seems to me that because you're no longer dropping __efivars->lock
> when reading from the EFI variable store, you actually don't need all
> the ->scanning and ->deleting logic because anything that sets those
> flags runs to completion while holding the lock.

The scanning and deleting logic is still needed.
In case an entry(A) is found, the pointer is saved to psi->data.
And efi_pstore_read() passes the entry(A) to a pstore filesystem by releasing  __efivars->lock.

And then, the pstore filesystem calls efi_pstore_read() again and the same entry(A), which is saved to
psi->data, is used for re-scanning a sysfs-list.
(That is why list_for_each_entry_safe_from () is used in efi_pstore_sysfs_entry_iter().)

So, to protect the entry(A), the logic is needed because, in pstore filesystem,  __efivars->lock
Is released. 

> Can't the patch be simplified to just allocating data.buf at the
> beginning of efi_pstore_read()? 

I think data.buf is simply allocated at the beginning of efi_pstore_read() with this patch v3.
If you are not satisfied with it, could you please show me your pseudo code?

>Also, it would be a good idea to
> introduce a #define for the 1024 magic number, e.g.
> 
> #define EFIVARS_DATA_SIZE_MAX	1024

It is good idea. I can fix it.

Seiji

<snip>
+/**
+ * efi_pstore_read
+ *
+ * This function returns a size of NVRAM entry logged via efi_pstore_write().
+ * The meaning and behavior of efi_pstore/pstore are as below.
+ *
+ * size > 0: Got data of an entry logged via efi_pstore_write() successfully,
+ *           and pstore filesystem will continue reading subsequent entries.
+ * size == 0: Entry was not logged via efi_pstore_write(),
+ *            and efi_pstore driver will continue reading subsequent entries.
+ * size < 0: Failed to get data of entry logging via efi_pstore_write(),
+ *           and pstore will stop reading entry.
+ */
 static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
 			       int *count, struct timespec *timespec,
 			       char **buf, bool *compressed,
 			       struct pstore_info *psi)
 {
 	struct pstore_read_data data;
+	ssize_t size;
 
 	data.id = id;
 	data.type = type;
@@ -112,8 +216,17 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
 	data.compressed = compressed;
 	data.buf = buf;
 
-	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
-				   (struct efivar_entry **)&psi->data);
+	*data.buf = kzalloc(1024, GFP_KERNEL);
+	if (!*data.buf)
+		return -ENOMEM;
+
+	efivar_entry_iter_begin();
+	size = efi_pstore_sysfs_entry_iter(&data,
+					   (struct efivar_entry **)&psi->data);
+	efivar_entry_iter_end();
+	if (size <= 0)
+		kfree(*data.buf);
+	return size;
 }
<snip>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-18 22:30   ` Seiji Aguchi
@ 2013-10-30 14:35     ` Matt Fleming
  2013-10-30 19:19       ` Seiji Aguchi
  0 siblings, 1 reply; 10+ messages in thread
From: Matt Fleming @ 2013-10-30 14:35 UTC (permalink / raw)
  To: Seiji Aguchi
  Cc: linux-kernel, linux-efi, matt.fleming, tony.luck,
	Tomoki Sekiyama, dle-develop

On Fri, 18 Oct, at 10:30:58PM, Seiji Aguchi wrote:
> The scanning and deleting logic is still needed.  In case an entry(A)
> is found, the pointer is saved to psi->data.  And efi_pstore_read()
> passes the entry(A) to a pstore filesystem by releasing
> __efivars->lock.
> 
> And then, the pstore filesystem calls efi_pstore_read() again and the
> same entry(A), which is saved to psi->data, is used for re-scanning a
> sysfs-list.  (That is why list_for_each_entry_safe_from () is used in
> efi_pstore_sysfs_entry_iter().)
> 
> So, to protect the entry(A), the logic is needed because, in pstore
> filesystem,  __efivars->lock Is released. 
 
Very good point.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed
  2013-10-30 14:35     ` Matt Fleming
@ 2013-10-30 19:19       ` Seiji Aguchi
  0 siblings, 0 replies; 10+ messages in thread
From: Seiji Aguchi @ 2013-10-30 19:19 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-kernel, linux-efi, matt.fleming, tony.luck,
	Tomoki Sekiyama, dle-develop

> On Fri, 18 Oct, at 10:30:58PM, Seiji Aguchi wrote:
> > The scanning and deleting logic is still needed.  In case an entry(A)
> > is found, the pointer is saved to psi->data.  And efi_pstore_read()
> > passes the entry(A) to a pstore filesystem by releasing
> > __efivars->lock.
> >
> > And then, the pstore filesystem calls efi_pstore_read() again and the
> > same entry(A), which is saved to psi->data, is used for re-scanning a
> > sysfs-list.  (That is why list_for_each_entry_safe_from () is used in
> > efi_pstore_sysfs_entry_iter().)
> >
> > So, to protect the entry(A), the logic is needed because, in pstore
> > filesystem,  __efivars->lock Is released.
> 
> Very good point.

Thanks,

I will add the description in the next patch.

Seiji

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2013-10-30 19:19 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-11 18:29 [PATCH v3] efivars,efi-pstore: Hold off deletion of sysfs entry until, the scan is completed Seiji Aguchi
2013-10-17  5:54 ` Madper Xie
2013-10-17 13:07   ` Seiji Aguchi
2013-10-17 14:30     ` Madper Xie
2013-10-17 15:07       ` Madper Xie
2013-10-17 15:11         ` Seiji Aguchi
2013-10-17 13:18 ` Matt Fleming
2013-10-18 22:30   ` Seiji Aguchi
2013-10-30 14:35     ` Matt Fleming
2013-10-30 19:19       ` Seiji Aguchi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).