public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] firmware_class: encapsulate firmware loading status
@ 2016-08-04  9:23 Daniel Wagner
  2016-08-04 10:33 ` kbuild test robot
  0 siblings, 1 reply; 9+ messages in thread
From: Daniel Wagner @ 2016-08-04  9:23 UTC (permalink / raw)
  To: Ming Lei
  Cc: linux-kernel, Daniel Wagner, Luis R . Rodriguez,
	Greg Kroah-Hartman

From: Daniel Wagner <daniel.wagner@bmw-carit.de>

The firmware user helper code tracks the current state of the loading
process via an member of struct firmware_buf and a completion. Let's
encapsulate this simple state machine into struct fw_status. The aim is
to encrease readiblity and reduce the usage of the fw_lock.

The fw_lock is not needed to protect the status update anymore. We don't
do any RMW operations. Instead we just do a write or a read, not both at
the same time.

Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de>
Cc: Ming Lei <ming.lei@canonical.com>
Cc: Luis R. Rodriguez <mcgrof@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---

Hi,

In [0] we have a discussion on how the firmware_class API might be
changed to improve the current handling of firmware loading. This
patch was part of the orignal RFC which triggered the discussion.

I think it is worth taking this one anyway. Maybe as I suggested it
could be part of the series from Luis.

It cleans up the code base (okay my opinion) and removes the
complete_all() call which is problematic for -rt. complete_all() can
be used in any context including IRQ. That could lead to unbounded
work in the IRQ context and that is a no go for -rt. So here the
attempt to reduce the number of complete_all() calls where possible. I
have left this argument out in the commit message because I was told
'-rt' arguments don't count for inclusion.

cheers,
daniel

[0] http://www.spinics.net/lists/linux-wireless/msg153005.html

drivers/base/firmware_class.c | 148 ++++++++++++++++++++++++------------------
 1 file changed, 85 insertions(+), 63 deletions(-)

diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 773fc30..665a803 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -30,6 +30,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/reboot.h>
 #include <linux/security.h>
+#include <linux/swait.h>
 
 #include <generated/utsrelease.h>
 
@@ -85,19 +86,78 @@ static inline bool fw_is_builtin_firmware(const struct firmware *fw)
 }
 #endif
 
+static int loading_timeout = 60;	/* In seconds */
+
+static inline long firmware_loading_timeout(void)
+{
+	return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
+}
+
 enum {
+	FW_STATUS_UNKNOWN,
 	FW_STATUS_LOADING,
 	FW_STATUS_DONE,
-	FW_STATUS_ABORT,
+	FW_STATUS_ABORTED,
 };
 
-static int loading_timeout = 60;	/* In seconds */
+struct fw_status {
+	unsigned long status;
+	struct swait_queue_head wq;
+};
 
-static inline long firmware_loading_timeout(void)
+
+static void fw_status_init(struct fw_status *fw_st)
 {
-	return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
+	fw_st->status = FW_STATUS_UNKNOWN;
+	init_swait_queue_head(&fw_st->wq);
+}
+
+static unsigned long __fw_status_get(struct fw_status *fw_st)
+{
+	return READ_ONCE(fw_st->status);
+}
+
+static inline bool is_fw_status_done(unsigned long status)
+{
+	return status == FW_STATUS_DONE ||
+	       status == FW_STATUS_ABORTED;
+}
+
+static int fw_status_wait_timeout(struct fw_status *fw_st, long timeout)
+{
+	int err;
+	err = swait_event_interruptible_timeout(fw_st->wq,
+				is_fw_status_done(READ_ONCE(fw_st->status)),
+				timeout);
+	if (err == 0 && fw_st->status == FW_STATUS_ABORTED)
+		return -ENOENT;
+
+	return err;
 }
 
+static void __fw_status_set(struct fw_status *fw_st,
+			  unsigned long status)
+{
+	WRITE_ONCE(fw_st->status, status);
+
+	if (status == FW_STATUS_DONE ||
+			status == FW_STATUS_ABORTED)
+		swake_up(&fw_st->wq);
+}
+
+#define fw_status_start(fw_st)					\
+	__fw_status_set(fw_st, FW_STATUS_LOADING)
+#define fw_status_done(fw_st)					\
+	__fw_status_set(fw_st, FW_STATUS_DONE)
+#define fw_status_aborted(fw_st)				\
+	__fw_status_set(fw_st, FW_STATUS_ABORTED)
+#define fw_status_is_loading(fw_st)				\
+	(__fw_status_get(fw_st) == FW_STATUS_LOADING)
+#define fw_status_is_done(fw_st)				\
+	(__fw_status_get(fw_st) == FW_STATUS_DONE)
+#define fw_status_is_aborted(fw_st)				\
+	(__fw_status_get(fw_st) == FW_STATUS_ABORTED)
+
 /* firmware behavior options */
 #define FW_OPT_UEVENT	(1U << 0)
 #define FW_OPT_NOWAIT	(1U << 1)
@@ -138,9 +198,8 @@ struct firmware_cache {
 struct firmware_buf {
 	struct kref ref;
 	struct list_head list;
-	struct completion completion;
+	struct fw_status fw_st;
 	struct firmware_cache *fwc;
-	unsigned long status;
 	void *data;
 	size_t size;
 #ifdef CONFIG_FW_LOADER_USER_HELPER
@@ -194,7 +253,7 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
 
 	kref_init(&buf->ref);
 	buf->fwc = fwc;
-	init_completion(&buf->completion);
+	fw_status_init(&buf->fw_st);
 #ifdef CONFIG_FW_LOADER_USER_HELPER
 	INIT_LIST_HEAD(&buf->pending_list);
 #endif
@@ -292,15 +351,6 @@ static const char * const fw_path[] = {
 module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
 MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
 
-static void fw_finish_direct_load(struct device *device,
-				  struct firmware_buf *buf)
-{
-	mutex_lock(&fw_lock);
-	set_bit(FW_STATUS_DONE, &buf->status);
-	complete_all(&buf->completion);
-	mutex_unlock(&fw_lock);
-}
-
 static int fw_get_filesystem_firmware(struct device *device,
 				       struct firmware_buf *buf)
 {
@@ -339,7 +389,7 @@ static int fw_get_filesystem_firmware(struct device *device,
 		}
 		dev_dbg(device, "direct-loading %s\n", buf->fw_id);
 		buf->size = size;
-		fw_finish_direct_load(device, buf);
+		fw_status_done(&buf->fw_st);
 		break;
 	}
 	__putname(path);
@@ -457,12 +507,11 @@ static void __fw_load_abort(struct firmware_buf *buf)
 	 * There is a small window in which user can write to 'loading'
 	 * between loading done and disappearance of 'loading'
 	 */
-	if (test_bit(FW_STATUS_DONE, &buf->status))
+	if (fw_status_is_done(&buf->fw_st))
 		return;
 
 	list_del_init(&buf->pending_list);
-	set_bit(FW_STATUS_ABORT, &buf->status);
-	complete_all(&buf->completion);
+	fw_status_aborted(&buf->fw_st);
 }
 
 static void fw_load_abort(struct firmware_priv *fw_priv)
@@ -475,9 +524,6 @@ static void fw_load_abort(struct firmware_priv *fw_priv)
 	fw_priv->buf = NULL;
 }
 
-#define is_fw_load_aborted(buf)	\
-	test_bit(FW_STATUS_ABORT, &(buf)->status)
-
 static LIST_HEAD(pending_fw_head);
 
 /* reboot notifier for avoid deadlock with usermode_lock */
@@ -575,10 +621,8 @@ static ssize_t firmware_loading_show(struct device *dev,
 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
 	int loading = 0;
 
-	mutex_lock(&fw_lock);
 	if (fw_priv->buf)
-		loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
-	mutex_unlock(&fw_lock);
+		loading = fw_status_is_loading(&fw_priv->buf->fw_st);
 
 	return sprintf(buf, "%d\n", loading);
 }
@@ -632,23 +676,20 @@ static ssize_t firmware_loading_store(struct device *dev,
 	switch (loading) {
 	case 1:
 		/* discarding any previous partial load */
-		if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) {
+		if (!fw_status_is_done(&fw_buf->fw_st)) {
 			for (i = 0; i < fw_buf->nr_pages; i++)
 				__free_page(fw_buf->pages[i]);
 			vfree(fw_buf->pages);
 			fw_buf->pages = NULL;
 			fw_buf->page_array_size = 0;
 			fw_buf->nr_pages = 0;
-			set_bit(FW_STATUS_LOADING, &fw_buf->status);
+			fw_status_start(&fw_buf->fw_st);
 		}
 		break;
 	case 0:
-		if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
+		if (fw_status_is_loading(&fw_buf->fw_st)) {
 			int rc;
 
-			set_bit(FW_STATUS_DONE, &fw_buf->status);
-			clear_bit(FW_STATUS_LOADING, &fw_buf->status);
-
 			/*
 			 * Several loading requests may be pending on
 			 * one same firmware buf, so let all requests
@@ -670,10 +711,11 @@ static ssize_t firmware_loading_store(struct device *dev,
 			 */
 			list_del_init(&fw_buf->pending_list);
 			if (rc) {
-				set_bit(FW_STATUS_ABORT, &fw_buf->status);
+				fw_status_aborted(&fw_buf->fw_st);
 				written = rc;
+			} else {
+				fw_status_done(&fw_buf->fw_st);
 			}
-			complete_all(&fw_buf->completion);
 			break;
 		}
 		/* fallthrough */
@@ -681,7 +723,7 @@ static ssize_t firmware_loading_store(struct device *dev,
 		dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
 		/* fallthrough */
 	case -1:
-		fw_load_abort(fw_priv);
+		fw_status_aborted(&fw_buf->fw_st);
 		break;
 	}
 out:
@@ -702,7 +744,7 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
 
 	mutex_lock(&fw_lock);
 	buf = fw_priv->buf;
-	if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
+	if (!buf || fw_status_is_done(&buf->fw_st)) {
 		ret_count = -ENODEV;
 		goto out;
 	}
@@ -799,7 +841,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
 
 	mutex_lock(&fw_lock);
 	buf = fw_priv->buf;
-	if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
+	if (!buf || fw_status_is_done(&buf->fw_st)) {
 		retval = -ENODEV;
 		goto out;
 	}
@@ -917,8 +959,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
 		timeout = MAX_JIFFY_OFFSET;
 	}
 
-	retval = wait_for_completion_interruptible_timeout(&buf->completion,
-			timeout);
+	retval = fw_status_wait_timeout(&buf->fw_st, timeout);
 	if (retval == -ERESTARTSYS || !retval) {
 		mutex_lock(&fw_lock);
 		fw_load_abort(fw_priv);
@@ -927,7 +968,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
 		retval = 0;
 	}
 
-	if (is_fw_load_aborted(buf))
+	if (fw_status_is_aborted(&buf->fw_st))
 		retval = -EAGAIN;
 	else if (!buf->data)
 		retval = -ENOMEM;
@@ -978,7 +1019,7 @@ fw_load_from_user_helper(struct firmware *firmware, const char *name,
 }
 
 /* No abort during direct loading */
-#define is_fw_load_aborted(buf) false
+#define fw_status_is_aborted(buf) false
 
 #ifdef CONFIG_PM_SLEEP
 static inline void kill_requests_without_uevent(void) { }
@@ -986,26 +1027,6 @@ static inline void kill_requests_without_uevent(void) { }
 
 #endif /* CONFIG_FW_LOADER_USER_HELPER */
 
-
-/* wait until the shared firmware_buf becomes ready (or error) */
-static int sync_cached_firmware_buf(struct firmware_buf *buf)
-{
-	int ret = 0;
-
-	mutex_lock(&fw_lock);
-	while (!test_bit(FW_STATUS_DONE, &buf->status)) {
-		if (is_fw_load_aborted(buf)) {
-			ret = -ENOENT;
-			break;
-		}
-		mutex_unlock(&fw_lock);
-		ret = wait_for_completion_interruptible(&buf->completion);
-		mutex_lock(&fw_lock);
-	}
-	mutex_unlock(&fw_lock);
-	return ret;
-}
-
 /* prepare firmware and firmware_buf structs;
  * return 0 if a firmware is already assigned, 1 if need to load one,
  * or a negative error code
@@ -1039,7 +1060,8 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
 	firmware->priv = buf;
 
 	if (ret > 0) {
-		ret = sync_cached_firmware_buf(buf);
+		ret = fw_status_wait_timeout(&buf->fw_st,
+					   firmware_loading_timeout());
 		if (!ret) {
 			fw_set_page_data(buf, firmware);
 			return 0; /* assigned */
@@ -1057,7 +1079,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
 	struct firmware_buf *buf = fw->priv;
 
 	mutex_lock(&fw_lock);
-	if (!buf->size || is_fw_load_aborted(buf)) {
+	if (!buf->size || fw_status_is_aborted(&buf->fw_st)) {
 		mutex_unlock(&fw_lock);
 		return -ENOENT;
 	}
-- 
2.7.4

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

* Re: [PATCH] firmware_class: encapsulate firmware loading status
  2016-08-04  9:23 [PATCH] firmware_class: encapsulate firmware loading status Daniel Wagner
@ 2016-08-04 10:33 ` kbuild test robot
  2016-08-04 12:27   ` [PATCH v1] " Daniel Wagner
  0 siblings, 1 reply; 9+ messages in thread
From: kbuild test robot @ 2016-08-04 10:33 UTC (permalink / raw)
  To: Daniel Wagner
  Cc: kbuild-all, Ming Lei, linux-kernel, Daniel Wagner,
	Luis R . Rodriguez, Greg Kroah-Hartman

[-- Attachment #1: Type: text/plain, Size: 1948 bytes --]

Hi Daniel,

[auto build test WARNING on v4.7-rc7]
[cannot apply to next-20160803]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Daniel-Wagner/firmware_class-encapsulate-firmware-loading-status/20160804-173551
config: cris-etrax-100lx_v2_defconfig (attached as .config)
compiler: cris-linux-gcc (GCC) 4.6.3
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=cris 

All warnings (new ones prefixed by >>):

>> drivers/base/firmware_class.c:1022:0: warning: "fw_status_is_aborted" redefined [enabled by default]
   drivers/base/firmware_class.c:158:0: note: this is the location of the previous definition
   drivers/base/firmware_class.c:115:22: warning: '__fw_status_get' defined but not used [-Wunused-function]

vim +/fw_status_is_aborted +1022 drivers/base/firmware_class.c

  1006				 __fw_load_abort(buf);
  1007		}
  1008		mutex_unlock(&fw_lock);
  1009	}
  1010	#endif
  1011	
  1012	#else /* CONFIG_FW_LOADER_USER_HELPER */
  1013	static inline int
  1014	fw_load_from_user_helper(struct firmware *firmware, const char *name,
  1015				 struct device *device, unsigned int opt_flags,
  1016				 long timeout)
  1017	{
  1018		return -ENOENT;
  1019	}
  1020	
  1021	/* No abort during direct loading */
> 1022	#define fw_status_is_aborted(buf) false
  1023	
  1024	#ifdef CONFIG_PM_SLEEP
  1025	static inline void kill_requests_without_uevent(void) { }
  1026	#endif
  1027	
  1028	#endif /* CONFIG_FW_LOADER_USER_HELPER */
  1029	
  1030	/* prepare firmware and firmware_buf structs;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 8254 bytes --]

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

* [PATCH v1] firmware_class: encapsulate firmware loading status
  2016-08-04 10:33 ` kbuild test robot
