Skip to contentSkip to conversation

firmware loader: retry _nowait requests when userhelper is not yet available

RFC In review Christian Lamparter 12 participants 111 messages Mar 3, 2012 – Mar 28, 2012

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 firmware loader: retry _nowait requests when userhelper is not yet available

During resume, the userhelper might not be available. However for drivers which use the request_firmware_nowait interface, this will only lead to a pointless WARNING and a device which no longer works after the resume [since it couldn't get the firmware, because the userhelper was not available to take the request].

In order to solve this "chicken or egg" dilemma, the code now retries _nowait requests at one second intervals until the "loading_timeout" time is up.

---
I'm aware about the previous "request_firmware* in probe" discussions. Unfortunately, the hardware needs firmware so there is no other way around it. So please, I just wanted to know what the general opinion about the idea behind this patch is.

Regards,
Christian

BTW: I'm not on the list, so please keep the 'CC'.

---
 drivers/base/firmware_class.c |   24 +++++++++++++++++++++++-
 1 files changed, 23 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +23 -1
index 6c9387d..9f70096 100644
@@ -19,6 +19,7 @@
 #include <linux/kthread.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
+#include <linux/delay.h>
 #include <linux/slab.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
@@ -535,6 +536,11 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
+	if (nowait && usermodehelper_is_disabled()) {
+		retval = -EBUSY;
+		goto out;
+	}
+
 	if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
@@ -633,7 +639,7 @@ static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
-	int ret;
+	int ret, timeout = loading_timeout;
 
 	if (!arg) {
 		WARN_ON(1);
@@ -642,6 +648,22 @@ static int request_firmware_work_func(void *arg)
 
 	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+
+	while (ret == -EBUSY) {
+		/*
+		 * Try to retrieve the firmware within the loading timeout.
+		 * To stick with the loading timeout convention from above:
+		 *	loading_timeout = 0 means 'try forever' as well.
+		 */
+
+		msleep(1000);
+		ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+					fw_work->uevent, true);
+
+		if (timeout != 0 && timeout-- == 1)
+			break;
+	};
+
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
Signature · 1 line
1.7.9.1
#1 lore raw
Quoted 5 lines · On Sat, 3 Mar 2012 21:22:36 +0100 Christian Lamparter…

During resume, the userhelper might not be available. However for drivers which use the request_firmware_nowait interface, this will only lead to a pointless WARNING and a device which no longer works after the resume [since it couldn't get the firmware, because the userhelper was not available to take the request].

Can you not load the firmware on the suspend path ?

Alan

#2 lore raw
Quoted 11 lines · On Sunday 04 March 2012 00:57:04 Alan Cox wrote:
Quoted 5 lines · On Sat, 3 Mar 2012 21:22:36 +0100 Christian Lamparter…

During resume, the userhelper might not be available. However for drivers which use the request_firmware_nowait interface, this will only lead to a pointless WARNING and a device which no longer works after the resume [since it couldn't get the firmware, because the userhelper was not available to take the request].

Can you not load the firmware on the suspend path ?

If I could, I would fetch the firmware "open" as it is supposed to be. But, but the driver's "remove" op is called during resume by the "bus". So, everything will vanish with it.

But see for yourself: In my case I have to deal with a pci-chip [isl3886, p54pci driver] that comes on board of a cardbus card. The code that does the "unbind/rebind" is in drivers/pcmcia/cs.c, function: socket_late_resume

Quoted 8 lines
	if (skt->state & SOCKET_CARDBUS) {
		/* We can't be sure the CardBus card is the same
		 * as the one previously inserted. Therefore, remove
		 * and re-add... */
		cb_free(skt);
		cb_alloc(skt);
		return 0;
	}

Of course cb_free(...) is just a elaborate way of saying:

	pci_remove_behind_bridge(pcmcia_socket);

[And cb_alloc obviously just re-"probe"s the device in the cardbus socket again...]

Of course, in my case, I could tuck the firmware away in a some static const struct firmware* somewhere within the driver code. But then someone [like you :-D] will yell at me for hiding it in a file-global stash of ugliness.

Regards,
Christian

#3 lore raw
In reply to Christian Lamparter

[ Adding more Cc's. ]

Quoted 84 lines · Christian Lamparter

During resume, the userhelper might not be available. However for drivers which use the request_firmware_nowait interface, this will only lead to a pointless WARNING and a device which no longer works after the resume [since it couldn't get the firmware, because the userhelper was not available to take the request].

In order to solve this "chicken or egg" dilemma, the code now retries _nowait requests at one second intervals until the "loading_timeout" time is up.

---
I'm aware about the previous "request_firmware* in probe" discussions. Unfortunately, the hardware needs firmware so there is no other way around it. So please, I just wanted to know what the general opinion about the idea behind this patch is.

Regards,
Christian

BTW: I'm not on the list, so please keep the 'CC'.

---
 drivers/base/firmware_class.c |   24 +++++++++++++++++++++++-
 1 files changed, 23 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +23 -1
index 6c9387d..9f70096 100644
@@ -19,6 +19,7 @@
 #include <linux/kthread.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
+#include <linux/delay.h>
 #include <linux/slab.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
@@ -535,6 +536,11 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
+	if (nowait && usermodehelper_is_disabled()) {
+		retval = -EBUSY;
+		goto out;
+	}
+
 	if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
@@ -633,7 +639,7 @@ static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
-	int ret;
+	int ret, timeout = loading_timeout;
 
 	if (!arg) {
 		WARN_ON(1);
@@ -642,6 +648,22 @@ static int request_firmware_work_func(void *arg)
 
 	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+
+	while (ret == -EBUSY) {
+		/*
+		 * Try to retrieve the firmware within the loading timeout.
+		 * To stick with the loading timeout convention from above:
+		 *	loading_timeout = 0 means 'try forever' as well.
+		 */
+
+		msleep(1000);
+		ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+					fw_work->uevent, true);
+
+		if (timeout != 0 && timeout-- == 1)
 
+			break;
 
+	};
 
+
drivers/base/firmware_class.c +0 -0
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
#4 lore raw

PATCH 1/1 firmware loader: don't cancel _nowait requests when helper is not yet available

This patch fixes a regression which was introduced by:
"PM: Print a warning if firmware is requested when tasks are frozen"

request_firmware_nowait does not stall in any system resume paths. Therefore, I think it is perfectly save to use request_firmware_nowait from at least the ->complete() callback.

---
 drivers/base/firmware_class.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +1 -1
index 6c9387d..017e020 100644
@@ -535,7 +535,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	if (WARN_ON(usermodehelper_is_disabled() && !(nowait && uevent))) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
 		goto out;
Signature · 1 line
1.7.9.1
maintainer
#5 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 6 lines · Christian Lamparter

This patch fixes a regression which was introduced by:
"PM: Print a warning if firmware is requested when tasks are frozen"

request_firmware_nowait does not stall in any system resume paths. Therefore, I think it is perfectly save to use request_firmware_nowait from at least the ->complete() callback.

Is there code somewhere in the kernel that wants to do this? Has commit a144c6a broken it somehow that this fix would resolve it?

Quoted 11 lines
---
 drivers/base/firmware_class.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..017e020 100644

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 538

Quoted 5 lines
drivers/base/firmware_class.c +1 -1
@@ -535,7 +535,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	if (WARN_ON(usermodehelper_is_disabled() && !(nowait && uevent))) {

What does uevent have to do with things here?

greg k-h

#6 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 9 lines · On Saturday 10 March 2012 00:36:59 Greg KH wrote:
Quoted 6 lines · Christian Lamparter

This patch fixes a regression which was introduced by:
"PM: Print a warning if firmware is requested when tasks are frozen"

request_firmware_nowait does not stall in any system resume paths. Therefore, I think it is perfectly save to use request_firmware_nowait from at least the ->complete() callback.

Is there code somewhere in the kernel that wants to do this?

uum, yes: The USB subsystem [Good thing, I'm already talking to you! :-D]

Take a look at drivers/usb/core/usb.c:

the ->complete callback is registered to usb_dev_complete(dev*), which is just a lengthy wrapper for usb_resume(dev, PMSG_ON).

Now, let's switch to drivers/usb/core/driver.c:

usb_resume calls do_unbind_rebind(udev, DO_REBIND) and there we go... So any usb driver which uses request_firmware_nowait in .probe is a possible target for the warning.

The one I stumbled across is the pcmcia subsystem. Currently it tries to rebind the devices during ->resume but I have already sent a patch to postpone that till the ->complete callback comes in.
<http://lkml.org/lkml/2012/3/3/101>

Quoted 1 line

Has commit a144c6a broken it somehow that this fix would resolve it?

When I read the commit, it only talked only about broken drivers doing "request_firmware" during .probe. But it does not mention what should be done with "request_firmware_nowait" in such a case at all. I think it was just an oversight from all involved parties that just got noticed now.

Quoted 11 lines
Quoted 11 lines
---
 drivers/base/firmware_class.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..017e020 100644
@@ -535,7 +535,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 
Quoted 6 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 538

Quoted 4 lines
drivers/base/firmware_class.c +1 -1
 	read_lock_usermodehelper();
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	if (WARN_ON(usermodehelper_is_disabled() && !(nowait && uevent))) {

What does uevent have to do with things here?

I found this "useful" comment about uevent in request_firmware_nowait's kdocs:
* @uevent: sends uevent to copy the firmware image if this flag
* is non-zero else the firmware copy must be done manually.
I think this flag controls whenever we want to sent a firmware request event to the usermodehelper...
[No idea why this should ever be 'false', maybe there is a good use-case for it when the firmware is built-in... But anyway let's just say I'm as ignorant as everybody else and I haven't got a single clue about it.]

Regards,
Christian

#7 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Greg KH
Quoted 23 lines · Greg KH
Quoted 6 lines · Christian Lamparter

This patch fixes a regression which was introduced by:
"PM: Print a warning if firmware is requested when tasks are frozen"

request_firmware_nowait does not stall in any system resume paths. Therefore, I think it is perfectly save to use request_firmware_nowait from at least the ->complete() callback.

Is there code somewhere in the kernel that wants to do this?  Has commit a144c6a broken it somehow that this fix would resolve it?

Quoted 12 lines

---
 drivers/base/firmware_class.c |    2 +-

 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..017e020 100644
@@ -535,7 +535,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 
Quoted 6 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 538

Quoted 4 lines
drivers/base/firmware_class.c +1 -1
       read_lock_usermodehelper();
 
-     if (WARN_ON(usermodehelper_is_disabled())) {
+     if (WARN_ON(usermodehelper_is_disabled() && !(nowait && uevent))) {

What does uevent have to do with things here?

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Kay

#8 lore raw

Re: Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 35 lines · Kay Sievers
Quoted 23 lines · Greg KH
Quoted 6 lines · Christian Lamparter

This patch fixes a regression which was introduced by:
"PM: Print a warning if firmware is requested when tasks are frozen"

request_firmware_nowait does not stall in any system resume paths. Therefore, I think it is perfectly save to use request_firmware_nowait from at least the ->complete() callback.

Is there code somewhere in the kernel that wants to do this? Has commit a144c6a broken it somehow that this fix would resolve it?

Quoted 12 lines
---
  drivers/base/firmware_class.c |    2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..017e020 100644
@@ -535,7 +535,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 
Quoted 6 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 538

Quoted 4 lines
drivers/base/firmware_class.c +1 -1
       read_lock_usermodehelper();
 
-     if (WARN_ON(usermodehelper_is_disabled())) {
+     if (WARN_ON(usermodehelper_is_disabled()&&  !(nowait&&  uevent))) {

What does uevent have to do with things here?

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

Quoted 5 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

We (mach-msm) just happened to be looking at similar issues with request_firmware. The recent changes to request_firmware to check for usermodehelper_is_disabled() was preventing us from using request_firmware() in what I think is a valid use case. I will get to that later.

To first suggest a solution specific the problem this patch is trying to address, I think it would be better to do something like below. It's just a quick RFC to show what I mean -- haven't even compiled it. If there is an agreement on this suggestion, I can send out a cleaner patch.

firmware class: Check for usermode helper availability only
when enabled.

---
 drivers/base/firmware_class.c |   15 ++++++++-------
 kernel/kmod.c                 |    2 +-
 2 files changed, 9 insertions(+), 8 deletions(-)
drivers/base/firmware_class.c +8 -7
index 06ed6b4..2a45bf7 100644
@@ -534,12 +534,6 @@ static int _request_firmware(const struct firmware **firmware_p,
                return 0;
        }
 
-       if (WARN_ON(usermodehelper_is_disabled())) {
-               dev_err(device, "firmware: %s will not be loaded\n", name);
-               retval = -EBUSY;
-               goto out;
-       }
-
        if (uevent)
                dev_dbg(device, "firmware: requesting %s\n", name);
 
@@ -555,12 +549,19 @@ static int _request_firmware(const struct firmware **firmware_p,
                                  round_jiffies_up(jiffies +
                                                   loading_timeout * HZ));
 
-               kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+               retval = kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+               if (retval) {
+                       dev_err(device, "firmware: %s will not be loaded\n", name);
+                       set_bit(FW_STATUS_ABORT, &fw_priv->status);
+                       goto abort;
+               }
        }
 
        wait_for_completion(&fw_priv->completion);
 
        set_bit(FW_STATUS_DONE, &fw_priv->status);
+
+abort:
        del_timer_sync(&fw_priv->timeout);
 
        mutex_lock(&fw_lock);
kernel/kmod.c +1 -1
index 47613df..e733afe3 100644
@@ -422,7 +422,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
        if (sub_info->path[0] == '\0')
                goto out;
 
-       if (!khelper_wq || usermodehelper_disabled) {
+       if (!khelper_wq || (uevent_helper[0] && usermodehelper_disabled)) {
                retval = -EBUSY;
                goto out;
        }
Signature · 16 lines
Now, getting to the issue we are facing -- the recent checks for usermode helper in request_firmare() is failing request_firmware() in a kthread that also activates a wake up source (or if you are familiar with Android terms -- grabs a wake lock). By activating a wakeup source, the kthread is properly indicating that a suspend shouldn't happen. So, I think it's a valid use case for request_firmware().

With the current checks, that doesn't seem to be sufficient since a kthread can coincidentally be running in parallel to the suspend sequence. The suspend sequence sets "usermodehelper_disabled" for the purpose of causing request_firmware() to fail immediately when called from the suspend ops. But that doesn't take into account that the kthread could also be running at the same time. If this check wasn't there, the suspend would be aborted (since the kthread has activated the wakeup source) and the request_firmware() would have succeeded.

I think the usermodehelper check in the request_firmware() flow is denying a wider swath of scenarios than it needs to. I think the real check should be to only disallow request_firmware() in all of the callbacks that are called from suspend_enter().

I was joking to my colleague (Stephen Boyd) about just walking up the stack to see if suspend_enter() is in the stack, but he seems to have ideas that would have a similar effect on the functionality without being insane code. I will leave it to him to present his ideas.

But while we are trying to figure out ways to immediately error out request_firmware() from suspend callbacks, I think we should remove the usermodehelper check in request_firmware() since it's actually preventing legitimate use cases.

Thanks,
Saravana

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#9 lore raw

Re: Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Kay Sievers
Quoted 35 lines · Kay Sievers
Quoted 23 lines · Greg KH
Quoted 6 lines · Christian Lamparter

This patch fixes a regression which was introduced by:
"PM: Print a warning if firmware is requested when tasks are frozen"

request_firmware_nowait does not stall in any system resume paths. Therefore, I think it is perfectly save to use request_firmware_nowait from at least the ->complete() callback.

Is there code somewhere in the kernel that wants to do this? Has commit a144c6a broken it somehow that this fix would resolve it?

Quoted 12 lines
---
  drivers/base/firmware_class.c |    2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..017e020 100644
@@ -535,7 +535,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 
Quoted 6 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 538

Quoted 4 lines
drivers/base/firmware_class.c +1 -1
       read_lock_usermodehelper();
 
-     if (WARN_ON(usermodehelper_is_disabled())) {
+     if (WARN_ON(usermodehelper_is_disabled()&&  !(nowait&&  uevent))) {

What does uevent have to do with things here?

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

Quoted 6 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Resending again after fixing my stupid email formatting.

We (mach-msm) just happened to be looking at similar issues with request_firmware. The recent changes to request_firmware to check for usermodehelper_is_disabled() was preventing us from using request_firmware() in what I think is a valid use case. I will get to that later.

To first suggest a solution specific the problem this patch is trying to address, I think it would be better to do something like below. It's just a quick RFC to show what I mean -- haven't even compiled it. If there is an agreement on this suggestion, I can send out a cleaner patch.

firmware class: Check for usermode helper availability only
when enabled.

---
 drivers/base/firmware_class.c |   15 ++++++++-------
 kernel/kmod.c                 |    2 +-
 2 files changed, 9 insertions(+), 8 deletions(-)
drivers/base/firmware_class.c +8 -7
index 06ed6b4..2a45bf7 100644
@@ -534,12 +534,6 @@ static int _request_firmware(const struct firmware **firmware_p,
                return 0;
        }
 
-       if (WARN_ON(usermodehelper_is_disabled())) {
-               dev_err(device, "firmware: %s will not be loaded\n", name);
-               retval = -EBUSY;
-               goto out;
-       }
-
        if (uevent)
                dev_dbg(device, "firmware: requesting %s\n", name);
 
@@ -555,12 +549,19 @@ static int _request_firmware(const struct firmware **firmware_p,
                                  round_jiffies_up(jiffies +
                                                   loading_timeout * HZ));
 
-               kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+               retval = kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+               if (retval) {
+                       dev_err(device, "firmware: %s will not be loaded\n", name);
+                       set_bit(FW_STATUS_ABORT, &fw_priv->status);
+                       goto abort;
+               }
        }
 
        wait_for_completion(&fw_priv->completion);
 
        set_bit(FW_STATUS_DONE, &fw_priv->status);
+
+abort:
        del_timer_sync(&fw_priv->timeout);
 
        mutex_lock(&fw_lock);
kernel/kmod.c +1 -1
index 47613df..e733afe3 100644
@@ -422,7 +422,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
        if (sub_info->path[0] == '\0')
                goto out;
 
-       if (!khelper_wq || usermodehelper_disabled) {
+       if (!khelper_wq || (uevent_helper[0] && usermodehelper_disabled)) {
                retval = -EBUSY;
                goto out;
        }

Now, getting to the issue we are facing -- the recent checks for usermode helper in request_firmare() is failing request_firmware() in a kthread that also activates a wake up source (or if you are familiar with Android terms -- grabs a wake lock). By activating a wakeup source, the kthread is properly indicating that a suspend shouldn't happen. So, I think it's a valid use case for request_firmware().

With the current checks, that doesn't seem to be sufficient since a kthread can coincidentally be running in parallel to the suspend sequence. The suspend sequence sets "usermodehelper_disabled" for the purpose of causing request_firmware() to fail immediately when called from the suspend ops. But that doesn't take into account that the kthread could also be running at the same time. If this check wasn't there, the suspend would be aborted (since the kthread has activated the wakeup source) and the request_firmware() would have succeeded.

I think the usermodehelper check in the request_firmware() flow is denying a wider swath of scenarios than it needs to. I think the real check should be to only disallow request_firmware() in all of the callbacks that are called from suspend_enter().

I was joking to my colleague (Stephen Boyd) about just walking up the stack to see if suspend_enter() is in the stack, but he seems to have ideas that would have a similar effect on the functionality without being insane code. I will leave it to him to present his ideas.

But while we are trying to figure out ways to immediately error out request_firmware() from suspend callbacks, I think we should remove the usermodehelper check in request_firmware() since it's actually preventing legitimate use cases.

Thanks,
Saravana

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#10 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 122 lines · Saravana Kannan
Quoted 35 lines · Kay Sievers
Quoted 23 lines · Greg KH
Quoted 6 lines · Christian Lamparter

This patch fixes a regression which was introduced by:
"PM: Print a warning if firmware is requested when tasks are frozen"

request_firmware_nowait does not stall in any system resume paths. Therefore, I think it is perfectly save to use request_firmware_nowait from at least the ->complete() callback.

Is there code somewhere in the kernel that wants to do this? Has commit a144c6a broken it somehow that this fix would resolve it?

Quoted 12 lines
---
  drivers/base/firmware_class.c |    2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..017e020 100644
@@ -535,7 +535,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 
Quoted 6 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p,

Quoted 4 lines
drivers/base/firmware_class.c +1 -1
       read_lock_usermodehelper();
 
-     if (WARN_ON(usermodehelper_is_disabled())) {
+     if (WARN_ON(usermodehelper_is_disabled()&&  !(nowait&&  uevent))) {

What does uevent have to do with things here?

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

Quoted 6 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Resending again after fixing my stupid email formatting.

We (mach-msm) just happened to be looking at similar issues with request_firmware. The recent changes to request_firmware to check for usermodehelper_is_disabled() was preventing us from using request_firmware() in what I think is a valid use case. I will get to that later.

To first suggest a solution specific the problem this patch is trying to address, I think it would be better to do something like below. It's just a quick RFC to show what I mean -- haven't even compiled it. If there is an agreement on this suggestion, I can send out a cleaner patch.

firmware class: Check for usermode helper availability only
when enabled.

---
  drivers/base/firmware_class.c |   15 ++++++++-------
  kernel/kmod.c                 |    2 +-
  2 files changed, 9 insertions(+), 8 deletions(-)
drivers/base/firmware_class.c +8 -7
index 06ed6b4..2a45bf7 100644
@@ -534,12 +534,6 @@ static int _request_firmware(const struct firmware 
 **firmware_p,
                 return 0;
         }
 
-       if (WARN_ON(usermodehelper_is_disabled())) {
-               dev_err(device, "firmware: %s will not be loaded\n", name);
-               retval = -EBUSY;
-               goto out;
-       }
-
         if (uevent)
                 dev_dbg(device, "firmware: requesting %s\n", name);
@@ -555,12 +549,19 @@ static int _request_firmware(const struct firmware 
 **firmware_p,
                                   round_jiffies_up(jiffies +
                                                    loading_timeout * HZ));
 
-               kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+               retval = kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+               if (retval) {
+                       dev_err(device, "firmware: %s will not be 
 loaded\n", name);
+                       set_bit(FW_STATUS_ABORT, &fw_priv->status);
+                       goto abort;
+               }
         }
 
         wait_for_completion(&fw_priv->completion);
 
         set_bit(FW_STATUS_DONE, &fw_priv->status);
+
+abort:
         del_timer_sync(&fw_priv->timeout);
         mutex_lock(&fw_lock);
kernel/kmod.c +1 -1
index 47613df..e733afe3 100644
@@ -422,7 +422,7 @@ int call_usermodehelper_exec(struct subprocess_info 
 *sub_info,
         if (sub_info->path[0] == '\0')
                 goto out;
 
-       if (!khelper_wq || usermodehelper_disabled) {
+       if (!khelper_wq || (uevent_helper[0] && usermodehelper_disabled)) {
                 retval = -EBUSY;
                 goto out;
         }
Quoted 6 lines

Now, getting to the issue we are facing -- the recent checks for usermode helper in request_firmare() is failing request_firmware() in a kthread that also activates a wake up source (or if you are familiar with Android terms -- grabs a wake lock). By activating a wakeup source, the kthread is properly indicating that a suspend shouldn't happen. So, I think it's a valid use case for request_firmware().

It isn't really, although it may seems so. In the mainline there is no guarantee that system suspend will be aborted when you activate a wakeup source (the suspend process may choose to ignore wakeup sources).

Quoted 5 lines

With the current checks, that doesn't seem to be sufficient since a kthread can coincidentally be running in parallel to the suspend sequence. The suspend sequence sets "usermodehelper_disabled" for the purpose of causing request_firmware() to fail immediately when called from the suspend ops.

No, it doesn't do that for this purpose. It does that to prevent a race between usermode helpers and the freezer from happening. The failing of request_firmware() when called during system suspend/resume is a consequence of that, not the reason why it is done.

Quoted 2 lines

But that doesn't take into account that the kthread could also be running at the same time.

Yes, it does. User space is frozen and your kthread can't possibly use a usermode helper at that time.

Quoted 3 lines

If this check wasn't there, the suspend would be aborted (since the kthread has activated the wakeup source) and the request_firmware() would have succeeded.

It would have succeeded _eventually_, after user space had been thawed.

Quoted 10 lines

I think the usermodehelper check in the request_firmware() flow is denying a wider swath of scenarios than it needs to. I think the real check should be to only disallow request_firmware() in all of the callbacks that are called from suspend_enter().

I was joking to my colleague (Stephen Boyd) about just walking up the stack to see if suspend_enter() is in the stack, but he seems to have ideas that would have a similar effect on the functionality without being insane code. I will leave it to him to present his ideas.

Quoted 4 lines

But while we are trying to figure out ways to immediately error out request_firmware() from suspend callbacks, I think we should remove the usermodehelper check in request_firmware() since it's actually preventing legitimate use cases.

All of those use cases are in fact of the "wait for user space to be thawed and then load the firmware" type, which I believe may be handled without changing that code.

Why don't you make your kthread freezable, for one example?

Why don't you use a freezable workqueue instead?

Rafael

#11 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 8 lines · Rafael J. Wysocki

All of those use cases are in fact of the "wait for user space to be thawed and then load the firmware" type, which I believe may be handled without changing that code.

Why don't you make your kthread freezable, for one example?

Why don't you use a freezable workqueue instead?

If we put it on the freezable workqueue or make it a freezable thread will it work?

In my scenario a wakeup interrupt comes in that wakes us up from suspend. Within that wakeup handler a work item is scheduled to the freezable workqueue. That work item then calls request_firmware().

It looks like we call schedule() after thawing the workqueues and tasks so the work item could run before usermodehelpers are enabled and then request_firmware() would fail. Do we need something like this (ignore the fact that we call usermodhelper_enable() twice)?

kernel/power/process.c +1 -0
index 7e42645..61bfa95 100644
@@ -187,6 +187,7 @@ void thaw_processes(void)
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 
+       usermodehelper_enable();
        schedule();
        printk("done.\n");
 }

Is there a reason we disable usermodehelpers if CONFIG_SUSPEND_FREEZER=n? Should we do this instead so that usermodehelpers are only disabled if we freeze userspace? Also what is that schedule() call in thaw_kernel_threads() for? It looks like we'll call schedule between kernel thread thawing and userspace thawing. I pushed out the schedule() call to the callers so that we don't call schedule() until userspace is thawed.

kernel/power/process.c +2 -1
index 7e42645..8fae228 100644
@@ -125,6 +125,7 @@ int freeze_processes(void)
 	if (!pm_freezing)
 		atomic_inc(&system_freezing_cnt);
 
+	usermodehelper_disable();
 	printk("Freezing user space processes ... ");
 	pm_freezing = true;
 	error = try_to_freeze_tasks(true);
@@ -187,6 +188,7 @@ void thaw_processes(void)
 	} while_each_thread(g, p);
 	read_unlock(&tasklist_lock);
 
+	usermodehelper_enable();
 	schedule();
 	printk("done.\n");
 }
@@ -207,6 +209,5 @@ void thaw_kernel_threads(void)
 	} while_each_thread(g, p);
 	read_unlock(&tasklist_lock);
 
-	schedule();
 	printk("done.\n");
 }
kernel/power/suspend.c +0 -6
index 4fd51be..1ab3e59 100644
@@ -101,17 +101,12 @@ static int suspend_prepare(void)
 	if (error)
 		goto Finish;
 
-	error = usermodehelper_disable();
-	if (error)
-		goto Finish;
-
 	error = suspend_freeze_processes();
 	if (!error)
 		return 0;
 
 	suspend_stats.failed_freeze++;
 	dpm_save_failed_step(SUSPEND_FREEZE);
-	usermodehelper_enable();
  Finish:
 	pm_notifier_call_chain(PM_POST_SUSPEND);
 	pm_restore_console();
@@ -259,7 +254,6 @@ int suspend_devices_and_enter(suspend_state_t state)
 static void suspend_finish(void)
 {
 	suspend_thaw_processes();
-	usermodehelper_enable();
 	pm_notifier_call_chain(PM_POST_SUSPEND);
 	pm_restore_console();
 }
kernel/power/user.c +4 -8
index 3e10007..1a8fd6f 100644
@@ -222,14 +222,8 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 		sys_sync();
 		printk("done.\n");
 
-		error = usermodehelper_disable();
-		if (error)
-			break;
-
 		error = freeze_processes();
-		if (error)
-			usermodehelper_enable();
-		else
+		if (!error)
 			data->frozen = 1;
 		break;
 
@@ -238,7 +232,6 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 			break;
 		pm_restore_gfp_mask();
 		thaw_processes();
-		usermodehelper_enable();
 		data->frozen = 0;
 		break;
 
@@ -251,6 +244,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 		error = hibernation_snapshot(data->platform_support);
 		if (error) {
 			thaw_kernel_threads();
+			schedule();
 		} else {
 			error = put_user(in_suspend, (int __user *)arg);
 			if (!error && !freezer_test_done)
@@ -258,6 +252,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 			if (freezer_test_done) {
 				freezer_test_done = false;
 				thaw_kernel_threads();
+				schedule();
 			}
 		}
 		break;
@@ -285,6 +280,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 		 * might fail or even deadlock).
 		 */
 		thaw_kernel_threads();
+		schedule();
 		break;
 
 	case SNAPSHOT_PREF_IMAGE_SIZE:
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#12 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 5 lines · Stephen Boyd
Quoted 4 lines · Rafael J. Wysocki

All of those use cases are in fact of the "wait for user space to be thawed and then load the firmware" type, which I believe may be handled without changing that code.

Quoted 7 lines
Quoted 4 lines

Why don't you make your kthread freezable, for one example?

Why don't you use a freezable workqueue instead?

If we put it on the freezable workqueue or make it a freezable thread will it work?

That depends on what exactly you want to achieve, which isn't entirely clear to me at this point.

Quoted 3 lines

In my scenario a wakeup interrupt comes in that wakes us up from suspend. Within that wakeup handler a work item is scheduled to the freezable workqueue. That work item then calls request_firmware().

That should work.

Quoted 10 lines

It looks like we call schedule() after thawing the workqueues and tasks so the work item could run before usermodehelpers are enabled and then request_firmware() would fail. Do we need something like this (ignore the fact that we call usermodhelper_enable() twice)?

kernel/power/process.c +0 -0
index 7e42645..61bfa95 100644

kernel/power/process.c void thaw_processes(void) · line 190

Quoted 4 lines
kernel/power/process.c +1 -0
@@ -187,6 +187,7 @@ void thaw_processes(void)
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 
+       usermodehelper_enable();

That would be a reasonable change.

Quoted 7 lines
kernel/power/process.c +0 -0
        schedule();
        printk("done.\n");
 }

Is there a reason we disable usermodehelpers if
CONFIG_SUSPEND_FREEZER=n?

Not really, but CONFIG_SUSPEND_FREEZER=n can only work reliably in a very limited set of cases, so I don't think it's even worth making the general code depend on it.

I'd actually prefer to remove CONFIG_SUSPEND_FREEZER altogether, because it's not very useful nowadays (probably isn't useful at all).

Quoted 4 lines

Should we do this instead so that
usermodehelpers are only disabled if we freeze userspace? Also what is that schedule() call in thaw_kernel_threads() for? It looks like we'll call schedule between kernel thread thawing and userspace thawing.

Which is OK, I think.

Quoted 2 lines

I pushed out the schedule() call to the callers so that we don't call schedule() until userspace is thawed.

Why did you do that?

Rafael

#13 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 55 lines · Rafael J. Wysocki
Quoted 5 lines · Stephen Boyd
Quoted 4 lines · Rafael J. Wysocki

All of those use cases are in fact of the "wait for user space to be thawed and then load the firmware" type, which I believe may be handled without changing that code.

Quoted 7 lines
Quoted 4 lines

Why don't you make your kthread freezable, for one example?

Why don't you use a freezable workqueue instead?

If we put it on the freezable workqueue or make it a freezable thread will it work?

That depends on what exactly you want to achieve, which isn't entirely clear to me at this point.

Quoted 3 lines

In my scenario a wakeup interrupt comes in that wakes us up from suspend. Within that wakeup handler a work item is scheduled to the freezable workqueue. That work item then calls request_firmware().

That should work.

Quoted 10 lines

It looks like we call schedule() after thawing the workqueues and tasks so the work item could run before usermodehelpers are enabled and then request_firmware() would fail. Do we need something like this (ignore the fact that we call usermodhelper_enable() twice)?

kernel/power/process.c +0 -0
index 7e42645..61bfa95 100644

kernel/power/process.c void thaw_processes(void) · line 190

Quoted 4 lines
kernel/power/process.c +1 -0
@@ -187,6 +187,7 @@ void thaw_processes(void)
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 
+       usermodehelper_enable();

That would be a reasonable change.

Quoted 7 lines
kernel/power/process.c +0 -0
        schedule();
        printk("done.\n");
 }

Is there a reason we disable usermodehelpers if
CONFIG_SUSPEND_FREEZER=n?

Not really, but CONFIG_SUSPEND_FREEZER=n can only work reliably in a very limited set of cases, so I don't think it's even worth making the general code depend on it.

I'd actually prefer to remove CONFIG_SUSPEND_FREEZER altogether, because it's not very useful nowadays (probably isn't useful at all).

Quoted 6 lines
Quoted 4 lines

Should we do this instead so that
usermodehelpers are only disabled if we freeze userspace? Also what is that schedule() call in thaw_kernel_threads() for? It looks like we'll call schedule between kernel thread thawing and userspace thawing.

Which is OK, I think.

Moreover, thaw_kernel_threads() is _only_ called by (a) freeze_kernel_threads() on error and (b) user-space hibernate interface in kernel/power/user.c (and please read the comment in there describing what it's there for, which also explains why the schedule() call in there is necessary).

In all other cases, thaw_processes() is used to thaw kernel threads and user space at the same time, so I believe we don't need the changes you're proposing in that area.

Thanks,
Rafael

#14 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 55 lines · Rafael J. Wysocki
Quoted 54 lines · Rafael J. Wysocki
Quoted 11 lines · Stephen Boyd
Quoted 8 lines · Rafael J. Wysocki

All of those use cases are in fact of the "wait for user space to be thawed and then load the firmware" type, which I believe may be handled without changing that code.

Why don't you make your kthread freezable, for one example?

Why don't you use a freezable workqueue instead?

If we put it on the freezable workqueue or make it a freezable thread will it work?

That depends on what exactly you want to achieve, which isn't entirely clear to me at this point.

Quoted 3 lines

In my scenario a wakeup interrupt comes in that wakes us up from suspend. Within that wakeup handler a work item is scheduled to the freezable workqueue. That work item then calls request_firmware().

That should work.

Quoted 10 lines

It looks like we call schedule() after thawing the workqueues and tasks so the work item could run before usermodehelpers are enabled and then request_firmware() would fail. Do we need something like this (ignore the fact that we call usermodhelper_enable() twice)?

kernel/power/process.c +0 -0
index 7e42645..61bfa95 100644

kernel/power/process.c void thaw_processes(void) · line 190

Quoted 4 lines
kernel/power/process.c +1 -0
@@ -187,6 +187,7 @@ void thaw_processes(void)
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 
+       usermodehelper_enable();

That would be a reasonable change.

Quoted 7 lines
kernel/power/process.c +0 -0
        schedule();
        printk("done.\n");
 }

Is there a reason we disable usermodehelpers if
CONFIG_SUSPEND_FREEZER=n?

Not really, but CONFIG_SUSPEND_FREEZER=n can only work reliably in a very limited set of cases, so I don't think it's even worth making the general code depend on it.

I'd actually prefer to remove CONFIG_SUSPEND_FREEZER altogether, because it's not very useful nowadays (probably isn't useful at all).

Quoted 4 lines

Should we do this instead so that
usermodehelpers are only disabled if we freeze userspace? Also what is that schedule() call in thaw_kernel_threads() for? It looks like we'll call schedule between kernel thread thawing and userspace thawing.

Quoted 5 lines
Quoted 1 line

Which is OK, I think.

Moreover, thaw_kernel_threads() is _only_ called by (a) freeze_kernel_threads() on error and (b) user-space hibernate interface in kernel/power/user.c (and please read the comment in there describing what it's there for, which also explains why the schedule() call in there is necessary).

Exactly. So in case (a) when the error occurs we'll have this call flow:

usermodehelpers_disable()
suspend_freeze_processes()
freeze_processes()
freeze_kernel_threads()
try_to_freeze_tasks() <-- returns error
thaw_kernel_threads()
schedule()
thaw_processes()
usermodehelpers_enable()

Shouldn't we schedule only after we thaw all processes (not just tasks)? Otherwise we may run a kernel thread before userspace is thawed?

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#15 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 74 lines · Stephen Boyd
Quoted 55 lines · Rafael J. Wysocki
Quoted 54 lines · Rafael J. Wysocki
Quoted 11 lines · Stephen Boyd
Quoted 8 lines · Rafael J. Wysocki

All of those use cases are in fact of the "wait for user space to be thawed and then load the firmware" type, which I believe may be handled without changing that code.

Why don't you make your kthread freezable, for one example?

Why don't you use a freezable workqueue instead?

If we put it on the freezable workqueue or make it a freezable thread will it work?

That depends on what exactly you want to achieve, which isn't entirely clear to me at this point.

Quoted 3 lines

In my scenario a wakeup interrupt comes in that wakes us up from suspend. Within that wakeup handler a work item is scheduled to the freezable workqueue. That work item then calls request_firmware().

That should work.

Quoted 10 lines

It looks like we call schedule() after thawing the workqueues and tasks so the work item could run before usermodehelpers are enabled and then request_firmware() would fail. Do we need something like this (ignore the fact that we call usermodhelper_enable() twice)?

kernel/power/process.c +0 -0
index 7e42645..61bfa95 100644

kernel/power/process.c void thaw_processes(void) · line 190

Quoted 4 lines
kernel/power/process.c +1 -0
@@ -187,6 +187,7 @@ void thaw_processes(void)
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 
+       usermodehelper_enable();

That would be a reasonable change.

Quoted 7 lines
kernel/power/process.c +0 -0
        schedule();
        printk("done.\n");
 }

Is there a reason we disable usermodehelpers if
CONFIG_SUSPEND_FREEZER=n?

Not really, but CONFIG_SUSPEND_FREEZER=n can only work reliably in a very limited set of cases, so I don't think it's even worth making the general code depend on it.

I'd actually prefer to remove CONFIG_SUSPEND_FREEZER altogether, because it's not very useful nowadays (probably isn't useful at all).

Quoted 4 lines

Should we do this instead so that
usermodehelpers are only disabled if we freeze userspace? Also what is that schedule() call in thaw_kernel_threads() for? It looks like we'll call schedule between kernel thread thawing and userspace thawing.

Quoted 5 lines
Quoted 1 line

Which is OK, I think.

Moreover, thaw_kernel_threads() is _only_ called by (a) freeze_kernel_threads() on error and (b) user-space hibernate interface in kernel/power/user.c (and please read the comment in there describing what it's there for, which also explains why the schedule() call in there is necessary).

Exactly. So in case (a) when the error occurs we'll have this call flow:

usermodehelpers_disable()
suspend_freeze_processes()
freeze_processes()
freeze_kernel_threads()
try_to_freeze_tasks() <-- returns error
thaw_kernel_threads()
schedule()
thaw_processes()
usermodehelpers_enable()

Quoted 2 lines

Shouldn't we schedule only after we thaw all processes (not just tasks)? Otherwise we may run a kernel thread before userspace is thawed?

Yes, we may, but that isn't wrong, is it?

Only a few kernel threads are freezable, so definitely kernel threads can run while user space is frozen.

Thanks,
Rafael

#16 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 21 lines · Rafael J. Wysocki
Quoted 20 lines · Stephen Boyd
Quoted 7 lines · Rafael J. Wysocki
Quoted 1 line · Rafael J. Wysocki

Which is OK, I think.

Moreover, thaw_kernel_threads() is _only_ called by (a) freeze_kernel_threads() on error and (b) user-space hibernate interface in kernel/power/user.c (and please read the comment in there describing what it's there for, which also explains why the schedule() call in there is necessary).

Exactly. So in case (a) when the error occurs we'll have this call flow:

usermodehelpers_disable()
suspend_freeze_processes()
freeze_processes()
freeze_kernel_threads()
try_to_freeze_tasks() <-- returns error
thaw_kernel_threads()
schedule()
thaw_processes()
usermodehelpers_enable()

Quoted 7 lines
Quoted 2 lines

Shouldn't we schedule only after we thaw all processes (not just tasks)? Otherwise we may run a kernel thread before userspace is thawed?

Yes, we may, but that isn't wrong, is it?

Only a few kernel threads are freezable, so definitely kernel threads can run while user space is frozen.

Yes but if someone calls request_firmware() from a kthread then they will hit the same problem where the thread runs and requests the firmware and usermodehelpers are still disabled. Currently my code is written with kthreads and that thread makes the request firmware call, so this doesn't seem far fetched (although in my case I can probably fix it). It looks like before 379e0be (PM / Freezer: Thaw only kernel threads if freezing of kernel threads fails, 2012-02-03) schedule wasn't called until userspace was thawed. It looks like that patch was about hibernation and not suspend?

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#17 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 30 lines · Stephen Boyd
Quoted 21 lines · Rafael J. Wysocki
Quoted 20 lines · Stephen Boyd
Quoted 7 lines · Rafael J. Wysocki
Quoted 1 line · Rafael J. Wysocki

Which is OK, I think.

Moreover, thaw_kernel_threads() is _only_ called by (a) freeze_kernel_threads() on error and (b) user-space hibernate interface in kernel/power/user.c (and please read the comment in there describing what it's there for, which also explains why the schedule() call in there is necessary).

Exactly. So in case (a) when the error occurs we'll have this call flow:

usermodehelpers_disable()
suspend_freeze_processes()
freeze_processes()
freeze_kernel_threads()
try_to_freeze_tasks() <-- returns error
thaw_kernel_threads()
schedule()
thaw_processes()
usermodehelpers_enable()

Quoted 7 lines
Quoted 2 lines

Shouldn't we schedule only after we thaw all processes (not just tasks)? Otherwise we may run a kernel thread before userspace is thawed?

Yes, we may, but that isn't wrong, is it?

Only a few kernel threads are freezable, so definitely kernel threads can run while user space is frozen.

Quoted 6 lines

Yes but if someone calls request_firmware() from a kthread then they will hit the same problem where the thread runs and requests the firmware and usermodehelpers are still disabled. Currently my code is written with kthreads and that thread makes the request firmware call, so this doesn't seem far fetched (although in my case I can probably fix it).

So again, please consider using suspend/resume notifiers to synchronize your kthread with system power transitions.

Quoted 4 lines

It looks like before 379e0be (PM / Freezer: Thaw only kernel threads if freezing of kernel threads fails, 2012-02-03) schedule wasn't called until userspace was thawed. It looks like that patch was about hibernation and not suspend?

It was about hibernation and it fixed a real bug.

Thanks,
Rafael

#18 lore raw

PATCH 1/1 firmware_class: Move request_firmware_nowait() to workqueues

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

I saw this while looking at this problem we're having.

 drivers/base/firmware_class.c |   29 +++++++----------------------
 1 file changed, 7 insertions(+), 22 deletions(-)
drivers/base/firmware_class.c +7 -22
index 6c9387d..1010c06 100644
@@ -16,7 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/mutex.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
@@ -629,25 +629,18 @@ struct firmware_work {
 	bool uevent;
 };
 
-static int request_firmware_work_func(void *arg)
+static void request_firmware_work_func(struct work_struct *work)
 {
-	struct firmware_work *fw_work = arg;
+	struct firmware_work *fw_work;
 	const struct firmware *fw;
-	int ret;
-
-	if (!arg) {
-		WARN_ON(1);
-		return 0;
-	}
+	fw_work = container_of(work, struct firmware_work, work);
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	_request_firmware(&fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
 	kfree(fw_work);
-
-	return ret;
 }
 
 /**
@@ -673,7 +666,6 @@ request_firmware_nowait(
 	const char *name, struct device *device, gfp_t gfp, void *context,
 	void (*cont)(const struct firmware *fw, void *context))
 {
-	struct task_struct *task;
 	struct firmware_work *fw_work;
 
 	fw_work = kzalloc(sizeof (struct firmware_work), gfp);
@@ -692,15 +684,8 @@ request_firmware_nowait(
 		return -EFAULT;
 	}
 
-	task = kthread_run(request_firmware_work_func, fw_work,
-			    "firmware/%s", name);
-	if (IS_ERR(task)) {
-		fw_work->cont(NULL, fw_work->context);
-		module_put(fw_work->module);
-		kfree(fw_work);
-		return PTR_ERR(task);
-	}
-
+	INIT_WORK(&fw_work->work, request_firmware_work_func);
+	schedule_work(&fw_work->work);
 	return 0;
 }
 
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#19 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 7 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Quoted 7 lines
Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

But I think we can get rid of the firmware_work work struct...

Regards,
Chr

#20 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 14 lines · Christian Lamparter
Quoted 13 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

Quoted 6 lines
Quoted 1 line

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

But I think we can get rid of the firmware_work work struct...

My understanding is that with concurrency managed workqueues when the work item blocks another will be scheduled to run almost immediately. So before that change by Tejun workqueues would have been a bad idea because it could have blocked up to 60 second but now it should be fine because that work item will just be put to sleep and another request will run.

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#21 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 22 lines · Stephen Boyd
Quoted 14 lines · Christian Lamparter
Quoted 13 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

Quoted 6 lines
Quoted 1 line

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

But I think we can get rid of the firmware_work work struct...

Quoted 6 lines

My understanding is that with concurrency managed workqueues when the work item blocks another will be scheduled to run almost immediately. So before that change by Tejun workqueues would have been a bad idea because it could have blocked up to 60 second but now it should be fine because that work item will just be put to sleep and another request will run.

Please read the description of system_wq in workqueue.h.

You should have used either system_long_wq or system_nrt_wq (depending on what you really need).

Thanks,
Rafael

#22 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 28 lines · Rafael J. Wysocki
Quoted 19 lines · Stephen Boyd
Quoted 18 lines · Christian Lamparter
Quoted 7 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Quoted 7 lines
Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

Quoted 8 lines
Quoted 2 lines

But I think we can get rid of the firmware_work work struct...

My understanding is that with concurrency managed workqueues when the work item blocks another will be scheduled to run almost immediately. So before that change by Tejun workqueues would have been a bad idea because it could have blocked up to 60 second but now it should be fine because that work item will just be put to sleep and another request will run.

Quoted 6 lines

Please read the description of system_wq in workqueue.h.

You should have used either system_long_wq or system_nrt_wq (depending on what you really need).

Thanks. I think we can use system_nrt_wq then? Or maybe even the unbounded workqueue system_unbound_wq?

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#23 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 31 lines · Stephen Boyd
Quoted 30 lines · Rafael J. Wysocki
Quoted 19 lines · Stephen Boyd
Quoted 18 lines · Christian Lamparter
Quoted 7 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Quoted 7 lines
Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

Quoted 8 lines
Quoted 2 lines

But I think we can get rid of the firmware_work work struct...

My understanding is that with concurrency managed workqueues when the work item blocks another will be scheduled to run almost immediately. So before that change by Tejun workqueues would have been a bad idea because it could have blocked up to 60 second but now it should be fine because that work item will just be put to sleep and another request will run.

Please read the description of system_wq in workqueue.h.

Quoted 7 lines
Quoted 4 lines

You should have used either system_long_wq or system_nrt_wq (depending on what you really need).

Thanks. I think we can use system_nrt_wq then? Or maybe even the unbounded workqueue system_unbound_wq?

Hmm. Can you please remind me what the exact role of that work item is?

It loads the device's firmware, but I'm not sure in what situations that's supposed to happen.

Thanks,
Rafael

#24 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 36 lines · Rafael J. Wysocki
Quoted 35 lines · Stephen Boyd
Quoted 34 lines · Rafael J. Wysocki
Quoted 19 lines · Stephen Boyd
Quoted 18 lines · Christian Lamparter
Quoted 7 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Quoted 7 lines
Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

Quoted 8 lines
Quoted 2 lines

But I think we can get rid of the firmware_work work struct...

My understanding is that with concurrency managed workqueues when the work item blocks another will be scheduled to run almost immediately. So before that change by Tejun workqueues would have been a bad idea because it could have blocked up to 60 second but now it should be fine because that work item will just be put to sleep and another request will run.

Please read the description of system_wq in workqueue.h.

You should have used either system_long_wq or system_nrt_wq (depending on what you really need).

Quoted 7 lines
Quoted 2 lines

Thanks. I think we can use system_nrt_wq then? Or maybe even the unbounded workqueue system_unbound_wq?

Hmm. Can you please remind me what the exact role of that work item is?

It loads the device's firmware, but I'm not sure in what situations that's supposed to happen.

request_firmware_nowait() is used by code that wants to get the firmware asynchronously. Callers pass in a callback function which is called once the firmware is retrieved. The work item will correspond to one call to request_firmware_nowait(), where the work item will handle the sysfs entry generation, uevent generation, and wait_for_completion() calls that _request_firmware() does.

The work item also executes the callback function the caller passes in which could do probably anything and could take an arbitrarily long time. It looks like some drivers even chain request_firmware_nowait() together by calling request_firmware_nowait() from the callback functions.

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#25 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 52 lines · Stephen Boyd
Quoted 36 lines · Rafael J. Wysocki
Quoted 35 lines · Stephen Boyd
Quoted 34 lines · Rafael J. Wysocki
Quoted 19 lines · Stephen Boyd
Quoted 18 lines · Christian Lamparter
Quoted 7 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Quoted 7 lines
Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

Quoted 8 lines
Quoted 2 lines

But I think we can get rid of the firmware_work work struct...

My understanding is that with concurrency managed workqueues when the work item blocks another will be scheduled to run almost immediately. So before that change by Tejun workqueues would have been a bad idea because it could have blocked up to 60 second but now it should be fine because that work item will just be put to sleep and another request will run.

Please read the description of system_wq in workqueue.h.

You should have used either system_long_wq or system_nrt_wq (depending on what you really need).

Quoted 7 lines
Quoted 2 lines

Thanks. I think we can use system_nrt_wq then? Or maybe even the unbounded workqueue system_unbound_wq?

Hmm. Can you please remind me what the exact role of that work item is?

It loads the device's firmware, but I'm not sure in what situations that's supposed to happen.

request_firmware_nowait() is used by code that wants to get the firmware asynchronously. Callers pass in a callback function which is called once the firmware is retrieved. The work item will correspond to one call to request_firmware_nowait(), where the work item will handle the sysfs entry generation, uevent generation, and wait_for_completion() calls that _request_firmware() does.

Quoted 4 lines

The work item also executes the callback function the caller passes in which could do probably anything and could take an arbitrarily long time. It looks like some drivers even chain request_firmware_nowait() together by calling request_firmware_nowait() from the callback functions.

So it looks like an unbound workqueue would be suitable for that, but perhaps it may even be an ordered one?

Rafael

#26 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 53 lines · On Friday 16 March 2012 22:45:52 Rafael J. Wysocki wrote:
Quoted 52 lines · Stephen Boyd
Quoted 36 lines · Rafael J. Wysocki
Quoted 35 lines · Stephen Boyd
Quoted 34 lines · Rafael J. Wysocki
Quoted 19 lines · Stephen Boyd
Quoted 18 lines · Christian Lamparter
Quoted 7 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Quoted 7 lines
Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

Quoted 8 lines
Quoted 2 lines

But I think we can get rid of the firmware_work work struct...

My understanding is that with concurrency managed workqueues when the work item blocks another will be scheduled to run almost immediately. So before that change by Tejun workqueues would have been a bad idea because it could have blocked up to 60 second but now it should be fine because that work item will just be put to sleep and another request will run.

Please read the description of system_wq in workqueue.h.

You should have used either system_long_wq or system_nrt_wq (depending on what you really need).

Quoted 7 lines
Quoted 2 lines

Thanks. I think we can use system_nrt_wq then? Or maybe even the unbounded workqueue system_unbound_wq?

Hmm. Can you please remind me what the exact role of that work item is?

It loads the device's firmware, but I'm not sure in what situations that's supposed to happen.

request_firmware_nowait() is used by code that wants to get the firmware asynchronously. Callers pass in a callback function which is called once the firmware is retrieved. The work item will correspond to one call to request_firmware_nowait(), where the work item will handle the sysfs entry generation, uevent generation, and wait_for_completion() calls that _request_firmware() does.

Quoted 7 lines
Quoted 4 lines

The work item also executes the callback function the caller passes in which could do probably anything and could take an arbitrarily long time. It looks like some drivers even chain request_firmware_nowait() together by calling request_firmware_nowait() from the callback functions.

So it looks like an unbound workqueue would be suitable for that, but perhaps it may even be an ordered one?

Just a note:
kernel/kmod.c currently defines a workqueue "khelper"... So since we are already "using" kmod's usermodehelper_*, then why not the mess with the workqueue as well?

Regards,
Chr

#27 lore raw

Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 59 lines · Christian Lamparter
Quoted 58 lines · On Friday 16 March 2012 22:45:52 Rafael J. Wysocki wrote:
Quoted 56 lines · Stephen Boyd
Quoted 36 lines · Rafael J. Wysocki
Quoted 35 lines · Stephen Boyd
Quoted 34 lines · Rafael J. Wysocki
Quoted 19 lines · Stephen Boyd
Quoted 18 lines · Christian Lamparter
Quoted 7 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary if workqueues are available.

Quoted 7 lines
Cc Greg Kroah-Hartman <[email protected]>
Cc Kay Sievers <[email protected]>
Cc Rafael J. Wysocki <[email protected]>

---

I saw this while looking at this problem we're having.

Correct me if I'm wrong, but wouldn't that stall all other global workqueue tasks for up to 60 seconds [in worst case]?

Quoted 8 lines
Quoted 2 lines

But I think we can get rid of the firmware_work work struct...

My understanding is that with concurrency managed workqueues when the work item blocks another will be scheduled to run almost immediately. So before that change by Tejun workqueues would have been a bad idea because it could have blocked up to 60 second but now it should be fine because that work item will just be put to sleep and another request will run.

Please read the description of system_wq in workqueue.h.

You should have used either system_long_wq or system_nrt_wq (depending on what you really need).

Quoted 7 lines
Quoted 2 lines

Thanks. I think we can use system_nrt_wq then? Or maybe even the unbounded workqueue system_unbound_wq?

Hmm. Can you please remind me what the exact role of that work item is?

It loads the device's firmware, but I'm not sure in what situations that's supposed to happen.

request_firmware_nowait() is used by code that wants to get the firmware asynchronously. Callers pass in a callback function which is called once the firmware is retrieved. The work item will correspond to one call to request_firmware_nowait(), where the work item will handle the sysfs entry generation, uevent generation, and wait_for_completion() calls that _request_firmware() does.

The work item also executes the callback function the caller passes in which could do probably anything and could take an arbitrarily long time. It looks like some drivers even chain request_firmware_nowait() together by calling request_firmware_nowait() from the callback functions.

Quoted 6 lines
Quoted 2 lines

So it looks like an unbound workqueue would be suitable for that, but perhaps it may even be an ordered one?

Just a note:
kernel/kmod.c currently defines a workqueue "khelper"... So since we are already "using" kmod's usermodehelper_*, then why not the mess with the workqueue as well?

Well, under a different name, maybe ...

Rafael

#28 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Rafael J. Wysocki
Quoted 30 lines · Rafael J. Wysocki
Quoted 29 lines · Stephen Boyd
Quoted 28 lines · Rafael J. Wysocki
Quoted 20 lines · Stephen Boyd
Quoted 7 lines · Rafael J. Wysocki
Quoted 1 line · Rafael J. Wysocki

Which is OK, I think.

Moreover, thaw_kernel_threads() is _only_ called by (a) freeze_kernel_threads() on error and (b) user-space hibernate interface in kernel/power/user.c (and please read the comment in there describing what it's there for, which also explains why the schedule() call in there is necessary).

Exactly. So in case (a) when the error occurs we'll have this call flow:

usermodehelpers_disable()
suspend_freeze_processes()
freeze_processes()
freeze_kernel_threads()
try_to_freeze_tasks() <-- returns error
thaw_kernel_threads()
schedule()
thaw_processes()
usermodehelpers_enable()

Quoted 2 lines

Shouldn't we schedule only after we thaw all processes (not just tasks)? Otherwise we may run a kernel thread before userspace is thawed?

Yes, we may, but that isn't wrong, is it?

Only a few kernel threads are freezable, so definitely kernel threads can run while user space is frozen.

Quoted 8 lines
Quoted 6 lines

Yes but if someone calls request_firmware() from a kthread then they will hit the same problem where the thread runs and requests the firmware and usermodehelpers are still disabled. Currently my code is written with kthreads and that thread makes the request firmware call, so this doesn't seem far fetched (although in my case I can probably fix it).

So again, please consider using suspend/resume notifiers to synchronize your kthread with system power transitions.

Ah. Freezable workqueues and threads are entirely not what I want. I have to use suspend/resume notifiers so that I don't call request_firmware() until suspend is over and I have to synchronize that with a rwsem so that suspend is blocked until my call to request_firmware() completes (and so request_firmware() is blocked until suspend completes). Every user of request_firmware() has to do the same check or be certain that their code won't run during a system-wide suspend/resume. Ouch.

Can we fix this in the core firmware loading code somehow? I really want to have a way to differentiate between tasks that can block suspend from progressing and tasks that won't. This way everyone who doesn't care about suspend can still call request_firmware() and go to sleep if we're in the middle of suspending and the ones who would block suspend get failed immediately. That would seem to be more in line with why this patch was made (i.e. don't block suspend/resume from progressing with request_firmware() calls).

What about this? It basically keeps track of which task is the suspending task and then only fails that one. I'm not completely sold on this one but it would work most of the time.

---8<-----

drivers/base/firmware_class.c +3 -4
index 6c9387d..5127534 100644
@@ -533,9 +533,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 		return 0;
 	}
 
-	read_lock_usermodehelper();
-
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	if (is_sleep_task()) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
 		goto out;
@@ -550,6 +548,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 		goto out;
 	}
 
+	read_lock_usermodehelper();
 	if (uevent) {
 		if (loading_timeout > 0)
 			mod_timer(&fw_priv->timeout,
@@ -560,6 +559,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 	}
 
 	wait_for_completion(&fw_priv->completion);
+	read_unlock_usermodehelper();
 
 	set_bit(FW_STATUS_DONE, &fw_priv->status);
 	del_timer_sync(&fw_priv->timeout);
@@ -573,7 +573,6 @@ static int _request_firmware(const struct firmware **firmware_p,
 	fw_destroy_instance(fw_priv);
 
 out:
-	read_unlock_usermodehelper();
 
 	if (retval) {
 		release_firmware(firmware);
include/linux/kmod.h +1 -0
index 9efeae6..d84da8c 100644
@@ -115,6 +115,7 @@ extern void usermodehelper_init(void);
 extern int usermodehelper_disable(void);
 extern void usermodehelper_enable(void);
 extern bool usermodehelper_is_disabled(void);
+extern bool is_sleep_task(void);
 extern void read_lock_usermodehelper(void);
 extern void read_unlock_usermodehelper(void);
 
kernel/kmod.c +10 -3
index 9faa560..50fae27 100644
@@ -368,6 +368,13 @@ void read_unlock_usermodehelper(void)
 }
 EXPORT_SYMBOL_GPL(read_unlock_usermodehelper);
 
+static struct task_struct *system_sleep_task;
+
+bool is_sleep_task(void)
+{
+	return current == system_sleep_task;
+}
+
 /**
  * usermodehelper_disable - prevent new helpers from being started
  */
@@ -375,9 +382,9 @@ int usermodehelper_disable(void)
 {
 	long retval;
 
+	system_sleep_task = current;
 	down_write(&umhelper_sem);
 	usermodehelper_disabled = 1;
-	up_write(&umhelper_sem);
 
 	/*
 	 * From now on call_usermodehelper_exec() won't start any new
@@ -391,9 +398,9 @@ int usermodehelper_disable(void)
 	if (retval)
 		return 0;
 
-	down_write(&umhelper_sem);
 	usermodehelper_disabled = 0;
 	up_write(&umhelper_sem);
+	system_sleep_task = NULL;
 	return -EAGAIN;
 }
 
@@ -402,9 +409,9 @@ int usermodehelper_disable(void)
  */
 void usermodehelper_enable(void)
 {
-	down_write(&umhelper_sem);
 	usermodehelper_disabled = 0;
 	up_write(&umhelper_sem);
+	system_sleep_task = NULL;
 }
 
 /**
Signature · 14 lines
Or maybe we should modify request_firmware_nowait() to have the suspend
notifier checks? Right now if I call request_firmware_nowait() from a
probe it just might happen to fail because the kthread could run before
resume is complete. I didn't call request_firmware(), so I really tried
not to block resume progress but I'm gambling that resume will be over
before my thread runs. I could write notifier rwsem code and then wait
for resume to finish before calling request_firmware_nowat(), but that
would require me to fork a thread/workqueue to run
request_firmware_nowait() which will then fork a thread again. Seems
useless to even use request_firmware_nowait() then.

How about this totally untested patch? We do the rwsem thing for
request_firmware_nowait() at least.
----8<-----
drivers/base/firmware_class.c +27 -0
index 6c9387d..f320ccd 100644
@@ -20,6 +20,8 @@
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/rwsem.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
 
@@ -629,6 +631,9 @@ struct firmware_work {
 	bool uevent;
 };
 
+/* Synchronize request_firmware_work_func() with suspend */
+static DECLARE_RWSEM(fw_pm_rwsem);
+
 static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
@@ -640,8 +645,10 @@ static int request_firmware_work_func(void *arg)
 		return 0;
 	}
 
+	down_read(&fw_pm_rwsem);
 	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+	up_read(&fw_pm_rwsem);
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
@@ -704,13 +711,33 @@ request_firmware_nowait(
 	return 0;
 }
 
+static int firmware_class_pm_notify(struct notifier_block *block,
+		unsigned long event, void *p)
+{
+	switch (event) {
+	case PM_SUSPEND_PREPARE:
+		down_write(&fw_pm_rwsem);
+		break;
+	case PM_POST_SUSPEND:
+		up_write(&fw_pm_rwsem);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block firmware_class_pm_notifier = {
+	.notifier_call = firmware_class_pm_notify,
+};
+
 static int __init firmware_class_init(void)
 {
+	register_pm_notifier(&firmware_class_pm_notifier);
 	return class_register(&firmware_class);
 }
 
 static void __exit firmware_class_exit(void)
 {
+	unregister_pm_notifier(&firmware_class_pm_notifier);
 	class_unregister(&firmware_class);
 }
 
Signature · 6 lines
Hmm it seems something similar is going on in the other part of the
thread. I'll work something up in a few hours.

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#29 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 3 lines · Stephen Boyd

-       if (WARN_ON(usermodehelper_is_disabled())) {
+       if (is_sleep_task()) {
               dev_err(device, "firmware: %s will not be loaded\n", name);

Stop these idiotic games already!

It's very simple: you cannot load firmware while the system is not readt. Your moronic "let's change the test to something else" is entirely and utterly misguided and totally misses the point.

It's not about sleeping, and it's not about anything even *remotely* about that. Stop the idiocy already.

How hard is it to understand? How many times do people have to tell you?

That warning is very much valid during bootup, and that warning has been *seen* during bootup. For example, try to compile in most wireless drivers as non-modular, and that warning *has* to trigger.

Rafael, please consider everything along these *IDIOTIC* lines completely NAK'ed. In fact, until Stephen starts showing any sign of understanding that it's not about just some random small detail, just ignore anything and everything from him.

Stephen, you've been told multiple times that that WARN_ON() is correct. Until you understand that, just stop sending these entirely random patches that change it to something completely wrong.

How hard can it be to understand that you cannot and must not load firmware when the system isn't up-and-running.

And *dammit*, the fact that you send these kinds of completely nonsensical patches that seem to think that "up-and-running" is just about suspend (and not bootup, for example), all you are showing is that you don't understand the problem. Stop, think, and read the emails that have been in this thread and that have explained how it *could* be solved.

Until you do that, any patch you send is just worthless. Really.

Linus

Linus

#30 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 14 lines · Linus Torvalds

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 537

Quoted 3 lines · Stephen Boyd
drivers/base/firmware_class.c +1 -1
-       if (WARN_ON(usermodehelper_is_disabled())) {
+       if (is_sleep_task()) {
               dev_err(device, "firmware: %s will not be loaded\n", name);

Stop these idiotic games already!

It's very simple: you cannot load firmware while the system is not readt. Your moronic "let's change the test to something else" is entirely and utterly misguided and totally misses the point.

It's not about sleeping, and it's not about anything even *remotely* about that. Stop the idiocy already.

Quoted 8 lines

How hard is it to understand? How many times do people have to tell you?

That warning is very much valid during bootup, and that warning has been *seen* during bootup. For example, try to compile in most wireless drivers as non-modular, and that warning *has* to trigger.

Rafael, please consider everything along these *IDIOTIC* lines completely NAK'ed.

I will.

Thanks,
Rafael

#31 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Linus Torvalds
Quoted 10 lines · Linus Torvalds

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 537

Quoted 3 lines · Stephen Boyd
drivers/base/firmware_class.c +1 -1
-       if (WARN_ON(usermodehelper_is_disabled())) {
+       if (is_sleep_task()) {
               dev_err(device, "firmware: %s will not be loaded\n", name);

Stop these idiotic games already!

It's very simple: you cannot load firmware while the system is not readt. Your moronic "let's change the test to something else" is entirely and utterly misguided and totally misses the point.

Quoted 8 lines

It's not about sleeping, and it's not about anything even *remotely* about that. Stop the idiocy already.

How hard is it to understand? How many times do people have to tell you?

That warning is very much valid during bootup, and that warning has been *seen* during bootup. For example, try to compile in most wireless drivers as non-modular, and that warning *has* to trigger.

Ok. I like where nowait() is going in the other part of the thread but I'm still confused about when request_firmware() is correct to use. It seems that the function is inherently racy with freezing. Does every user of request_firmware() need to synchronize with freezing?

For example, if one CPU is in the middle of a driver probe that makes a request_firmware() call and another CPU is starting to suspend we will have a race between usermodehelpers being disabled and the request_firmware() call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and the request_firmware() call will return -EBUSY and warn.

CPU0 CPU1
driver_probe() suspend_prepare()
... usermodehelper_disable()
_request_firmware() down_write(&umhelper_sem)
usermodehelper_disabled = 1 up_write(&umhelper_sem)
down_read(&umhelper_sem) ....
WARN_ON(...) freeze_processes()
<freeze>

Hopefully I'm missing something here? This is all theoretical right now as I haven't actually seen any of this in practice.

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#32 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 25 lines · Stephen Boyd
Quoted 10 lines · Linus Torvalds

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 537

Quoted 3 lines · Stephen Boyd
drivers/base/firmware_class.c +1 -1
-       if (WARN_ON(usermodehelper_is_disabled())) {
+       if (is_sleep_task()) {
               dev_err(device, "firmware: %s will not be loaded\n", name);

Stop these idiotic games already!

It's very simple: you cannot load firmware while the system is not readt. Your moronic "let's change the test to something else" is entirely and utterly misguided and totally misses the point.

Quoted 8 lines

It's not about sleeping, and it's not about anything even *remotely* about that. Stop the idiocy already.

How hard is it to understand? How many times do people have to tell you?

That warning is very much valid during bootup, and that warning has been *seen* during bootup. For example, try to compile in most wireless drivers as non-modular, and that warning *has* to trigger.

Ok. I like where nowait() is going in the other part of the thread but I'm still confused about when request_firmware() is correct to use. It seems that the function is inherently racy with freezing. Does every user of request_firmware() need to synchronize with freezing?

Quoted 6 lines

For example, if one CPU is in the middle of a driver probe that makes a request_firmware() call and another CPU is starting to suspend we will have a race between usermodehelpers being disabled and the request_firmware() call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and the request_firmware() call will return -EBUSY and warn.

Yes, it will.

Thanks,
Rafael

#33 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 7 lines · Rafael J. Wysocki
Quoted 5 lines · Stephen Boyd

Ok. I like where nowait() is going in the other part of the thread but I'm still confused about when request_firmware() is correct to use. It seems that the function is inherently racy with freezing. Does every user of request_firmware() need to synchronize with freezing?

Quoted 7 lines
Quoted 6 lines

For example, if one CPU is in the middle of a driver probe that makes a request_firmware() call and another CPU is starting to suspend we will have a race between usermodehelpers being disabled and the request_firmware() call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and the request_firmware() call will return -EBUSY and warn.

Yes, it will.

That sounds wrong then, no? Why don't we have request_firmware() do a read_lock on the usermodehelpers sem and then have suspend do a write lock, disable usermodehelpers, wait for any users to finish, freeze processes and then unlock the write lock? That way we don't hit a case where request_firmware() races with suspend, and we don't have to change the warning or conditional.

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#34 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 14 lines · Stephen Boyd
Quoted 13 lines · Rafael J. Wysocki
Quoted 11 lines · Stephen Boyd

Ok. I like where nowait() is going in the other part of the thread but I'm still confused about when request_firmware() is correct to use. It seems that the function is inherently racy with freezing. Does every user of request_firmware() need to synchronize with freezing?

For example, if one CPU is in the middle of a driver probe that makes a request_firmware() call and another CPU is starting to suspend we will have a race between usermodehelpers being disabled and the request_firmware() call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and the request_firmware() call will return -EBUSY and warn.

Quoted 8 lines
Quoted 1 line

Yes, it will.

That sounds wrong then, no? Why don't we have request_firmware() do a read_lock on the usermodehelpers sem and then have suspend do a write lock, disable usermodehelpers, wait for any users to finish, freeze processes and then unlock the write lock? That way we don't hit a case where request_firmware() races with suspend, and we don't have to change the warning or conditional.

So, you're postulating that the freezing of tasks be done under umhelper_sem write-locked, right?

That would lead to freezing failures if a user space task waited in request_firmware() for umhelper_sem to become available for read-locking and unfortunately we don't have an interruptible variant of down_read().

However, we may catch request_firmware() and try to freeze the task calling it instead. I'll try to prototype something along these lines later today (on top of the three "firmware_class" patches I posted yesterday).

Thanks,
Rafael

#35 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 27 lines · Rafael J. Wysocki
Quoted 14 lines · Stephen Boyd
Quoted 13 lines · Rafael J. Wysocki
Quoted 11 lines · Stephen Boyd

Ok. I like where nowait() is going in the other part of the thread but I'm still confused about when request_firmware() is correct to use. It seems that the function is inherently racy with freezing. Does every user of request_firmware() need to synchronize with freezing?

For example, if one CPU is in the middle of a driver probe that makes a request_firmware() call and another CPU is starting to suspend we will have a race between usermodehelpers being disabled and the request_firmware() call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and the request_firmware() call will return -EBUSY and warn.

Quoted 8 lines
Quoted 1 line

Yes, it will.

That sounds wrong then, no? Why don't we have request_firmware() do a read_lock on the usermodehelpers sem and then have suspend do a write lock, disable usermodehelpers, wait for any users to finish, freeze processes and then unlock the write lock? That way we don't hit a case where request_firmware() races with suspend, and we don't have to change the warning or conditional.

So, you're postulating that the freezing of tasks be done under umhelper_sem write-locked, right?

Quoted 7 lines

That would lead to freezing failures if a user space task waited in request_firmware() for umhelper_sem to become available for read-locking and unfortunately we don't have an interruptible variant of down_read().

However, we may catch request_firmware() and try to freeze the task calling it instead. I'll try to prototype something along these lines later today (on top of the three "firmware_class" patches I posted yesterday).

Well, that's going to take some more time, as it's more complicated than I thought initially. Please, stay tuned.

Thanks,
Rafael

#36 lore raw

cover firmware_class: Fix problems with usermodehelper test

Hi all,

The following series of patches fixes two problems with request_firmware() and request_firmware_nowait() resulting from commit a144c6a6c924aa1da04dd77fb84b89927354fdff

PM: Print a warning if firmware is requested when tasks are frozen

The first problem is that request_firmware_nowait() may fail if it happens to run in parallel with system suspend. It should't fail in such situations and that is addressed by the first three patches (that code has been discussed with Linus at al already).

The second issue is that request_firmware() may be called in a thread which isn't related to system suspend and if suspend happens exactly at that time, request_firmware() will fail (and print a scary warning), although it shouldn't. This problem is addressed by the remaining three patches, which are new.

[1/6] - Rework the usermodehelper check in _request_firmware().
[2/6] - Split _request_firmware() into three functions.
[3/6] - If firmware is to be loaded asynchronously, wait for usermodehelper_disabled
to be unset instead of failing the operation.
[4/6] - Unify the hibernation/suspend code's usage of usermodehelper_disable(). [5/6] - Move usermodehelper_disable() into freeze_processes().
[6/6] - Make freezable threads calling request_firware() avoid the race with the
freezer.

Thanks,
Rafael

#37 lore raw

PATCH 1/6 firmware_class: Rework usermodehelper check

From: Rafael J. Wysocki <[email protected]>

Instead of two functions, read_lock_usermodehelper() and usermodehelper_is_disabled(), used in combination, introduce usermodehelper_read_trylock() that will only return with umhelper_sem held if usermodehelper_disabled is unset (and will return -EAGAIN otherwise) and make _request_firmware() use it.

Rename read_unlock_usermodehelper() to usermodehelper_read_unlock() to follow the naming convention of the new function.

---
 drivers/base/firmware_class.c |   11 +++++------
 include/linux/kmod.h          |    5 ++---
 kernel/kmod.c                 |   24 +++++++++++-------------
 3 files changed, 18 insertions(+), 22 deletions(-)
include/linux/kmod.h +2 -3
Index: linux/include/linux/kmod.h
===================================================================
@@ -116,8 +116,7 @@ extern void usermodehelper_init(void);
 
 extern int usermodehelper_disable(void);
 extern void usermodehelper_enable(void);
-extern bool usermodehelper_is_disabled(void);
-extern void read_lock_usermodehelper(void);
-extern void read_unlock_usermodehelper(void);
+extern int usermodehelper_read_trylock(void);
+extern void usermodehelper_read_unlock(void);
 
 #endif /* __LINUX_KMOD_H__ */
kernel/kmod.c +11 -13
Index: linux/kernel/kmod.c
===================================================================
@@ -296,17 +296,24 @@ static DECLARE_WAIT_QUEUE_HEAD(running_h
  */
 #define RUNNING_HELPERS_TIMEOUT	(5 * HZ)
 
-void read_lock_usermodehelper(void)
+int usermodehelper_read_trylock(void)
 {
+	int ret = 0;
+
 	down_read(&umhelper_sem);
+	if (usermodehelper_disabled) {
+		up_read(&umhelper_sem);
+		ret = -EAGAIN;
+	}
+	return ret;
 }
-EXPORT_SYMBOL_GPL(read_lock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_trylock);
 
-void read_unlock_usermodehelper(void)
+void usermodehelper_read_unlock(void)
 {
 	up_read(&umhelper_sem);
 }
-EXPORT_SYMBOL_GPL(read_unlock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_unlock);
 
 /**
  * usermodehelper_disable - prevent new helpers from being started
@@ -347,15 +354,6 @@ void usermodehelper_enable(void)
 	up_write(&umhelper_sem);
 }
 
-/**
- * usermodehelper_is_disabled - check if new helpers are allowed to be started
- */
-bool usermodehelper_is_disabled(void)
-{
-	return usermodehelper_disabled;
-}
-EXPORT_SYMBOL_GPL(usermodehelper_is_disabled);
-
 static void helper_lock(void)
 {
 	atomic_inc(&running_helpers);
drivers/base/firmware_class.c +5 -6
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -533,12 +533,10 @@ static int _request_firmware(const struc
 		return 0;
 	}
 
-	read_lock_usermodehelper();
-
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	retval = usermodehelper_read_trylock();
+	if (WARN_ON(retval)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
-		retval = -EBUSY;
-		goto out;
+		goto out_nolock;
 	}
 
 	if (uevent)
@@ -573,8 +571,9 @@ static int _request_firmware(const struc
 	fw_destroy_instance(fw_priv);
 
 out:
-	read_unlock_usermodehelper();
+	usermodehelper_read_unlock();
 
+out_nolock:
 	if (retval) {
 		release_firmware(firmware);
 		*firmware_p = NULL;
#38 lore raw

PATCH 2/6 firmware_class: Split _request_firmware() into three functions

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

Split _request_firmware() into three functions, _request_firmware_prepare() doing preparatory work that need not be done under umhelper_sem, _request_firmware_cleanup() doing the post-error cleanup and _request_firmware() carrying out the remaining operations.

This change is requisite for moving the acquisition of umhelper_sem from _request_firmware() to the callers, which is going to be done subsequently.

---
 drivers/base/firmware_class.c |   57 +++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 17 deletions(-)
drivers/base/firmware_class.c +40 -17
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firm
 		goto err_out;
 	}
 
-	fw_priv->fw = firmware;
+	fw_priv->fw = (struct firmware *)firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);
@@ -510,13 +510,10 @@ static void fw_destroy_instance(struct f
 	device_unregister(f_dev);
 }
 
-static int _request_firmware(const struct firmware **firmware_p,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait)
+static int _request_firmware_prepare(const struct firmware **firmware_p,
+				     const char *name, struct device *device)
 {
-	struct firmware_priv *fw_priv;
 	struct firmware *firmware;
-	int retval = 0;
 
 	if (!firmware_p)
 		return -EINVAL;
@@ -533,10 +530,26 @@ static int _request_firmware(const struc
 		return 0;
 	}
 
+	return 1;
+}
+
+static void _request_firmware_cleanup(const struct firmware **firmware_p)
+{
+	release_firmware(*firmware_p);
+	*firmware_p = NULL;
+}
+
+static int _request_firmware(const struct firmware *firmware,
+			     const char *name, struct device *device,
+			     bool uevent, bool nowait)
+{
+	struct firmware_priv *fw_priv;
+	int retval;
+
 	retval = usermodehelper_read_trylock();
 	if (WARN_ON(retval)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
-		goto out_nolock;
+		return retval;
 	}
 
 	if (uevent)
@@ -572,13 +585,6 @@ static int _request_firmware(const struc
 
 out:
 	usermodehelper_read_unlock();
-
-out_nolock:
-	if (retval) {
-		release_firmware(firmware);
-		*firmware_p = NULL;
-	}
-
 	return retval;
 }
 
@@ -601,7 +607,17 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
-        return _request_firmware(firmware_p, name, device, true, false);
+	int ret;
+
+	ret = _request_firmware_prepare(firmware_p, name, device);
+	if (ret <= 0)
+		return ret;
+
+	ret = _request_firmware(*firmware_p, name, device, true, false);
+	if (ret)
+		_request_firmware_cleanup(firmware_p);
+
+	return ret;
 }
 
 /**
@@ -639,8 +655,15 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+	if (ret <= 0)
+		return ret;
+
+	ret = _request_firmware(fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+	if (ret)
+		_request_firmware_cleanup(&fw);
+
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
#39 lore raw

Re: [PATCH 2/6] firmware_class: Split _request_firmware() into three functions

Quoted 17 lines · Rafael J. Wysocki
 drivers/base/firmware_class.c |   57 +++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 17 deletions(-)

Index: linux/drivers/base/firmware_class.c
===================================================================

drivers/base/firmware_class.c +1 -1
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;

drivers/base/firmware_class.c fw_create_instance(struct firmware *firm · line 455

Quoted 8 lines
drivers/base/firmware_class.c +1 -1
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firm
 		goto err_out;
 	}
 
-	fw_priv->fw = firmware;
+	fw_priv->fw = (struct firmware *)firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);

Can we avoid this cast? If we do some parts of fw_create_instance() during the setup phase I think we can avoid it.

drivers/base/firmware_class.c static int request_firmware_work_func(vo · line 660

Quoted 8 lines
drivers/base/firmware_class.c +3 -1
@@ -639,8 +655,15 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+	if (ret <= 0)
+		return ret;

This needs to jump to the cont function so that users know loading failed or that the firmware was builtin.

drivers/base/firmware_class.c static int request_firmware_work_func(vo · line 669

Quoted 10 lines
drivers/base/firmware_class.c +5 -0
+
+	ret = _request_firmware(fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+	if (ret)
+		_request_firmware_cleanup(&fw);
+
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);

Here's a compile only tested patch on top. The idea is return a fw_priv pointer from the setup function and use that to indicate failure (error pointer), builtin (NULL) or loadable (non-NULL). Then we can move all the uevent/device stuff to the loading phase

----8<-----

drivers/base/firmware_class.c +63 -64
index 5db254d..fbe98a8 100644
@@ -440,13 +440,11 @@ fw_create_instance(const struct firmware *firmware, const char *fw_name,
 {
 	struct firmware_priv *fw_priv;
 	struct device *f_dev;
-	int error;
 
 	fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
 	if (!fw_priv) {
 		dev_err(device, "%s: kmalloc failed\n", __func__);
-		error = -ENOMEM;
-		goto err_out;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	fw_priv->fw = (struct firmware *)firmware;
@@ -463,42 +461,7 @@ fw_create_instance(const struct firmware *firmware, const char *fw_name,
 	f_dev->parent = device;
 	f_dev->class = &firmware_class;
 
-	dev_set_uevent_suppress(f_dev, true);
-
-	/* Need to pin this module until class device is destroyed */
-	__module_get(THIS_MODULE);
-
-	error = device_add(f_dev);
-	if (error) {
-		dev_err(device, "%s: device_register failed\n", __func__);
-		goto err_put_dev;
-	}
-
-	error = device_create_bin_file(f_dev, &firmware_attr_data);
-	if (error) {
-		dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
-		goto err_del_dev;
-	}
-
-	error = device_create_file(f_dev, &dev_attr_loading);
-	if (error) {
-		dev_err(device, "%s: device_create_file failed\n", __func__);
-		goto err_del_bin_attr;
-	}
-
-	if (uevent)
-		dev_set_uevent_suppress(f_dev, false);
-
 	return fw_priv;
-
-err_del_bin_attr:
-	device_remove_bin_file(f_dev, &firmware_attr_data);
-err_del_dev:
-	device_del(f_dev);
-err_put_dev:
-	put_device(f_dev);
-err_out:
-	return ERR_PTR(error);
 }
 
 static void fw_destroy_instance(struct firmware_priv *fw_priv)
@@ -510,27 +473,34 @@ static void fw_destroy_instance(struct firmware_priv *fw_priv)
 	device_unregister(f_dev);
 }
 
-static int _request_firmware_prepare(const struct firmware **firmware_p,
-				     const char *name, struct device *device)
+static struct firmware_priv *
+_request_firmware_prepare(const struct firmware **firmware_p, const char *name,
+			  struct device *device, bool uevent, bool nowait)
 {
 	struct firmware *firmware;
+	struct firmware_priv *fw_priv;
 
 	if (!firmware_p)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
 	if (!firmware) {
 		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
 			__func__);
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	if (fw_get_builtin_firmware(firmware, name)) {
 		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
-		return 0;
+		return NULL;
 	}
 
-	return 1;
+	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
+	if (IS_ERR(fw_priv)) {
+		release_firmware(firmware);
+		*firmware_p = NULL;
+	}
+	return fw_priv;
 }
 
 static void _request_firmware_cleanup(const struct firmware **firmware_p)
@@ -539,29 +509,44 @@ static void _request_firmware_cleanup(const struct firmware **firmware_p)
 	*firmware_p = NULL;
 }
 
-static int _request_firmware(const struct firmware *firmware,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait)
+static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent)
 {
-	struct firmware_priv *fw_priv;
 	int retval;
+	struct device *f_dev = &fw_priv->dev;
 
 	retval = usermodehelper_read_trylock();
 	if (WARN_ON(retval)) {
-		dev_err(device, "firmware: %s will not be loaded\n", name);
+		dev_err(f_dev, "firmware: %s will not be loaded\n",
+				fw_priv->fw_id);
 		return retval;
 	}
 
-	if (uevent)
-		dev_dbg(device, "firmware: requesting %s\n", name);
+	dev_set_uevent_suppress(f_dev, true);
 
-	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
-	if (IS_ERR(fw_priv)) {
-		retval = PTR_ERR(fw_priv);
-		goto out;
+	/* Need to pin this module until class device is destroyed */
+	__module_get(THIS_MODULE);
+
+	retval = device_add(f_dev);
+	if (retval) {
+		dev_err(f_dev, "%s: device_register failed\n", __func__);
+		goto err_put_dev;
+	}
+
+	retval = device_create_bin_file(f_dev, &firmware_attr_data);
+	if (retval) {
+		dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__);
+		goto err_del_dev;
+	}
+
+	retval = device_create_file(f_dev, &dev_attr_loading);
+	if (retval) {
+		dev_err(f_dev, "%s: device_create_file failed\n", __func__);
+		goto err_del_bin_attr;
 	}
 
 	if (uevent) {
+		dev_set_uevent_suppress(f_dev, false);
+		dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
 		if (loading_timeout > 0)
 			mod_timer(&fw_priv->timeout,
 				  round_jiffies_up(jiffies +
@@ -586,6 +571,14 @@ static int _request_firmware(const struct firmware *firmware,
 out:
 	usermodehelper_read_unlock();
 	return retval;
+
+err_del_bin_attr:
+	device_remove_bin_file(f_dev, &firmware_attr_data);
+err_del_dev:
+	device_del(f_dev);
+err_put_dev:
+	put_device(f_dev);
+	goto out;
 }
 
 /**
@@ -607,13 +600,15 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
+	struct firmware_priv *fw_priv;
 	int ret;
 
-	ret = _request_firmware_prepare(firmware_p, name, device);
-	if (ret <= 0)
-		return ret;
+	fw_priv = _request_firmware_prepare(firmware_p, name, device, true,
+					    false);
+	if (IS_ERR_OR_NULL(fw_priv))
+		return PTR_RET(fw_priv);
 
-	ret = _request_firmware(*firmware_p, name, device, true, false);
+	ret = _request_firmware_load(fw_priv, true);
 	if (ret)
 		_request_firmware_cleanup(firmware_p);
 
@@ -648,6 +643,7 @@ static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
+	struct firmware_priv *fw_priv;
 	int ret;
 
 	if (!arg) {
@@ -655,15 +651,18 @@ static int request_firmware_work_func(void *arg)
 		return 0;
 	}
 
-	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
-	if (ret <= 0)
-		return ret;
+	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
+			fw_work->uevent, true);
+	if (IS_ERR_OR_NULL(fw_priv)) {
+		ret = PTR_RET(fw_priv);
+		goto out;
+	}
 
-	ret = _request_firmware(fw, fw_work->name, fw_work->device,
-				fw_work->uevent, true);
+	ret = _request_firmware_load(fw_priv, fw_work->uevent);
 	if (ret)
 		_request_firmware_cleanup(&fw);
 
+out:
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#40 lore raw

Re: [PATCH 2/6] firmware_class: Split _request_firmware() into three functions

Quoted 21 lines · Stephen Boyd
Quoted 20 lines · Rafael J. Wysocki
 drivers/base/firmware_class.c |   57 +++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 17 deletions(-)

Index: linux/drivers/base/firmware_class.c
===================================================================

drivers/base/firmware_class.c +1 -1
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firm
 		goto err_out;
 	}
 
Quoted 8 lines

drivers/base/firmware_class.c fw_create_instance(struct firmware *firm · line 455

Quoted 5 lines
drivers/base/firmware_class.c +1 -1
-	fw_priv->fw = firmware;
+	fw_priv->fw = (struct firmware *)firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);

Can we avoid this cast? If we do some parts of fw_create_instance() during the setup phase I think we can avoid it.

Oops. With the other patch you can squash this in to do what I originally talked about.

-----8<-----

drivers/base/firmware_class.c +2 -2
index fbe98a8..113d37d 100644
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_long data)
 }
 
 static struct firmware_priv *
-fw_create_instance(const struct firmware *firmware, const char *fw_name,
+fw_create_instance(struct firmware *firmware, const char *fw_name,
                   struct device *device, bool uevent, bool nowait)
 {
        struct firmware_priv *fw_priv;
@@ -447,7 +447,7 @@ fw_create_instance(const struct firmware *firmware, const char *fw_name,
                return ERR_PTR(-ENOMEM);
        }
 
-       fw_priv->fw = (struct firmware *)firmware;
+       fw_priv->fw = firmware;
        fw_priv->nowait = nowait;
        strcpy(fw_priv->fw_id, fw_name);
        init_completion(&fw_priv->completion);
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#41 lore raw

Re: [PATCH 2/6] firmware_class: Split _request_firmware() into three functions

Quoted 49 lines · Stephen Boyd
Quoted 21 lines · Stephen Boyd
Quoted 20 lines · Rafael J. Wysocki
 drivers/base/firmware_class.c |   57 +++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 17 deletions(-)

Index: linux/drivers/base/firmware_class.c
===================================================================

drivers/base/firmware_class.c +1 -1
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firm
 		goto err_out;
 	}
 
Quoted 8 lines

drivers/base/firmware_class.c fw_create_instance(const struct firmware *firmware, const char *fw_name, · line 453

Quoted 5 lines
drivers/base/firmware_class.c +1 -1
-	fw_priv->fw = firmware;
+	fw_priv->fw = (struct firmware *)firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);

Can we avoid this cast? If we do some parts of fw_create_instance() during the setup phase I think we can avoid it.

Oops. With the other patch you can squash this in to do what I originally talked about.

-----8<-----

drivers/base/firmware_class.c +1 -1
index fbe98a8..113d37d 100644
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_long data)
 }
 
 static struct firmware_priv *
-fw_create_instance(const struct firmware *firmware, const char *fw_name,
+fw_create_instance(struct firmware *firmware, const char *fw_name,
                   struct device *device, bool uevent, bool nowait)
 {
        struct firmware_priv *fw_priv;

drivers/base/firmware_class.c fw_create_instance(const struct firmware *firmware, const char *fw_name, · line 453

Quoted 8 lines
drivers/base/firmware_class.c +1 -1
@@ -447,7 +447,7 @@ fw_create_instance(const struct firmware *firmware, const char *fw_name,
                return ERR_PTR(-ENOMEM);
        }
 
-       fw_priv->fw = (struct firmware *)firmware;
+       fw_priv->fw = firmware;
        fw_priv->nowait = nowait;
        strcpy(fw_priv->fw_id, fw_name);
        init_completion(&fw_priv->completion);

Hmm, what other patch? The one you send previously?

Rafael

#42 lore raw

Re: [PATCH 2/6] firmware_class: Split _request_firmware() into three functions

Quoted 1 line · Rafael J. Wysocki

Hmm, what other patch? The one you send previously?

Yes.

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#43 lore raw

Re: [PATCH 2/6] firmware_class: Split _request_firmware() into three functions

In reply to Stephen Boyd
Quoted 21 lines · Stephen Boyd
Quoted 20 lines · Rafael J. Wysocki
 drivers/base/firmware_class.c |   57 +++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 17 deletions(-)

Index: linux/drivers/base/firmware_class.c
===================================================================

drivers/base/firmware_class.c +1 -1
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firm
 		goto err_out;
 	}
 
Quoted 8 lines

drivers/base/firmware_class.c fw_create_instance(struct firmware *firm · line 455

Quoted 5 lines
drivers/base/firmware_class.c +1 -1
-	fw_priv->fw = firmware;
+	fw_priv->fw = (struct firmware *)firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);

Can we avoid this cast? If we do some parts of fw_create_instance() during the setup phase I think we can avoid it.

Yes, we can, but I'm not sure I'd like to make so many changes at once.

Quoted 11 lines

drivers/base/firmware_class.c static int request_firmware_work_func(vo · line 660

Quoted 8 lines
drivers/base/firmware_class.c +3 -1
@@ -639,8 +655,15 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+	if (ret <= 0)
+		return ret;

This needs to jump to the cont function so that users know loading failed or that the firmware was builtin.

You're right, sorry. That should have been

	if (ret > 0) {
		ret = _request_firmware(fw, fw_work->name, fw_work->device,
			fw_work->uevent, true);
		if (ret)
			_request_firmware_cleanup(&fw);
	}

but actually using a jump makes the next patch look better.

Updated patch is appended.

Quoted 8 lines
Quoted 8 lines
drivers/base/firmware_class.c +5 -0
+
+	ret = _request_firmware(fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+	if (ret)
+		_request_firmware_cleanup(&fw);
+
 	fw_work->cont(fw, fw_work->context);
 
Quoted 7 lines

drivers/base/firmware_class.c static int request_firmware_work_func(vo · line 669

Quoted 2 lines
drivers/base/firmware_class.c +0 -0
 	module_put(fw_work->module);

Here's a compile only tested patch on top. The idea is return a fw_priv pointer from the setup function and use that to indicate failure (error pointer), builtin (NULL) or loadable (non-NULL). Then we can move all the uevent/device stuff to the loading phase

Good. Can you please send that as a patch on top of the whole series? I'd like to avoid mixing your code with mine. Also, I'd rather avoid invalidating tests that have been carried out already with this series by adding changes in the middle of it.

Quoted 217 lines

----8<-----

drivers/base/firmware_class.c +61 -62
index 5db254d..fbe98a8 100644
@@ -440,13 +440,11 @@ fw_create_instance(const struct firmware *firmware, const char *fw_name,
 {
 	struct firmware_priv *fw_priv;
 	struct device *f_dev;
-	int error;
 
 	fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
 	if (!fw_priv) {
 		dev_err(device, "%s: kmalloc failed\n", __func__);
-		error = -ENOMEM;
-		goto err_out;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	fw_priv->fw = (struct firmware *)firmware;
@@ -463,42 +461,7 @@ fw_create_instance(const struct firmware *firmware, const char *fw_name,
 	f_dev->parent = device;
 	f_dev->class = &firmware_class;
 
-	dev_set_uevent_suppress(f_dev, true);
-
-	/* Need to pin this module until class device is destroyed */
-	__module_get(THIS_MODULE);
-
-	error = device_add(f_dev);
-	if (error) {
-		dev_err(device, "%s: device_register failed\n", __func__);
-		goto err_put_dev;
-	}
-
-	error = device_create_bin_file(f_dev, &firmware_attr_data);
-	if (error) {
-		dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
-		goto err_del_dev;
-	}
-
-	error = device_create_file(f_dev, &dev_attr_loading);
-	if (error) {
-		dev_err(device, "%s: device_create_file failed\n", __func__);
-		goto err_del_bin_attr;
-	}
-
-	if (uevent)
-		dev_set_uevent_suppress(f_dev, false);
-
 	return fw_priv;
-
-err_del_bin_attr:
-	device_remove_bin_file(f_dev, &firmware_attr_data);
-err_del_dev:
-	device_del(f_dev);
-err_put_dev:
-	put_device(f_dev);
-err_out:
-	return ERR_PTR(error);
 }
 
 static void fw_destroy_instance(struct firmware_priv *fw_priv)
@@ -510,27 +473,34 @@ static void fw_destroy_instance(struct firmware_priv *fw_priv)
 	device_unregister(f_dev);
 }
 
-static int _request_firmware_prepare(const struct firmware **firmware_p,
-				     const char *name, struct device *device)
+static struct firmware_priv *
+_request_firmware_prepare(const struct firmware **firmware_p, const char *name,
+			  struct device *device, bool uevent, bool nowait)
 {
 	struct firmware *firmware;
+	struct firmware_priv *fw_priv;
 
 	if (!firmware_p)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
 	if (!firmware) {
 		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
 			__func__);
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	if (fw_get_builtin_firmware(firmware, name)) {
 		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
-		return 0;
+		return NULL;
 	}
 
-	return 1;
+	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
+	if (IS_ERR(fw_priv)) {
+		release_firmware(firmware);
+		*firmware_p = NULL;
+	}
+	return fw_priv;
 }
 
 static void _request_firmware_cleanup(const struct firmware **firmware_p)
@@ -539,29 +509,44 @@ static void _request_firmware_cleanup(const struct firmware **firmware_p)
 	*firmware_p = NULL;
 }
 
-static int _request_firmware(const struct firmware *firmware,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait)
+static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent)
 {
-	struct firmware_priv *fw_priv;
 	int retval;
+	struct device *f_dev = &fw_priv->dev;
 
 	retval = usermodehelper_read_trylock();
 	if (WARN_ON(retval)) {
-		dev_err(device, "firmware: %s will not be loaded\n", name);
+		dev_err(f_dev, "firmware: %s will not be loaded\n",
+				fw_priv->fw_id);
 		return retval;
 	}
 
-	if (uevent)
-		dev_dbg(device, "firmware: requesting %s\n", name);
+	dev_set_uevent_suppress(f_dev, true);
 
-	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
-	if (IS_ERR(fw_priv)) {
-		retval = PTR_ERR(fw_priv);
-		goto out;
+	/* Need to pin this module until class device is destroyed */
+	__module_get(THIS_MODULE);
+
+	retval = device_add(f_dev);
+	if (retval) {
+		dev_err(f_dev, "%s: device_register failed\n", __func__);
+		goto err_put_dev;
+	}
+
+	retval = device_create_bin_file(f_dev, &firmware_attr_data);
+	if (retval) {
+		dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__);
+		goto err_del_dev;
+	}
+
+	retval = device_create_file(f_dev, &dev_attr_loading);
+	if (retval) {
+		dev_err(f_dev, "%s: device_create_file failed\n", __func__);
+		goto err_del_bin_attr;
 	}
 
 	if (uevent) {
+		dev_set_uevent_suppress(f_dev, false);
+		dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
 		if (loading_timeout > 0)
 			mod_timer(&fw_priv->timeout,
 				  round_jiffies_up(jiffies +
@@ -586,6 +571,14 @@ static int _request_firmware(const struct firmware *firmware,
 out:
 	usermodehelper_read_unlock();
 	return retval;
+
+err_del_bin_attr:
+	device_remove_bin_file(f_dev, &firmware_attr_data);
+err_del_dev:
+	device_del(f_dev);
+err_put_dev:
+	put_device(f_dev);
+	goto out;
 }
 
 /**
@@ -607,13 +600,15 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
+	struct firmware_priv *fw_priv;
 	int ret;
 
-	ret = _request_firmware_prepare(firmware_p, name, device);
-	if (ret <= 0)
-		return ret;
+	fw_priv = _request_firmware_prepare(firmware_p, name, device, true,
+					    false);
+	if (IS_ERR_OR_NULL(fw_priv))
+		return PTR_RET(fw_priv);
 
-	ret = _request_firmware(*firmware_p, name, device, true, false);
+	ret = _request_firmware_load(fw_priv, true);
 	if (ret)
 		_request_firmware_cleanup(firmware_p);
 
@@ -648,6 +643,7 @@ static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
+	struct firmware_priv *fw_priv;
 	int ret;
 
 	if (!arg) {
@@ -655,15 +651,18 @@ static int request_firmware_work_func(void *arg)
 		return 0;
 	}
 
-	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
-	if (ret <= 0)
-		return ret;
+	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
+			fw_work->uevent, true);
+	if (IS_ERR_OR_NULL(fw_priv)) {
+		ret = PTR_RET(fw_priv);
+		goto out;
+	}
 

drivers/base/firmware_class.c static int request_firmware_work_func(void *arg) · line 668

Quoted 10 lines
drivers/base/firmware_class.c +2 -2
-	ret = _request_firmware(fw, fw_work->name, fw_work->device,
-				fw_work->uevent, true);
+	ret = _request_firmware_load(fw_priv, fw_work->uevent);
 	if (ret)
 		_request_firmware_cleanup(&fw);
 
+out:
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);

---
From: Rafael J. Wysocki <[email protected]>
Subject: firmware_class: Split _request_firmware() into three functions, v2

Split _request_firmware() into three functions, _request_firmware_prepare() doing preparatory work that need not be done under umhelper_sem, _request_firmware_cleanup() doing the post-error cleanup and _request_firmware() carrying out the remaining operations.

This change is requisite for moving the acquisition of umhelper_sem from _request_firmware() to the callers, which is going to be done subsequently.

---
 drivers/base/firmware_class.c |   58 +++++++++++++++++++++++++++++-------------
 1 file changed, 41 insertions(+), 17 deletions(-)
drivers/base/firmware_class.c +41 -17
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firm
 		goto err_out;
 	}
 
-	fw_priv->fw = firmware;
+	fw_priv->fw = (struct firmware *)firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);
@@ -510,13 +510,10 @@ static void fw_destroy_instance(struct f
 	device_unregister(f_dev);
 }
 
-static int _request_firmware(const struct firmware **firmware_p,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait)
+static int _request_firmware_prepare(const struct firmware **firmware_p,
+				     const char *name, struct device *device)
 {
-	struct firmware_priv *fw_priv;
 	struct firmware *firmware;
-	int retval = 0;
 
 	if (!firmware_p)
 		return -EINVAL;
@@ -533,10 +530,26 @@ static int _request_firmware(const struc
 		return 0;
 	}
 
+	return 1;
+}
+
+static void _request_firmware_cleanup(const struct firmware **firmware_p)
+{
+	release_firmware(*firmware_p);
+	*firmware_p = NULL;
+}
+
+static int _request_firmware(const struct firmware *firmware,
+			     const char *name, struct device *device,
+			     bool uevent, bool nowait)
+{
+	struct firmware_priv *fw_priv;
+	int retval;
+
 	retval = usermodehelper_read_trylock();
 	if (WARN_ON(retval)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
-		goto out_nolock;
+		return retval;
 	}
 
 	if (uevent)
@@ -572,13 +585,6 @@ static int _request_firmware(const struc
 
 out:
 	usermodehelper_read_unlock();
-
-out_nolock:
-	if (retval) {
-		release_firmware(firmware);
-		*firmware_p = NULL;
-	}
-
 	return retval;
 }
 
@@ -601,7 +607,17 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
-        return _request_firmware(firmware_p, name, device, true, false);
+	int ret;
+
+	ret = _request_firmware_prepare(firmware_p, name, device);
+	if (ret <= 0)
+		return ret;
+
+	ret = _request_firmware(*firmware_p, name, device, true, false);
+	if (ret)
+		_request_firmware_cleanup(firmware_p);
+
+	return ret;
 }
 
 /**
@@ -639,8 +655,16 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+	if (ret <= 0)
+		goto out;
+
+	ret = _request_firmware(fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+	if (ret)
+		_request_firmware_cleanup(&fw);
+
+ out:
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
Reviewed-by
#44 lore raw

Re: [PATCH 2/6] firmware_class: Split _request_firmware() into three functions

Quoted 34 lines · Rafael J. Wysocki
Quoted 11 lines · Stephen Boyd

drivers/base/firmware_class.c static int request_firmware_work_func(vo · line 660

Quoted 8 lines · Rafael J. Wysocki
drivers/base/firmware_class.c +3 -1
@@ -639,8 +655,15 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+	if (ret <= 0)
+		return ret;

This needs to jump to the cont function so that users know loading failed or that the firmware was builtin.

You're right, sorry. That should have been

	if (ret > 0) {
		ret = _request_firmware(fw, fw_work->name, fw_work->device,
			fw_work->uevent, true);
		if (ret)
			_request_firmware_cleanup(&fw);
	}

but actually using a jump makes the next patch look better.

Updated patch is appended.
---
From: Rafael J. Wysocki <[email protected]>
Subject: firmware_class: Split _request_firmware() into three functions, v2

Split _request_firmware() into three functions, _request_firmware_prepare() doing preparatory work that need not be done under umhelper_sem, _request_firmware_cleanup() doing the post-error cleanup and _request_firmware() carrying out the remaining operations.

Quoted 3 lines

This change is requisite for moving the acquisition of umhelper_sem from _request_firmware() to the callers, which is going to be done subsequently.

Quoted 2 lines

---

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#45 lore raw

Re: [PATCH 2/6] firmware_class: Split _request_firmware() into three functions

Quoted 35 lines · Stephen Boyd
Quoted 34 lines · Rafael J. Wysocki
Quoted 11 lines · Stephen Boyd

drivers/base/firmware_class.c static int request_firmware_work_func(vo · line 660

Quoted 8 lines · Rafael J. Wysocki
drivers/base/firmware_class.c +3 -1
@@ -639,8 +655,15 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+	if (ret <= 0)
+		return ret;

This needs to jump to the cont function so that users know loading failed or that the firmware was builtin.

You're right, sorry. That should have been

	if (ret > 0) {
		ret = _request_firmware(fw, fw_work->name, fw_work->device,
			fw_work->uevent, true);
		if (ret)
			_request_firmware_cleanup(&fw);
	}

but actually using a jump makes the next patch look better.

Updated patch is appended.
---
From: Rafael J. Wysocki <[email protected]>
Subject: firmware_class: Split _request_firmware() into three functions, v2

Split _request_firmware() into three functions, _request_firmware_prepare() doing preparatory work that need not be done under umhelper_sem, _request_firmware_cleanup() doing the post-error cleanup and _request_firmware() carrying out the remaining operations.

Quoted 5 lines
Quoted 3 lines

This change is requisite for moving the acquisition of umhelper_sem from _request_firmware() to the callers, which is going to be done subsequently.

Thanks!

Rafael

#46 lore raw

PATCH 3/6 firmware_class: Do not warn that system is not ready from async loads

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

If firmware is requested asynchronously, by calling request_firmware_nowait(), there is no reason to fail the request (and warn the user) when the system is (presumably temporarily) unready to handle it (because user space is not available yet or frozen). For this reason, introduce an alternative routine for read-locking umhelper_sem, usermodehelper_read_lock_wait(), that will wait for usermodehelper_disabled to be unset (possibly with a timeout) and make request_firmware_work_func() use it instead of usermodehelper_read_trylock().

Accordingly, modify request_firmware() so that it uses usermodehelper_read_trylock() to acquire umhelper_sem and remove the code related to that lock from _request_firmware().

---
 drivers/base/firmware_class.c |   51 +++++++++++++++++++++---------------
 include/linux/kmod.h          |    1 
 kernel/kmod.c                 |   58 ++++++++++++++++++++++++++++++++----------
 3 files changed, 76 insertions(+), 34 deletions(-)
include/linux/kmod.h +1 -0
Index: linux/include/linux/kmod.h
===================================================================
@@ -117,6 +117,7 @@ extern void usermodehelper_init(void);
 extern int usermodehelper_disable(void);
 extern void usermodehelper_enable(void);
 extern int usermodehelper_read_trylock(void);
+extern long usermodehelper_read_lock_wait(long timeout);
 extern void usermodehelper_read_unlock(void);
 
 #endif /* __LINUX_KMOD_H__ */
kernel/kmod.c +45 -13

85 lines not on this page · raw message

drivers/base/firmware_class.c +30 -21

97 lines not on this page · raw message

#47 lore raw

PATCH 4/6 PM / Hibernate: Disable usermode helpers right before freezing tasks

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

There is no reason to call usermodehelper_disable() before creating memory bitmaps in hibernate() and software_resume(), so call it right before freeze_processes(), in accordance with the other suspend and hibernation code. Consequently, call usermodehelper_enable() right after the thawing of tasks rather than after freeing the memory bitmaps.

---
 kernel/power/hibernate.c |   23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)
kernel/power/hibernate.c +10 -13
Index: linux/kernel/power/hibernate.c
===================================================================
@@ -611,19 +611,19 @@ int hibernate(void)
 	if (error)
 		goto Exit;
 
-	error = usermodehelper_disable();
-	if (error)
-		goto Exit;
-
 	/* Allocate memory management structures */
 	error = create_basic_memory_bitmaps();
 	if (error)
-		goto Enable_umh;
+		goto Exit;
 
 	printk(KERN_INFO "PM: Syncing filesystems ... ");
 	sys_sync();
 	printk("done.\n");
 
+	error = usermodehelper_disable();
+	if (error)
+		goto Exit;
+
 	error = freeze_processes();
 	if (error)
 		goto Free_bitmaps;
@@ -660,9 +660,8 @@ int hibernate(void)
 	freezer_test_done = false;
 
  Free_bitmaps:
-	free_basic_memory_bitmaps();
- Enable_umh:
 	usermodehelper_enable();
+	free_basic_memory_bitmaps();
  Exit:
 	pm_notifier_call_chain(PM_POST_HIBERNATION);
 	pm_restore_console();
@@ -777,15 +776,13 @@ static int software_resume(void)
 	if (error)
 		goto close_finish;
 
-	error = usermodehelper_disable();
+	error = create_basic_memory_bitmaps();
 	if (error)
 		goto close_finish;
 
-	error = create_basic_memory_bitmaps();
-	if (error) {
-		usermodehelper_enable();
+	error = usermodehelper_disable();
+	if (error)
 		goto close_finish;
-	}
 
 	pr_debug("PM: Preparing processes for restore.\n");
 	error = freeze_processes();
@@ -805,8 +802,8 @@ static int software_resume(void)
 	swsusp_free();
 	thaw_processes();
  Done:
-	free_basic_memory_bitmaps();
 	usermodehelper_enable();
+	free_basic_memory_bitmaps();
  Finish:
 	pm_notifier_call_chain(PM_POST_RESTORE);
 	pm_restore_console();
#48 lore raw

PATCH 5/6 PM / Sleep: Move disabling of usermode helpers to the freezer

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

The core suspend/hibernation code calls usermodehelper_disable() to avoid race conditions between the freezer and the starting of usermode helpers and each code path has to do that on its own. However, it is always called right before freeze_processes() and usermodehelper_enable() is always called right after thaw_processes(). For this reason, to avoid code duplication and to make the connection between usermodehelper_disable() and the freezer more visible, make freeze_processes() call it and remove the direct usermodehelper_disable() and usermodehelper_enable() calls from all suspend/hibernation code paths.

---
 kernel/power/hibernate.c |   11 -----------
 kernel/power/process.c   |    7 +++++++
 kernel/power/suspend.c   |    7 -------
 kernel/power/user.c      |   10 +---------
 4 files changed, 8 insertions(+), 27 deletions(-)
kernel/power/process.c +7 -0
Index: linux/kernel/power/process.c
===================================================================
@@ -16,6 +16,7 @@
 #include <linux/freezer.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
+#include <linux/kmod.h>
 
 /* 
  * Timeout for stopping processes
@@ -122,6 +123,10 @@ int freeze_processes(void)
 {
 	int error;
 
+	error = usermodehelper_disable();
+	if (error)
+		return error;
+
 	if (!pm_freezing)
 		atomic_inc(&system_freezing_cnt);
 
@@ -187,6 +192,8 @@ void thaw_processes(void)
 	} while_each_thread(g, p);
 	read_unlock(&tasklist_lock);
 
+	usermodehelper_enable();
+
 	schedule();
 	printk("done.\n");
 }
kernel/power/suspend.c +0 -7
Index: linux/kernel/power/suspend.c
===================================================================
@@ -12,7 +12,6 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/init.h>
-#include <linux/kmod.h>
 #include <linux/console.h>
 #include <linux/cpu.h>
 #include <linux/syscalls.h>
@@ -102,17 +101,12 @@ static int suspend_prepare(void)
 	if (error)
 		goto Finish;
 
-	error = usermodehelper_disable();
-	if (error)
-		goto Finish;
-
 	error = suspend_freeze_processes();
 	if (!error)
 		return 0;
 
 	suspend_stats.failed_freeze++;
 	dpm_save_failed_step(SUSPEND_FREEZE);
-	usermodehelper_enable();
  Finish:
 	pm_notifier_call_chain(PM_POST_SUSPEND);
 	pm_restore_console();
@@ -259,7 +253,6 @@ int suspend_devices_and_enter(suspend_st
 static void suspend_finish(void)
 {
 	suspend_thaw_processes();
-	usermodehelper_enable();
 	pm_notifier_call_chain(PM_POST_SUSPEND);
 	pm_restore_console();
 }
kernel/power/hibernate.c +0 -11
Index: linux/kernel/power/hibernate.c
===================================================================
@@ -16,7 +16,6 @@
 #include <linux/string.h>
 #include <linux/device.h>
 #include <linux/async.h>
-#include <linux/kmod.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
 #include <linux/mount.h>
@@ -620,10 +619,6 @@ int hibernate(void)
 	sys_sync();
 	printk("done.\n");
 
-	error = usermodehelper_disable();
-	if (error)
-		goto Exit;
-
 	error = freeze_processes();
 	if (error)
 		goto Free_bitmaps;
@@ -660,7 +655,6 @@ int hibernate(void)
 	freezer_test_done = false;
 
  Free_bitmaps:
-	usermodehelper_enable();
 	free_basic_memory_bitmaps();
  Exit:
 	pm_notifier_call_chain(PM_POST_HIBERNATION);
@@ -780,10 +774,6 @@ static int software_resume(void)
 	if (error)
 		goto close_finish;
 
-	error = usermodehelper_disable();
-	if (error)
-		goto close_finish;
-
 	pr_debug("PM: Preparing processes for restore.\n");
 	error = freeze_processes();
 	if (error) {
@@ -802,7 +792,6 @@ static int software_resume(void)
 	swsusp_free();
 	thaw_processes();
  Done:
-	usermodehelper_enable();
 	free_basic_memory_bitmaps();
  Finish:
 	pm_notifier_call_chain(PM_POST_RESTORE);
kernel/power/user.c +1 -9
Index: linux/kernel/power/user.c
===================================================================
@@ -12,7 +12,6 @@
 #include <linux/suspend.h>
 #include <linux/syscalls.h>
 #include <linux/reboot.h>
-#include <linux/kmod.h>
 #include <linux/string.h>
 #include <linux/device.h>
 #include <linux/miscdevice.h>
@@ -222,14 +221,8 @@ static long snapshot_ioctl(struct file *
 		sys_sync();
 		printk("done.\n");
 
-		error = usermodehelper_disable();
-		if (error)
-			break;
-
 		error = freeze_processes();
-		if (error)
-			usermodehelper_enable();
-		else
+		if (!error)
 			data->frozen = 1;
 		break;
 
@@ -238,7 +231,6 @@ static long snapshot_ioctl(struct file *
 			break;
 		pm_restore_gfp_mask();
 		thaw_processes();
-		usermodehelper_enable();
 		data->frozen = 0;
 		break;
 
#49 lore raw

PATCH 6/6 PM / Sleep: Mitigate race between the freezer and request_firmware()

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

There is a race condition between the freezer and request_firmware() such that if request_firmware() is run on one CPU and freeze_processes() is run on another CPU and usermodehelper_disable() called by it succeeds to grab umhelper_sem for writing before usermodehelper_read_trylock() called from request_firmware() acquires it for reading, the request_firmware() will fail and trigger a WARN_ON() complaining that it was called at a wrong time. However, in fact, it wasn't called at a wrong time and freeze_processes() simply happened to be executed simultaneously.

To avoid this race, at least in some cases, modify usermodehelper_read_trylock() so that it doesn't fail if the freezing of tasks has just started and hasn't been completed yet. Instead, during the freezing of tasks, it will try to freeze the task that has called it so that it can wait until user space is thawed without triggering the scary warning.

For this purpose, change usermodehelper_disabled so that it can take three different values, UMH_ENABLED (0), UMH_FREEZING and UMH_DISABLED. The first one means that usermode helpers are enabled, the last one means "hard disable" (i.e. the system is not ready for usermode helpers to be used) and the second one is reserved for the freezer. Namely, when freeze_processes() is started, it sets usermodehelper_disabled to UMH_FREEZING which tells usermodehelper_read_trylock() that it shouldn't fail just yet and should call try_to_freeze() if woken up and cannot return immediately. This way all freezable tasks that happen to call request_firmware() right before freeze_processes() is started and lose the race for umhelper_sem with it will be frozen and will sleep until thaw_processes() unsets usermodehelper_disabled. [For the non-freezable callers of request_firmware() the race for umhelper_sem against freeze_processes() is unfortunately unavoidable.]

---
 include/linux/kmod.h   |   21 +++++++++++++++++++--
 kernel/kmod.c          |   47 +++++++++++++++++++++++++++++++++++++----------
 kernel/power/process.c |    3 ++-
 3 files changed, 58 insertions(+), 13 deletions(-)
include/linux/kmod.h +19 -2
Index: linux/include/linux/kmod.h
===================================================================
@@ -112,10 +112,27 @@ call_usermodehelper(char *path, char **a
 
 extern struct ctl_table usermodehelper_table[];
 
+enum umh_disable_depth {
+	UMH_ENABLED = 0,
+	UMH_FREEZING,
+	UMH_DISABLED,
+};
+
 extern void usermodehelper_init(void);
 
-extern int usermodehelper_disable(void);
-extern void usermodehelper_enable(void);
+extern int __usermodehelper_disable(enum umh_disable_depth depth);
+extern void __usermodehelper_reset(enum umh_disable_depth depth);
+
+static inline int usermodehelper_disable(void)
+{
+	return __usermodehelper_disable(UMH_DISABLED);
+}
+
+static inline void usermodehelper_enable(void)
+{
+	__usermodehelper_reset(UMH_ENABLED);
+}
+
 extern int usermodehelper_read_trylock(void);
 extern long usermodehelper_read_lock_wait(long timeout);
 extern void usermodehelper_read_unlock(void);
kernel/kmod.c +37 -10

89 lines not on this page · raw message

kernel/power/process.c +2 -1
Index: linux/kernel/power/process.c
===================================================================
@@ -123,7 +123,7 @@ int freeze_processes(void)
 {
 	int error;
 
-	error = usermodehelper_disable();
+	error = __usermodehelper_disable(UMH_FREEZING);
 	if (error)
 		return error;
 
@@ -135,6 +135,7 @@ int freeze_processes(void)
 	error = try_to_freeze_tasks(true);
 	if (!error) {
 		printk("done.");
+		__usermodehelper_reset(UMH_DISABLED);
 		oom_killer_disable();
 	}
 	printk("\n");
#50 lore raw

Re: [PATCH 6/6] PM / Sleep: Mitigate race between the freezer and request_firmware()

kernel/power/process.c int freeze_processes(void) · line 141

Quoted 12 lines · Rafael J. Wysocki

Index: linux/kernel/power/process.c
===================================================================

kernel/power/process.c +1 -0
@@ -135,6 +135,7 @@ int freeze_processes(void)
 	error = try_to_freeze_tasks(true);
 	if (!error) {
 		printk("done.");
+		__usermodehelper_reset(UMH_DISABLED);
 		oom_killer_disable();
 	}
 	printk("\n");

nitpick: This doesn't seem to be doing a reset, more like a set. Perhaps this function should be called __usermodehelper_set()?

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#51 lore raw

Re: [PATCH 6/6] PM / Sleep: Mitigate race between the freezer and request_firmware()

Quoted 16 lines · Stephen Boyd

kernel/power/process.c int freeze_processes(void) · line 141

Quoted 12 lines · Rafael J. Wysocki

Index: linux/kernel/power/process.c
===================================================================

kernel/power/process.c +1 -0
@@ -135,6 +135,7 @@ int freeze_processes(void)
 	error = try_to_freeze_tasks(true);
 	if (!error) {
 		printk("done.");
+		__usermodehelper_reset(UMH_DISABLED);
 		oom_killer_disable();
 	}
 	printk("\n");

nitpick: This doesn't seem to be doing a reset, more like a set. Perhaps this function should be called __usermodehelper_set()?

Yes, you are right. I changed that before, but somehow managed to send an old patch. The new version follows.

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Mitigate race between the freezer and request_firmware()

There is a race condition between the freezer and request_firmware() such that if request_firmware() is run on one CPU and freeze_processes() is run on another CPU and usermodehelper_disable() called by it succeeds to grab umhelper_sem for writing before usermodehelper_read_trylock() called from request_firmware() acquires it for reading, the request_firmware() will fail and trigger a WARN_ON() complaining that it was called at a wrong time. However, in fact, it wasn't called at a wrong time and freeze_processes() simply happened to be executed simultaneously.

To avoid this race, at least in some cases, modify usermodehelper_read_trylock() so that it doesn't fail if the freezing of tasks has just started and hasn't been completed yet. Instead, during the freezing of tasks, it will try to freeze the task that has called it so that it can wait until user space is thawed without triggering the scary warning.

For this purpose, change usermodehelper_disabled so that it can take three different values, UMH_ENABLED (0), UMH_FREEZING and UMH_DISABLED. The first one means that usermode helpers are enabled, the last one means "hard disable" (i.e. the system is not ready for usermode helpers to be used) and the second one is reserved for the freezer. Namely, when freeze_processes() is started, it sets usermodehelper_disabled to UMH_FREEZING which tells usermodehelper_read_trylock() that it shouldn't fail just yet and should call try_to_freeze() if woken up and cannot return immediately. This way all freezable tasks that happen to call request_firmware() right before freeze_processes() is started and lose the race for umhelper_sem with it will be frozen and will sleep until thaw_processes() unsets usermodehelper_disabled. [For the non-freezable callers of request_firmware() the race for umhelper_sem against freeze_processes() is unfortunately unavoidable.]

---
 include/linux/kmod.h   |   21 +++++++++++++++++++--
 kernel/kmod.c          |   47 +++++++++++++++++++++++++++++++++++++----------
 kernel/power/process.c |    3 ++-
 3 files changed, 58 insertions(+), 13 deletions(-)
include/linux/kmod.h +19 -2
Index: linux/include/linux/kmod.h
===================================================================
@@ -112,10 +112,27 @@ call_usermodehelper(char *path, char **a
 
 extern struct ctl_table usermodehelper_table[];
 
+enum umh_disable_depth {
+	UMH_ENABLED = 0,
+	UMH_FREEZING,
+	UMH_DISABLED,
+};
+
 extern void usermodehelper_init(void);
 
-extern int usermodehelper_disable(void);
-extern void usermodehelper_enable(void);
+extern int __usermodehelper_disable(enum umh_disable_depth depth);
+extern void __usermodehelper_set_disable_depth(enum umh_disable_depth depth);
+
+static inline int usermodehelper_disable(void)
+{
+	return __usermodehelper_disable(UMH_DISABLED);
+}
+
+static inline void usermodehelper_enable(void)
+{
+	__usermodehelper_set_disable_depth(UMH_ENABLED);
+}
+
 extern int usermodehelper_read_trylock(void);
 extern long usermodehelper_read_lock_wait(long timeout);
 extern void usermodehelper_read_unlock(void);
kernel/kmod.c +37 -10

89 lines not on this page · raw message

kernel/power/process.c +2 -1
Index: linux/kernel/power/process.c
===================================================================
@@ -123,7 +123,7 @@ int freeze_processes(void)
 {
 	int error;
 
-	error = usermodehelper_disable();
+	error = __usermodehelper_disable(UMH_FREEZING);
 	if (error)
 		return error;
 
@@ -135,6 +135,7 @@ int freeze_processes(void)
 	error = try_to_freeze_tasks(true);
 	if (!error) {
 		printk("done.");
+		__usermodehelper_set_disable_depth(UMH_DISABLED);
 		oom_killer_disable();
 	}
 	printk("\n");
#52 lore raw

Re: [PATCH 6/6] PM / Sleep: Mitigate race between the freezer and request_firmware()

Quoted 16 lines · Rafael J. Wysocki
Quoted 15 lines · Stephen Boyd

kernel/power/process.c int freeze_processes(void) · line 141

Quoted 12 lines · Rafael J. Wysocki

Index: linux/kernel/power/process.c
===================================================================

kernel/power/process.c +1 -0
@@ -135,6 +135,7 @@ int freeze_processes(void)
 	error = try_to_freeze_tasks(true);
 	if (!error) {
 		printk("done.");
+		__usermodehelper_reset(UMH_DISABLED);
 		oom_killer_disable();
 	}
 	printk("\n");

nitpick: This doesn't seem to be doing a reset, more like a set. Perhaps this function should be called __usermodehelper_set()?

Quoted 2 lines

Yes, you are right. I changed that before, but somehow managed to send an old patch. The new version follows.

Looks good. Thanks.

Quoted 21 lines

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Mitigate race between the freezer and request_firmware()

There is a race condition between the freezer and request_firmware() such that if request_firmware() is run on one CPU and freeze_processes() is run on another CPU and usermodehelper_disable() called by it succeeds to grab umhelper_sem for writing before usermodehelper_read_trylock() called from request_firmware() acquires it for reading, the request_firmware() will fail and trigger a WARN_ON() complaining that it was called at a wrong time. However, in fact, it wasn't called at a wrong time and freeze_processes() simply happened to be executed simultaneously.

To avoid this race, at least in some cases, modify usermodehelper_read_trylock() so that it doesn't fail if the freezing of tasks has just started and hasn't been completed yet. Instead, during the freezing of tasks, it will try to freeze the task that has called it so that it can wait until user space is thawed without triggering the scary warning.

Quoted 16 lines

For this purpose, change usermodehelper_disabled so that it can take three different values, UMH_ENABLED (0), UMH_FREEZING and UMH_DISABLED. The first one means that usermode helpers are enabled, the last one means "hard disable" (i.e. the system is not ready for usermode helpers to be used) and the second one is reserved for the freezer. Namely, when freeze_processes() is started, it sets usermodehelper_disabled to UMH_FREEZING which tells usermodehelper_read_trylock() that it shouldn't fail just yet and should call try_to_freeze() if woken up and cannot return immediately. This way all freezable tasks that happen to call request_firmware() right before freeze_processes() is started and lose the race for umhelper_sem with it will be frozen and will sleep until thaw_processes() unsets usermodehelper_disabled. [For the non-freezable callers of request_firmware() the race for umhelper_sem against freeze_processes() is unfortunately unavoidable.]

Quoted 3 lines

---

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#53 lore raw

Re: [PATCH 6/6] PM / Sleep: Mitigate race between the freezer and request_firmware()

Quoted 60 lines · Stephen Boyd
Quoted 16 lines · Rafael J. Wysocki
Quoted 15 lines · Stephen Boyd

kernel/power/process.c int freeze_processes(void) · line 141

Quoted 12 lines · Rafael J. Wysocki

Index: linux/kernel/power/process.c
===================================================================

kernel/power/process.c +1 -0
@@ -135,6 +135,7 @@ int freeze_processes(void)
 	error = try_to_freeze_tasks(true);
 	if (!error) {
 		printk("done.");
+		__usermodehelper_reset(UMH_DISABLED);
 		oom_killer_disable();
 	}
 	printk("\n");

nitpick: This doesn't seem to be doing a reset, more like a set. Perhaps this function should be called __usermodehelper_set()?

Quoted 2 lines

Yes, you are right. I changed that before, but somehow managed to send an old patch. The new version follows.

Looks good. Thanks.

Quoted 21 lines

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Mitigate race between the freezer and request_firmware()

There is a race condition between the freezer and request_firmware() such that if request_firmware() is run on one CPU and freeze_processes() is run on another CPU and usermodehelper_disable() called by it succeeds to grab umhelper_sem for writing before usermodehelper_read_trylock() called from request_firmware() acquires it for reading, the request_firmware() will fail and trigger a WARN_ON() complaining that it was called at a wrong time. However, in fact, it wasn't called at a wrong time and freeze_processes() simply happened to be executed simultaneously.

To avoid this race, at least in some cases, modify usermodehelper_read_trylock() so that it doesn't fail if the freezing of tasks has just started and hasn't been completed yet. Instead, during the freezing of tasks, it will try to freeze the task that has called it so that it can wait until user space is thawed without triggering the scary warning.

Quoted 16 lines

For this purpose, change usermodehelper_disabled so that it can take three different values, UMH_ENABLED (0), UMH_FREEZING and UMH_DISABLED. The first one means that usermode helpers are enabled, the last one means "hard disable" (i.e. the system is not ready for usermode helpers to be used) and the second one is reserved for the freezer. Namely, when freeze_processes() is started, it sets usermodehelper_disabled to UMH_FREEZING which tells usermodehelper_read_trylock() that it shouldn't fail just yet and should call try_to_freeze() if woken up and cannot return immediately. This way all freezable tasks that happen to call request_firmware() right before freeze_processes() is started and lose the race for umhelper_sem with it will be frozen and will sleep until thaw_processes() unsets usermodehelper_disabled. [For the non-freezable callers of request_firmware() the race for umhelper_sem against freeze_processes() is unfortunately unavoidable.]

Quoted 1 line

Yeah, sorry.

maintainer Acked-by on the v1 series
#54 lore raw

Re: [PATCH 0/6] firmware_class: Fix problems with usermodehelper test

In reply to Rafael J. Wysocki
Quoted 13 lines · Rafael J. Wysocki

Hi all,

The following series of patches fixes two problems with request_firmware() and request_firmware_nowait() resulting from commit a144c6a6c924aa1da04dd77fb84b89927354fdff

PM: Print a warning if firmware is requested when tasks are frozen

The first problem is that request_firmware_nowait() may fail if it happens to run in parallel with system suspend. It should't fail in such situations and that is addressed by the first three patches (that code has been discussed with Linus at al already).

Quoted 4 lines

The second issue is that request_firmware() may be called in a thread which isn't related to system suspend and if suspend happens exactly at that time, request_firmware() will fail (and print a scary warning), although it shouldn't. This problem is addressed by the remaining three patches, which are new.

At first glance, they all look good to me, thanks for doing this work:

I'm guessing this will go in through one of your trees, right?

thanks,

greg k-h

#55 lore raw

Re: [PATCH 0/6] firmware_class: Fix problems with usermodehelper test

Quoted 14 lines · Greg KH
Quoted 13 lines · Rafael J. Wysocki

Hi all,

The following series of patches fixes two problems with request_firmware() and request_firmware_nowait() resulting from commit a144c6a6c924aa1da04dd77fb84b89927354fdff

PM: Print a warning if firmware is requested when tasks are frozen

The first problem is that request_firmware_nowait() may fail if it happens to run in parallel with system suspend. It should't fail in such situations and that is addressed by the first three patches (that code has been discussed with Linus at al already).

Quoted 7 lines
Quoted 4 lines

The second issue is that request_firmware() may be called in a thread which isn't related to system suspend and if suspend happens exactly at that time, request_firmware() will fail (and print a scary warning), although it shouldn't. This problem is addressed by the remaining three patches, which are new.

At first glance, they all look good to me, thanks for doing this work:

Thanks!

Quoted 1 line

I'm guessing this will go in through one of your trees, right?

That's correct.

Thanks,
Rafael

#56 lore raw

PATCH 1/2 firmware_class: Reorganize fw_create_instance()

In reply to Rafael J. Wysocki

Recent patches to split up the three phases of request_firmware() lead to a casting away of const in fw_create_instance(). We can avoid this cast by splitting up fw_create_instance() a bit.

Make _request_firmware_setup() return a struct fw_priv and use that struct instead of passing struct firmware to _request_firmware(). Move the uevent and device file creation bits to the loading phase and rename the function to _request_firmware_load() to better reflect its purpose.

---
 drivers/base/firmware_class.c |  135 +++++++++++++++++++----------------------
 1 file changed, 62 insertions(+), 73 deletions(-)
drivers/base/firmware_class.c +62 -73
index 72c644b..ae00a2f 100644
@@ -440,21 +440,19 @@ static void firmware_class_timeout(u_long data)
 }
 
 static struct firmware_priv *
-fw_create_instance(const struct firmware *firmware, const char *fw_name,
+fw_create_instance(struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
 	struct device *f_dev;
-	int error;
 
 	fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
 	if (!fw_priv) {
 		dev_err(device, "%s: kmalloc failed\n", __func__);
-		error = -ENOMEM;
-		goto err_out;
+		return ERR_PTR(-ENOMEM);
 	}
 
-	fw_priv->fw = (struct firmware *)firmware;
+	fw_priv->fw = firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);
@@ -468,74 +466,37 @@ fw_create_instance(const struct firmware *firmware, const char *fw_name,
 	f_dev->parent = device;
 	f_dev->class = &firmware_class;
 
-	dev_set_uevent_suppress(f_dev, true);
-
-	/* Need to pin this module until class device is destroyed */
-	__module_get(THIS_MODULE);
-
-	error = device_add(f_dev);
-	if (error) {
-		dev_err(device, "%s: device_register failed\n", __func__);
-		goto err_put_dev;
-	}
-
-	error = device_create_bin_file(f_dev, &firmware_attr_data);
-	if (error) {
-		dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
-		goto err_del_dev;
-	}
-
-	error = device_create_file(f_dev, &dev_attr_loading);
-	if (error) {
-		dev_err(device, "%s: device_create_file failed\n", __func__);
-		goto err_del_bin_attr;
-	}
-
-	if (uevent)
-		dev_set_uevent_suppress(f_dev, false);
-
 	return fw_priv;
-
-err_del_bin_attr:
-	device_remove_bin_file(f_dev, &firmware_attr_data);
-err_del_dev:
-	device_del(f_dev);
-err_put_dev:
-	put_device(f_dev);
-err_out:
-	return ERR_PTR(error);
-}
-
-static void fw_destroy_instance(struct firmware_priv *fw_priv)
-{
-	struct device *f_dev = &fw_priv->dev;
-
-	device_remove_file(f_dev, &dev_attr_loading);
-	device_remove_bin_file(f_dev, &firmware_attr_data);
-	device_unregister(f_dev);
 }
 
-static int _request_firmware_prepare(const struct firmware **firmware_p,
-				     const char *name, struct device *device)
+static struct firmware_priv *
+_request_firmware_prepare(const struct firmware **firmware_p, const char *name,
+			  struct device *device, bool uevent, bool nowait)
 {
 	struct firmware *firmware;
+	struct firmware_priv *fw_priv;
 
 	if (!firmware_p)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
 	if (!firmware) {
 		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
 			__func__);
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	if (fw_get_builtin_firmware(firmware, name)) {
 		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
-		return 0;
+		return NULL;
 	}
 
-	return 1;
+	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
+	if (IS_ERR(fw_priv)) {
+		release_firmware(firmware);
+		*firmware_p = NULL;
+	}
+	return fw_priv;
 }
 
 static void _request_firmware_cleanup(const struct firmware **firmware_p)
@@ -544,21 +505,38 @@ static void _request_firmware_cleanup(const struct firmware **firmware_p)
 	*firmware_p = NULL;
 }
 
-static int _request_firmware(const struct firmware *firmware,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait, long timeout)
+static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
+				  long timeout)
 {
-	struct firmware_priv *fw_priv;
 	int retval = 0;
+	struct device *f_dev = &fw_priv->dev;
+
+	dev_set_uevent_suppress(f_dev, true);
 
-	if (uevent)
-		dev_dbg(device, "firmware: requesting %s\n", name);
+	/* Need to pin this module until class device is destroyed */
+	__module_get(THIS_MODULE);
 
-	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
-	if (IS_ERR(fw_priv))
-		return PTR_ERR(fw_priv);
+	retval = device_add(f_dev);
+	if (retval) {
+		dev_err(f_dev, "%s: device_register failed\n", __func__);
+		goto err_put_dev;
+	}
+
+	retval = device_create_bin_file(f_dev, &firmware_attr_data);
+	if (retval) {
+		dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__);
+		goto err_del_dev;
+	}
+
+	retval = device_create_file(f_dev, &dev_attr_loading);
+	if (retval) {
+		dev_err(f_dev, "%s: device_create_file failed\n", __func__);
+		goto err_del_bin_attr;
+	}
 
 	if (uevent) {
+		dev_set_uevent_suppress(f_dev, false);
+		dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
 		if (timeout != MAX_SCHEDULE_TIMEOUT)
 			mod_timer(&fw_priv->timeout,
 				  round_jiffies_up(jiffies + timeout));
@@ -577,7 +555,13 @@ static int _request_firmware(const struct firmware *firmware,
 	fw_priv->fw = NULL;
 	mutex_unlock(&fw_lock);
 
-	fw_destroy_instance(fw_priv);
+	device_remove_file(f_dev, &dev_attr_loading);
+err_del_bin_attr:
+	device_remove_bin_file(f_dev, &firmware_attr_data);
+err_del_dev:
+	device_del(f_dev);
+err_put_dev:
+	put_device(f_dev);
 	return retval;
 }
 
@@ -600,17 +584,19 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
+	struct firmware_priv *fw_priv;
 	int ret;
 
-	ret = _request_firmware_prepare(firmware_p, name, device);
-	if (ret <= 0)
-		return ret;
+	fw_priv = _request_firmware_prepare(firmware_p, name, device, true,
+					    false);
+	if (IS_ERR_OR_NULL(fw_priv))
+		return PTR_RET(fw_priv);
 
 	ret = usermodehelper_read_trylock();
 	if (WARN_ON(ret)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 	} else {
-		ret = _request_firmware(*firmware_p, name, device, true, false,
+		ret = _request_firmware_load(fw_priv, true,
 					firmware_loading_timeout());
 		usermodehelper_read_unlock();
 	}
@@ -648,6 +634,7 @@ static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
+	struct firmware_priv *fw_priv;
 	long timeout;
 	int ret;
 
@@ -656,14 +643,16 @@ static int request_firmware_work_func(void *arg)
 		return 0;
 	}
 
-	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
-	if (ret <= 0)
+	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
+			fw_work->uevent, true);
+	if (IS_ERR_OR_NULL(fw_priv)) {
+		ret = PTR_RET(fw_priv);
 		goto out;
+	}
 
 	timeout = usermodehelper_read_lock_wait(firmware_loading_timeout());
 	if (timeout) {
-		ret = _request_firmware(fw, fw_work->name, fw_work->device,
-					fw_work->uevent, true, timeout);
+		ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout);
 		usermodehelper_read_unlock();
 	} else {
 		dev_dbg(fw_work->device, "firmware: %s loading timed out\n",
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#57 lore raw

PATCH 2/2 firmware_class: Move request_firmware_nowait() to workqueues

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the long system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---

Is it better to use alloc_workqueue() and not put these on the system long workqueue?

 drivers/base/firmware_class.c |   27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)
drivers/base/firmware_class.c +7 -20
index ae00a2f..4d8cb8b 100644
@@ -16,10 +16,11 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/mutex.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
 
@@ -630,19 +631,15 @@ struct firmware_work {
 	bool uevent;
 };
 
-static int request_firmware_work_func(void *arg)
+static void request_firmware_work_func(struct work_struct *work)
 {
-	struct firmware_work *fw_work = arg;
+	struct firmware_work *fw_work;
 	const struct firmware *fw;
 	struct firmware_priv *fw_priv;
 	long timeout;
 	int ret;
 
-	if (!arg) {
-		WARN_ON(1);
-		return 0;
-	}
-
+	fw_work = container_of(work, struct firmware_work, work);
 	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
 			fw_work->uevent, true);
 	if (IS_ERR_OR_NULL(fw_priv)) {
@@ -667,8 +664,6 @@ static int request_firmware_work_func(void *arg)
 
 	module_put(fw_work->module);
 	kfree(fw_work);
-
-	return ret;
 }
 
 /**
@@ -694,7 +689,6 @@ request_firmware_nowait(
 	const char *name, struct device *device, gfp_t gfp, void *context,
 	void (*cont)(const struct firmware *fw, void *context))
 {
-	struct task_struct *task;
 	struct firmware_work *fw_work;
 
 	fw_work = kzalloc(sizeof (struct firmware_work), gfp);
@@ -713,15 +707,8 @@ request_firmware_nowait(
 		return -EFAULT;
 	}
 
-	task = kthread_run(request_firmware_work_func, fw_work,
-			    "firmware/%s", name);
-	if (IS_ERR(task)) {
-		fw_work->cont(NULL, fw_work->context);
-		module_put(fw_work->module);
-		kfree(fw_work);
-		return PTR_ERR(task);
-	}
-
+	INIT_WORK(&fw_work->work, request_firmware_work_func);
+	queue_work(system_long_wq, &fw_work->work);
 	return 0;
 }
 
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#58 lore raw

Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 11 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the long system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---

Is it better to use alloc_workqueue() and not put these on the system long workqueue?

I think if that happens to interfere destructively with something, we can always use a new workqueue. For now, let's try to avoid adding yet another one if possible.

Thanks,
Rafael

Quoted 79 lines
 drivers/base/firmware_class.c |   27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)
drivers/base/firmware_class.c +7 -20
index ae00a2f..4d8cb8b 100644
@@ -16,10 +16,11 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/mutex.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
 
@@ -630,19 +631,15 @@ struct firmware_work {
 	bool uevent;
 };
 
-static int request_firmware_work_func(void *arg)
+static void request_firmware_work_func(struct work_struct *work)
 {
-	struct firmware_work *fw_work = arg;
+	struct firmware_work *fw_work;
 	const struct firmware *fw;
 	struct firmware_priv *fw_priv;
 	long timeout;
 	int ret;
 
-	if (!arg) {
-		WARN_ON(1);
-		return 0;
-	}
-
+	fw_work = container_of(work, struct firmware_work, work);
 	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
 			fw_work->uevent, true);
 	if (IS_ERR_OR_NULL(fw_priv)) {
@@ -667,8 +664,6 @@ static int request_firmware_work_func(void *arg)
 
 	module_put(fw_work->module);
 	kfree(fw_work);
-
-	return ret;
 }
 
 /**
@@ -694,7 +689,6 @@ request_firmware_nowait(
 	const char *name, struct device *device, gfp_t gfp, void *context,
 	void (*cont)(const struct firmware *fw, void *context))
 {
-	struct task_struct *task;
 	struct firmware_work *fw_work;
 
 	fw_work = kzalloc(sizeof (struct firmware_work), gfp);
@@ -713,15 +707,8 @@ request_firmware_nowait(
 		return -EFAULT;
 	}
 
-	task = kthread_run(request_firmware_work_func, fw_work,
-			    "firmware/%s", name);
-	if (IS_ERR(task)) {
-		fw_work->cont(NULL, fw_work->context);
-		module_put(fw_work->module);
-		kfree(fw_work);
-		return PTR_ERR(task);
-	}
-
+	INIT_WORK(&fw_work->work, request_firmware_work_func);
+	queue_work(system_long_wq, &fw_work->work);
 	return 0;
 }
 
#59 lore raw

Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues

In reply to Stephen Boyd
Quoted 11 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the long system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---

Is it better to use alloc_workqueue() and not put these on the system long workqueue?

No, just use schedule_work() unless there are specific requirements which can't be fulfilled that way (e.g. it's on memory allocation path, may consume large amount of cpu cycles, ...)

Thanks.

Signature · 1 line
tejun
#60 lore raw

Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 10 lines · Tejun Heo
Quoted 9 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the long system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---

Quoted 6 lines
Quoted 2 lines

Is it better to use alloc_workqueue() and not put these on the system long workqueue?

No, just use schedule_work() unless there are specific requirements which can't be fulfilled that way (e.g. it's on memory allocation path, may consume large amount of cpu cycles, ...)

It may wait quite long.

Rafael

#61 lore raw

Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 11 lines · Rafael J. Wysocki
Quoted 10 lines · Tejun Heo
Quoted 9 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the long system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

Is it better to use alloc_workqueue() and not put these on the system long workqueue?

No, just use schedule_work() unless there are specific requirements which can't be fulfilled that way (e.g. it's on memory allocation path, may consume large amount of cpu cycles, ...)

It may wait quite long.

That shouldn't matter. system_long_wq's name is a bit misleading at this point. The only reason it's used currently is to avoid cyclic dependency involving flush_workqueue(), which calls for clearer solution anyway. So, yeap, using system_wq should be fine here.

Thank you.

Signature · 1 line
tejun
#62 lore raw

Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 19 lines · Tejun Heo
Quoted 18 lines · Rafael J. Wysocki
Quoted 16 lines · Tejun Heo
Quoted 11 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the long system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---

Is it better to use alloc_workqueue() and not put these on the system long workqueue?

No, just use schedule_work() unless there are specific requirements which can't be fulfilled that way (e.g. it's on memory allocation path, may consume large amount of cpu cycles, ...)

Quoted 6 lines
Quoted 1 line

It may wait quite long.

That shouldn't matter. system_long_wq's name is a bit misleading at this point. The only reason it's used currently is to avoid cyclic dependency involving flush_workqueue(), which calls for clearer solution anyway. So, yeap, using system_wq should be fine here.

Good, thanks for the explanation.

Stephen, care to respin?

Rafael

#63 lore raw

Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues

Quoted 18 lines · Rafael J. Wysocki
Quoted 17 lines · Tejun Heo
Quoted 16 lines · Rafael J. Wysocki
Quoted 15 lines · Tejun Heo
Quoted 11 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the long system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---

Is it better to use alloc_workqueue() and not put these on the system long workqueue?

No, just use schedule_work() unless there are specific requirements which can't be fulfilled that way (e.g. it's on memory allocation path, may consume large amount of cpu cycles, ...)

Quoted 9 lines
Quoted 5 lines
Quoted 1 line

It may wait quite long.

That shouldn't matter. system_long_wq's name is a bit misleading at this point. The only reason it's used currently is to avoid cyclic dependency involving flush_workqueue(), which calls for clearer solution anyway. So, yeap, using system_wq should be fine here.

Good, thanks for the explanation.

Stephen, care to respin?

Sure. We want system_wq instead of system_long_wq? If so let's use this patch.

-----8<-- cut here -->8------

Subject: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---
 drivers/base/firmware_class.c |   27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)
drivers/base/firmware_class.c +7 -20
index ae00a2f..5401814 100644
@@ -16,10 +16,11 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/mutex.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
 
@@ -630,19 +631,15 @@ struct firmware_work {
 	bool uevent;
 };
 
-static int request_firmware_work_func(void *arg)
+static void request_firmware_work_func(struct work_struct *work)
 {
-	struct firmware_work *fw_work = arg;
+	struct firmware_work *fw_work;
 	const struct firmware *fw;
 	struct firmware_priv *fw_priv;
 	long timeout;
 	int ret;
 
-	if (!arg) {
-		WARN_ON(1);
-		return 0;
-	}
-
+	fw_work = container_of(work, struct firmware_work, work);
 	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
 			fw_work->uevent, true);
 	if (IS_ERR_OR_NULL(fw_priv)) {
@@ -667,8 +664,6 @@ static int request_firmware_work_func(void *arg)
 
 	module_put(fw_work->module);
 	kfree(fw_work);
-
-	return ret;
 }
 
 /**
@@ -694,7 +689,6 @@ request_firmware_nowait(
 	const char *name, struct device *device, gfp_t gfp, void *context,
 	void (*cont)(const struct firmware *fw, void *context))
 {
-	struct task_struct *task;
 	struct firmware_work *fw_work;
 
 	fw_work = kzalloc(sizeof (struct firmware_work), gfp);
@@ -713,15 +707,8 @@ request_firmware_nowait(
 		return -EFAULT;
 	}
 
-	task = kthread_run(request_firmware_work_func, fw_work,
-			    "firmware/%s", name);
-	if (IS_ERR(task)) {
-		fw_work->cont(NULL, fw_work->context);
-		module_put(fw_work->module);
-		kfree(fw_work);
-		return PTR_ERR(task);
-	}
-
+	INIT_WORK(&fw_work->work, request_firmware_work_func);
+	schedule_work(&fw_work->work);
 	return 0;
 }
 
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#64 lore raw

Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues

In reply to Rafael J. Wysocki
Quoted 18 lines · Rafael J. Wysocki
Quoted 17 lines · Tejun Heo
Quoted 16 lines · Rafael J. Wysocki
Quoted 15 lines · Tejun Heo
Quoted 11 lines · Stephen Boyd

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the long system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---

Is it better to use alloc_workqueue() and not put these on the system long workqueue?

No, just use schedule_work() unless there are specific requirements which can't be fulfilled that way (e.g. it's on memory allocation path, may consume large amount of cpu cycles, ...)

Quoted 9 lines
Quoted 5 lines
Quoted 1 line

It may wait quite long.

That shouldn't matter. system_long_wq's name is a bit misleading at this point. The only reason it's used currently is to avoid cyclic dependency involving flush_workqueue(), which calls for clearer solution anyway. So, yeap, using system_wq should be fine here.

Good, thanks for the explanation.

Stephen, care to respin?

Sure. We want system_wq instead of system_long_wq? If so let's use this patch.

-----8<-- cut here -->8------

Subject: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---
 drivers/base/firmware_class.c |   27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)
drivers/base/firmware_class.c +7 -20
index ae00a2f..5401814 100644
@@ -16,10 +16,11 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/mutex.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
 
@@ -630,19 +631,15 @@ struct firmware_work {
 	bool uevent;
 };
 
-static int request_firmware_work_func(void *arg)
+static void request_firmware_work_func(struct work_struct *work)
 {
-	struct firmware_work *fw_work = arg;
+	struct firmware_work *fw_work;
 	const struct firmware *fw;
 	struct firmware_priv *fw_priv;
 	long timeout;
 	int ret;
 
-	if (!arg) {
-		WARN_ON(1);
-		return 0;
-	}
-
+	fw_work = container_of(work, struct firmware_work, work);
 	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
 			fw_work->uevent, true);
 	if (IS_ERR_OR_NULL(fw_priv)) {
@@ -667,8 +664,6 @@ static int request_firmware_work_func(void *arg)
 
 	module_put(fw_work->module);
 	kfree(fw_work);
-
-	return ret;
 }
 
 /**
@@ -694,7 +689,6 @@ request_firmware_nowait(
 	const char *name, struct device *device, gfp_t gfp, void *context,
 	void (*cont)(const struct firmware *fw, void *context))
 {
-	struct task_struct *task;
 	struct firmware_work *fw_work;
 
 	fw_work = kzalloc(sizeof (struct firmware_work), gfp);
@@ -713,15 +707,8 @@ request_firmware_nowait(
 		return -EFAULT;
 	}
 
-	task = kthread_run(request_firmware_work_func, fw_work,
-			    "firmware/%s", name);
-	if (IS_ERR(task)) {
-		fw_work->cont(NULL, fw_work->context);
-		module_put(fw_work->module);
-		kfree(fw_work);
-		return PTR_ERR(task);
-	}
-
+	INIT_WORK(&fw_work->work, request_firmware_work_func);
+	schedule_work(&fw_work->work);
 	return 0;
 }
 
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#65 lore raw

Re: [PATCH 1/2] firmware_class: Reorganize fw_create_instance()

In reply to Stephen Boyd
Quoted 11 lines · Stephen Boyd

Recent patches to split up the three phases of request_firmware() lead to a casting away of const in fw_create_instance(). We can avoid this cast by splitting up fw_create_instance() a bit.

Make _request_firmware_setup() return a struct fw_priv and use that struct instead of passing struct firmware to _request_firmware(). Move the uevent and device file creation bits to the loading phase and rename the function to _request_firmware_load() to better reflect its purpose.

Looks good to me.

Greg, if that's fine with you, I'll add this patch (and the next one) to my firmware_class series you've looked at recently.

Thanks,
Rafael

Quoted 238 lines
---
 drivers/base/firmware_class.c |  135 +++++++++++++++++++----------------------
 1 file changed, 62 insertions(+), 73 deletions(-)
drivers/base/firmware_class.c +62 -73
index 72c644b..ae00a2f 100644
@@ -440,21 +440,19 @@ static void firmware_class_timeout(u_long data)
 }
 
 static struct firmware_priv *
-fw_create_instance(const struct firmware *firmware, const char *fw_name,
+fw_create_instance(struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
 	struct device *f_dev;
-	int error;
 
 	fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
 	if (!fw_priv) {
 		dev_err(device, "%s: kmalloc failed\n", __func__);
-		error = -ENOMEM;
-		goto err_out;
+		return ERR_PTR(-ENOMEM);
 	}
 
-	fw_priv->fw = (struct firmware *)firmware;
+	fw_priv->fw = firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);
@@ -468,74 +466,37 @@ fw_create_instance(const struct firmware *firmware, const char *fw_name,
 	f_dev->parent = device;
 	f_dev->class = &firmware_class;
 
-	dev_set_uevent_suppress(f_dev, true);
-
-	/* Need to pin this module until class device is destroyed */
-	__module_get(THIS_MODULE);
-
-	error = device_add(f_dev);
-	if (error) {
-		dev_err(device, "%s: device_register failed\n", __func__);
-		goto err_put_dev;
-	}
-
-	error = device_create_bin_file(f_dev, &firmware_attr_data);
-	if (error) {
-		dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
-		goto err_del_dev;
-	}
-
-	error = device_create_file(f_dev, &dev_attr_loading);
-	if (error) {
-		dev_err(device, "%s: device_create_file failed\n", __func__);
-		goto err_del_bin_attr;
-	}
-
-	if (uevent)
-		dev_set_uevent_suppress(f_dev, false);
-
 	return fw_priv;
-
-err_del_bin_attr:
-	device_remove_bin_file(f_dev, &firmware_attr_data);
-err_del_dev:
-	device_del(f_dev);
-err_put_dev:
-	put_device(f_dev);
-err_out:
-	return ERR_PTR(error);
-}
-
-static void fw_destroy_instance(struct firmware_priv *fw_priv)
-{
-	struct device *f_dev = &fw_priv->dev;
-
-	device_remove_file(f_dev, &dev_attr_loading);
-	device_remove_bin_file(f_dev, &firmware_attr_data);
-	device_unregister(f_dev);
 }
 
-static int _request_firmware_prepare(const struct firmware **firmware_p,
-				     const char *name, struct device *device)
+static struct firmware_priv *
+_request_firmware_prepare(const struct firmware **firmware_p, const char *name,
+			  struct device *device, bool uevent, bool nowait)
 {
 	struct firmware *firmware;
+	struct firmware_priv *fw_priv;
 
 	if (!firmware_p)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
 	if (!firmware) {
 		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
 			__func__);
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	if (fw_get_builtin_firmware(firmware, name)) {
 		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
-		return 0;
+		return NULL;
 	}
 
-	return 1;
+	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
+	if (IS_ERR(fw_priv)) {
+		release_firmware(firmware);
+		*firmware_p = NULL;
+	}
+	return fw_priv;
 }
 
 static void _request_firmware_cleanup(const struct firmware **firmware_p)
@@ -544,21 +505,38 @@ static void _request_firmware_cleanup(const struct firmware **firmware_p)
 	*firmware_p = NULL;
 }
 
-static int _request_firmware(const struct firmware *firmware,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait, long timeout)
+static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
+				  long timeout)
 {
-	struct firmware_priv *fw_priv;
 	int retval = 0;
+	struct device *f_dev = &fw_priv->dev;
+
+	dev_set_uevent_suppress(f_dev, true);
 
-	if (uevent)
-		dev_dbg(device, "firmware: requesting %s\n", name);
+	/* Need to pin this module until class device is destroyed */
+	__module_get(THIS_MODULE);
 
-	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
-	if (IS_ERR(fw_priv))
-		return PTR_ERR(fw_priv);
+	retval = device_add(f_dev);
+	if (retval) {
+		dev_err(f_dev, "%s: device_register failed\n", __func__);
+		goto err_put_dev;
+	}
+
+	retval = device_create_bin_file(f_dev, &firmware_attr_data);
+	if (retval) {
+		dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__);
+		goto err_del_dev;
+	}
+
+	retval = device_create_file(f_dev, &dev_attr_loading);
+	if (retval) {
+		dev_err(f_dev, "%s: device_create_file failed\n", __func__);
+		goto err_del_bin_attr;
+	}
 
 	if (uevent) {
+		dev_set_uevent_suppress(f_dev, false);
+		dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
 		if (timeout != MAX_SCHEDULE_TIMEOUT)
 			mod_timer(&fw_priv->timeout,
 				  round_jiffies_up(jiffies + timeout));
@@ -577,7 +555,13 @@ static int _request_firmware(const struct firmware *firmware,
 	fw_priv->fw = NULL;
 	mutex_unlock(&fw_lock);
 
-	fw_destroy_instance(fw_priv);
+	device_remove_file(f_dev, &dev_attr_loading);
+err_del_bin_attr:
+	device_remove_bin_file(f_dev, &firmware_attr_data);
+err_del_dev:
+	device_del(f_dev);
+err_put_dev:
+	put_device(f_dev);
 	return retval;
 }
 
@@ -600,17 +584,19 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
+	struct firmware_priv *fw_priv;
 	int ret;
 
-	ret = _request_firmware_prepare(firmware_p, name, device);
-	if (ret <= 0)
-		return ret;
+	fw_priv = _request_firmware_prepare(firmware_p, name, device, true,
+					    false);
+	if (IS_ERR_OR_NULL(fw_priv))
+		return PTR_RET(fw_priv);
 
 	ret = usermodehelper_read_trylock();
 	if (WARN_ON(ret)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 	} else {
-		ret = _request_firmware(*firmware_p, name, device, true, false,
+		ret = _request_firmware_load(fw_priv, true,
 					firmware_loading_timeout());
 		usermodehelper_read_unlock();
 	}
@@ -648,6 +634,7 @@ static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
+	struct firmware_priv *fw_priv;
 	long timeout;
 	int ret;
 
@@ -656,14 +643,16 @@ static int request_firmware_work_func(void *arg)
 		return 0;
 	}
 
-	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
-	if (ret <= 0)
+	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
+			fw_work->uevent, true);
+	if (IS_ERR_OR_NULL(fw_priv)) {
+		ret = PTR_RET(fw_priv);
 		goto out;
+	}
 
 	timeout = usermodehelper_read_lock_wait(firmware_loading_timeout());
 	if (timeout) {
-		ret = _request_firmware(fw, fw_work->name, fw_work->device,
-					fw_work->uevent, true, timeout);
+		ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout);
 		usermodehelper_read_unlock();
 	} else {
 		dev_dbg(fw_work->device, "firmware: %s loading timed out\n",
maintainer Acked-by
#66 lore raw

Re: [PATCH 1/2] firmware_class: Reorganize fw_create_instance()

Quoted 11 lines · Rafael J. Wysocki
Quoted 10 lines · Stephen Boyd

Recent patches to split up the three phases of request_firmware() lead to a casting away of const in fw_create_instance(). We can avoid this cast by splitting up fw_create_instance() a bit.

Make _request_firmware_setup() return a struct fw_priv and use that struct instead of passing struct firmware to _request_firmware(). Move the uevent and device file creation bits to the loading phase and rename the function to _request_firmware_load() to better reflect its purpose.

Quoted 6 lines
Quoted 1 line

Looks good to me.

Greg, if that's fine with you, I'll add this patch (and the next one) to my firmware_class series you've looked at recently.

Yes, it's fine with me:

#67 lore raw

Re: [PATCH 1/2] firmware_class: Reorganize fw_create_instance()

Quoted 14 lines · Greg KH
Quoted 13 lines · Rafael J. Wysocki
Quoted 11 lines · Stephen Boyd

Recent patches to split up the three phases of request_firmware() lead to a casting away of const in fw_create_instance(). We can avoid this cast by splitting up fw_create_instance() a bit.

Make _request_firmware_setup() return a struct fw_priv and use that struct instead of passing struct firmware to _request_firmware(). Move the uevent and device file creation bits to the loading phase and rename the function to _request_firmware_load() to better reflect its purpose.

Quoted 7 lines
Quoted 4 lines

Looks good to me.

Greg, if that's fine with you, I'll add this patch (and the next one) to my firmware_class series you've looked at recently.

Yes, it's fine with me:

Cool, thanks!

Rafael

#68 lore raw

v2 cover firmware_class: Fix problems with usermodehelper test

In reply to Rafael J. Wysocki

Hi all,

Quoted 12 lines · Rafael J. Wysocki

The following series of patches fixes two problems with request_firmware() and request_firmware_nowait() resulting from commit a144c6a6c924aa1da04dd77fb84b89927354fdff

PM: Print a warning if firmware is requested when tasks are frozen

The first problem is that request_firmware_nowait() may fail if it happens to run in parallel with system suspend. It should't fail in such situations and that is addressed by the first three patches (that code has been discussed with Linus at al already).

Quoted 4 lines

The second issue is that request_firmware() may be called in a thread which isn't related to system suspend and if suspend happens exactly at that time, request_firmware() will fail (and print a scary warning), although it shouldn't. This problem is addressed by the remaining three patches, which are new.

The following series is a respin of this one including fixes of minor bugs found by the reviewers in the previous version and two additional patches from Stephen cleaning up things a bit more.

Since patches [1-6/8] fix regressions, I'd like the series to go into v3.4 if that's not too late.

[1/8] - Rework the usermodehelper check in _request_firmware().
[2/8] - Split _request_firmware() into three functions.
[3/8] - If firmware is to be loaded asynchronously, wait for usermodehelper_disabled
to be unset instead of failing the operation.
[4/8] - Unify the hibernation/suspend code's usage of usermodehelper_disable(). [5/8] - Move usermodehelper_disable() into freeze_processes().
[6/8] - Make freezable threads calling request_firware() avoid the race with the
freezer.
[7/8] - Split up fw_create_instance() to avoid casting out of const in it. [8/8] - Use workqueues instead of kthreads in request_firmware_nowait().

Thanks,
Rafael

#69 lore raw

PATCH v3 3/8 firmware_class: Do not warn that system is not ready from async loads

From: Rafael J. Wysocki <[email protected]>

If firmware is requested asynchronously, by calling request_firmware_nowait(), there is no reason to fail the request (and warn the user) when the system is (presumably temporarily) unready to handle it (because user space is not available yet or frozen). For this reason, introduce an alternative routine for read-locking umhelper_sem, usermodehelper_read_lock_wait(), that will wait for usermodehelper_disabled to be unset (possibly with a timeout) and make request_firmware_work_func() use it instead of usermodehelper_read_trylock().

Accordingly, modify request_firmware() so that it uses usermodehelper_read_trylock() to acquire umhelper_sem and remove the code related to that lock from _request_firmware().

---
 drivers/base/firmware_class.c |   51 +++++++++++++++++++++---------------
 include/linux/kmod.h          |    1 
 kernel/kmod.c                 |   58 ++++++++++++++++++++++++++++++++----------
 3 files changed, 76 insertions(+), 34 deletions(-)
include/linux/kmod.h +1 -0
Index: linux/include/linux/kmod.h
===================================================================
@@ -115,6 +115,7 @@ extern void usermodehelper_init(void);
 extern int usermodehelper_disable(void);
 extern void usermodehelper_enable(void);
 extern int usermodehelper_read_trylock(void);
+extern long usermodehelper_read_lock_wait(long timeout);
 extern void usermodehelper_read_unlock(void);
 
 #endif /* __LINUX_KMOD_H__ */
kernel/kmod.c +45 -13

85 lines not on this page · raw message

drivers/base/firmware_class.c +30 -21

97 lines not on this page · raw message

#70 lore raw

PATCH v2 1/8 firmware_class: Rework usermodehelper check

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

Instead of two functions, read_lock_usermodehelper() and usermodehelper_is_disabled(), used in combination, introduce usermodehelper_read_trylock() that will only return with umhelper_sem held if usermodehelper_disabled is unset (and will return -EAGAIN otherwise) and make _request_firmware() use it.

Rename read_unlock_usermodehelper() to usermodehelper_read_unlock() to follow the naming convention of the new function.

---
 drivers/base/firmware_class.c |   11 +++++------
 include/linux/kmod.h          |    5 ++---
 kernel/kmod.c                 |   24 +++++++++++-------------
 3 files changed, 18 insertions(+), 22 deletions(-)
include/linux/kmod.h +2 -3
Index: linux/include/linux/kmod.h
===================================================================
@@ -114,8 +114,7 @@ extern void usermodehelper_init(void);
 
 extern int usermodehelper_disable(void);
 extern void usermodehelper_enable(void);
-extern bool usermodehelper_is_disabled(void);
-extern void read_lock_usermodehelper(void);
-extern void read_unlock_usermodehelper(void);
+extern int usermodehelper_read_trylock(void);
+extern void usermodehelper_read_unlock(void);
 
 #endif /* __LINUX_KMOD_H__ */
kernel/kmod.c +11 -13
Index: linux/kernel/kmod.c
===================================================================
@@ -339,17 +339,24 @@ static DECLARE_WAIT_QUEUE_HEAD(running_h
  */
 #define RUNNING_HELPERS_TIMEOUT	(5 * HZ)
 
-void read_lock_usermodehelper(void)
+int usermodehelper_read_trylock(void)
 {
+	int ret = 0;
+
 	down_read(&umhelper_sem);
+	if (usermodehelper_disabled) {
+		up_read(&umhelper_sem);
+		ret = -EAGAIN;
+	}
+	return ret;
 }
-EXPORT_SYMBOL_GPL(read_lock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_trylock);
 
-void read_unlock_usermodehelper(void)
+void usermodehelper_read_unlock(void)
 {
 	up_read(&umhelper_sem);
 }
-EXPORT_SYMBOL_GPL(read_unlock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_unlock);
 
 /**
  * usermodehelper_disable - prevent new helpers from being started
@@ -390,15 +397,6 @@ void usermodehelper_enable(void)
 	up_write(&umhelper_sem);
 }
 
-/**
- * usermodehelper_is_disabled - check if new helpers are allowed to be started
- */
-bool usermodehelper_is_disabled(void)
-{
-	return usermodehelper_disabled;
-}
-EXPORT_SYMBOL_GPL(usermodehelper_is_disabled);
-
 static void helper_lock(void)
 {
 	atomic_inc(&running_helpers);
drivers/base/firmware_class.c +5 -6
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -533,12 +533,10 @@ static int _request_firmware(const struc
 		return 0;
 	}
 
-	read_lock_usermodehelper();
-
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	retval = usermodehelper_read_trylock();
+	if (WARN_ON(retval)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
-		retval = -EBUSY;
-		goto out;
+		goto out_nolock;
 	}
 
 	if (uevent)
@@ -573,8 +571,9 @@ static int _request_firmware(const struc
 	fw_destroy_instance(fw_priv);
 
 out:
-	read_unlock_usermodehelper();
+	usermodehelper_read_unlock();
 
+out_nolock:
 	if (retval) {
 		release_firmware(firmware);
 		*firmware_p = NULL;
#71 lore raw

PATCH v2 2/8 firmware_class: Split _request_firmware() into three functions, v2

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

Split _request_firmware() into three functions, _request_firmware_prepare() doing preparatory work that need not be done under umhelper_sem, _request_firmware_cleanup() doing the post-error cleanup and _request_firmware() carrying out the remaining operations.

This change is requisite for moving the acquisition of umhelper_sem from _request_firmware() to the callers, which is going to be done subsequently.

---
 drivers/base/firmware_class.c |   58 +++++++++++++++++++++++++++++-------------
 1 file changed, 41 insertions(+), 17 deletions(-)
drivers/base/firmware_class.c +41 -17
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firm
 		goto err_out;
 	}
 
-	fw_priv->fw = firmware;
+	fw_priv->fw = (struct firmware *)firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);
@@ -510,13 +510,10 @@ static void fw_destroy_instance(struct f
 	device_unregister(f_dev);
 }
 
-static int _request_firmware(const struct firmware **firmware_p,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait)
+static int _request_firmware_prepare(const struct firmware **firmware_p,
+				     const char *name, struct device *device)
 {
-	struct firmware_priv *fw_priv;
 	struct firmware *firmware;
-	int retval = 0;
 
 	if (!firmware_p)
 		return -EINVAL;
@@ -533,10 +530,26 @@ static int _request_firmware(const struc
 		return 0;
 	}
 
+	return 1;
+}
+
+static void _request_firmware_cleanup(const struct firmware **firmware_p)
+{
+	release_firmware(*firmware_p);
+	*firmware_p = NULL;
+}
+
+static int _request_firmware(const struct firmware *firmware,
+			     const char *name, struct device *device,
+			     bool uevent, bool nowait)
+{
+	struct firmware_priv *fw_priv;
+	int retval;
+
 	retval = usermodehelper_read_trylock();
 	if (WARN_ON(retval)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
-		goto out_nolock;
+		return retval;
 	}
 
 	if (uevent)
@@ -572,13 +585,6 @@ static int _request_firmware(const struc
 
 out:
 	usermodehelper_read_unlock();
-
-out_nolock:
-	if (retval) {
-		release_firmware(firmware);
-		*firmware_p = NULL;
-	}
-
 	return retval;
 }
 
@@ -601,7 +607,17 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
-        return _request_firmware(firmware_p, name, device, true, false);
+	int ret;
+
+	ret = _request_firmware_prepare(firmware_p, name, device);
+	if (ret <= 0)
+		return ret;
+
+	ret = _request_firmware(*firmware_p, name, device, true, false);
+	if (ret)
+		_request_firmware_cleanup(firmware_p);
+
+	return ret;
 }
 
 /**
@@ -639,8 +655,16 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+	if (ret <= 0)
+		goto out;
+
+	ret = _request_firmware(fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+	if (ret)
+		_request_firmware_cleanup(&fw);
+
+ out:
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
#72 lore raw

PATCH v2 4/8 PM / Hibernate: Disable usermode helpers right before freezing tasks

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

There is no reason to call usermodehelper_disable() before creating memory bitmaps in hibernate() and software_resume(), so call it right before freeze_processes(), in accordance with the other suspend and hibernation code. Consequently, call usermodehelper_enable() right after the thawing of tasks rather than after freeing the memory bitmaps.

---
 kernel/power/hibernate.c |   23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)
kernel/power/hibernate.c +10 -13
Index: linux/kernel/power/hibernate.c
===================================================================
@@ -611,19 +611,19 @@ int hibernate(void)
 	if (error)
 		goto Exit;
 
-	error = usermodehelper_disable();
-	if (error)
-		goto Exit;
-
 	/* Allocate memory management structures */
 	error = create_basic_memory_bitmaps();
 	if (error)
-		goto Enable_umh;
+		goto Exit;
 
 	printk(KERN_INFO "PM: Syncing filesystems ... ");
 	sys_sync();
 	printk("done.\n");
 
+	error = usermodehelper_disable();
+	if (error)
+		goto Exit;
+
 	error = freeze_processes();
 	if (error)
 		goto Free_bitmaps;
@@ -660,9 +660,8 @@ int hibernate(void)
 	freezer_test_done = false;
 
  Free_bitmaps:
-	free_basic_memory_bitmaps();
- Enable_umh:
 	usermodehelper_enable();
+	free_basic_memory_bitmaps();
  Exit:
 	pm_notifier_call_chain(PM_POST_HIBERNATION);
 	pm_restore_console();
@@ -777,15 +776,13 @@ static int software_resume(void)
 	if (error)
 		goto close_finish;
 
-	error = usermodehelper_disable();
+	error = create_basic_memory_bitmaps();
 	if (error)
 		goto close_finish;
 
-	error = create_basic_memory_bitmaps();
-	if (error) {
-		usermodehelper_enable();
+	error = usermodehelper_disable();
+	if (error)
 		goto close_finish;
-	}
 
 	pr_debug("PM: Preparing processes for restore.\n");
 	error = freeze_processes();
@@ -805,8 +802,8 @@ static int software_resume(void)
 	swsusp_free();
 	thaw_processes();
  Done:
-	free_basic_memory_bitmaps();
 	usermodehelper_enable();
+	free_basic_memory_bitmaps();
  Finish:
 	pm_notifier_call_chain(PM_POST_RESTORE);
 	pm_restore_console();
#73 lore raw

PATCH v2 5/8 PM / Sleep: Move disabling of usermode helpers to the freezer

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

The core suspend/hibernation code calls usermodehelper_disable() to avoid race conditions between the freezer and the starting of usermode helpers and each code path has to do that on its own. However, it is always called right before freeze_processes() and usermodehelper_enable() is always called right after thaw_processes(). For this reason, to avoid code duplication and to make the connection between usermodehelper_disable() and the freezer more visible, make freeze_processes() call it and remove the direct usermodehelper_disable() and usermodehelper_enable() calls from all suspend/hibernation code paths.

---
 kernel/power/hibernate.c |   11 -----------
 kernel/power/process.c   |    7 +++++++
 kernel/power/suspend.c   |    7 -------
 kernel/power/user.c      |   10 +---------
 4 files changed, 8 insertions(+), 27 deletions(-)
kernel/power/process.c +7 -0
Index: linux/kernel/power/process.c
===================================================================
@@ -16,6 +16,7 @@
 #include <linux/freezer.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
+#include <linux/kmod.h>
 
 /* 
  * Timeout for stopping processes
@@ -122,6 +123,10 @@ int freeze_processes(void)
 {
 	int error;
 
+	error = usermodehelper_disable();
+	if (error)
+		return error;
+
 	if (!pm_freezing)
 		atomic_inc(&system_freezing_cnt);
 
@@ -187,6 +192,8 @@ void thaw_processes(void)
 	} while_each_thread(g, p);
 	read_unlock(&tasklist_lock);
 
+	usermodehelper_enable();
+
 	schedule();
 	printk("done.\n");
 }
kernel/power/suspend.c +0 -7
Index: linux/kernel/power/suspend.c
===================================================================
@@ -12,7 +12,6 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/init.h>
-#include <linux/kmod.h>
 #include <linux/console.h>
 #include <linux/cpu.h>
 #include <linux/syscalls.h>
@@ -102,17 +101,12 @@ static int suspend_prepare(void)
 	if (error)
 		goto Finish;
 
-	error = usermodehelper_disable();
-	if (error)
-		goto Finish;
-
 	error = suspend_freeze_processes();
 	if (!error)
 		return 0;
 
 	suspend_stats.failed_freeze++;
 	dpm_save_failed_step(SUSPEND_FREEZE);
-	usermodehelper_enable();
  Finish:
 	pm_notifier_call_chain(PM_POST_SUSPEND);
 	pm_restore_console();
@@ -259,7 +253,6 @@ int suspend_devices_and_enter(suspend_st
 static void suspend_finish(void)
 {
 	suspend_thaw_processes();
-	usermodehelper_enable();
 	pm_notifier_call_chain(PM_POST_SUSPEND);
 	pm_restore_console();
 }
kernel/power/hibernate.c +0 -11
Index: linux/kernel/power/hibernate.c
===================================================================
@@ -16,7 +16,6 @@
 #include <linux/string.h>
 #include <linux/device.h>
 #include <linux/async.h>
-#include <linux/kmod.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
 #include <linux/mount.h>
@@ -620,10 +619,6 @@ int hibernate(void)
 	sys_sync();
 	printk("done.\n");
 
-	error = usermodehelper_disable();
-	if (error)
-		goto Exit;
-
 	error = freeze_processes();
 	if (error)
 		goto Free_bitmaps;
@@ -660,7 +655,6 @@ int hibernate(void)
 	freezer_test_done = false;
 
  Free_bitmaps:
-	usermodehelper_enable();
 	free_basic_memory_bitmaps();
  Exit:
 	pm_notifier_call_chain(PM_POST_HIBERNATION);
@@ -780,10 +774,6 @@ static int software_resume(void)
 	if (error)
 		goto close_finish;
 
-	error = usermodehelper_disable();
-	if (error)
-		goto close_finish;
-
 	pr_debug("PM: Preparing processes for restore.\n");
 	error = freeze_processes();
 	if (error) {
@@ -802,7 +792,6 @@ static int software_resume(void)
 	swsusp_free();
 	thaw_processes();
  Done:
-	usermodehelper_enable();
 	free_basic_memory_bitmaps();
  Finish:
 	pm_notifier_call_chain(PM_POST_RESTORE);
kernel/power/user.c +1 -9
Index: linux/kernel/power/user.c
===================================================================
@@ -12,7 +12,6 @@
 #include <linux/suspend.h>
 #include <linux/syscalls.h>
 #include <linux/reboot.h>
-#include <linux/kmod.h>
 #include <linux/string.h>
 #include <linux/device.h>
 #include <linux/miscdevice.h>
@@ -222,14 +221,8 @@ static long snapshot_ioctl(struct file *
 		sys_sync();
 		printk("done.\n");
 
-		error = usermodehelper_disable();
-		if (error)
-			break;
-
 		error = freeze_processes();
-		if (error)
-			usermodehelper_enable();
-		else
+		if (!error)
 			data->frozen = 1;
 		break;
 
@@ -238,7 +231,6 @@ static long snapshot_ioctl(struct file *
 			break;
 		pm_restore_gfp_mask();
 		thaw_processes();
-		usermodehelper_enable();
 		data->frozen = 0;
 		break;
 
#74 lore raw

PATCH v2 6/8 PM / Sleep: Mitigate race between the freezer and request_firmware()

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

There is a race condition between the freezer and request_firmware() such that if request_firmware() is run on one CPU and freeze_processes() is run on another CPU and usermodehelper_disable() called by it succeeds to grab umhelper_sem for writing before usermodehelper_read_trylock() called from request_firmware() acquires it for reading, the request_firmware() will fail and trigger a WARN_ON() complaining that it was called at a wrong time. However, in fact, it wasn't called at a wrong time and freeze_processes() simply happened to be executed simultaneously.

To avoid this race, at least in some cases, modify usermodehelper_read_trylock() so that it doesn't fail if the freezing of tasks has just started and hasn't been completed yet. Instead, during the freezing of tasks, it will try to freeze the task that has called it so that it can wait until user space is thawed without triggering the scary warning.

For this purpose, change usermodehelper_disabled so that it can take three different values, UMH_ENABLED (0), UMH_FREEZING and UMH_DISABLED. The first one means that usermode helpers are enabled, the last one means "hard disable" (i.e. the system is not ready for usermode helpers to be used) and the second one is reserved for the freezer. Namely, when freeze_processes() is started, it sets usermodehelper_disabled to UMH_FREEZING which tells usermodehelper_read_trylock() that it shouldn't fail just yet and should call try_to_freeze() if woken up and cannot return immediately. This way all freezable tasks that happen to call request_firmware() right before freeze_processes() is started and lose the race for umhelper_sem with it will be frozen and will sleep until thaw_processes() unsets usermodehelper_disabled. [For the non-freezable callers of request_firmware() the race for umhelper_sem against freeze_processes() is unfortunately unavoidable.]

---
 include/linux/kmod.h   |   21 +++++++++++++++++++--
 kernel/kmod.c          |   47 +++++++++++++++++++++++++++++++++++++----------
 kernel/power/process.c |    3 ++-
 3 files changed, 58 insertions(+), 13 deletions(-)
include/linux/kmod.h +19 -2
Index: linux/include/linux/kmod.h
===================================================================
@@ -110,10 +110,27 @@ call_usermodehelper(char *path, char **a
 
 extern struct ctl_table usermodehelper_table[];
 
+enum umh_disable_depth {
+	UMH_ENABLED = 0,
+	UMH_FREEZING,
+	UMH_DISABLED,
+};
+
 extern void usermodehelper_init(void);
 
-extern int usermodehelper_disable(void);
-extern void usermodehelper_enable(void);
+extern int __usermodehelper_disable(enum umh_disable_depth depth);
+extern void __usermodehelper_set_disable_depth(enum umh_disable_depth depth);
+
+static inline int usermodehelper_disable(void)
+{
+	return __usermodehelper_disable(UMH_DISABLED);
+}
+
+static inline void usermodehelper_enable(void)
+{
+	__usermodehelper_set_disable_depth(UMH_ENABLED);
+}
+
 extern int usermodehelper_read_trylock(void);
 extern long usermodehelper_read_lock_wait(long timeout);
 extern void usermodehelper_read_unlock(void);
kernel/kmod.c +37 -10

89 lines not on this page · raw message

kernel/power/process.c +2 -1
Index: linux/kernel/power/process.c
===================================================================
@@ -123,7 +123,7 @@ int freeze_processes(void)
 {
 	int error;
 
-	error = usermodehelper_disable();
+	error = __usermodehelper_disable(UMH_FREEZING);
 	if (error)
 		return error;
 
@@ -135,6 +135,7 @@ int freeze_processes(void)
 	error = try_to_freeze_tasks(true);
 	if (!error) {
 		printk("done.");
+		__usermodehelper_set_disable_depth(UMH_DISABLED);
 		oom_killer_disable();
 	}
 	printk("\n");
#75 lore raw

PATCH v2 7/8 firmware_class: Reorganize fw_create_instance()

In reply to Rafael J. Wysocki

From: Stephen Boyd <[email protected]>

Recent patches to split up the three phases of request_firmware() lead to a casting away of const in fw_create_instance(). We can avoid this cast by splitting up fw_create_instance() a bit.

Make _request_firmware_setup() return a struct fw_priv and use that struct instead of passing struct firmware to _request_firmware(). Move the uevent and device file creation bits to the loading phase and rename the function to _request_firmware_load() to better reflect its purpose.

---
 drivers/base/firmware_class.c |  135 +++++++++++++++++++-----------------------
 1 file changed, 62 insertions(+), 73 deletions(-)
drivers/base/firmware_class.c +62 -73
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -440,21 +440,19 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(const struct firmware *firmware, const char *fw_name,
+fw_create_instance(struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
 	struct device *f_dev;
-	int error;
 
 	fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
 	if (!fw_priv) {
 		dev_err(device, "%s: kmalloc failed\n", __func__);
-		error = -ENOMEM;
-		goto err_out;
+		return ERR_PTR(-ENOMEM);
 	}
 
-	fw_priv->fw = (struct firmware *)firmware;
+	fw_priv->fw = firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);
@@ -468,74 +466,37 @@ fw_create_instance(const struct firmware
 	f_dev->parent = device;
 	f_dev->class = &firmware_class;
 
-	dev_set_uevent_suppress(f_dev, true);
-
-	/* Need to pin this module until class device is destroyed */
-	__module_get(THIS_MODULE);
-
-	error = device_add(f_dev);
-	if (error) {
-		dev_err(device, "%s: device_register failed\n", __func__);
-		goto err_put_dev;
-	}
-
-	error = device_create_bin_file(f_dev, &firmware_attr_data);
-	if (error) {
-		dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
-		goto err_del_dev;
-	}
-
-	error = device_create_file(f_dev, &dev_attr_loading);
-	if (error) {
-		dev_err(device, "%s: device_create_file failed\n", __func__);
-		goto err_del_bin_attr;
-	}
-
-	if (uevent)
-		dev_set_uevent_suppress(f_dev, false);
-
 	return fw_priv;
-
-err_del_bin_attr:
-	device_remove_bin_file(f_dev, &firmware_attr_data);
-err_del_dev:
-	device_del(f_dev);
-err_put_dev:
-	put_device(f_dev);
-err_out:
-	return ERR_PTR(error);
 }
 
-static void fw_destroy_instance(struct firmware_priv *fw_priv)
-{
-	struct device *f_dev = &fw_priv->dev;
-
-	device_remove_file(f_dev, &dev_attr_loading);
-	device_remove_bin_file(f_dev, &firmware_attr_data);
-	device_unregister(f_dev);
-}
-
-static int _request_firmware_prepare(const struct firmware **firmware_p,
-				     const char *name, struct device *device)
+static struct firmware_priv *
+_request_firmware_prepare(const struct firmware **firmware_p, const char *name,
+			  struct device *device, bool uevent, bool nowait)
 {
 	struct firmware *firmware;
+	struct firmware_priv *fw_priv;
 
 	if (!firmware_p)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
 	if (!firmware) {
 		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
 			__func__);
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	if (fw_get_builtin_firmware(firmware, name)) {
 		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
-		return 0;
+		return NULL;
 	}
 
-	return 1;
+	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
+	if (IS_ERR(fw_priv)) {
+		release_firmware(firmware);
+		*firmware_p = NULL;
+	}
+	return fw_priv;
 }
 
 static void _request_firmware_cleanup(const struct firmware **firmware_p)
@@ -544,21 +505,38 @@ static void _request_firmware_cleanup(co
 	*firmware_p = NULL;
 }
 
-static int _request_firmware(const struct firmware *firmware,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait, long timeout)
+static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
+				  long timeout)
 {
-	struct firmware_priv *fw_priv;
 	int retval = 0;
+	struct device *f_dev = &fw_priv->dev;
+
+	dev_set_uevent_suppress(f_dev, true);
 
-	if (uevent)
-		dev_dbg(device, "firmware: requesting %s\n", name);
+	/* Need to pin this module until class device is destroyed */
+	__module_get(THIS_MODULE);
 
-	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
-	if (IS_ERR(fw_priv))
-		return PTR_ERR(fw_priv);
+	retval = device_add(f_dev);
+	if (retval) {
+		dev_err(f_dev, "%s: device_register failed\n", __func__);
+		goto err_put_dev;
+	}
+
+	retval = device_create_bin_file(f_dev, &firmware_attr_data);
+	if (retval) {
+		dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__);
+		goto err_del_dev;
+	}
+
+	retval = device_create_file(f_dev, &dev_attr_loading);
+	if (retval) {
+		dev_err(f_dev, "%s: device_create_file failed\n", __func__);
+		goto err_del_bin_attr;
+	}
 
 	if (uevent) {
+		dev_set_uevent_suppress(f_dev, false);
+		dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
 		if (timeout != MAX_SCHEDULE_TIMEOUT)
 			mod_timer(&fw_priv->timeout,
 				  round_jiffies_up(jiffies + timeout));
@@ -577,7 +555,13 @@ static int _request_firmware(const struc
 	fw_priv->fw = NULL;
 	mutex_unlock(&fw_lock);
 
-	fw_destroy_instance(fw_priv);
+	device_remove_file(f_dev, &dev_attr_loading);
+err_del_bin_attr:
+	device_remove_bin_file(f_dev, &firmware_attr_data);
+err_del_dev:
+	device_del(f_dev);
+err_put_dev:
+	put_device(f_dev);
 	return retval;
 }
 
@@ -600,17 +584,19 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
+	struct firmware_priv *fw_priv;
 	int ret;
 
-	ret = _request_firmware_prepare(firmware_p, name, device);
-	if (ret <= 0)
-		return ret;
+	fw_priv = _request_firmware_prepare(firmware_p, name, device, true,
+					    false);
+	if (IS_ERR_OR_NULL(fw_priv))
+		return PTR_RET(fw_priv);
 
 	ret = usermodehelper_read_trylock();
 	if (WARN_ON(ret)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 	} else {
-		ret = _request_firmware(*firmware_p, name, device, true, false,
+		ret = _request_firmware_load(fw_priv, true,
 					firmware_loading_timeout());
 		usermodehelper_read_unlock();
 	}
@@ -648,6 +634,7 @@ static int request_firmware_work_func(vo
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
+	struct firmware_priv *fw_priv;
 	long timeout;
 	int ret;
 
@@ -656,14 +643,16 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
-	if (ret <= 0)
+	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
+			fw_work->uevent, true);
+	if (IS_ERR_OR_NULL(fw_priv)) {
+		ret = PTR_RET(fw_priv);
 		goto out;
+	}
 
 	timeout = usermodehelper_read_lock_wait(firmware_loading_timeout());
 	if (timeout) {
-		ret = _request_firmware(fw, fw_work->name, fw_work->device,
-					fw_work->uevent, true, timeout);
+		ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout);
 		usermodehelper_read_unlock();
 	} else {
 		dev_dbg(fw_work->device, "firmware: %s loading timed out\n",
#76 lore raw

PATCH v2 8/8 firmware_class: Move request_firmware_nowait() to workqueues

In reply to Rafael J. Wysocki

From: Stephen Boyd <[email protected]>

Oddly enough a work_struct was already part of the firmware_work structure but nobody was using it. Instead of creating a new kthread for each request_firmware_nowait() call just schedule the work on the system workqueue. This should avoid some overhead in forking new threads when they're not strictly necessary.

---
 drivers/base/firmware_class.c |   27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)
drivers/base/firmware_class.c +7 -20
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -16,10 +16,11 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/mutex.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
 
@@ -630,19 +631,15 @@ struct firmware_work {
 	bool uevent;
 };
 
-static int request_firmware_work_func(void *arg)
+static void request_firmware_work_func(struct work_struct *work)
 {
-	struct firmware_work *fw_work = arg;
+	struct firmware_work *fw_work;
 	const struct firmware *fw;
 	struct firmware_priv *fw_priv;
 	long timeout;
 	int ret;
 
-	if (!arg) {
-		WARN_ON(1);
-		return 0;
-	}
-
+	fw_work = container_of(work, struct firmware_work, work);
 	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
 			fw_work->uevent, true);
 	if (IS_ERR_OR_NULL(fw_priv)) {
@@ -667,8 +664,6 @@ static int request_firmware_work_func(vo
 
 	module_put(fw_work->module);
 	kfree(fw_work);
-
-	return ret;
 }
 
 /**
@@ -694,7 +689,6 @@ request_firmware_nowait(
 	const char *name, struct device *device, gfp_t gfp, void *context,
 	void (*cont)(const struct firmware *fw, void *context))
 {
-	struct task_struct *task;
 	struct firmware_work *fw_work;
 
 	fw_work = kzalloc(sizeof (struct firmware_work), gfp);
@@ -713,15 +707,8 @@ request_firmware_nowait(
 		return -EFAULT;
 	}
 
-	task = kthread_run(request_firmware_work_func, fw_work,
-			    "firmware/%s", name);
-	if (IS_ERR(task)) {
-		fw_work->cont(NULL, fw_work->context);
-		module_put(fw_work->module);
-		kfree(fw_work);
-		return PTR_ERR(task);
-	}
-
+	INIT_WORK(&fw_work->work, request_firmware_work_func);
+	schedule_work(&fw_work->work);
 	return 0;
 }
 
#77 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Rafael J. Wysocki
Quoted 14 lines · Rafael J. Wysocki
Quoted 5 lines · Stephen Boyd
Quoted 4 lines · Rafael J. Wysocki

All of those use cases are in fact of the "wait for user space to be thawed and then load the firmware" type, which I believe may be handled without changing that code.

Quoted 7 lines
Quoted 4 lines

Why don't you make your kthread freezable, for one example?

Why don't you use a freezable workqueue instead?

If we put it on the freezable workqueue or make it a freezable thread will it work?

Quoted 8 lines

That depends on what exactly you want to achieve, which isn't entirely clear to me at this point.

Quoted 3 lines

In my scenario a wakeup interrupt comes in that wakes us up from suspend. Within that wakeup handler a work item is scheduled to the freezable workqueue. That work item then calls request_firmware().

That should work.

I would consider using suspend/resume notifiers, however.

Thanks,
Rafael

#78 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Kay Sievers
Quoted 26 lines · Kay Sievers
Quoted 25 lines · Greg KH
Quoted 6 lines · Christian Lamparter

This patch fixes a regression which was introduced by:
"PM: Print a warning if firmware is requested when tasks are frozen"

request_firmware_nowait does not stall in any system resume paths. Therefore, I think it is perfectly save to use request_firmware_nowait from at least the ->complete() callback.

Is there code somewhere in the kernel that wants to do this? Has commit a144c6a broken it somehow that this fix would resolve it?

Quoted 14 lines
---
 drivers/base/firmware_class.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..017e020 100644
@@ -535,7 +535,7 @@ static int _request_firmware(const struct firmware **firmware_p,
 
      read_lock_usermodehelper();
 
Quoted 8 lines
Quoted 4 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 538

Quoted 2 lines
drivers/base/firmware_class.c +1 -1
-     if (WARN_ON(usermodehelper_is_disabled())) {
+     if (WARN_ON(usermodehelper_is_disabled() && !(nowait && uevent))) {

What does uevent have to do with things here?

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

It's there to warn people that their drivers do stupid things like loading frimware during system resume, which is guaranteed not to work.

IOW, it's there very much on purpose.

Quoted 5 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Which I'm not sure why is relevant here.

Thanks,
Rafael

#79 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 5 lines · Rafael J. Wysocki
Quoted 4 lines · Kay Sievers
Quoted 1 line · Greg KH

What does uevent have to do with things here?

Quoted 8 lines
Quoted 3 lines

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

It's there to warn people that their drivers do stupid things like loading frimware during system resume, which is guaranteed not to work.

IOW, it's there very much on purpose.

Using the /sbin/hotplug is no case that needs any warning. It' such a broken model these days, that firmware loading is the least problem that occurs with it.

Quoted 7 lines
Quoted 5 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Which I'm not sure why is relevant here.

It is relevant in the sense that the firmware loader should not even know that a uevent *can* cause a usermodehelper exec() if it runs in legacy mode. The firmware loader just has no business in fiddling with the details of driver core legacy stuff. I don't think his warning makes much sense.

Kay

#80 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 25 lines · Kay Sievers
Quoted 5 lines · Rafael J. Wysocki
Quoted 4 lines · Kay Sievers
Quoted 1 line · Greg KH

What does uevent have to do with things here?

Quoted 8 lines
Quoted 3 lines

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

It's there to warn people that their drivers do stupid things like loading frimware during system resume, which is guaranteed not to work.

IOW, it's there very much on purpose.

Using the /sbin/hotplug is no case that needs any warning. It' such a broken model these days, that firmware loading is the least problem that occurs with it.

Quoted 6 lines
Quoted 5 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Quoted 7 lines
Quoted 1 line

Which I'm not sure why is relevant here.

It is relevant in the sense that the firmware loader should not even know that a uevent *can* cause a usermodehelper exec() if it runs in legacy mode. The firmware loader just has no business in fiddling with the details of driver core legacy stuff. I don't think his warning makes much sense.

But that warning actually triggers for drivers that attempt to use request_firmware() during system resume, even though /sbin/hotplug isn't used any more.

usermodehelper_is_disabled() means "we are in the middle of system power transition" rather than anything else (I agree it should be called suspend_in_progress() or something similar these days).

Thanks,
Rafael

#81 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 8 lines · Rafael J. Wysocki
Quoted 5 lines · Kay Sievers

It is relevant in the sense that the firmware loader should not even know that a uevent *can* cause a usermodehelper exec() if it runs in legacy mode. The firmware loader just has no business in fiddling with the details of driver core legacy stuff. I don't think his warning makes much sense.

Quoted 7 lines

But that warning actually triggers for drivers that attempt to use request_firmware() during system resume, even though /sbin/hotplug isn't used any more.

usermodehelper_is_disabled() means "we are in the middle of system power transition" rather than anything else (I agree it should be called suspend_in_progress() or something similar these days).

Yeah it's certainly useful to disable the exec() during suspend calls, much more than using the exec() inhibit flag for the firmware loader to throw a warning about suspend issues.

Such confusing hacks need at least a comment, that makes this very obvious. :)

Kay

#82 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 4 lines · Kay Sievers

Yeah it's certainly useful to disable the exec() during suspend calls, much more than using the exec() inhibit flag for the firmware loader to throw a warning about suspend issues.

Kay, just stop confusing the issue already.

It has *nothing* to do with exec. It has *nothing* to do with /sbin/hotplug. Stop repeating that stupid mantra. It's totally and utterly irrelevant.

You can't load firmware while the system is suspended. End of story. You can't load it using /sbin/hotplug, you can't load it with dbus, you cannot load it FULL STOP.

The filesystem you'd want to load it from may simply not be ready yet.

This has nothing to do with exec or anything else. It's very simple:
you cannot load firmware while the system isn't yet fully up. Seriously.

You need to wait to load the firmware until everything is all done, and things work.

Or you should - preferably - just have preloaded the damn thing.

Linus

#83 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 18 lines · Linus Torvalds
Quoted 4 lines · Kay Sievers

Yeah it's certainly useful to disable the exec() during suspend calls, much more than using the exec() inhibit flag for the firmware loader to throw a warning about suspend issues.

Kay, just stop confusing the issue already.

It has *nothing* to do with exec. It has *nothing* to do with /sbin/hotplug. Stop repeating that stupid mantra. It's totally and utterly irrelevant.

You can't load firmware while the system is suspended. End of story. You can't load it using /sbin/hotplug, you can't load it with dbus, you cannot load it FULL STOP.

The filesystem you'd want to load it from may simply not be ready yet.

Quoted 8 lines

This has nothing to do with exec or anything else. It's very simple:
you cannot load firmware while the system isn't yet fully up. Seriously.

You need to wait to load the firmware until everything is all done, and things work.

Or you should - preferably - just have preloaded the damn thing.

usermodehelper_is_disabled() is a flag from kmod which is all about exec(). That flag should not be used to decide if a firmware request can be issued by a driver and queued up or not. There is no usermodehelper involved at all in loading firmware today, hence we should not use that flag.

Firmware requests are asynchronous like all other driver-core uevents too, and they can be queued at any time. firmware is not special here. The kernel has no real business in warning about that, or refusing a request, just the same way it has no business to tell that other events are queued but not directly delivered to userspace at that very moment.

The firmware requests will travel into userspace just fine when stuff is ready.

Kay

#84 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 4 lines · Kay Sievers

usermodehelper_is_disabled() is a flag from kmod which is all about exec(). That flag should not be used to decide if a firmware request can be issued by a driver and queued up or not.

Sorry, you're wrong. And Rafael *told* you why you are wrong, and you ignored him and talked about "exec" some more.

So go back and read Rafael's email. The point is, you are focusing too much on the name (which Rafael talked about and said is misleading).

But it's not the name that matters, it's the concept. Loading firmware during the suspend process is broken (read Rafael's email about suggested renaming).

AND IT HAS NOTHING TO DO WITH EXEC. It doesn't matter *how* you load it. You could load it by reading a file directly from disk, AND IT WOULD STILL BE WRONG FOR THE EXACT SAME DAMN REASON.

I outlined that reason too. The filesystem may not be up and running yet.

Get it?

So please, read the emails. People actually *agree* that the name may be a bit misleading, but instead of harping on bogus issues, just read the emails from people like Rafael about it.

So STOP with this idiocy already. It has nothing to do with exec. Seriously. Get that through your head already.

Linus

#85 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 22 lines · Linus Torvalds
Quoted 4 lines · Kay Sievers

usermodehelper_is_disabled() is a flag from kmod which is all about exec(). That flag should not be used to decide if a firmware request can be issued by a driver and queued up or not.

Sorry, you're wrong. And Rafael *told* you why you are wrong, and you ignored him and talked about "exec" some more.

So go back and read Rafael's email. The point is, you are focusing too much on the name (which Rafael talked about and said is misleading).

But it's not the name that matters, it's the concept. Loading firmware during the suspend process is broken (read Rafael's email about suggested renaming).

AND IT HAS NOTHING TO DO WITH EXEC. It doesn't matter *how* you load it. You could load it by reading a file directly from disk, AND IT WOULD STILL BE WRONG FOR THE EXACT SAME DAMN REASON.

I outlined that reason too. The filesystem may not be up and running yet.

Quoted 8 lines

Get it?

So please, read the emails. People actually *agree* that the name may be a bit misleading, but instead of harping on bogus issues, just read the emails from people like Rafael about it.

So STOP with this idiocy already. It has nothing to do with exec. Seriously. Get that through your head already.

I do NOT talk about exec, I talk about a REQUEST which can be QUEUED just fine, but which the kernel refuses to QUEUE, even when it will not harm anything. That check and warning is wrong.

Kay

#86 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 30 lines · Kay Sievers
Quoted 28 lines · Linus Torvalds
Quoted 4 lines · Kay Sievers

usermodehelper_is_disabled() is a flag from kmod which is all about exec(). That flag should not be used to decide if a firmware request can be issued by a driver and queued up or not.

Sorry, you're wrong. And Rafael *told* you why you are wrong, and you ignored him and talked about "exec" some more.

So go back and read Rafael's email. The point is, you are focusing too much on the name (which Rafael talked about and said is misleading).

But it's not the name that matters, it's the concept. Loading firmware during the suspend process is broken (read Rafael's email about suggested renaming).

AND IT HAS NOTHING TO DO WITH EXEC. It doesn't matter *how* you load it. You could load it by reading a file directly from disk, AND IT WOULD STILL BE WRONG FOR THE EXACT SAME DAMN REASON.

I outlined that reason too. The filesystem may not be up and running yet.

Get it?

So please, read the emails. People actually *agree* that the name may be a bit misleading, but instead of harping on bogus issues, just read the emails from people like Rafael about it.

Quoted 6 lines
Quoted 2 lines

So STOP with this idiocy already. It has nothing to do with exec. Seriously. Get that through your head already.

I do NOT talk about exec, I talk about a REQUEST which can be QUEUED just fine, but which the kernel refuses to QUEUE, even when it will not harm anything. That check and warning is wrong.

In simpler words: request_firmware() does nothing but queue up an asynchrounous uevent in a socket, that always workls, there is no need to refuse that in the kernel and throw any warning at that point in time.

Kay

#87 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Kay Sievers
Quoted 4 lines · Kay Sievers

I do NOT talk about exec, I talk about a REQUEST which can be QUEUED just fine, but which the kernel refuses to QUEUE, even when it will not harm anything. That check and warning is wrong.

No. request_firmware() is synchronous. People call it, and then use the loaded firmware immediately after success.

And no, it's not at all clear that you can just say "we'll just wait". The device that wants the firmware may be something that can be brought up asynchronously (so "wait until everything is ok" can work), BUT IT IS NOT AT ALL GUARANTEED.

Seriously, you don't know what you are talking about. We *used* to just wait, and have a thirty-second timeout etc instead of failing aggressively. It often worked.

And then quite often it DID NOT WORK, and it caused 30-second delays at resume time (maybe it was 60s, I forget). Because it turns out that people were waiting for that device to get through its resume until the resume was completed. And the system wasn't up. Deadlock.

So a 30-second "let's try delaying this" has been done. It didn't work. Stop arguing for it, when you clearly don't know what the problems were. The 30-second delay is when the user just says "resume didn't work" and has long since pressed the power button.

So fail fast, fail hard. DO NOT REQUEST FIRMWARE WHILE THE SYSTEM IS SUSPENDED.

It really is that simple. Your "let's just queue it up" is crap. Stop arguing for it, we used to do it, it "worked" much of the time, and when it didn't work it killed the system.

Which is why it now fails with a big warning. EXACTLY SO THAT DRIVER WRITERS WOULD KNOW NOT TO DO THAT BROKEN THING.

I'm shouting at you, because you are arguing from ignorance. Stop doing it. "Works most of the time" just isn't good enough. It needs to *always* work, and the way to do that is to just do firmware load when the system is up and running, not when it is suspended.

If a driver is ok with being delayed for a firmware load, it should just do its resume *without* doing the firmware load at all, and schedule one serparately for later. But no, we can not just say "let's delay request_firmware()", because we used to do that and it was shown to not work.

Comprende?

Linus

#88 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Rafael J. Wysocki
Quoted 34 lines · Rafael J. Wysocki
Quoted 25 lines · Kay Sievers
Quoted 5 lines · Rafael J. Wysocki
Quoted 4 lines · Kay Sievers
Quoted 1 line · Greg KH

What does uevent have to do with things here?

Quoted 8 lines
Quoted 3 lines

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

It's there to warn people that their drivers do stupid things like loading frimware during system resume, which is guaranteed not to work.

IOW, it's there very much on purpose.

Using the /sbin/hotplug is no case that needs any warning. It' such a broken model these days, that firmware loading is the least problem that occurs with it.

Quoted 6 lines
Quoted 5 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Quoted 7 lines
Quoted 1 line

Which I'm not sure why is relevant here.

It is relevant in the sense that the firmware loader should not even know that a uevent *can* cause a usermodehelper exec() if it runs in legacy mode. The firmware loader just has no business in fiddling with the details of driver core legacy stuff. I don't think his warning makes much sense.

Quoted 4 lines

But that warning actually triggers for drivers that attempt to use request_firmware() during system resume, even though /sbin/hotplug isn't used any more.

I agree with Rafael about why the warning and the bail out is required, including the part about the races with freezer which he explained in his other mail. These problems have already been well documented too. (See Documentation/power/freezing-of-tasks.txt).

Quoted 4 lines

usermodehelper_is_disabled() means "we are in the middle of system power transition" rather than anything else (I agree it should be called suspend_in_progress() or something similar these days).

How about this patch then?

---

From: Srivatsa S. Bhat <[email protected]>
Subject: PM/firmware loader: Use better name for usermodehelper_is_disabled()

Rafael J. Wysocki <[email protected]> wrote:

| usermodehelper_is_disabled() means "we are in the middle of system power
| transition" rather than anything else (I agree it should be called
| suspend_in_progress() or something similar these days).

But simply renaming usermodehelper_is_disabled() to suspend_in_progress() isn't the best thing to do since that would be misleading because suspend transitions are begun much before usermodehelpers are disabled.

Apart from that, we don't want people to suddenly start abusing this function in future in a totally different context to check if suspend is in progress.

So, add an alias specific to firmware loaders alone, that will internally call usermodehelpers_is_disabled().

---

 drivers/base/firmware_class.c |   12 +++++++++++-
 1 files changed, 11 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +11 -1
index 6c9387d..9e401e1 100644
@@ -510,6 +510,8 @@ static void fw_destroy_instance(struct firmware_priv *fw_priv)
 	device_unregister(f_dev);
 }
 
+#define suspend_in_progress()	usermodehelper_is_disabled()
+
 static int _request_firmware(const struct firmware **firmware_p,
 			     const char *name, struct device *device,
 			     bool uevent, bool nowait)
@@ -535,7 +537,15 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	/*
+	 * It is wrong to request firmware when the system is suspended,
+	 * because it simply won't work reliably. Also, it can cause races with
+	 * the freezer, leading to freezing failures. So check if the system is
+	 * in a state which is unsuitable for requesting firmware (because the
+	 * system is suspended or not yet fully resumed) and bail out early if
+	 * needed.
+	 */
+	if (WARN_ON(suspend_in_progress())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
 		goto out;
#89 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 88 lines · Srivatsa S. Bhat
Quoted 34 lines · Rafael J. Wysocki
Quoted 25 lines · Kay Sievers
Quoted 5 lines · Rafael J. Wysocki
Quoted 4 lines · Kay Sievers
Quoted 1 line · Greg KH

What does uevent have to do with things here?

Quoted 8 lines
Quoted 3 lines

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

It's there to warn people that their drivers do stupid things like loading frimware during system resume, which is guaranteed not to work.

IOW, it's there very much on purpose.

Using the /sbin/hotplug is no case that needs any warning. It' such a broken model these days, that firmware loading is the least problem that occurs with it.

Quoted 6 lines
Quoted 5 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Quoted 7 lines
Quoted 1 line

Which I'm not sure why is relevant here.

It is relevant in the sense that the firmware loader should not even know that a uevent *can* cause a usermodehelper exec() if it runs in legacy mode. The firmware loader just has no business in fiddling with the details of driver core legacy stuff. I don't think his warning makes much sense.

Quoted 4 lines

But that warning actually triggers for drivers that attempt to use request_firmware() during system resume, even though /sbin/hotplug isn't used any more.

I agree with Rafael about why the warning and the bail out is required, including the part about the races with freezer which he explained in his other mail. These problems have already been well documented too. (See Documentation/power/freezing-of-tasks.txt).

Quoted 4 lines

usermodehelper_is_disabled() means "we are in the middle of system power transition" rather than anything else (I agree it should be called suspend_in_progress() or something similar these days).

How about this patch then?

---

From: Srivatsa S. Bhat <[email protected]>
Subject: PM/firmware loader: Use better name for usermodehelper_is_disabled()

Rafael J. Wysocki <[email protected]> wrote:

| usermodehelper_is_disabled() means "we are in the middle of system power
| transition" rather than anything else (I agree it should be called
| suspend_in_progress() or something similar these days).

But simply renaming usermodehelper_is_disabled() to suspend_in_progress() isn't the best thing to do since that would be misleading because suspend transitions are begun much before usermodehelpers are disabled.

Apart from that, we don't want people to suddenly start abusing this function in future in a totally different context to check if suspend is in progress.

So, add an alias specific to firmware loaders alone, that will internally call usermodehelpers_is_disabled().

---

 drivers/base/firmware_class.c |   12 +++++++++++-
 1 files changed, 11 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..9e401e1 100644

drivers/base/firmware_class.c static void fw_destroy_instance(struct firmware_priv *fw_priv) · line 513

Quoted 4 lines
drivers/base/firmware_class.c +1 -0
@@ -510,6 +510,8 @@ static void fw_destroy_instance(struct firmware_priv *fw_priv)
 	device_unregister(f_dev);
 }
 
+#define suspend_in_progress()	usermodehelper_is_disabled()

This looks like an overstretch to me. I think a comment would be sufficient.

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 542

Quoted 12 lines
drivers/base/firmware_class.c +4 -1
+
 static int _request_firmware(const struct firmware **firmware_p,
 			     const char *name, struct device *device,
 			     bool uevent, bool nowait)
@@ -535,7 +537,15 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	/*
+	 * It is wrong to request firmware when the system is suspended,
+	 * because it simply won't work reliably.

In fact, it won't work at all.

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 544

Quoted 2 lines
drivers/base/firmware_class.c +2 -0
+	 Also, it can cause races with
+	 * the freezer, leading to freezing failures.

It actually is worse than that too. It may cause a user space process to run when we think we have frozen user space and _that_ may lead to all kinds of interesting breakage.

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 546

Quoted 4 lines
drivers/base/firmware_class.c +3 -0
 	 * So check if the system is
+	 * in a state which is unsuitable for requesting firmware (because the
+	 * system is suspended or not yet fully resumed) and bail out early if
+	 * needed.

And here I'd explain why usermodehelper_is_disabled() is used for that.

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 551

Quoted 5 lines
drivers/base/firmware_class.c +2 -0
+	 */
+	if (WARN_ON(suspend_in_progress())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
 		goto out;

Thanks,
Rafael

#90 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 92 lines · Rafael J. Wysocki
Quoted 91 lines · Srivatsa S. Bhat
Quoted 34 lines · Rafael J. Wysocki
Quoted 25 lines · Kay Sievers
Quoted 5 lines · Rafael J. Wysocki
Quoted 4 lines · Kay Sievers
Quoted 1 line · Greg KH

What does uevent have to do with things here?

Quoted 8 lines
Quoted 3 lines

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

It's there to warn people that their drivers do stupid things like loading frimware during system resume, which is guaranteed not to work.

IOW, it's there very much on purpose.

Using the /sbin/hotplug is no case that needs any warning. It' such a broken model these days, that firmware loading is the least problem that occurs with it.

Quoted 6 lines
Quoted 5 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Quoted 7 lines
Quoted 1 line

Which I'm not sure why is relevant here.

It is relevant in the sense that the firmware loader should not even know that a uevent *can* cause a usermodehelper exec() if it runs in legacy mode. The firmware loader just has no business in fiddling with the details of driver core legacy stuff. I don't think his warning makes much sense.

Quoted 4 lines

But that warning actually triggers for drivers that attempt to use request_firmware() during system resume, even though /sbin/hotplug isn't used any more.

I agree with Rafael about why the warning and the bail out is required, including the part about the races with freezer which he explained in his other mail. These problems have already been well documented too. (See Documentation/power/freezing-of-tasks.txt).

Quoted 4 lines

usermodehelper_is_disabled() means "we are in the middle of system power transition" rather than anything else (I agree it should be called suspend_in_progress() or something similar these days).

How about this patch then?

---

From: Srivatsa S. Bhat <[email protected]>
Subject: PM/firmware loader: Use better name for usermodehelper_is_disabled()

Rafael J. Wysocki <[email protected]> wrote:

| usermodehelper_is_disabled() means "we are in the middle of system power
| transition" rather than anything else (I agree it should be called
| suspend_in_progress() or something similar these days).

But simply renaming usermodehelper_is_disabled() to suspend_in_progress() isn't the best thing to do since that would be misleading because suspend transitions are begun much before usermodehelpers are disabled.

Apart from that, we don't want people to suddenly start abusing this function in future in a totally different context to check if suspend is in progress.

So, add an alias specific to firmware loaders alone, that will internally call usermodehelpers_is_disabled().

---

 drivers/base/firmware_class.c |   12 +++++++++++-
 1 files changed, 11 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..9e401e1 100644
@@ -510,6 +510,8 @@ static void fw_destroy_instance(struct firmware_priv *fw_priv)
 	device_unregister(f_dev);
 }
 
Quoted 3 lines

drivers/base/firmware_class.c static void fw_destroy_instance(struct firmware_priv *fw_priv) · line 513

Quoted 1 line
drivers/base/firmware_class.c +1 -0
+#define suspend_in_progress()	usermodehelper_is_disabled()

This looks like an overstretch to me. I think a comment would be sufficient.

On second thoughts... I agree, a comment is good enough.

Quoted 16 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 542

Quoted 13 lines
drivers/base/firmware_class.c +4 -1
+
 static int _request_firmware(const struct firmware **firmware_p,
 			     const char *name, struct device *device,
 			     bool uevent, bool nowait)
@@ -535,7 +537,15 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	/*
+	 * It is wrong to request firmware when the system is suspended,
+	 * because it simply won't work reliably.

In fact, it won't work at all.

Quoted 7 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 544

Quoted 2 lines
drivers/base/firmware_class.c +2 -0
+	 Also, it can cause races with
+	 * the freezer, leading to freezing failures.

It actually is worse than that too. It may cause a user space process to run when we think we have frozen user space and _that_ may lead to all kinds of interesting breakage.

Oh, yes! That would be really dreadful!

Quoted 7 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 548

Quoted 4 lines
drivers/base/firmware_class.c +3 -0
 	 * So check if the system is
+	 * in a state which is unsuitable for requesting firmware (because the
+	 * system is suspended or not yet fully resumed) and bail out early if
+	 * needed.

And here I'd explain why usermodehelper_is_disabled() is used for that.

OK

Quoted 6 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p,

Quoted 5 lines
drivers/base/firmware_class.c +2 -0
+	 */
+	if (WARN_ON(suspend_in_progress())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
 		goto out;

So here is the updated patch:
(I know its a bit verbose, but given that it is causing a considerable amount of confusion, may be a proper comment with good explanation is worthwhile).

---

From: Srivatsa S. Bhat <[email protected]>
Subject: PM/firmware loader: Explain why usermodehelper_is_disabled() check is used

Rafael J. Wysocki <[email protected]> wrote:

| usermodehelper_is_disabled() means "we are in the middle of system power
| transition" rather than anything else (I agree it should be called
| suspend_in_progress() or something similar these days).

But instead of renaming usermodehelper_is_disabled(), add a comment explaining its importance and also why the warning and bail out at _request_firmware() makes sense.

---

 drivers/base/firmware_class.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
drivers/base/firmware_class.c +16 -0
index 6c9387d..9199e3e 100644
@@ -535,6 +535,22 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
+	/*
+	 * It is wrong to request firmware when the system is suspended,
+	 * because it simply won't work. Also, it can cause races with
+	 * the freezer, leading to freezing failures. Worse than that,
+	 * it may even cause a user space process to run when we think
+	 * we have frozen the user space! - and that can lead to all kinds
+	 * of interesting breakage..
+	 *
+	 * So check if the system is in a state which is unsuitable for
+	 * requesting firmware (because it is suspended or not yet fully
+	 * resumed) and bail out early if needed.
+	 * Usermodehelpers are disabled at the beginning of suspend, before
+	 * freezing tasks and re-enabled only towards the end of resume, after
+	 * thawing tasks, when it is safe. So all we need to do here is ensure
+	 * that we don't request firmware when usermodehelpers are disabled.
+	 */
 	if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
#91 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 164 lines · Srivatsa S. Bhat
Quoted 92 lines · Rafael J. Wysocki
Quoted 91 lines · Srivatsa S. Bhat
Quoted 34 lines · Rafael J. Wysocki
Quoted 25 lines · Kay Sievers
Quoted 5 lines · Rafael J. Wysocki
Quoted 4 lines · Kay Sievers
Quoted 1 line · Greg KH

What does uevent have to do with things here?

Quoted 8 lines
Quoted 3 lines

I don't think that the firmware loader should care about the usermodehelper at all, and that stuff fiddling should just be removed from the firmware class.

It's there to warn people that their drivers do stupid things like loading frimware during system resume, which is guaranteed not to work.

IOW, it's there very much on purpose.

Using the /sbin/hotplug is no case that needs any warning. It' such a broken model these days, that firmware loading is the least problem that occurs with it.

Quoted 6 lines
Quoted 5 lines

Forking /sbin/hotplug is disabled by default, it is a broken concept, and it cannot work reliably on today's systems.

Firmware is not loaded by /sbin/hotplug since many years, but by udev or whatever service handles uevents, like ueventd on android.

Quoted 7 lines
Quoted 1 line

Which I'm not sure why is relevant here.

It is relevant in the sense that the firmware loader should not even know that a uevent *can* cause a usermodehelper exec() if it runs in legacy mode. The firmware loader just has no business in fiddling with the details of driver core legacy stuff. I don't think his warning makes much sense.

Quoted 4 lines

But that warning actually triggers for drivers that attempt to use request_firmware() during system resume, even though /sbin/hotplug isn't used any more.

I agree with Rafael about why the warning and the bail out is required, including the part about the races with freezer which he explained in his other mail. These problems have already been well documented too. (See Documentation/power/freezing-of-tasks.txt).

Quoted 4 lines

usermodehelper_is_disabled() means "we are in the middle of system power transition" rather than anything else (I agree it should be called suspend_in_progress() or something similar these days).

How about this patch then?

---

From: Srivatsa S. Bhat <[email protected]>
Subject: PM/firmware loader: Use better name for usermodehelper_is_disabled()

Rafael J. Wysocki <[email protected]> wrote:

| usermodehelper_is_disabled() means "we are in the middle of system power
| transition" rather than anything else (I agree it should be called
| suspend_in_progress() or something similar these days).

But simply renaming usermodehelper_is_disabled() to suspend_in_progress() isn't the best thing to do since that would be misleading because suspend transitions are begun much before usermodehelpers are disabled.

Apart from that, we don't want people to suddenly start abusing this function in future in a totally different context to check if suspend is in progress.

So, add an alias specific to firmware loaders alone, that will internally call usermodehelpers_is_disabled().

---

 drivers/base/firmware_class.c |   12 +++++++++++-
 1 files changed, 11 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +0 -0
index 6c9387d..9e401e1 100644
@@ -510,6 +510,8 @@ static void fw_destroy_instance(struct firmware_priv *fw_priv)
 	device_unregister(f_dev);
 }
 
Quoted 3 lines

drivers/base/firmware_class.c static void fw_destroy_instance(struct firmware_priv *fw_priv) · line 513

Quoted 1 line
drivers/base/firmware_class.c +1 -0
+#define suspend_in_progress()	usermodehelper_is_disabled()

This looks like an overstretch to me. I think a comment would be sufficient.

On second thoughts... I agree, a comment is good enough.

Quoted 16 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 542

Quoted 13 lines
drivers/base/firmware_class.c +4 -1
+
 static int _request_firmware(const struct firmware **firmware_p,
 			     const char *name, struct device *device,
 			     bool uevent, bool nowait)
@@ -535,7 +537,15 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	/*
+	 * It is wrong to request firmware when the system is suspended,
+	 * because it simply won't work reliably.

In fact, it won't work at all.

Quoted 7 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 544

Quoted 2 lines
drivers/base/firmware_class.c +2 -0
+	 Also, it can cause races with
+	 * the freezer, leading to freezing failures.

It actually is worse than that too. It may cause a user space process to run when we think we have frozen user space and _that_ may lead to all kinds of interesting breakage.

Oh, yes! That would be really dreadful!

Quoted 7 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p, · line 548

Quoted 4 lines
drivers/base/firmware_class.c +3 -0
 	 * So check if the system is
+	 * in a state which is unsuitable for requesting firmware (because the
+	 * system is suspended or not yet fully resumed) and bail out early if
+	 * needed.

And here I'd explain why usermodehelper_is_disabled() is used for that.

OK

Quoted 6 lines

drivers/base/firmware_class.c static int _request_firmware(const struct firmware **firmware_p,

Quoted 5 lines
drivers/base/firmware_class.c +2 -0
+	 */
+	if (WARN_ON(suspend_in_progress())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
 		goto out;

So here is the updated patch:
(I know its a bit verbose, but given that it is causing a considerable amount of confusion, may be a proper comment with good explanation is worthwhile).

---

From: Srivatsa S. Bhat <[email protected]>
Subject: PM/firmware loader: Explain why usermodehelper_is_disabled() check is used

Rafael J. Wysocki <[email protected]> wrote:

| usermodehelper_is_disabled() means "we are in the middle of system power
| transition" rather than anything else (I agree it should be called
| suspend_in_progress() or something similar these days).
Quoted 5 lines

But instead of renaming usermodehelper_is_disabled(), add a comment explaining its importance and also why the warning and bail out at _request_firmware() makes sense.

That's fine by me.

If no one objects, I'll apply it.

Thanks,
Rafael

Quoted 36 lines

---

 drivers/base/firmware_class.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
drivers/base/firmware_class.c +16 -0
index 6c9387d..9199e3e 100644
@@ -535,6 +535,22 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
+	/*
+	 * It is wrong to request firmware when the system is suspended,
+	 * because it simply won't work. Also, it can cause races with
+	 * the freezer, leading to freezing failures. Worse than that,
+	 * it may even cause a user space process to run when we think
+	 * we have frozen the user space! - and that can lead to all kinds
+	 * of interesting breakage..
+	 *
+	 * So check if the system is in a state which is unsuitable for
+	 * requesting firmware (because it is suspended or not yet fully
+	 * resumed) and bail out early if needed.
+	 * Usermodehelpers are disabled at the beginning of suspend, before
+	 * freezing tasks and re-enabled only towards the end of resume, after
+	 * thawing tasks, when it is safe. So all we need to do here is ensure
+	 * that we don't request firmware when usermodehelpers are disabled.
+	 */
 	if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
#92 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 3 lines

That's fine by me.

If no one objects, I'll apply it.

Congrats on that nice, long and "obvious" explanation. Really, what do you mean by the "end of resume"? As far as I know it is NOT "after" all ->resume calls have finished, in fact the usermodehelper is still disabled during ->complete! Maybe a hint about "after/before function X() has finished/starts"

Furthermore, I wonder what happends about built-in modules for devices that need a firmware at probe [e.g.: different firmware support different special features. This is quite common for wifi stuff. e.g.: Special firmwares for APs]?

Because As far as I know "driver_init();" is called before "usermodehelper_enable();" in "do_basic_setup()". So, theoretical these people should see the same WARNINGs and read the comment about suspend/resume and I'm sure they will be thinking: WTF!

Regards,
Chr

Quoted 32 lines
Quoted 32 lines

---

 drivers/base/firmware_class.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
drivers/base/firmware_class.c +16 -0
index 6c9387d..9199e3e 100644
@@ -535,6 +535,22 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
+	/*
+	 * It is wrong to request firmware when the system is suspended,
+	 * because it simply won't work. Also, it can cause races with
+	 * the freezer, leading to freezing failures. Worse than that,
+	 * it may even cause a user space process to run when we think
+	 * we have frozen the user space! - and that can lead to all kinds
+	 * of interesting breakage..
+	 *
+	 * So check if the system is in a state which is unsuitable for
+	 * requesting firmware (because it is suspended or not yet fully
+	 * resumed) and bail out early if needed.
+	 * Usermodehelpers are disabled at the beginning of suspend, before
+	 * freezing tasks and re-enabled only towards the end of resume, after
+	 * thawing tasks, when it is safe. So all we need to do here is ensure
+	 * that we don't request firmware when usermodehelpers are disabled.
+	 */
 	if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
#93 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

Quoted 6 lines · Christian Lamparter

Because As far as I know "driver_init();" is called before "usermodehelper_enable();" in "do_basic_setup()". So, theoretical these people should see the same WARNINGs and read the comment about suspend/resume and I'm sure they will be thinking: WTF!

Just make the explanation be simple:

"The system is not 8in a fully initialized state, and you cannot
load firmware at this point. User mode is not running, not all devices have necessarily been initialized, user space applications may or may not be frozen, and the filesystem may or may not exist yet. Loading firmware is buggy under these circumstances. Go away"

Don't try to go into any details. The "you can race with freezer" is too much detail. Even "suspend/resume" is too much detail. The fact is, the system just isn't ready yet. The details are almost entirely irrelevant, at best they can be examples of some things that can cause problems.

Linus

#94 lore raw

Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available

In reply to Christian Lamparter
Quoted 10 lines · Christian Lamparter
Quoted 3 lines

That's fine by me.

If no one objects, I'll apply it.

Congrats on that nice, long and "obvious" explanation. Really, what do you mean by the "end of resume"? As far as I know it is NOT "after" all ->resume calls have finished, in fact the usermodehelper is still disabled during ->complete! Maybe a hint about "after/before function X() has finished/starts"

Unfortunately, it appears that you missed the context implied in my comment. Probably you thought that we were referring only to device suspend and resume. But actually we were talking about the entire system suspend and resume, in which device suspend and resume is just one part.

I thought that point was quite clear from the words used in the comment:
"It is wrong to request firmware... system is suspended... So check if the system is in a state which is unsuitable...". Moreover the comment also mentions the freezing of tasks etc, which are not part of device suspend and resume, and (hence) was another indication that we were talking of suspend/resume at a higher level - the entire suspend/resume sequence, not just a part of it.

With this clarified, I am pretty sure the comment will make more sense. Also, the part of the code where usermodehelpers are disabled/enabled during suspend/resume sequence is also pretty straightforward, and matches perfectly with what I described in the comment:

kernel/power/suspend.c (or hibernate.c for hibernation sequence):

static int suspend_prepare(void)

{
	...
	pm_notifier_call_chain(PM_SUSPEND_PREPARE);
	...
	usermodehelper_disable();
	...
	suspend_freeze_processes();
	...
}

static void suspend_finish(void)

{
        suspend_thaw_processes();
        usermodehelper_enable();
        pm_notifier_call_chain(PM_POST_SUSPEND);
	...
}

int enter_state(suspend_state_t state)

{
	...
        pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
	suspend_prepare();
	...
        pr_debug("PM: Entering %s sleep\n", pm_states[state]);
	...
	suspend_devices_and_enter(state);
	...
 Finish:
        pr_debug("PM: Finishing wakeup.\n");
        suspend_finish();
	...
}

[Anyway, this comment patch is probably moot at the moment, with Rafael's new patches...]

Quoted 35 lines

Regards,
Chr

Quoted 32 lines
Quoted 32 lines

---

 drivers/base/firmware_class.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
drivers/base/firmware_class.c +16 -0
index 6c9387d..9199e3e 100644
@@ -535,6 +535,22 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
+	/*
+	 * It is wrong to request firmware when the system is suspended,
+	 * because it simply won't work. Also, it can cause races with
+	 * the freezer, leading to freezing failures. Worse than that,
+	 * it may even cause a user space process to run when we think
+	 * we have frozen the user space! - and that can lead to all kinds
+	 * of interesting breakage..
+	 *
+	 * So check if the system is in a state which is unsuitable for
+	 * requesting firmware (because it is suspended or not yet fully
+	 * resumed) and bail out early if needed.
+	 * Usermodehelpers are disabled at the beginning of suspend, before
+	 * freezing tasks and re-enabled only towards the end of resume, after
+	 * thawing tasks, when it is safe. So all we need to do here is ensure
+	 * that we don't request firmware when usermodehelpers are disabled.
+	 */
 	if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
#95 lore raw
In reply to Srivatsa S. Bhat
Quoted 20 lines · Srivatsa S. Bhat

[ Adding more Cc's. ]

Quoted 15 lines · Christian Lamparter

During resume, the userhelper might not be available. However for drivers which use the request_firmware_nowait interface, this will only lead to a pointless WARNING and a device which no longer works after the resume [since it couldn't get the firmware, because the userhelper was not available to take the request].

In order to solve this "chicken or egg" dilemma, the code now retries _nowait requests at one second intervals until the "loading_timeout" time is up.

---
I'm aware about the previous "request_firmware* in probe" discussions. Unfortunately, the hardware needs firmware so there is no other way around it. So please, I just wanted to know what the general opinion about the idea behind this patch is.

BTW, I wonder what comments on this patch were posted?

Rafael

Quoted 71 lines
Quoted 61 lines
---
 drivers/base/firmware_class.c |   24 +++++++++++++++++++++++-
 1 files changed, 23 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +23 -1
index 6c9387d..9f70096 100644
@@ -19,6 +19,7 @@
 #include <linux/kthread.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
+#include <linux/delay.h>
 #include <linux/slab.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
@@ -535,6 +536,11 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
+	if (nowait && usermodehelper_is_disabled()) {
+		retval = -EBUSY;
+		goto out;
+	}
+
 	if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
@@ -633,7 +639,7 @@ static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
-	int ret;
+	int ret, timeout = loading_timeout;
 
 	if (!arg) {
 		WARN_ON(1);
@@ -642,6 +648,22 @@ static int request_firmware_work_func(void *arg)
 
 	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+
+	while (ret == -EBUSY) {
+		/*
+		 * Try to retrieve the firmware within the loading timeout.
+		 * To stick with the loading timeout convention from above:
+		 *	loading_timeout = 0 means 'try forever' as well.
+		 */
+
+		msleep(1000);
+		ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+					fw_work->uevent, true);
+
+		if (timeout != 0 && timeout-- == 1)
 
+			break;
 
+	};
 
+

drivers/base/firmware_class.c static int request_firmware_work_func(void *arg) · line 669

Quoted 3 lines
drivers/base/firmware_class.c +0 -0
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#96 lore raw
Quoted 12 lines · On Friday 16 March 2012 23:19:53 Rafael J. Wysocki wrote:
Quoted 12 lines · Christian Lamparter
Quoted 11 lines

During resume, the userhelper might not be available. However for drivers which use the request_firmware_nowait interface, this will only lead to a pointless WARNING and a device which no longer works after the resume [since it couldn't get the firmware, because the userhelper was not available to take the request].

In order to solve this "chicken or egg" dilemma, the code now retries _nowait requests at one second intervals until the "loading_timeout" time is up.

Quoted 7 lines
Quoted 5 lines
Quoted 5 lines

---
I'm aware about the previous "request_firmware* in probe" discussions. Unfortunately, the hardware needs firmware so there is no other way around it. So please, I just wanted to know what the general opinion about the idea behind this patch is.

BTW, I wonder what comments on this patch were posted?

Only Alan Cox was kind enough to drop me a few words.

Why? Do you think it is actually sane from a specific POV? [Don't tell me you do :D !]

Really, it wasn't until:
"[PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available"
https://lkml.org/lkml/2012/3/9/612
that things started to pick up.

Regards,
Chr

Quoted 65 lines
Quoted 65 lines
Quoted 64 lines
---
 drivers/base/firmware_class.c |   24 +++++++++++++++++++++++-
 1 files changed, 23 insertions(+), 1 deletions(-)
drivers/base/firmware_class.c +23 -1
index 6c9387d..9f70096 100644
@@ -19,6 +19,7 @@
 #include <linux/kthread.h>
 #include <linux/highmem.h>
 #include <linux/firmware.h>
+#include <linux/delay.h>
 #include <linux/slab.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
@@ -535,6 +536,11 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 	read_lock_usermodehelper();
 
+	if (nowait && usermodehelper_is_disabled()) {
+		retval = -EBUSY;
+		goto out;
+	}
+
 	if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
@@ -633,7 +639,7 @@ static int request_firmware_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
-	int ret;
+	int ret, timeout = loading_timeout;
 
 	if (!arg) {
 		WARN_ON(1);
@@ -642,6 +648,22 @@ static int request_firmware_work_func(void *arg)
 
 	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+
+	while (ret == -EBUSY) {
+		/*
+		 * Try to retrieve the firmware within the loading timeout.
+		 * To stick with the loading timeout convention from above:
+		 *	loading_timeout = 0 means 'try forever' as well.
+		 */
+
+		msleep(1000);
+		ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+					fw_work->uevent, true);
+
+		if (timeout != 0 && timeout-- == 1)
 
+			break;
 
+	};
 
+
drivers/base/firmware_class.c +0 -0
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
#97 lore raw
Quoted 19 lines · Christian Lamparter
Quoted 18 lines · On Friday 16 March 2012 23:19:53 Rafael J. Wysocki wrote:
Quoted 17 lines · Christian Lamparter
Quoted 16 lines

During resume, the userhelper might not be available. However for drivers which use the request_firmware_nowait interface, this will only lead to a pointless WARNING and a device which no longer works after the resume [since it couldn't get the firmware, because the userhelper was not available to take the request].

In order to solve this "chicken or egg" dilemma, the code now retries _nowait requests at one second intervals until the "loading_timeout" time is up.

---
I'm aware about the previous "request_firmware* in probe" discussions. Unfortunately, the hardware needs firmware so there is no other way around it. So please, I just wanted to know what the general opinion about the idea behind this patch is.

Quoted 5 lines
Quoted 1 line

BTW, I wonder what comments on this patch were posted?

Only Alan Cox was kind enough to drop me a few words.

Why? Do you think it is actually sane from a specific POV? [Don't tell me you do :D !]

I don't think it's really wrong.

I agree that the WARN_ON() isn't really useful in the request_firmware_nowait() case, because the user of that doesn't really know when exactly the firmware is going to be requested, so it can't really do anything about the warning.

Moreover, failures of request_firmware_nowait() just because it happens to race with system suspend (or something of that kind), just because of "bad" timing, aren't really useful either.

So, I think it makes sense for it to wait until the firmware can be loaded.

I'd do that a bit differently, though, for example like in the appended patch (untested).

Thanks,
Rafael

---
 drivers/base/firmware_class.c |   31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)
drivers/base/firmware_class.c +27 -4
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -20,6 +20,7 @@
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
 
@@ -535,10 +536,31 @@ static int _request_firmware(const struc
 
 	read_lock_usermodehelper();
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	if (nowait) {
+		int limit = loading_timeout * MSEC_PER_SEC;
+		int timeout = 10;  /* in msec */
+
+		while (usermodehelper_is_disabled()) {
+			read_unlock_usermodehelper();
+
+			msleep(timeout);
+			if (loading_timeout > 0) {
+				limit -= timeout;
+				if (limit <= 0) {
+					retval = -EBUSY;
+					goto out;
+				}
+			}
+			timeout += timeout;
+			if (loading_timeout > 0 && timeout > limit)
+				timeout = limit;
+
+			read_lock_usermodehelper();
+		}
+	} else if (WARN_ON(usermodehelper_is_disabled())) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
 		retval = -EBUSY;
-		goto out;
+		goto unlock;
 	}
 
 	if (uevent)
@@ -547,7 +569,7 @@ static int _request_firmware(const struc
 	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
 	if (IS_ERR(fw_priv)) {
 		retval = PTR_ERR(fw_priv);
-		goto out;
+		goto unlock;
 	}
 
 	if (uevent) {
@@ -572,9 +594,10 @@ static int _request_firmware(const struc
 
 	fw_destroy_instance(fw_priv);
 
-out:
+unlock:
 	read_unlock_usermodehelper();
 
+out:
 	if (retval) {
 		release_firmware(firmware);
 		*firmware_p = NULL;
#98 lore raw
Quoted 13 lines · On Friday 16 March 2012 23:57:10 Rafael J. Wysocki wrote:
Quoted 12 lines · Christian Lamparter
Quoted 11 lines · On Friday 16 March 2012 23:19:53 Rafael J. Wysocki wrote:
Quoted 10 lines · Christian Lamparter
Quoted 9 lines

During resume, the userhelper might not be available. However for drivers which use the request_firmware_nowait interface, this will only lead to a pointless WARNING and a device which no longer works after the resume [since it couldn't get the firmware, because the userhelper was not available to take the request].

In order to solve this "chicken or egg" dilemma, the code now retries _nowait requests at one second intervals until the "loading_timeout" time is up.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

BTW, I wonder what comments on this patch were posted?

Only Alan Cox was kind enough to drop me a few words.

Why? Do you think it is actually sane from a specific POV? [Don't tell me you do :D !]

I don't think it's really wrong.

Of course, I've tested both patches [the RFC and the other one]. The RFC might not be "really wrong" but the concept of busy msleeping strikes me as a bit insane. [Maybe, that's because I write drivers and I hate it when IO needs msleeps... brrr]

Quoted 8 lines

I agree that the WARN_ON() isn't really useful in the request_firmware_nowait() case, because the user of that doesn't really know when exactly the firmware is going to be requested, so it can't really do anything about the warning.

Moreover, failures of request_firmware_nowait() just because it happens to race with system suspend (or something of that kind), just because of "bad" timing, aren't really useful either.

If it's just about "waiting until the firmware can be loaded" then why not go with the "easy approach in the [PATCH] don't cancel...". This just queues the request in the _nowait case. And once the userspace helper is running, it will pick up all backlogged firmware requests [of course, only the ones that have not been timeouted yet] and life goes on!

Anyway, I know that I don't really have a say in what will be accepted. So, it's all up to you guys! But I'll happily test any patches.

Quoted 4 lines

So, I think it makes sense for it to wait until the firmware can be loaded.

I'd do that a bit differently, though, for example like in the appended patch (untested).

Just one comment. [see below]

And now, I'm off to bed.

Good night,
Chr

Quoted 20 lines
---
 drivers/base/firmware_class.c |   31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

Index: linux/drivers/base/firmware_class.c
===================================================================

drivers/base/firmware_class.c +1 -0
@@ -20,6 +20,7 @@
 #include <linux/highmem.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 
 #define to_dev(obj) container_of(obj, struct device, kobj)
 
@@ -535,10 +536,31 @@ static int _request_firmware(const struc
 
 	read_lock_usermodehelper();

drivers/base/firmware_class.c static int _request_firmware(const struc · line 546

Quoted 10 lines
drivers/base/firmware_class.c +8 -1
 
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	if (nowait) {
+		int limit = loading_timeout * MSEC_PER_SEC;
+		int timeout = 10;  /* in msec */
+
+		while (usermodehelper_is_disabled()) {
+			read_unlock_usermodehelper();
+
+			msleep(timeout);

timeout is 10 ms, right?

so this might apply:
Documentation/timers/timers-howto.txt
- Why not msleep for (1ms - 20ms)?
Explained originally here:
http://lkml.org/lkml/2007/8/3/250
msleep(1~20) may not do what the caller intends, and will often sleep longer (~20 ms actual sleep for any value given in the 1~20ms range). In many cases this is not the desired behavior.

Of course, that's of little importance.

#99 lore raw
In reply to Rafael J. Wysocki

drivers/base/firmware_class.c static int _request_firmware(const struc · line 546

Quoted 8 lines · Rafael J. Wysocki

+       if (nowait) {
+               int limit = loading_timeout * MSEC_PER_SEC;
+               int timeout = 10;  /* in msec */

drivers/base/firmware_class.c +5 -0
+
+               while (usermodehelper_is_disabled()) {
+                       read_unlock_usermodehelper();
+
+                       msleep(timeout);

Ugh, this is disgusting.

The whole point of nowait was that it's not synchronous - so it should just be driven by timers, not some kind of random "while sleep" loop.

And that fw thing already does have a timeout associated with it, and quite frankly, the *sane* approach is to do all this not in _request_firmware(), but in request_firmware_work_func() - never even call _request_firmware() in the first place if the system isn't ready, just reset the timeout to retry it again later.

Seriously. The rule should be really simple: nothing should *ever* call "request_firmware()" (or the _request_firmware() helper function) while the system is not up. That WARN_ON() should remain totally and utterly unconditional, and it should *not* be conditional on "nowait" or any idiotic crappy hack like that.

If there is an asynchronous thread - and there is, for the _nowait() case - that asynchronous thread should set up the timer and retry in ten seconds or whatever. It should *not* call 'request_firmware()" and expect that to do something special.

Linus

#100 lore raw
Quoted 27 lines · Linus Torvalds

drivers/base/firmware_class.c static int _request_firmware(const struc · line 546

Quoted 8 lines · Rafael J. Wysocki
drivers/base/firmware_class.c +8 -0
+       if (nowait) {
+               int limit = loading_timeout * MSEC_PER_SEC;
+               int timeout = 10;  /* in msec */
+
+               while (usermodehelper_is_disabled()) {
+                       read_unlock_usermodehelper();
+
+                       msleep(timeout);

Ugh, this is disgusting.

The whole point of nowait was that it's not synchronous - so it should just be driven by timers, not some kind of random "while sleep" loop.

And that fw thing already does have a timeout associated with it, and quite frankly, the *sane* approach is to do all this not in _request_firmware(), but in request_firmware_work_func() - never even call _request_firmware() in the first place if the system isn't ready, just reset the timeout to retry it again later.

Seriously. The rule should be really simple: nothing should *ever* call "request_firmware()" (or the _request_firmware() helper function) while the system is not up. That WARN_ON() should remain totally and utterly unconditional, and it should *not* be conditional on "nowait" or any idiotic crappy hack like that.

Quoted 4 lines

If there is an asynchronous thread - and there is, for the _nowait() case - that asynchronous thread should set up the timer and retry in ten seconds or whatever. It should *not* call 'request_firmware()" and expect that to do something special.

OK, but that asynchronous thread needs to know whether or not the system is up.

It can use the usermodehelper_is_disabled() check, but that needs to be done under read_lock_usermodehelper() and it can't release the lock before calling _request_firmware(), or all that thing would be racy. If it doesn't release the lock, then _request_firmware() will take it again, reentrantly, which isn't nice. So, it looks like it has to use a different check.

I was pondering a suspend/resume notifier that would block either request_firmware_work_func() (possibly with a timeout), if system suspend is in progress, or system suspend, if _request_firmware() is in progress, but that wouldn't cover the initialization case.

Thanks,
Rafael

#101 lore raw
Quoted 2 lines · Rafael J. Wysocki

OK, but that asynchronous thread needs to know whether or not the system is up.

Sure.

Quoted 3 lines

It can use the usermodehelper_is_disabled() check, but that needs to be done under read_lock_usermodehelper() and it can't release the lock before calling _request_firmware(), or all that thing would be racy.

Well, I think that it would actually be a good idea to perhaps split up that existing _request_firmware() a bit. In fact, it might be good to split up that whole "fw_create_instance()" too - and re-organize the code a bit so that you end up creating the "firmware_priv" thing first.

So maybe we could have a helper function that does that first part of fw_create_instance() (the part that allocates it and does the __module_get() part and other really basic initialization), and that can be called unconditionally by the request_firmware_nowait() code early.

That early part probably should *not* create the device attribute files etc (although who knows - maybe sysfs is ok at this point).

So I think we really could re-organize the code to do a sane job - and move just the actual udev setup etc to the final part that needs to be delayed.

Hmm?

I haven't looked very deeply into it, but my *gut* feel is that it should be doable pretty cleanly.

But yeah, it would be more than a little one-liner. I think it might be worth it, though. Clearly separating out the three stages: "setup" -> "wait for system to be ready" -> "do the actual load".

Then, the regular request_firmware() function would do exactly the same things, except it would never do the "wait for system to be ready" part: it would just fail with the warning if it wasn't already ready. So they'd still share all the basic core code, it would just be a slightly different organization from what it is now.

Linus

#102 lore raw
Quoted 38 lines · Linus Torvalds
Quoted 2 lines · Rafael J. Wysocki

OK, but that asynchronous thread needs to know whether or not the system is up.

Sure.

Quoted 3 lines

It can use the usermodehelper_is_disabled() check, but that needs to be done under read_lock_usermodehelper() and it can't release the lock before calling _request_firmware(), or all that thing would be racy.

Well, I think that it would actually be a good idea to perhaps split up that existing _request_firmware() a bit. In fact, it might be good to split up that whole "fw_create_instance()" too - and re-organize the code a bit so that you end up creating the "firmware_priv" thing first.

So maybe we could have a helper function that does that first part of fw_create_instance() (the part that allocates it and does the __module_get() part and other really basic initialization), and that can be called unconditionally by the request_firmware_nowait() code early.

That early part probably should *not* create the device attribute files etc (although who knows - maybe sysfs is ok at this point).

So I think we really could re-organize the code to do a sane job - and move just the actual udev setup etc to the final part that needs to be delayed.

Hmm?

I haven't looked very deeply into it, but my *gut* feel is that it should be doable pretty cleanly.

But yeah, it would be more than a little one-liner. I think it might be worth it, though. Clearly separating out the three stages: "setup" -> "wait for system to be ready" -> "do the actual load".

Quoted 5 lines

Then, the regular request_firmware() function would do exactly the same things, except it would never do the "wait for system to be ready" part: it would just fail with the warning if it wasn't already ready. So they'd still share all the basic core code, it would just be a slightly different organization from what it is now.

The patch below (untested) goes slightly into that direction, although not as far as to modify fw_create_instance(). It does, however, split _request_firmware() into "prepare", "load" and "cleanup" parts and moves the usermodehelper check along with the read-locking of umhelper_sem down to the callers, ie. request_firmware() and request_firmware_work_func().

The difference between them is that request_firmware() fails immediately with a WARN_ON() if it sees usermodehelper_disabled set after acquiring umhelper_sem, while request_firmware_work_func() waits for usermodehelper_disabled to be unset, with a timeout (the wait time is subtracted from the _request_firmware() timeout). The reason why request_firmware_work_func() does it this way is that it can't wait for usermodehelper_disabled to be unset with umhelper_sem held and it has to call _request_firmware() under umhelper_sem (otherwise user space might be frozen out from under it).

I'm falling asleep now, but hopefully the patch isn't totally busted. :-) It should be split into a series of patches, though.

Thanks,
Rafael

---
 drivers/base/firmware_class.c |   98 +++++++++++++++++++++++++++---------------
 include/linux/kmod.h          |    6 +-
 kernel/kmod.c                 |   83 ++++++++++++++++++++++++-----------
 3 files changed, 124 insertions(+), 63 deletions(-)
kernel/kmod.c +57 -26
Index: linux/kernel/kmod.c
===================================================================
@@ -291,22 +291,74 @@ static atomic_t running_helpers = ATOMIC
 static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq);
 
 /*
+ * Used by usermodehelper_read_lock_wait() to wait for usermodehelper_disabled
+ * to become 'false'.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(usermodehelper_disabled_waitq);
+
+/*
  * Time to wait for running_helpers to become zero before the setting of
  * usermodehelper_disabled in usermodehelper_disable() fails
  */
 #define RUNNING_HELPERS_TIMEOUT	(5 * HZ)
 
-void read_lock_usermodehelper(void)
+int usermodehelper_read_trylock(void)
+{
+	int ret = 0;
+
+	down_read(&umhelper_sem);
+	if (usermodehelper_disabled) {
+		up_read(&umhelper_sem);
+		ret = -EAGAIN;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usermodehelper_read_trylock);
+
+long usermodehelper_read_lock_wait(long timeout)
 {
+	DEFINE_WAIT(wait);
+	long ret = timeout;
+
+	if (timeout < 0)
+		return -EINVAL;
+
 	down_read(&umhelper_sem);
+	for (;;) {
+		prepare_to_wait(&usermodehelper_disabled_waitq, &wait,
+				TASK_UNINTERRUPTIBLE);
+		if (!usermodehelper_disabled)
+			break;
+
+		up_read(&umhelper_sem);
+
+		ret = schedule_timeout(timeout);
+		if (!ret)
+			break;
+
+		down_read(&umhelper_sem);
+	}
+	finish_wait(&usermodehelper_disabled_waitq, &wait);
+	return ret;
 }
-EXPORT_SYMBOL_GPL(read_lock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_lock_wait);
 
-void read_unlock_usermodehelper(void)
+void usermodehelper_read_unlock(void)
 {
 	up_read(&umhelper_sem);
 }
-EXPORT_SYMBOL_GPL(read_unlock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_unlock);
+
+/**
+ * usermodehelper_enable - allow new helpers to be started again
+ */
+void usermodehelper_enable(void)
+{
+	down_write(&umhelper_sem);
+	usermodehelper_disabled = 0;
+	wake_up(&usermodehelper_disabled_waitq);
+	up_write(&umhelper_sem);
+}
 
 /**
  * usermodehelper_disable - prevent new helpers from being started
@@ -331,31 +383,10 @@ int usermodehelper_disable(void)
 	if (retval)
 		return 0;
 
-	down_write(&umhelper_sem);
-	usermodehelper_disabled = 0;
-	up_write(&umhelper_sem);
+	usermodehelper_enable();
 	return -EAGAIN;
 }
 
-/**
- * usermodehelper_enable - allow new helpers to be started again
- */
-void usermodehelper_enable(void)
-{
-	down_write(&umhelper_sem);
-	usermodehelper_disabled = 0;
-	up_write(&umhelper_sem);
-}
-
-/**
- * usermodehelper_is_disabled - check if new helpers are allowed to be started
- */
-bool usermodehelper_is_disabled(void)
-{
-	return usermodehelper_disabled;
-}
-EXPORT_SYMBOL_GPL(usermodehelper_is_disabled);
-
 static void helper_lock(void)
 {
 	atomic_inc(&running_helpers);
include/linux/kmod.h +3 -3
Index: linux/include/linux/kmod.h
===================================================================
@@ -116,8 +116,8 @@ extern void usermodehelper_init(void);
 
 extern int usermodehelper_disable(void);
 extern void usermodehelper_enable(void);
-extern bool usermodehelper_is_disabled(void);
-extern void read_lock_usermodehelper(void);
-extern void read_unlock_usermodehelper(void);
+extern int usermodehelper_read_trylock(void);
+extern long usermodehelper_read_lock_wait(long timeout);
+extern void usermodehelper_read_unlock(void);
 
 #endif /* __LINUX_KMOD_H__ */
drivers/base/firmware_class.c +64 -34

157 lines not on this page · raw message

#103 lore raw
Quoted 6 lines · Rafael J. Wysocki

The patch below (untested) goes slightly into that direction, although not as far as to modify fw_create_instance().  It does, however, split _request_firmware() into "prepare", "load" and "cleanup" parts and moves the usermodehelper check along with the read-locking of umhelper_sem down to the callers, ie. request_firmware() and request_firmware_work_func().

Looks fairly reasonable to me from a quick read-through, and the code seems to make sense.

Linus

#104 lore raw
Quoted 10 lines · Linus Torvalds
Quoted 6 lines · Rafael J. Wysocki

The patch below (untested) goes slightly into that direction, although not as far as to modify fw_create_instance(). It does, however, split _request_firmware() into "prepare", "load" and "cleanup" parts and moves the usermodehelper check along with the read-locking of umhelper_sem down to the callers, ie. request_firmware() and request_firmware_work_func().

Looks fairly reasonable to me from a quick read-through, and the code seems to make sense.

Thanks!

I'll split it into a series of patches, then, and post for testing.

Rafael

#105 lore raw
In reply to Rafael J. Wysocki
Quoted 16 lines · On Sunday 18 March 2012 01:29:38 Rafael J. Wysocki wrote:

The patch below (untested) goes slightly into that direction, although not as far as to modify fw_create_instance(). It does, however, split _request_firmware() into "prepare", "load" and "cleanup" parts and moves the usermodehelper check along with the read-locking of umhelper_sem down to the callers, ie. request_firmware() and request_firmware_work_func().

The difference between them is that request_firmware() fails immediately with a WARN_ON() if it sees usermodehelper_disabled set after acquiring umhelper_sem, while request_firmware_work_func() waits for usermodehelper_disabled to be unset, with a timeout (the wait time is subtracted from the _request_firmware() timeout). The reason why request_firmware_work_func() does it this way is that it can't wait for usermodehelper_disabled to be unset with umhelper_sem held and it has to call _request_firmware() under umhelper_sem (otherwise user space might be frozen out from under it).

Quoted 2 lines

I'm falling asleep now, but hopefully the patch isn't totally busted. :-) It should be split into a series of patches, though.

I'm happy to report that my test system [with the patch applied] suspended and resumed without any incidents.

Regards,
Christian

#106 lore raw

cover firmware_class: Fix problem with async requests (was: Re: [RFC] firmware loader: retry ...)

Quoted 17 lines · Christian Lamparter
Quoted 16 lines · On Sunday 18 March 2012 01:29:38 Rafael J. Wysocki wrote:

The patch below (untested) goes slightly into that direction, although not as far as to modify fw_create_instance(). It does, however, split _request_firmware() into "prepare", "load" and "cleanup" parts and moves the usermodehelper check along with the read-locking of umhelper_sem down to the callers, ie. request_firmware() and request_firmware_work_func().

The difference between them is that request_firmware() fails immediately with a WARN_ON() if it sees usermodehelper_disabled set after acquiring umhelper_sem, while request_firmware_work_func() waits for usermodehelper_disabled to be unset, with a timeout (the wait time is subtracted from the _request_firmware() timeout). The reason why request_firmware_work_func() does it this way is that it can't wait for usermodehelper_disabled to be unset with umhelper_sem held and it has to call _request_firmware() under umhelper_sem (otherwise user space might be frozen out from under it).

Quoted 4 lines
Quoted 2 lines

I'm falling asleep now, but hopefully the patch isn't totally busted. :-) It should be split into a series of patches, though.

I'm happy to report that my test system [with the patch applied] suspended and resumed without any incidents.

Thanks for testing!

The following three patches should result in analogous code, although slightly cleaned up.

Thanks,
Rafael

#107 lore raw

PATCH 1/3 firmware_class: Rework usermodehelper check

From: Rafael J. Wysocki <[email protected]>

Instead of two functions, read_lock_usermodehelper() and usermodehelper_is_disabled(), used in combination, introduce usermodehelper_read_trylock() that will only return with umhelper_sem held if usermodehelper_disabled is unset (and will return -EAGAIN otherwise) and make _request_firmware() use it.

Rename read_unlock_usermodehelper() to usermodehelper_read_unlock() to follow the naming convention of the new function.

---
 drivers/base/firmware_class.c |   11 +++++------
 include/linux/kmod.h          |    5 ++---
 kernel/kmod.c                 |   24 +++++++++++-------------
 3 files changed, 18 insertions(+), 22 deletions(-)
include/linux/kmod.h +2 -3
Index: linux/include/linux/kmod.h
===================================================================
@@ -116,8 +116,7 @@ extern void usermodehelper_init(void);
 
 extern int usermodehelper_disable(void);
 extern void usermodehelper_enable(void);
-extern bool usermodehelper_is_disabled(void);
-extern void read_lock_usermodehelper(void);
-extern void read_unlock_usermodehelper(void);
+extern int usermodehelper_read_trylock(void);
+extern void usermodehelper_read_unlock(void);
 
 #endif /* __LINUX_KMOD_H__ */
kernel/kmod.c +11 -13
Index: linux/kernel/kmod.c
===================================================================
@@ -296,17 +296,24 @@ static DECLARE_WAIT_QUEUE_HEAD(running_h
  */
 #define RUNNING_HELPERS_TIMEOUT	(5 * HZ)
 
-void read_lock_usermodehelper(void)
+int usermodehelper_read_trylock(void)
 {
+	int ret = 0;
+
 	down_read(&umhelper_sem);
+	if (usermodehelper_disabled) {
+		up_read(&umhelper_sem);
+		ret = -EAGAIN;
+	}
+	return ret;
 }
-EXPORT_SYMBOL_GPL(read_lock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_trylock);
 
-void read_unlock_usermodehelper(void)
+void usermodehelper_read_unlock(void)
 {
 	up_read(&umhelper_sem);
 }
-EXPORT_SYMBOL_GPL(read_unlock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_unlock);
 
 /**
  * usermodehelper_disable - prevent new helpers from being started
@@ -347,15 +354,6 @@ void usermodehelper_enable(void)
 	up_write(&umhelper_sem);
 }
 
-/**
- * usermodehelper_is_disabled - check if new helpers are allowed to be started
- */
-bool usermodehelper_is_disabled(void)
-{
-	return usermodehelper_disabled;
-}
-EXPORT_SYMBOL_GPL(usermodehelper_is_disabled);
-
 static void helper_lock(void)
 {
 	atomic_inc(&running_helpers);
drivers/base/firmware_class.c +5 -6
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -533,12 +533,10 @@ static int _request_firmware(const struc
 		return 0;
 	}
 
-	read_lock_usermodehelper();
-
-	if (WARN_ON(usermodehelper_is_disabled())) {
+	retval = usermodehelper_read_trylock();
+	if (WARN_ON(retval)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
-		retval = -EBUSY;
-		goto out;
+		goto out_nolock;
 	}
 
 	if (uevent)
@@ -573,8 +571,9 @@ static int _request_firmware(const struc
 	fw_destroy_instance(fw_priv);
 
 out:
-	read_unlock_usermodehelper();
+	usermodehelper_read_unlock();
 
+out_nolock:
 	if (retval) {
 		release_firmware(firmware);
 		*firmware_p = NULL;
#108 lore raw

PATCH 2/3 firmware_class: Split _request_firmware() into three functions

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

Split _request_firmware() into three functions, _request_firmware_prepare() doing preparatory work that need not be done under umhelper_sem, _request_firmware_cleanup() doing the post-error cleanup and _request_firmware() carrying out the remaining operations.

This change is requisite for moving the acquisition of umhelper_sem from _request_firmware() to the callers, which is going to be done subsequently.

---
 drivers/base/firmware_class.c |   57 +++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 17 deletions(-)
drivers/base/firmware_class.c +40 -17
Index: linux/drivers/base/firmware_class.c
===================================================================
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_lon
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
 		   struct device *device, bool uevent, bool nowait)
 {
 	struct firmware_priv *fw_priv;
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firm
 		goto err_out;
 	}
 
-	fw_priv->fw = firmware;
+	fw_priv->fw = (struct firmware *)firmware;
 	fw_priv->nowait = nowait;
 	strcpy(fw_priv->fw_id, fw_name);
 	init_completion(&fw_priv->completion);
@@ -510,13 +510,10 @@ static void fw_destroy_instance(struct f
 	device_unregister(f_dev);
 }
 
-static int _request_firmware(const struct firmware **firmware_p,
-			     const char *name, struct device *device,
-			     bool uevent, bool nowait)
+static int _request_firmware_prepare(const struct firmware **firmware_p,
+				     const char *name, struct device *device)
 {
-	struct firmware_priv *fw_priv;
 	struct firmware *firmware;
-	int retval = 0;
 
 	if (!firmware_p)
 		return -EINVAL;
@@ -533,10 +530,26 @@ static int _request_firmware(const struc
 		return 0;
 	}
 
+	return 1;
+}
+
+static void _request_firmware_cleanup(const struct firmware **firmware_p)
+{
+	release_firmware(*firmware_p);
+	*firmware_p = NULL;
+}
+
+static int _request_firmware(const struct firmware *firmware,
+			     const char *name, struct device *device,
+			     bool uevent, bool nowait)
+{
+	struct firmware_priv *fw_priv;
+	int retval;
+
 	retval = usermodehelper_read_trylock();
 	if (WARN_ON(retval)) {
 		dev_err(device, "firmware: %s will not be loaded\n", name);
-		goto out_nolock;
+		return retval;
 	}
 
 	if (uevent)
@@ -572,13 +585,6 @@ static int _request_firmware(const struc
 
 out:
 	usermodehelper_read_unlock();
-
-out_nolock:
-	if (retval) {
-		release_firmware(firmware);
-		*firmware_p = NULL;
-	}
-
 	return retval;
 }
 
@@ -601,7 +607,17 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
-        return _request_firmware(firmware_p, name, device, true, false);
+	int ret;
+
+	ret = _request_firmware_prepare(firmware_p, name, device);
+	if (ret <= 0)
+		return ret;
+
+	ret = _request_firmware(*firmware_p, name, device, true, false);
+	if (ret)
+		_request_firmware_cleanup(firmware_p);
+
+	return ret;
 }
 
 /**
@@ -639,8 +655,15 @@ static int request_firmware_work_func(vo
 		return 0;
 	}
 
-	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+	ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+	if (ret <= 0)
+		return ret;
+
+	ret = _request_firmware(fw, fw_work->name, fw_work->device,
 				fw_work->uevent, true);
+	if (ret)
+		_request_firmware_cleanup(&fw);
+
 	fw_work->cont(fw, fw_work->context);
 
 	module_put(fw_work->module);
#109 lore raw

PATCH 3/3 firmware_class: Do not warn that system is not ready for async loads

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

If firmware is requested asynchronously, by calling request_firmware_nowait(), there is no reason to fail the request (and warn the user) when the system is (presumably temporarily) unready for handling it (because user space is not available yet or frozen). For this reason, introduce an alternative routine for read-locking umhelper_sem, usermodehelper_read_lock_wait(), that will wait for usermodehelper_disabled to be unset (possibly with a timeout) and make request_firmware_work_func() use it instead of usermodehelper_read_trylock().

Accordingly, modify request_firmware() so that it uses usermodehelper_read_trylock() to acquire umhelper_sem and remove the code related to that lock from _request_firmware().

---
 drivers/base/firmware_class.c |   51 +++++++++++++++++++++---------------
 include/linux/kmod.h          |    1 
 kernel/kmod.c                 |   59 ++++++++++++++++++++++++++++++++----------
 3 files changed, 77 insertions(+), 34 deletions(-)
include/linux/kmod.h +1 -0
Index: linux/include/linux/kmod.h
===================================================================
@@ -117,6 +117,7 @@ extern void usermodehelper_init(void);
 extern int usermodehelper_disable(void);
 extern void usermodehelper_enable(void);
 extern int usermodehelper_read_trylock(void);
+extern long usermodehelper_read_lock_wait(long timeout);
 extern void usermodehelper_read_unlock(void);
 
 #endif /* __LINUX_KMOD_H__ */
kernel/kmod.c +46 -13

86 lines not on this page · raw message

drivers/base/firmware_class.c +30 -21

97 lines not on this page · raw message

#110 lore raw
In reply to Linus Torvalds
Quoted 27 lines · Linus Torvalds

drivers/base/firmware_class.c static int _request_firmware(const struc · line 546

Quoted 8 lines · Rafael J. Wysocki

+       if (nowait) {
+               int limit = loading_timeout * MSEC_PER_SEC;
+               int timeout = 10;  /* in msec */

drivers/base/firmware_class.c +5 -0
+
+               while (usermodehelper_is_disabled()) {
+                       read_unlock_usermodehelper();
+
+                       msleep(timeout);

Ugh, this is disgusting.

The whole point of nowait was that it's not synchronous - so it should just be driven by timers, not some kind of random "while sleep" loop.

And that fw thing already does have a timeout associated with it, and quite frankly, the *sane* approach is to do all this not in _request_firmware(), but in request_firmware_work_func() - never even call _request_firmware() in the first place if the system isn't ready, just reset the timeout to retry it again later.

Seriously. The rule should be really simple: nothing should *ever* call "request_firmware()" (or the _request_firmware() helper function) while the system is not up. That WARN_ON() should remain totally and utterly unconditional, and it should *not* be conditional on "nowait" or any idiotic crappy hack like that.

Quoted 5 lines

If there is an asynchronous thread - and there is, for the _nowait() case - that asynchronous thread should set up the timer and retry in ten seconds or whatever. It should *not* call 'request_firmware()" and expect that to do something special.

Could an alternative be a new event/callback to the driver informing it that the request_firmware() functionallity is present or not. The driver would then get a callback on each transition from "request_firmware() present" to/from "request_firmware() absent". So, on boot up, the driver ".probe" gets called, and only when enough infrastructure is up and running does the "request_firmware() present" callback get called.
The question then becomes
Q1: "how do we determine when enough infrastructure is up for request_firmware() to succeed?"
Now, if that question can actually be answered, I would prefer that approach, than to each driver starting a timer based request, to keep requesting every 2 seconds until it works.

Other similar themes:
could be the driver registers an interest in a particular firmware file in a particular location, and then when the system determines that that particular file is now readable, it makes a callback to the driver. It is certainly an easier question to answer than Q1. No timers needed.
This could be implemented with a "request_firmware(filename)" that returns imeadiately but adds the filename to the list of firmware files to look out for.
The driver then gets a callback "here_is_requested_firmware(filename)" when the system determines that it can read it.
This list could be scanned immediately and after that; only when a filesystem is mounted, or the user requests a rescan.

Kind Regards

James