@ 2016-08-04 12:27   ` Daniel Wagner
  2016-08-10  1:10     ` Luis R. Rodriguez
  0 siblings, 1 reply; 9+ messages in thread
From: Daniel Wagner @ 2016-08-04 12:27 UTC (permalink / raw)
  To: Ming Lei
  Cc: linux-kernel, Daniel Wagner, Luis R . Rodriguez,
	Greg Kroah-Hartman

From: Daniel Wagner <daniel.wagner@bmw-carit.de>

The firmware user helper code tracks the current state of the loading
process via an member of struct firmware_buf and a completion. Let's
encapsulate this simple state machine into struct fw_status. The aim is
to encrease readiblity and reduce the usage of the fw_lock.

The fw_lock is not needed to protect the status update anymore. We don't
do any RMW operations. Instead we just do a write or a read, not both at
the same time.

[v1: moved fw_status into !CONFIG_FW_LOADER_USER_HELPER section,
     reported by 0day kbuild]

Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de>
Cc: Ming Lei <ming.lei@canonical.com>
Cc: Luis R. Rodriguez <mcgrof@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---

Hi,

In [0] we have a discussion on how the firmware_class API might be
changed to improve the current handling of firmware loading. This
patch was part of the orignal RFC which triggered the discussion.

I think it is worth taking this one anyway. Maybe as I suggested it
could be part of the series from Luis.

It cleans up the code base (okay my opinion) and removes the
complete_all() call which is problematic for -rt. complete_all() can
be used in any context including IRQ. That could lead to unbounded
work in the IRQ context and that is a no go for -rt. So here the
attempt to reduce the number of complete_all() calls where possible. I
have left this argument out in the commit message because I was told
'-rt' arguments don't count for inclusion.

cheers,
daniel

[0] http://www.spinics.net/lists/linux-wireless/msg153005.html

drivers/base/firmware_class.c | 154 ++++++++++++++++++++++++------------------
 1 file changed, 89 insertions(+), 65 deletions(-)

diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 22d1760..33eb372 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -30,6 +30,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/reboot.h>
 #include <linux/security.h>
+#include <linux/swait.h>
 
 #include <generated/utsrelease.h>
 
@@ -91,19 +92,83 @@ static inline bool fw_is_builtin_firmware(const struct firmware *fw)
 }
 #endif
 
+static int loading_timeout = 60;	/* In seconds */
+
+static inline long firmware_loading_timeout(void)
+{
+	return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
+}
+
 enum {
+	FW_STATUS_UNKNOWN,
 	FW_STATUS_LOADING,
 	FW_STATUS_DONE,
-	FW_STATUS_ABORT,
+	FW_STATUS_ABORTED,
 };
 
-static int loading_timeout = 60;	/* In seconds */
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+struct fw_status {
+	unsigned long status;
+	struct swait_queue_head wq;
+};
 
-static inline long firmware_loading_timeout(void)
+static void fw_status_init(struct fw_status *fw_st)
 {
-	return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
+	fw_st->status = FW_STATUS_UNKNOWN;
+	init_swait_queue_head(&fw_st->wq);
+}
+
+static inline bool is_fw_status_done(unsigned long status)
+{
+	return status == FW_STATUS_DONE ||
+	       status == FW_STATUS_ABORTED;
+}
+
+static int fw_status_wait_timeout(struct fw_status *fw_st, long timeout)
+{
+	int err;
+	err = swait_event_interruptible_timeout(fw_st->wq,
+				is_fw_status_done(READ_ONCE(fw_st->status)),
+				timeout);
+	if (err == 0 && fw_st->status == FW_STATUS_ABORTED)
+		return -ENOENT;
+
+	return err;
+}
+
+static void __fw_status_set(struct fw_status *fw_st,
+			  unsigned long status)
+{
+	WRITE_ONCE(fw_st->status, status);
+
+	if (status == FW_STATUS_DONE ||
+			status == FW_STATUS_ABORTED)
+		swake_up(&fw_st->wq);
+}
+
+static unsigned long __fw_status_get(struct fw_status *fw_st)
+{
+	return READ_ONCE(fw_st->status);
 }
 
+#define fw_status_start(fw_st)					\
+	__fw_status_set(fw_st, FW_STATUS_LOADING)
+#define fw_status_done(fw_st)					\
+	__fw_status_set(fw_st, FW_STATUS_DONE)
+#define fw_status_aborted(fw_st)				\
+	__fw_status_set(fw_st, FW_STATUS_ABORTED)
+#define fw_status_is_loading(fw_st)				\
+	(__fw_status_get(fw_st) == FW_STATUS_LOADING)
+#define fw_status_is_done(fw_st)				\
+	(__fw_status_get(fw_st) == FW_STATUS_DONE)
+#define fw_status_is_aborted(fw_st)				\
+	(__fw_status_get(fw_st) == FW_STATUS_ABORTED)
+#else
+#define fw_status_wait_timeout(fw_st, timeout)	0
+#define fw_status_done(fw_st)
+#define fw_status_is_aborted(fw_st)		false
+#endif /* CONFIG_FW_LOADER_USER_HELPER */
+
 /* firmware behavior options */
 #define FW_OPT_UEVENT	(1U << 0)
 #define FW_OPT_NOWAIT	(1U << 1)
@@ -145,13 +210,12 @@ struct firmware_cache {
 struct firmware_buf {
 	struct kref ref;
 	struct list_head list;
-	struct completion completion;
 	struct firmware_cache *fwc;
-	unsigned long status;
 	void *data;
 	size_t size;
 	size_t allocated_size;
 #ifdef CONFIG_FW_LOADER_USER_HELPER
+	struct fw_status fw_st;
 	bool is_paged_buf;
 	bool need_uevent;
 	struct page **pages;
@@ -205,8 +269,8 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
 	buf->fwc = fwc;
 	buf->data = dbuf;
 	buf->allocated_size = size;
-	init_completion(&buf->completion);
 #ifdef CONFIG_FW_LOADER_USER_HELPER
+	fw_status_init(&buf->fw_st);
 	INIT_LIST_HEAD(&buf->pending_list);
 #endif
 
@@ -305,15 +369,6 @@ static const char * const fw_path[] = {
 module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
 MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
 
-static void fw_finish_direct_load(struct device *device,
-				  struct firmware_buf *buf)
-{
-	mutex_lock(&fw_lock);
-	set_bit(FW_STATUS_DONE, &buf->status);
-	complete_all(&buf->completion);
-	mutex_unlock(&fw_lock);
-}
-
 static int
 fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
 {
@@ -360,7 +415,7 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
 		}
 		dev_dbg(device, "direct-loading %s\n", buf->fw_id);
 		buf->size = size;
-		fw_finish_direct_load(device, buf);
+		fw_status_done(&buf->fw_st);
 		break;
 	}
 	__putname(path);
@@ -478,12 +533,11 @@ static void __fw_load_abort(struct firmware_buf *buf)
 	 * There is a small window in which user can write to 'loading'
 	 * between loading done and disappearance of 'loading'
 	 */
-	if (test_bit(FW_STATUS_DONE, &buf->status))
+	if (fw_status_is_done(&buf->fw_st))
 		return;
 
 	list_del_init(&buf->pending_list);
-	set_bit(FW_STATUS_ABORT, &buf->status);
-	complete_all(&buf->completion);
+	fw_status_aborted(&buf->fw_st);
 }
 
 static void fw_load_abort(struct firmware_priv *fw_priv)
@@ -496,9 +550,6 @@ static void fw_load_abort(struct firmware_priv *fw_priv)
 	fw_priv->buf = NULL;
 }
 
-#define is_fw_load_aborted(buf)	\
-	test_bit(FW_STATUS_ABORT, &(buf)->status)
-
 static LIST_HEAD(pending_fw_head);
 
 /* reboot notifier for avoid deadlock with usermode_lock */
@@ -596,10 +647,8 @@ static ssize_t firmware_loading_show(struct device *dev,
 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
 	int loading = 0;
 
-	mutex_lock(&fw_lock);
 	if (fw_priv->buf)
-		loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
-	mutex_unlock(&fw_lock);
+		loading = fw_status_is_loading(&fw_priv->buf->fw_st);
 
 	return sprintf(buf, "%d\n", loading);
 }
@@ -653,23 +702,20 @@ static ssize_t firmware_loading_store(struct device *dev,
 	switch (loading) {
 	case 1:
 		/* discarding any previous partial load */
-		if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) {
+		if (!fw_status_is_done(&fw_buf->fw_st)) {
 			for (i = 0; i < fw_buf->nr_pages; i++)
 				__free_page(fw_buf->pages[i]);
 			vfree(fw_buf->pages);
 			fw_buf->pages = NULL;
 			fw_buf->page_array_size = 0;
 			fw_buf->nr_pages = 0;
-			set_bit(FW_STATUS_LOADING, &fw_buf->status);
+			fw_status_start(&fw_buf->fw_st);
 		}
 		break;
 	case 0:
-		if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
+		if (fw_status_is_loading(&fw_buf->fw_st)) {
 			int rc;
 
-			set_bit(FW_STATUS_DONE, &fw_buf->status);
-			clear_bit(FW_STATUS_LOADING, &fw_buf->status);
-
 			/*
 			 * Several loading requests may be pending on
 			 * one same firmware buf, so let all requests
@@ -691,10 +737,11 @@ static ssize_t firmware_loading_store(struct device *dev,
 			 */
 			list_del_init(&fw_buf->pending_list);
 			if (rc) {
-				set_bit(FW_STATUS_ABORT, &fw_buf->status);
+				fw_status_aborted(&fw_buf->fw_st);
 				written = rc;
+			} else {
+				fw_status_done(&fw_buf->fw_st);
 			}
-			complete_all(&fw_buf->completion);
 			break;
 		}
 		/* fallthrough */
@@ -702,7 +749,7 @@ static ssize_t firmware_loading_store(struct device *dev,
 		dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
 		/* fallthrough */
 	case -1:
-		fw_load_abort(fw_priv);
+		fw_status_aborted(&fw_buf->fw_st);
 		break;
 	}
 out:
@@ -755,7 +802,7 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
 
 	mutex_lock(&fw_lock);
 	buf = fw_priv->buf;
-	if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
+	if (!buf || fw_status_is_done(&buf->fw_st)) {
 		ret_count = -ENODEV;
 		goto out;
 	}
@@ -842,7 +889,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
 
 	mutex_lock(&fw_lock);
 	buf = fw_priv->buf;
-	if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
+	if (!buf || fw_status_is_done(&buf->fw_st)) {
 		retval = -ENODEV;
 		goto out;
 	}
@@ -955,8 +1002,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
 		timeout = MAX_JIFFY_OFFSET;
 	}
 
-	retval = wait_for_completion_interruptible_timeout(&buf->completion,
-			timeout);
+	retval = fw_status_wait_timeout(&buf->fw_st, timeout);
 	if (retval == -ERESTARTSYS || !retval) {
 		mutex_lock(&fw_lock);
 		fw_load_abort(fw_priv);
@@ -965,7 +1011,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
 		retval = 0;
 	}
 
-	if (is_fw_load_aborted(buf))
+	if (fw_status_is_aborted(&buf->fw_st))
 		retval = -EAGAIN;
 	else if (buf->is_paged_buf && !buf->data)
 		retval = -ENOMEM;
@@ -1015,35 +1061,12 @@ fw_load_from_user_helper(struct firmware *firmware, const char *name,
 	return -ENOENT;
 }
 
-/* No abort during direct loading */
-#define is_fw_load_aborted(buf) false
-
 #ifdef CONFIG_PM_SLEEP
 static inline void kill_requests_without_uevent(void) { }
 #endif
 
 #endif /* CONFIG_FW_LOADER_USER_HELPER */
 
-
-/* wait until the shared firmware_buf becomes ready (or error) */
-static int sync_cached_firmware_buf(struct firmware_buf *buf)
-{
-	int ret = 0;
-
-	mutex_lock(&fw_lock);
-	while (!test_bit(FW_STATUS_DONE, &buf->status)) {
-		if (is_fw_load_aborted(buf)) {
-			ret = -ENOENT;
-			break;
-		}
-		mutex_unlock(&fw_lock);
-		ret = wait_for_completion_interruptible(&buf->completion);
-		mutex_lock(&fw_lock);
-	}
-	mutex_unlock(&fw_lock);
-	return ret;
-}
-
 /* prepare firmware and firmware_buf structs;
  * return 0 if a firmware is already assigned, 1 if need to load one,
  * or a negative error code
@@ -1077,7 +1100,8 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
 	firmware->priv = buf;
 
 	if (ret > 0) {
-		ret = sync_cached_firmware_buf(buf);
+		ret = fw_status_wait_timeout(&buf->fw_st,
+					   firmware_loading_timeout());
 		if (!ret) {
 			fw_set_page_data(buf, firmware);
 			return 0; /* assigned */
@@ -1095,7 +1119,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
 	struct firmware_buf *buf = fw->priv;
 
 	mutex_lock(&fw_lock);
-	if (!buf->size || is_fw_load_aborted(buf)) {
+	if (!buf->size || fw_status_is_aborted(&buf->fw_st)) {
 		mutex_unlock(&fw_lock);
 		return -ENOENT;
 	}
-- 
2.7.4

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

* Re: [PATCH v1] firmware_class: encapsulate firmware loading status
  2016-08-04 12:27   ` [PATCH v1] " Daniel Wagner
@ 2016-08-10  1:10     ` Luis R. Rodriguez
  2016-08-10  7:02       ` Daniel Wagner
  0 siblings, 1 reply; 9+ messages in thread
From: Luis R. Rodriguez @ 2016-08-10  1:10 UTC (permalink / raw)
  To: Daniel Wagner
  Cc: Ming Lei, linux-kernel, Daniel Wagner, Luis R . Rodriguez,
	Greg Kroah-Hartman, Takashi Iwai, Kees Cook, Dmitry Torokhov

On Thu, Aug 04, 2016 at 02:27:16PM +0200, Daniel Wagner wrote:
> From: Daniel Wagner <daniel.wagner@bmw-carit.de>
> 
> The firmware user helper code tracks the current state of the loading
> process via an member of struct firmware_buf and a completion. Let's
> encapsulate this simple state machine into struct fw_status. The aim is
> to encrease readiblity and reduce the usage of the fw_lock.

Great, emphasis, reduce use of fw_lock, good stuff!

> The fw_lock is not needed to protect the status update anymore. We don't
> do any RMW operations. Instead we just do a write or a read, not both at
> the same time.
> 
> [v1: moved fw_status into !CONFIG_FW_LOADER_USER_HELPER section,
>      reported by 0day kbuild]
> 
> Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de>
> Cc: Ming Lei <ming.lei@canonical.com>
> Cc: Luis R. Rodriguez <mcgrof@kernel.org>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> ---
> 
> Hi,
> 
> In [0] we have a discussion on how the firmware_class API might be
> changed to improve the current handling of firmware loading. This
> patch was part of the orignal RFC which triggered the discussion.
> 
> I think it is worth taking this one anyway. Maybe as I suggested it
> could be part of the series from Luis.

I'm happy to queue it in on my end however your changes are a bit orthogonal
as you help optimize us away from the usermode helper, I just compartamentalize
that whole API away into a new one so this can go in separately. In terms of
coordination -- sure order will help to get right so I can queue it in, in
that sense. But we're not yet sure if sysdsata will go in first, and I'm happy
for this to go in first as it does not conflict as its slightly orthogonal.

So order here does not interfere with my series -- lets just review this and
its good lets let it go in.

What you do is strip us further from the user mode helper and that
is a good thing.

My review below.

> It cleans up the code base (okay my opinion) 

You do little to sell this. In fact, if this is OK, it does a good
compartamentalization of a completion and a lock and implicates the
wait stuff only onto the usermoder helper, indeed that's a win
worth documenting on the commit log.

> and removes the
> complete_all() call which is problematic for -rt. complete_all() can
> be used in any context including IRQ.

I see. But in this case the code in question should never run in IRQ context?

> That could lead to unbounded
> work in the IRQ context and that is a no go for -rt.

Is the fear of the call to be used in IRQ context or the waiters to
somehow work in IRQ context somehow. The waiters were sleeping.. so
that I think leaves only the call site of the complete_all() to worry
about, but I can't see that happening in IRQ context. Please
correct me if I'm wrong.

> So here the
> attempt to reduce the number of complete_all() calls where possible.

OK so this is the real motivation.

> I have left this argument out in the commit message because I was told '-rt'
> arguments don't count for inclusion.

Sure, but I appreciate this explanation, thanks for that !

Can you provide a set of commits accepted upstream or on linux-next
where such conversion has been done and accepted as well elsewhere
in the kernel ?

I know its just pending patches for review but this has me thinking, is
the use of async functionality in the sysdata patches kosher for RT ?

> cheers,
> daniel
> 
> [0] http://www.spinics.net/lists/linux-wireless/msg153005.html
> 
> drivers/base/firmware_class.c | 154 ++++++++++++++++++++++++------------------
>  1 file changed, 89 insertions(+), 65 deletions(-)
> 
> diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
> index 22d1760..33eb372 100644
> --- a/drivers/base/firmware_class.c
> +++ b/drivers/base/firmware_class.c
> @@ -30,6 +30,7 @@
>  #include <linux/syscore_ops.h>
>  #include <linux/reboot.h>
>  #include <linux/security.h>
> +#include <linux/swait.h>
>  
>  #include <generated/utsrelease.h>
>  
> @@ -91,19 +92,83 @@ static inline bool fw_is_builtin_firmware(const struct firmware *fw)
>  }
>  #endif
>  
> +static int loading_timeout = 60;	/* In seconds */
> +
> +static inline long firmware_loading_timeout(void)
> +{
> +	return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
> +}

Seems like we can wrap the above loading_timeout and firmware_loading_timeout onto
CONFIG_FW_LOADER_USER_HELPER -- or provide a helper that returns some
static nonsense value that works for !CONFIG_FW_LOADER_USER_HELPER.

The move of the code above also makes this change harder to review.

> +
>  enum {
> +	FW_STATUS_UNKNOWN,
>  	FW_STATUS_LOADING,
>  	FW_STATUS_DONE,
> -	FW_STATUS_ABORT,
> +	FW_STATUS_ABORTED,
>  };

Come to think of it, even if CONFIG_FW_LOADER_USER_HELPER is enabled
we should only have a need to use this wait crap if an explicit
caller in the kernel requested to use the usermode helper, and as
my patches show there are only 2 of those cases left in the kernel.

To be clear if CONFIG_FW_LOADER_USER_HELPER_FALLBACK (not many distros left) is
set we're stuck and always have to use this, if you only have
CONFIG_FW_LOADER_USER_HELPER (most distros) then only explicit calls
will need this. I really don't want to be adding further crap for the
the user mode helper but since you are optimizing this I think a
compromise here can be to extend the status further for when an explicit
caller with usermode helper is passed, FW_STATUS_WAIT, then we only
really need to wait CONFIG_FW_LOADER_USER_HELPER_FALLBACK or if
FW_STATUS_WAIT is set.

For all intents and purposes this is 0 times for most distros these days
given we have only 2 drivers using this crap explicitly right now. But
I think this can be a separate atomic change.

> -static int loading_timeout = 60;	/* In seconds */
> +#ifdef CONFIG_FW_LOADER_USER_HELPER
> +struct fw_status {
> +	unsigned long status;
> +	struct swait_queue_head wq;
> +};
>  
> -static inline long firmware_loading_timeout(void)
> +static void fw_status_init(struct fw_status *fw_st)
>  {
> -	return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
> +	fw_st->status = FW_STATUS_UNKNOWN;
> +	init_swait_queue_head(&fw_st->wq);
> +}
> +
> +static inline bool is_fw_status_done(unsigned long status)
> +{
> +	return status == FW_STATUS_DONE ||
> +	       status == FW_STATUS_ABORTED;
> +}
> +
> +static int fw_status_wait_timeout(struct fw_status *fw_st, long timeout)
> +{
> +	int err;
> +	err = swait_event_interruptible_timeout(fw_st->wq,
> +				is_fw_status_done(READ_ONCE(fw_st->status)),
> +				timeout);
> +	if (err == 0 && fw_st->status == FW_STATUS_ABORTED)
> +		return -ENOENT;
> +
> +	return err;
> +}
> +
> +static void __fw_status_set(struct fw_status *fw_st,
> +			  unsigned long status)
> +{
> +	WRITE_ONCE(fw_st->status, status);
> +
> +	if (status == FW_STATUS_DONE ||
> +			status == FW_STATUS_ABORTED)
> +		swake_up(&fw_st->wq);
> +}
> +
> +static unsigned long __fw_status_get(struct fw_status *fw_st)
> +{
> +	return READ_ONCE(fw_st->status);
>  }
>  
> +#define fw_status_start(fw_st)					\
> +	__fw_status_set(fw_st, FW_STATUS_LOADING)
> +#define fw_status_done(fw_st)					\
> +	__fw_status_set(fw_st, FW_STATUS_DONE)
> +#define fw_status_aborted(fw_st)				\
> +	__fw_status_set(fw_st, FW_STATUS_ABORTED)
> +#define fw_status_is_loading(fw_st)				\
> +	(__fw_status_get(fw_st) == FW_STATUS_LOADING)
> +#define fw_status_is_done(fw_st)				\
> +	(__fw_status_get(fw_st) == FW_STATUS_DONE)
> +#define fw_status_is_aborted(fw_st)				\
> +	(__fw_status_get(fw_st) == FW_STATUS_ABORTED)
> +#else
> +#define fw_status_wait_timeout(fw_st, timeout)	0
> +#define fw_status_done(fw_st)
> +#define fw_status_is_aborted(fw_st)		false
> +#endif /* CONFIG_FW_LOADER_USER_HELPER */
> +
>  /* firmware behavior options */
>  #define FW_OPT_UEVENT	(1U << 0)
>  #define FW_OPT_NOWAIT	(1U << 1)
> @@ -145,13 +210,12 @@ struct firmware_cache {
>  struct firmware_buf {
>  	struct kref ref;
>  	struct list_head list;
> -	struct completion completion;
>  	struct firmware_cache *fwc;
> -	unsigned long status;
>  	void *data;
>  	size_t size;
>  	size_t allocated_size;
>  #ifdef CONFIG_FW_LOADER_USER_HELPER
> +	struct fw_status fw_st;
>  	bool is_paged_buf;
>  	bool need_uevent;
>  	struct page **pages;
> @@ -205,8 +269,8 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
>  	buf->fwc = fwc;
>  	buf->data = dbuf;
>  	buf->allocated_size = size;
> -	init_completion(&buf->completion);
>  #ifdef CONFIG_FW_LOADER_USER_HELPER
> +	fw_status_init(&buf->fw_st);
>  	INIT_LIST_HEAD(&buf->pending_list);
>  #endif
>  
> @@ -305,15 +369,6 @@ static const char * const fw_path[] = {
>  module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
>  MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
>  
> -static void fw_finish_direct_load(struct device *device,
> -				  struct firmware_buf *buf)
> -{
> -	mutex_lock(&fw_lock);
> -	set_bit(FW_STATUS_DONE, &buf->status);
> -	complete_all(&buf->completion);
> -	mutex_unlock(&fw_lock);
> -}
> -
>  static int
>  fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
>  {
> @@ -360,7 +415,7 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
>  		}
>  		dev_dbg(device, "direct-loading %s\n", buf->fw_id);
>  		buf->size = size;
> -		fw_finish_direct_load(device, buf);
> +		fw_status_done(&buf->fw_st);
>  		break;
>  	}
>  	__putname(path);
> @@ -478,12 +533,11 @@ static void __fw_load_abort(struct firmware_buf *buf)
>  	 * There is a small window in which user can write to 'loading'
>  	 * between loading done and disappearance of 'loading'
>  	 */
> -	if (test_bit(FW_STATUS_DONE, &buf->status))
> +	if (fw_status_is_done(&buf->fw_st))
>  		return;
>  
>  	list_del_init(&buf->pending_list);
> -	set_bit(FW_STATUS_ABORT, &buf->status);
> -	complete_all(&buf->completion);
> +	fw_status_aborted(&buf->fw_st);
>  }
>  
>  static void fw_load_abort(struct firmware_priv *fw_priv)
> @@ -496,9 +550,6 @@ static void fw_load_abort(struct firmware_priv *fw_priv)
>  	fw_priv->buf = NULL;
>  }
>  
> -#define is_fw_load_aborted(buf)	\
> -	test_bit(FW_STATUS_ABORT, &(buf)->status)
> -
>  static LIST_HEAD(pending_fw_head);
>  
>  /* reboot notifier for avoid deadlock with usermode_lock */
> @@ -596,10 +647,8 @@ static ssize_t firmware_loading_show(struct device *dev,
>  	struct firmware_priv *fw_priv = to_firmware_priv(dev);
>  	int loading = 0;
>  
> -	mutex_lock(&fw_lock);
>  	if (fw_priv->buf)
> -		loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
> -	mutex_unlock(&fw_lock);
> +		loading = fw_status_is_loading(&fw_priv->buf->fw_st);
>  
>  	return sprintf(buf, "%d\n", loading);
>  }
> @@ -653,23 +702,20 @@ static ssize_t firmware_loading_store(struct device *dev,
>  	switch (loading) {
>  	case 1:
>  		/* discarding any previous partial load */
> -		if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) {
> +		if (!fw_status_is_done(&fw_buf->fw_st)) {
>  			for (i = 0; i < fw_buf->nr_pages; i++)
>  				__free_page(fw_buf->pages[i]);
>  			vfree(fw_buf->pages);
>  			fw_buf->pages = NULL;
>  			fw_buf->page_array_size = 0;
>  			fw_buf->nr_pages = 0;
> -			set_bit(FW_STATUS_LOADING, &fw_buf->status);
> +			fw_status_start(&fw_buf->fw_st);
>  		}
>  		break;
>  	case 0:
> -		if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
> +		if (fw_status_is_loading(&fw_buf->fw_st)) {
>  			int rc;
>  
> -			set_bit(FW_STATUS_DONE, &fw_buf->status);
> -			clear_bit(FW_STATUS_LOADING, &fw_buf->status);
> -
>  			/*
>  			 * Several loading requests may be pending on
>  			 * one same firmware buf, so let all requests
> @@ -691,10 +737,11 @@ static ssize_t firmware_loading_store(struct device *dev,
>  			 */
>  			list_del_init(&fw_buf->pending_list);
>  			if (rc) {
> -				set_bit(FW_STATUS_ABORT, &fw_buf->status);
> +				fw_status_aborted(&fw_buf->fw_st);
>  				written = rc;
> +			} else {
> +				fw_status_done(&fw_buf->fw_st);
>  			}
> -			complete_all(&fw_buf->completion);
>  			break;
>  		}
>  		/* fallthrough */
> @@ -702,7 +749,7 @@ static ssize_t firmware_loading_store(struct device *dev,
>  		dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
>  		/* fallthrough */
>  	case -1:
> -		fw_load_abort(fw_priv);
> +		fw_status_aborted(&fw_buf->fw_st);
>  		break;
>  	}
>  out:
> @@ -755,7 +802,7 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
>  
>  	mutex_lock(&fw_lock);
>  	buf = fw_priv->buf;
> -	if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
> +	if (!buf || fw_status_is_done(&buf->fw_st)) {
>  		ret_count = -ENODEV;
>  		goto out;
>  	}
> @@ -842,7 +889,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
>  
>  	mutex_lock(&fw_lock);
>  	buf = fw_priv->buf;
> -	if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
> +	if (!buf || fw_status_is_done(&buf->fw_st)) {
>  		retval = -ENODEV;
>  		goto out;
>  	}
> @@ -955,8 +1002,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
>  		timeout = MAX_JIFFY_OFFSET;
>  	}
>  
> -	retval = wait_for_completion_interruptible_timeout(&buf->completion,
> -			timeout);
> +	retval = fw_status_wait_timeout(&buf->fw_st, timeout);
>  	if (retval == -ERESTARTSYS || !retval) {
>  		mutex_lock(&fw_lock);
>  		fw_load_abort(fw_priv);
> @@ -965,7 +1011,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
>  		retval = 0;
>  	}
>  
> -	if (is_fw_load_aborted(buf))
> +	if (fw_status_is_aborted(&buf->fw_st))
>  		retval = -EAGAIN;
>  	else if (buf->is_paged_buf && !buf->data)
>  		retval = -ENOMEM;
> @@ -1015,35 +1061,12 @@ fw_load_from_user_helper(struct firmware *firmware, const char *name,
>  	return -ENOENT;
>  }
>  
> -/* No abort during direct loading */
> -#define is_fw_load_aborted(buf) false
> -
>  #ifdef CONFIG_PM_SLEEP
>  static inline void kill_requests_without_uevent(void) { }
>  #endif
>  
>  #endif /* CONFIG_FW_LOADER_USER_HELPER */
>  
> -
> -/* wait until the shared firmware_buf becomes ready (or error) */
> -static int sync_cached_firmware_buf(struct firmware_buf *buf)
> -{
> -	int ret = 0;
> -
> -	mutex_lock(&fw_lock);
> -	while (!test_bit(FW_STATUS_DONE, &buf->status)) {
> -		if (is_fw_load_aborted(buf)) {
> -			ret = -ENOENT;
> -			break;
> -		}
> -		mutex_unlock(&fw_lock);
> -		ret = wait_for_completion_interruptible(&buf->completion);
> -		mutex_lock(&fw_lock);
> -	}
> -	mutex_unlock(&fw_lock);
> -	return ret;
> -}
> -
>  /* prepare firmware and firmware_buf structs;
>   * return 0 if a firmware is already assigned, 1 if need to load one,
>   * or a negative error code
> @@ -1077,7 +1100,8 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
>  	firmware->priv = buf;
>  
>  	if (ret > 0) {
> -		ret = sync_cached_firmware_buf(buf);
> +		ret = fw_status_wait_timeout(&buf->fw_st,
> +					   firmware_loading_timeout());
>  		if (!ret) {
>  			fw_set_page_data(buf, firmware);
>  			return 0; /* assigned */
> @@ -1095,7 +1119,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
>  	struct firmware_buf *buf = fw->priv;
>  
>  	mutex_lock(&fw_lock);
> -	if (!buf->size || is_fw_load_aborted(buf)) {
> +	if (!buf->size || fw_status_is_aborted(&buf->fw_st)) {
>  		mutex_unlock(&fw_lock);
>  		return -ENOENT;
>  	}

Other than that this looks nice so far. Can you please run the  tests

tools/testing/selftests/firmware/fw_filesystem.sh
tools/testing/selftests/firmware/fw_userhelper.sh

against lib/test_firmware.c

  Luis

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

* Re: [PATCH v1] firmware_class: encapsulate firmware loading status
  2016-08-10  1:10     ` Luis R. Rodriguez
@ 2016-08-10  7:02       ` Daniel Wagner
  2016-08-10 18:52         ` Luis R. Rodriguez
  0 siblings, 1 reply; 9+ messages in thread
From: Daniel Wagner @ 2016-08-10  7:02 UTC (permalink / raw)
  To: Luis R. Rodriguez
  Cc: Ming Lei, linux-kernel, Daniel Wagner, Greg Kroah-Hartman,
	Takashi Iwai, Kees Cook, Dmitry Torokhov

On 10.08.2016 03:10, Luis R. Rodriguez wrote:
> On Thu, Aug 04, 2016 at 02:27:16PM +0200, Daniel Wagner wrote:
>> From: Daniel Wagner <daniel.wagner@bmw-carit.de>

>> In [0] we have a discussion on how the firmware_class API might be
>> changed to improve the current handling of firmware loading. This
>> patch was part of the orignal RFC which triggered the discussion.
>>
>> I think it is worth taking this one anyway. Maybe as I suggested it
>> could be part of the series from Luis.
> 
> I'm happy to queue it in on my end however your changes are a bit orthogonal
> as you help optimize us away from the usermode helper, I just compartamentalize
> that whole API away into a new one so this can go in separately. 

Okay.

> In terms of
> coordination -- sure order will help to get right so I can queue it in, in
> that sense. But we're not yet sure if sysdsata will go in first, and I'm happy
> for this to go in first as it does not conflict as its slightly orthogonal.
> 
> So order here does not interfere with my series -- lets just review this and
> its good lets let it go in.

Alright, let's do this then first.

> What you do is strip us further from the user mode helper and that
> is a good thing.
> 
> My review below.
> 
>> It cleans up the code base (okay my opinion) 
> 
> You do little to sell this. In fact, if this is OK, it does a good
> compartamentalization of a completion and a lock and implicates the
> wait stuff only onto the usermoder helper, indeed that's a win
> worth documenting on the commit log.

My selling skill has still a lot of room to grow. I'll add your points
to the commit message.

>> and removes the
>> complete_all() call which is problematic for -rt. complete_all() can
>> be used in any context including IRQ.
> 
> I see. But in this case the code in question should never run in IRQ context?

No, this code is not running in IRQ context. See below.

>> That could lead to unbounded
>> work in the IRQ context and that is a no go for -rt.
> 
> Is the fear of the call to be used in IRQ context or the waiters to
> somehow work in IRQ context somehow. The waiters were sleeping.. so
> that I think leaves only the call site of the complete_all() to worry
> about, but I can't see that happening in IRQ context. Please
> correct me if I'm wrong.

The only problem for -rt is if the complete_all() happens in IRQ
context. If that happens the waker wakes up all waiters in one go (in
IRQ). That leads to the 'unbounded work' which can't be preempted. There
is no further restriction for -rt on waiters or wakers.

>> So here the
>> attempt to reduce the number of complete_all() calls where possible.
> 
> OK so this is the real motivation.

Yes, this is more ore less a clean up work :)

>> I have left this argument out in the commit message because I was told '-rt'
>> arguments don't count for inclusion.
> 
> Sure, but I appreciate this explanation, thanks for that !
> 
> Can you provide a set of commits accepted upstream or on linux-next
> where such conversion has been done and accepted as well elsewhere
> in the kernel ?

Not so far. I have started to send out patches last week. It seems most
people are enjoying holiday.

https://lkml.org/lkml/2016/8/4/264
https://patchwork.kernel.org/project/linux-amlogic/list/?submitter=47731

> I know its just pending patches for review but this has me thinking, is
> the use of async functionality in the sysdata patches kosher for RT ?

I haven't looked into deeply but from what I saw there is no obvious
show stopper. I'll give it another look next week. I am AFK till next
week ;)


> 
>> cheers,
>> daniel
>>
>> [0] http://www.spinics.net/lists/linux-wireless/msg153005.html
>>
>> drivers/base/firmware_class.c | 154 ++++++++++++++++++++++++------------------
>>  1 file changed, 89 insertions(+), 65 deletions(-)
>>
>> diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
>> index 22d1760..33eb372 100644
>> --- a/drivers/base/firmware_class.c
>> +++ b/drivers/base/firmware_class.c
>> @@ -30,6 +30,7 @@
>>  #include <linux/syscore_ops.h>
>>  #include <linux/reboot.h>
>>  #include <linux/security.h>
>> +#include <linux/swait.h>
>>  
>>  #include <generated/utsrelease.h>
>>  
>> @@ -91,19 +92,83 @@ static inline bool fw_is_builtin_firmware(const struct firmware *fw)
>>  }
>>  #endif
>>  
>> +static int loading_timeout = 60;	/* In seconds */
>> +
>> +static inline long firmware_loading_timeout(void)
>> +{
>> +	return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
>> +}
> 
> Seems like we can wrap the above loading_timeout and firmware_loading_timeout onto
> CONFIG_FW_LOADER_USER_HELPER -- or provide a helper that returns some
> static nonsense value that works for !CONFIG_FW_LOADER_USER_HELPER.
> 
> The move of the code above also makes this change harder to review.

Yeah, I moved it up because in one version I had a dependency to
firmware_loading_timeout. Hmm, maybe it can be removed or integrated a
bit more tightly.

[...]

> Other than that this looks nice so far. Can you please run the  tests
> 
> tools/testing/selftests/firmware/fw_filesystem.sh
> tools/testing/selftests/firmware/fw_userhelper.sh
> 
> against lib/test_firmware.c

I've done this. After this change, they are still passing. Obviously,
after patching those two tests. I came up with almost the identical
patches as you have in your queue.

cheers,
daniel

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

* Re: [PATCH v1] firmware_class: encapsulate firmware loading status
  2016-08-10  7:02       ` Daniel Wagner
@ 2016-08-10 18:52         ` Luis R. Rodriguez
  2016-08-17  6:47           ` Daniel Wagner
  0 siblings, 1 reply; 9+ messages in thread
From: Luis R. Rodriguez @ 2016-08-10 18:52 UTC (permalink / raw)
  To: Daniel Wagner
  Cc: Luis R. Rodriguez, Ming Lei, linux-kernel, Daniel Wagner,
	Greg Kroah-Hartman, Takashi Iwai, Kees Cook, Dmitry Torokhov,
	Julia Lawall, Josh Poimboeuf, Jessica Yu, Jiri Kosina,
	Miroslav Benes, Petr Mladek, Peter Zijlstra

On Wed, Aug 10, 2016 at 09:02:08AM +0200, Daniel Wagner wrote:
> On 10.08.2016 03:10, Luis R. Rodriguez wrote:
> > On Thu, Aug 04, 2016 at 02:27:16PM +0200, Daniel Wagner wrote:
> >> From: Daniel Wagner <daniel.wagner@bmw-carit.de>
> > I see. But in this case the code in question should never run in IRQ context?
> 
> No, this code is not running in IRQ context. See below.

OK so even for RT this is not needed then. Is that right ?

If this is true there must be some gains of swait over old wait
API even if its not important for -rt, what are the selling points,
in summary ?

> >> That could lead to unbounded
> >> work in the IRQ context and that is a no go for -rt.
> > 
> > Is the fear of the call to be used in IRQ context or the waiters to
> > somehow work in IRQ context somehow. The waiters were sleeping.. so
> > that I think leaves only the call site of the complete_all() to worry
> > about, but I can't see that happening in IRQ context. Please
> > correct me if I'm wrong.
> 
> The only problem for -rt is if the complete_all() happens in IRQ
> context. If that happens the waker wakes up all waiters in one go (in
> IRQ). That leads to the 'unbounded work' which can't be preempted. There
> is no further restriction for -rt on waiters or wakers.

In that case, even when -rt, this is not needed. However the compartamentalizing
of usermode sleep crap to usermode helper only seems worthy endeavor and I
wonder if we can split the work in this patch to 2, one which splits the
stuff, and the other one that makes then the conversion from old wait to
the new swait. If this is possible there are three gains:

 o makes code easier to review
 o makes each change atomically justifiable
 o once you have only a conversion from old wait to new swait you can
   inspect the delta and try to write SmPL grammar to see if you can
   generalize the change, so grammar can do the change for other
   use cases. Of course, you'd need first to look for the IRQ context,
   and I wonder if that's possible. If there are however generic
   benefits of swait over old wait when complete_all() is used (is
   live patching one?) then this will be very handy.

> >> So here the
> >> attempt to reduce the number of complete_all() calls where possible.
> > 
> > OK so this is the real motivation.
> 
> Yes, this is more ore less a clean up work :)
> 
> >> I have left this argument out in the commit message because I was told '-rt'
> >> arguments don't count for inclusion.
> > 
> > Sure, but I appreciate this explanation, thanks for that !
> > 
> > Can you provide a set of commits accepted upstream or on linux-next
> > where such conversion has been done and accepted as well elsewhere
> > in the kernel ?
> 
> Not so far. I have started to send out patches last week. It seems most
> people are enjoying holiday.
> 
> https://lkml.org/lkml/2016/8/4/264
> https://patchwork.kernel.org/project/linux-amlogic/list/?submitter=47731

OK thanks do we have a kselftest for swait ?

> > I know its just pending patches for review but this has me thinking, is
> > the use of async functionality in the sysdata patches kosher for RT ?
> 
> I haven't looked into deeply but from what I saw there is no obvious
> show stopper. I'll give it another look next week. I am AFK till next
> week ;)

OK thanks.

> >> cheers,
> >> daniel
> >>
> >> [0] http://www.spinics.net/lists/linux-wireless/msg153005.html
> >>
> >> drivers/base/firmware_class.c | 154 ++++++++++++++++++++++++------------------
> >>  1 file changed, 89 insertions(+), 65 deletions(-)
> >>
> >> diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
> >> index 22d1760..33eb372 100644
> >> --- a/drivers/base/firmware_class.c
> >> +++ b/drivers/base/firmware_class.c
> >> @@ -30,6 +30,7 @@
> >>  #include <linux/syscore_ops.h>
> >>  #include <linux/reboot.h>
> >>  #include <linux/security.h>
> >> +#include <linux/swait.h>
> >>  
> >>  #include <generated/utsrelease.h>
> >>  
> >> @@ -91,19 +92,83 @@ static inline bool fw_is_builtin_firmware(const struct firmware *fw)
> >>  }
> >>  #endif
> >>  
> >> +static int loading_timeout = 60;	/* In seconds */
> >> +
> >> +static inline long firmware_loading_timeout(void)
> >> +{
> >> +	return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
> >> +}
> > 
> > Seems like we can wrap the above loading_timeout and firmware_loading_timeout onto
> > CONFIG_FW_LOADER_USER_HELPER -- or provide a helper that returns some
> > static nonsense value that works for !CONFIG_FW_LOADER_USER_HELPER.
> > 
> > The move of the code above also makes this change harder to review.
> 
> Yeah, I moved it up because in one version I had a dependency to
> firmware_loading_timeout. Hmm, maybe it can be removed or integrated a
> bit more tightly.
> 
> [...]
> 
> > Other than that this looks nice so far. Can you please run the  tests
> > 
> > tools/testing/selftests/firmware/fw_filesystem.sh
> > tools/testing/selftests/firmware/fw_userhelper.sh
> > 
> > against lib/test_firmware.c
> 
> I've done this. After this change, they are still passing. Obviously,
> after patching those two tests. I came up with almost the identical
> patches as you have in your queue.

OK great. Thanks. In that case just the above observations are pending.

  Luis

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

* Re: [PATCH v1] firmware_class: encapsulate firmware loading status
  2016-08-10 18:52         ` Luis R. Rodriguez
@ 2016-08-17  6:47           ` Daniel Wagner
  2016-08-18 16:30             ` Luis R. Rodriguez
  0 siblings, 1 reply; 9+ messages in thread
From: Daniel Wagner @ 2016-08-17  6:47 UTC (permalink / raw)
  To: Luis R. Rodriguez
  Cc: Ming Lei, linux-kernel, Daniel Wagner, Greg Kroah-Hartman,
	Takashi Iwai, Kees Cook, Dmitry Torokhov, Julia Lawall,
	Josh Poimboeuf, Jessica Yu, Jiri Kosina, Miroslav Benes,
	Petr Mladek, Peter Zijlstra

Hi Luis,

On 08/10/2016 08:52 PM, Luis R. Rodriguez wrote:
> On Wed, Aug 10, 2016 at 09:02:08AM +0200, Daniel Wagner wrote:
>> On 10.08.2016 03:10, Luis R. Rodriguez wrote:
>>> On Thu, Aug 04, 2016 at 02:27:16PM +0200, Daniel Wagner wrote:
>>>> From: Daniel Wagner <daniel.wagner@bmw-carit.de>
>>> I see. But in this case the code in question should never run in IRQ context?
>>
>> No, this code is not running in IRQ context. See below.
>
> OK so even for RT this is not needed then. Is that right ?
>
> If this is true there must be some gains of swait over old wait
> API even if its not important for -rt, what are the selling points,
> in summary ?

Clearly I need to improve my commit message writing. Your observation is 
correct.

The current 'state machine' uses three variables to handle the state and 
the transitions.

struct completion {
	unsigned int done;
	wait_queue_head_t wait;
};

struct firmware_buf {
	...
	struct completion completion;
	unsigned long status;
	...
};

Obviously, the variable 'status' holds the state. 'wait' and 'done' 
handles the synchronization. 'done' remembers how many waiters will be 
woken at max. complete_all() sets it to UMAX/2. That should be enough in 
most of the cases. So any future wait_for_completion() call will not block.

The patch just drops the 'done' completely because it is not necessary. 
We have a waiter queue for all those pending waiters and as soon the 
final state is reached we just wake them up. The future waiters will 
never be queued because we just check for the state first.

wait vs swait: The main difference between the two APIs is the 
implementation. So it is pretty simple to switch from one to the other. 
So why swait, I hear you asking. The swait implentation is pretty simple 
for the price that you can't do all the stuff what wait offers. As long 
you don't need the extra features of wait just go with swait.

While the above points are nice side effect the real reason is the 
cleanup of the code and getting rid of the mutex operations.

>>>> That could lead to unbounded
>>>> work in the IRQ context and that is a no go for -rt.
>>>
>>> Is the fear of the call to be used in IRQ context or the waiters to
>>> somehow work in IRQ context somehow. The waiters were sleeping.. so
>>> that I think leaves only the call site of the complete_all() to worry
>>> about, but I can't see that happening in IRQ context. Please
>>> correct me if I'm wrong.
>>
>> The only problem for -rt is if the complete_all() happens in IRQ
>> context. If that happens the waker wakes up all waiters in one go (in
>> IRQ). That leads to the 'unbounded work' which can't be preempted. There
>> is no further restriction for -rt on waiters or wakers.
>
> In that case, even when -rt, this is not needed. However the compartamentalizing
> of usermode sleep crap to usermode helper only seems worthy endeavor and I
> wonder if we can split the work in this patch to 2, one which splits the
> stuff, and the other one that makes then the conversion from old wait to
> the new swait. If this is possible there are three gains:
>
>  o makes code easier to review
>  o makes each change atomically justifiable

I can try to split the patch into two steps. Let's see how this works 
out. But I wouldn't mind if we go with this version :)

>  o once you have only a conversion from old wait to new swait you can
>    inspect the delta and try to write SmPL grammar to see if you can
>    generalize the change, so grammar can do the change for other
>    use cases. Of course, you'd need first to look for the IRQ context,
>    and I wonder if that's possible. If there are however generic
>    benefits of swait over old wait when complete_all() is used (is
>    live patching one?) then this will be very handy.

 From my attempts to figure out the execution context with SmPL I fear 
that is rather hard to achieve because you need to create a call graph 
and track the state.

>>>> So here the
>>>> attempt to reduce the number of complete_all() calls where possible.
>>>
>>> OK so this is the real motivation.
>>
>> Yes, this is more ore less a clean up work :)
>>
>>>> I have left this argument out in the commit message because I was told '-rt'
>>>> arguments don't count for inclusion.
>>>
>>> Sure, but I appreciate this explanation, thanks for that !
>>>
>>> Can you provide a set of commits accepted upstream or on linux-next
>>> where such conversion has been done and accepted as well elsewhere
>>> in the kernel ?
>>
>> Not so far. I have started to send out patches last week. It seems most
>> people are enjoying holiday.
>>
>> https://lkml.org/lkml/2016/8/4/264
>> https://patchwork.kernel.org/project/linux-amlogic/list/?submitter=47731
>
> OK thanks do we have a kselftest for swait ?

No. A quick grep didn't show any test for wait either. I should still 
have some test code around for swait while hacking on it. I'll add it to 
my todo list if you think that is a worthwhile exercise.

cheers,
daniel

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

* Re: [PATCH v1] firmware_class: encapsulate firmware loading status
  2016-08-17  6:47           ` Daniel Wagner
@ 2016-08-18 16:30             ` Luis R. Rodriguez
  2016-08-18 18:55               ` Daniel Wagner
  0 siblings, 1 reply; 9+ messages in thread
From: Luis R. Rodriguez @ 2016-08-18 16:30 UTC (permalink / raw)
  To: Daniel Wagner
  Cc: Luis R. Rodriguez, Ming Lei, linux-kernel, Daniel Wagner,
	Greg Kroah-Hartman, Takashi Iwai, Kees Cook, Dmitry Torokhov,
	Julia Lawall, Josh Poimboeuf, Jessica Yu, Jiri Kosina,
	Miroslav Benes, Petr Mladek, Peter Zijlstra

On Wed, Aug 17, 2016 at 08:47:24AM +0200, Daniel Wagner wrote:
> Hi Luis,
> 
> On 08/10/2016 08:52 PM, Luis R. Rodriguez wrote:
> >On Wed, Aug 10, 2016 at 09:02:08AM +0200, Daniel Wagner wrote:
> >>On 10.08.2016 03:10, Luis R. Rodriguez wrote:
> >>>On Thu, Aug 04, 2016 at 02:27:16PM +0200, Daniel Wagner wrote:
> >>>>From: Daniel Wagner <daniel.wagner@bmw-carit.de>
> >>>I see. But in this case the code in question should never run in IRQ context?
> >>
> >>No, this code is not running in IRQ context. See below.
> >
> >OK so even for RT this is not needed then. Is that right ?
> >
> >If this is true there must be some gains of swait over old wait
> >API even if its not important for -rt, what are the selling points,
> >in summary ?
> 
> Clearly I need to improve my commit message writing. Your
> observation is correct.
> 
> The current 'state machine' uses three variables to handle the state
> and the transitions.
> 
> struct completion {
> 	unsigned int done;
> 	wait_queue_head_t wait;
> };
> 
> struct firmware_buf {
> 	...
> 	struct completion completion;
> 	unsigned long status;
> 	...
> };
> 
> Obviously, the variable 'status' holds the state. 'wait' and 'done'
> handles the synchronization. 'done' remembers how many waiters will
> be woken at max. complete_all() sets it to UMAX/2. That should be
> enough in most of the cases. 

Thanks, this helps and makes sense. How many data structures
in comparison does the new swait require ? Is it smaller ? If
so that is a nice simplification indeed, however we should make
sure we have no compromises then.

> So any future wait_for_completion() call will not block.

This I don't get, do you mean that if we have already UMAX/2
waiters on a completion and another one comes in, it will not
wait at all ?

Is this documented well ? Either way clarifying exactly what is
done here would be of huge help understanding the striking
differences between a switch to the new API.

> The patch just drops the 'done' completely because it is not
> necessary. We have a waiter queue for all those pending waiters 

So there is no limit to waiters with the new API ?

> and
> as soon the final state is reached we just wake them up. The future
> waiters will never be queued because we just check for the state
> first.

I do not follow what this means, I take it here we are talking about
possible race conditions between a wait and some work about to be
done?

> wait vs swait: The main difference between the two APIs is the
> implementation. So it is pretty simple to switch from one to the
> other. So why swait, I hear you asking. The swait implentation is
> pretty simple for the price that you can't do all the stuff what
> wait offers. As long you don't need the extra features of wait just
> go with swait.

OK so wait offers more features and its a kitchen sink of stuff,
we only require a simple wait and swait is better and more light
weight. The above number of waiters is still something I'd like
a bit clarification on.

> While the above points are nice side effect the real reason is the
> cleanup of the code and getting rid of the mutex operations.

This indeed is huge and this can better be reflected on the commit log.
In fact I wonder if its possible to do the switch without the change
to swait, and do the conversion to swait as a secondary step.

> >>>>That could lead to unbounded
> >>>>work in the IRQ context and that is a no go for -rt.
> >>>
> >>>Is the fear of the call to be used in IRQ context or the waiters to
> >>>somehow work in IRQ context somehow. The waiters were sleeping.. so
> >>>that I think leaves only the call site of the complete_all() to worry
> >>>about, but I can't see that happening in IRQ context. Please
> >>>correct me if I'm wrong.
> >>
> >>The only problem for -rt is if the complete_all() happens in IRQ
> >>context. If that happens the waker wakes up all waiters in one go (in
> >>IRQ). That leads to the 'unbounded work' which can't be preempted. There
> >>is no further restriction for -rt on waiters or wakers.
> >
> >In that case, even when -rt, this is not needed. However the compartamentalizing
> >of usermode sleep crap to usermode helper only seems worthy endeavor and I
> >wonder if we can split the work in this patch to 2, one which splits the
> >stuff, and the other one that makes then the conversion from old wait to
> >the new swait. If this is possible there are three gains:
> >
> > o makes code easier to review
> > o makes each change atomically justifiable
> 
> I can try to split the patch into two steps. Let's see how this
> works out. But I wouldn't mind if we go with this version :)

I understand -- however I have to ask as if its possible it makes
things easier to review and makes two logical changes split up. This
would in turn be easier to debug if there are issues.

> > o once you have only a conversion from old wait to new swait you can
> >   inspect the delta and try to write SmPL grammar to see if you can
> >   generalize the change, so grammar can do the change for other
> >   use cases. Of course, you'd need first to look for the IRQ context,
> >   and I wonder if that's possible. If there are however generic
> >   benefits of swait over old wait when complete_all() is used (is
> >   live patching one?) then this will be very handy.
> 
> From my attempts to figure out the execution context with SmPL I
> fear that is rather hard to achieve because you need to create a
> call graph and track the state.

OK..

> >>>>So here the
> >>>>attempt to reduce the number of complete_all() calls where possible.
> >>>
> >>>OK so this is the real motivation.
> >>
> >>Yes, this is more ore less a clean up work :)
> >>
> >>>>I have left this argument out in the commit message because I was told '-rt'
> >>>>arguments don't count for inclusion.
> >>>
> >>>Sure, but I appreciate this explanation, thanks for that !
> >>>
> >>>Can you provide a set of commits accepted upstream or on linux-next
> >>>where such conversion has been done and accepted as well elsewhere
> >>>in the kernel ?
> >>
> >>Not so far. I have started to send out patches last week. It seems most
> >>people are enjoying holiday.
> >>
> >>https://lkml.org/lkml/2016/8/4/264
> >>https://patchwork.kernel.org/project/linux-amlogic/list/?submitter=47731
> >
> >OK thanks do we have a kselftest for swait ?
> 
> No. A quick grep didn't show any test for wait either. I should
> still have some test code around for swait while hacking on it. I'll
> add it to my todo list if you think that is a worthwhile exercise.

Definitely!

  Luis

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

* Re: [PATCH v1] firmware_class: encapsulate firmware loading status
  2016-08-18 16:30             ` Luis R. Rodriguez
@ 2016-08-18 18:55               ` Daniel Wagner
  0 siblings, 0 replies; 9+ messages in thread
From: Daniel Wagner @ 2016-08-18 18:55 UTC (permalink / raw)
  To: Luis R. Rodriguez
  Cc: Ming Lei, linux-kernel, Daniel Wagner, Greg Kroah-Hartman,
	Takashi Iwai, Kees Cook, Dmitry Torokhov, Julia Lawall,
	Josh Poimboeuf, Jessica Yu, Jiri Kosina, Miroslav Benes,
	Petr Mladek, Peter Zijlstra

On 18.08.2016 18:30, Luis R. Rodriguez wrote:
> On Wed, Aug 17, 2016 at 08:47:24AM +0200, Daniel Wagner wrote:
>> On 08/10/2016 08:52 PM, Luis R. Rodriguez wrote:
>> The current 'state machine' uses three variables to handle the state
>> and the transitions.
>>
>> struct completion {
>> 	unsigned int done;
>> 	wait_queue_head_t wait;
>> };
>>
>> struct firmware_buf {
>> 	...
>> 	struct completion completion;
>> 	unsigned long status;
>> 	...
>> };
>>
>> Obviously, the variable 'status' holds the state. 'wait' and 'done'
>> handles the synchronization. 'done' remembers how many waiters will
>> be woken at max. complete_all() sets it to UMAX/2. That should be
>> enough in most of the cases. 
> 
> Thanks, this helps and makes sense. How many data structures
> in comparison does the new swait require ? Is it smaller ? If
> so that is a nice simplification indeed, however we should make
> sure we have no compromises then.

Yes we save one 'unsigned int', that is the done member of struct
completion. For an earlier version of this patch I did check the size
changes. While we save a little on the data section, the code section
increased slightly, IIRC it was around 60 bytes. Will do another
measurement.

>> So any future wait_for_completion() call will not block.
> 
> This I don't get, do you mean that if we have already UMAX/2
> waiters on a completion and another one comes in, it will not
> wait at all ?

Sorry, I think I just confused you here with a implementation detail.
Whenever wait_for_completion() is woken it checks if done > 0. Then it
will decrement the counter. complete() increases the counter and then
wakes the waiter. Basically it is comparable with semamphore put and get
operation. complete_all() just sets done to max almost infinite value :)

> Is this documented well ? Either way clarifying exactly what is
> done here would be of huge help understanding the striking
> differences between a switch to the new API.

Obviously, there is Documentation/scheduler/completion.txt but the small
detail on UMAX/2 is not mentioned. I don't think it was considered to be
a real problem. I guess before you run into the problem of waking 2
billion threads you see other scaling issues first.

Note this has nothing to do with wait vs swait.

>> The patch just drops the 'done' completely because it is not
>> necessary. We have a waiter queue for all those pending waiters 
> 
> So there is no limit to waiters with the new API ?

Correct, the limit is gone, though I don't expect that there are so many
firmware user helper waiting that we hit the UMAX/2 limit ever.

>> and
>> as soon the final state is reached we just wake them up. The future
>> waiters will never be queued because we just check for the state
>> first.
> 
> I do not follow what this means, I take it here we are talking about
> possible race conditions between a wait and some work about to be
> done?

Let me reword that. I was not really concerned about race condition
here. I was just trying to point out that we just check for the condition.

Either we have reached FW_STATUS_{DONE|ABORTED} and just continue or we
put the thread to sleep and wait for the wake call. Because we check for
the a single condition (status == FW_STATUS_{DONE|ABORTED} in
swait_event_interruptable_timeout() we don't need any addition
synchronization. Come to think about it, that is why the mutex can be
removed.

>> wait vs swait: The main difference between the two APIs is the
>> implementation. So it is pretty simple to switch from one to the
>> other. So why swait, I hear you asking. The swait implentation is
>> pretty simple for the price that you can't do all the stuff what
>> wait offers. As long you don't need the extra features of wait just
>> go with swait.
> 
> OK so wait offers more features and its a kitchen sink of stuff,
> we only require a simple wait and swait is better and more light
> weight.

Yes, that summarized it pretty good.

> The above number of waiters is still something I'd like
> a bit clarification on.

As I understand the firmware loader helper userland API there is only
one waiter.

>> While the above points are nice side effect the real reason is the
>> cleanup of the code and getting rid of the mutex operations.
> 
> This indeed is huge and this can better be reflected on the commit log.
> In fact I wonder if its possible to do the switch without the change
> to swait, and do the conversion to swait as a secondary step.

Not sure about it because 'status' and the operation of completion need
to be synchronized. I'll give it a try just haven't had time yet. It is
not about wait or swait, it's about completion vs s/wait.

>> I can try to split the patch into two steps. Let's see how this
>> works out. But I wouldn't mind if we go with this version :)
> 
> I understand -- however I have to ask as if its possible it makes
> things easier to review and makes two logical changes split up. This
> would in turn be easier to debug if there are issues.

Sure, I completely understand. BTW, I just updated the patch and avoided
the moving of the loading_timeout. Now it doesn't contain any hard to
read section anymore.

>>> o once you have only a conversion from old wait to new swait you can
>>>   inspect the delta and try to write SmPL grammar to see if you can
>>>   generalize the change, so grammar can do the change for other
>>>   use cases. Of course, you'd need first to look for the IRQ context,
>>>   and I wonder if that's possible. If there are however generic
>>>   benefits of swait over old wait when complete_all() is used (is
>>>   live patching one?) then this will be very handy.
>>
>> From my attempts to figure out the execution context with SmPL I
>> fear that is rather hard to achieve because you need to create a
>> call graph and track the state.
> 
> OK..

I know you have a far better understanding. We need to discuss this over
a beer :)

cheers,
daniel

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

end of thread, other threads:[~2016-08-19  1:35 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-08-04  9:23 [PATCH] firmware_class: encapsulate firmware loading status Daniel Wagner
2016-08-04 10:33 ` kbuild test robot
2016-08-04 12:27   ` [PATCH v1] " Daniel Wagner
2016-08-10  1:10     ` Luis R. Rodriguez
2016-08-10  7:02       ` Daniel Wagner
2016-08-10 18:52         ` Luis R. Rodriguez
2016-08-17  6:47           ` Daniel Wagner
2016-08-18 16:30             ` Luis R. Rodriguez
2016-08-18 18:55               ` Daniel Wagner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox