firmware loader: retry _nowait requests when userhelper is not yet available
Subsystems (4)
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
@@ -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
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_nowaitinterface, 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
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_nowaitinterface, 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
[ 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_nowaitinterface, 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
_nowaitrequests 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,
ChristianBTW: 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@@ -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
-0fw_work->cont (fw, fw_work->context);module_put (fw_work->module);
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
@@ -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
Greg KH
maintainerRe: [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_nowaitdoes not stall in any system resume paths. Therefore, I think it is perfectly save to userequest_firmware_nowaitfrom 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
Signed-off-by Christian Lamparter <[email protected]>--- drivers/base/firmware_class.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0
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
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_nowaitdoes not stall in any system resume paths. Therefore, I think it is perfectly save to userequest_firmware_nowaitfrom 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
Signed-off-by Christian Lamparter <[email protected]>--- drivers/base/firmware_class.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0@@ -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
-1read_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
Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Greg KHQuoted 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_nowaitdoes not stall in any system resume paths. Therefore, I think it is perfectly save to userequest_firmware_nowaitfrom 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
Signed-off-by Christian Lamparter <[email protected]>---
drivers/base/firmware_class.c | 2 +-1 files changed, 1 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0@@ -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
-1read_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
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_nowaitdoes not stall in any system resume paths. Therefore, I think it is perfectly save to userequest_firmware_nowaitfrom 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
Signed-off-by Christian Lamparter<[email protected]>--- drivers/base/firmware_class.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0@@ -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
-1read_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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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
@@ -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
@@ -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.
Re: Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Kay SieversQuoted 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_nowaitdoes not stall in any system resume paths. Therefore, I think it is perfectly save to userequest_firmware_nowaitfrom 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
Signed-off-by Christian Lamparter<[email protected]>--- drivers/base/firmware_class.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0@@ -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
-1read_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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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
@@ -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
@@ -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.
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_nowaitdoes not stall in any system resume paths. Therefore, I think it is perfectly save to userequest_firmware_nowaitfrom 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
Signed-off-by Christian Lamparter<[email protected]>--- drivers/base/firmware_class.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0@@ -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
-1read_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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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 torequest_firmwareto check forusermodehelper_is_disabled()was preventing us from usingrequest_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.Signed-off-by Saravana Kannan <[email protected]>--- drivers/base/firmware_class.c | 15 ++++++++------- kernel/kmod.c | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-)drivers/base/firmware_class.c +8
-7@@ -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@@ -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 failingrequest_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 forrequest_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 causingrequest_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 disallowrequest_firmware()in all of the callbacks that are called fromsuspend_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 inrequest_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
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
@@ -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
@@ -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
@@ -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
@@ -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.
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 thenrequest_firmware()would fail. Do we need something like this (ignore the fact that we callusermodhelper_enable()twice)?kernel/power/process.c +0
-0
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
-0schedule ();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 thatschedule()call inthaw_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 callschedule()until userspace is thawed.
Why did you do that?
Rafael
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 thenrequest_firmware()would fail. Do we need something like this (ignore the fact that we callusermodhelper_enable()twice)?kernel/power/process.c +0
-0kernel/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
-0schedule ();printk ("done.\n" ); }Is there a reason we disable usermodehelpers if
CONFIG_SUSPEND_FREEZER=n?Not really, but
CONFIG_SUSPEND_FREEZER=ncan 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_FREEZERaltogether, 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 thatschedule()call inthaw_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
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 thenrequest_firmware()would fail. Do we need something like this (ignore the fact that we callusermodhelper_enable()twice)?kernel/power/process.c +0
-0kernel/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
-0schedule ();printk ("done.\n" ); }Is there a reason we disable usermodehelpers if
CONFIG_SUSPEND_FREEZER=n?Not really, but
CONFIG_SUSPEND_FREEZER=ncan 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_FREEZERaltogether, 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 thatschedule()call inthaw_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 inkernel/power/user.c(and please read the comment in there describing what it's there for, which also explains why theschedule()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.
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 thenrequest_firmware()would fail. Do we need something like this (ignore the fact that we callusermodhelper_enable()twice)?kernel/power/process.c +0
-0kernel/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
-0schedule ();printk ("done.\n" ); }Is there a reason we disable usermodehelpers if
CONFIG_SUSPEND_FREEZER=n?Not really, but
CONFIG_SUSPEND_FREEZER=ncan 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_FREEZERaltogether, 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 thatschedule()call inthaw_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 inkernel/power/user.c(and please read the comment in there describing what it's there for, which also explains why theschedule()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
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 inkernel/power/user.c(and please read the comment in there describing what it's there for, which also explains why theschedule()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.
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 inkernel/power/user.c(and please read the comment in there describing what it's there for, which also explains why theschedule()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
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.
---
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
@@ -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.
Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues
Quoted 7 lines · Stephen Boyd
Oddly enough a
work_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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
Signed-off-by Stephen Boyd <[email protected]>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
Re: [PATCH] firmware_class: Move request_firmware_nowait() to workqueues
Quoted 14 lines · Christian Lamparter
Quoted 13 lines · Stephen Boyd
Oddly enough a
work_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>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_workwork 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.
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>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_workwork 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
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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
Signed-off-by Stephen Boyd <[email protected]>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_workwork 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_wqinworkqueue.h.You should have used either
system_long_wqorsystem_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.
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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
Signed-off-by Stephen Boyd <[email protected]>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_workwork 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_wqinworkqueue.h.
Quoted 7 lines
Quoted 4 lines
You should have used either
system_long_wqorsystem_nrt_wq(depending on what you really need).Thanks. I think we can use
system_nrt_wqthen? Or maybe even the unbounded workqueuesystem_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
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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
Signed-off-by Stephen Boyd <[email protected]>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_workwork 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_wqinworkqueue.h.You should have used either
system_long_wqorsystem_nrt_wq(depending on what you really need).
Quoted 7 lines
Quoted 2 lines
Thanks. I think we can use
system_nrt_wqthen? Or maybe even the unbounded workqueuesystem_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.
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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
Signed-off-by Stephen Boyd <[email protected]>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_workwork 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_wqinworkqueue.h.You should have used either
system_long_wqorsystem_nrt_wq(depending on what you really need).Quoted 7 lines
Quoted 2 lines
Thanks. I think we can use
system_nrt_wqthen? Or maybe even the unbounded workqueuesystem_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 torequest_firmware_nowait(), where the work item will handle the sysfs entry generation, uevent generation, andwait_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 callingrequest_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
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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
Signed-off-by Stephen Boyd <[email protected]>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_workwork 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_wqinworkqueue.h.You should have used either
system_long_wqorsystem_nrt_wq(depending on what you really need).Quoted 7 lines
Quoted 2 lines
Thanks. I think we can use
system_nrt_wqthen? Or maybe even the unbounded workqueuesystem_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 torequest_firmware_nowait(), where the work item will handle the sysfs entry generation, uevent generation, andwait_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 callingrequest_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
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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
Signed-off-by Stephen Boyd <[email protected]>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_workwork 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_wqinworkqueue.h.You should have used either
system_long_wqorsystem_nrt_wq(depending on what you really need).Quoted 7 lines
Quoted 2 lines
Thanks. I think we can use
system_nrt_wqthen? Or maybe even the unbounded workqueuesystem_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 torequest_firmware_nowait(), where the work item will handle the sysfs entry generation, uevent generation, andwait_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 callingrequest_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.ccurrently 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
Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Rafael J. WysockiQuoted 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 inkernel/power/user.c(and please read the comment in there describing what it's there for, which also explains why theschedule()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
@@ -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
@@ -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
@@ -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
@@ -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.
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
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
Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Linus TorvaldsQuoted 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 CPU1driver_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.
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 whenrequest_firmware()is correct to use. It seems that the function is inherently racy with freezing. Does every user ofrequest_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 therequest_firmware()call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and therequest_firmware()call will return-EBUSYand warn.
Yes, it will.
Thanks,
Rafael
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 whenrequest_firmware()is correct to use. It seems that the function is inherently racy with freezing. Does every user ofrequest_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 therequest_firmware()call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and therequest_firmware()call will return-EBUSYand 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.
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 whenrequest_firmware()is correct to use. It seems that the function is inherently racy with freezing. Does every user ofrequest_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 therequest_firmware()call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and therequest_firmware()call will return-EBUSYand warn.
Quoted 8 lines
Quoted 1 line
Yes, it will.
That sounds wrong then, no? Why don't we have
request_firmware()do aread_lockon 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 whererequest_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
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 whenrequest_firmware()is correct to use. It seems that the function is inherently racy with freezing. Does every user ofrequest_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 therequest_firmware()call acquiring the usermodehelper rwsem. If the suspending CPU wins the race it will disable usermodehelpers and therequest_firmware()call will return-EBUSYand warn.Quoted 8 lines
Quoted 1 line
Yes, it will.
That sounds wrong then, no? Why don't we have
request_firmware()do aread_lockon 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 whererequest_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_semwrite-locked, right?
Quoted 7 lines
That would lead to freezing failures if a user space task waited in
request_firmware()forumhelper_semto become available for read-locking and unfortunately we don't have an interruptible variant ofdown_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
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
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
@@ -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
@@ -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
@@ -533,12 +533,10 @@ static int _request_firmware(const strucreturn 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 strucfw_destroy_instance (fw_priv); out: -read_unlock_usermodehelper (); +usermodehelper_read_unlock (); +out_nolock:if (retval) {release_firmware (firmware); *firmware_p =NULL ;
PATCH 2/6 firmware_class: Split _request_firmware() into three functions
In reply to Rafael J. WysockiFrom: 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
@@ -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 fdevice_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 strucreturn 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(voreturn 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);
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(voreturn 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
@@ -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.
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
@@ -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.
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@@ -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
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.
Re: [PATCH 2/6] firmware_class: Split _request_firmware() into three functions
In reply to Stephen BoydQuoted 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(voreturn 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
-0module_put (fw_work->module);Here's a compile only tested patch on top. The idea is return a
fw_privpointer 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@@ -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
@@ -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 fdevice_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 strucreturn 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(voreturn 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);
Stephen Boyd
Reviewed-byRe: [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(voreturn 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, v2Split
_request_firmware()into three functions,_request_firmware_prepare()doing preparatory work that need not be done underumhelper_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_semfrom_request_firmware()to the callers, which is going to be done subsequently.
Quoted 2 lines
Signed-off-by Rafael J. Wysocki <[email protected]>---
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.
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(voreturn 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, v2Split
_request_firmware()into three functions,_request_firmware_prepare()doing preparatory work that need not be done underumhelper_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_semfrom_request_firmware()to the callers, which is going to be done subsequently.Reviewed-by Stephen Boyd <[email protected]>
Thanks!
Rafael
PATCH 3/6 firmware_class: Do not warn that system is not ready from async loads
In reply to Rafael J. WysockiFrom: 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
@@ -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
PATCH 4/6 PM / Hibernate: Disable usermode helpers right before freezing tasks
In reply to Rafael J. WysockiFrom: 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
@@ -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 ();
PATCH 5/6 PM / Sleep: Move disabling of usermode helpers to the freezer
In reply to Rafael J. WysockiFrom: 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
@@ -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
@@ -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_ststatic 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
@@ -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
@@ -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 ;
PATCH 6/6 PM / Sleep: Mitigate race between the freezer and request_firmware()
In reply to Rafael J. WysockiFrom: 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
@@ -112,10 +112,27 @@ call_usermodehelper(char *path, char **aextern 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
@@ -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" );
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.
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
@@ -112,10 +112,27 @@ call_usermodehelper(char *path, char **aextern 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
@@ -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" );
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 andrequest_firmware()There is a race condition between the freezer and
request_firmware()such that ifrequest_firmware()is run on one CPU andfreeze_processes()is run on another CPU andusermodehelper_disable()called by it succeeds to grabumhelper_semfor writing beforeusermodehelper_read_trylock()called fromrequest_firmware()acquires it for reading, therequest_firmware()will fail and trigger aWARN_ON()complaining that it was called at a wrong time. However, in fact, it wasn't called at a wrong time andfreeze_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_disabledso that it can take three different values,UMH_ENABLED(0),UMH_FREEZINGandUMH_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, whenfreeze_processes()is started, it setsusermodehelper_disabledtoUMH_FREEZINGwhich tellsusermodehelper_read_trylock()that it shouldn't fail just yet and should calltry_to_freeze()if woken up and cannot return immediately. This way all freezable tasks that happen to callrequest_firmware()right beforefreeze_processes()is started and lose the race forumhelper_semwith it will be frozen and will sleep untilthaw_processes()unsetsusermodehelper_disabled. [For the non-freezable callers ofrequest_firmware()the race forumhelper_semagainstfreeze_processes()is unfortunately unavoidable.]
Quoted 3 lines
Signed-off-by Rafael J. Wysocki <[email protected]>---
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.
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 andrequest_firmware()There is a race condition between the freezer and
request_firmware()such that ifrequest_firmware()is run on one CPU andfreeze_processes()is run on another CPU andusermodehelper_disable()called by it succeeds to grabumhelper_semfor writing beforeusermodehelper_read_trylock()called fromrequest_firmware()acquires it for reading, therequest_firmware()will fail and trigger aWARN_ON()complaining that it was called at a wrong time. However, in fact, it wasn't called at a wrong time andfreeze_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_disabledso that it can take three different values,UMH_ENABLED(0),UMH_FREEZINGandUMH_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, whenfreeze_processes()is started, it setsusermodehelper_disabledtoUMH_FREEZINGwhich tellsusermodehelper_read_trylock()that it shouldn't fail just yet and should calltry_to_freeze()if woken up and cannot return immediately. This way all freezable tasks that happen to callrequest_firmware()right beforefreeze_processes()is started and lose the race forumhelper_semwith it will be frozen and will sleep untilthaw_processes()unsetsusermodehelper_disabled. [For the non-freezable callers ofrequest_firmware()the race forumhelper_semagainstfreeze_processes()is unfortunately unavoidable.]
Quoted 1 line
Reported-by Stephen Boyd <[email protected]>
Yeah, sorry.
Re: [PATCH 0/6] firmware_class: Fix problems with usermodehelper test
In reply to Rafael J. WysockiQuoted 13 lines · Rafael J. Wysocki
Hi all,
The following series of patches fixes two problems with
request_firmware()andrequest_firmware_nowait()resulting from commita144c6a6c924aa1da04dd77fb84b89927354fdffPM: 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
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()andrequest_firmware_nowait()resulting from commita144c6a6c924aa1da04dd77fb84b89927354fdffPM: 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
PATCH 1/2 firmware_class: Reorganize fw_create_instance()
In reply to Rafael J. WysockiRecent 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
@@ -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.
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
@@ -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.
Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues
Quoted 11 lines · Stephen Boyd
Oddly enough a
work_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>---
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@@ -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 ; }
Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues
In reply to Stephen BoydQuoted 11 lines · Stephen Boyd
Oddly enough a
work_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>---
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
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>---
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
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>---
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
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>---
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, usingsystem_wqshould be fine here.
Good, thanks for the explanation.
Stephen, care to respin?
Rafael
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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>---
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, usingsystem_wqshould 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
@@ -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.
Re: [PATCH 2/2] firmware_class: Move request_firmware_nowait() to workqueues
In reply to Rafael J. WysockiQuoted 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_structwas already part of thefirmware_workstructure but nobody was using it. Instead of creating a new kthread for eachrequest_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.Signed-off-by Stephen Boyd <[email protected]>---
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, usingsystem_wqshould 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
@@ -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.
Re: [PATCH 1/2] firmware_class: Reorganize fw_create_instance()
In reply to Stephen BoydQuoted 11 lines · Stephen Boyd
Recent patches to split up the three phases of
request_firmware()lead to a casting away of const infw_create_instance(). We can avoid this cast by splitting upfw_create_instance()a bit.Make
_request_firmware_setup()return a structfw_privand 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.Signed-off-by Stephen Boyd <[email protected]>
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@@ -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",
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 infw_create_instance(). We can avoid this cast by splitting upfw_create_instance()a bit.Make
_request_firmware_setup()return a structfw_privand 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
Signed-off-by Stephen Boyd <[email protected]>Looks good to me.
Greg, if that's fine with you, I'll add this patch (and the next one) to my
firmware_classseries you've looked at recently.
Yes, it's fine with me:
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 infw_create_instance(). We can avoid this cast by splitting upfw_create_instance()a bit.Make
_request_firmware_setup()return a structfw_privand 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.Signed-off-by Stephen Boyd <[email protected]>
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_classseries you've looked at recently.Yes, it's fine with me:
Cool, thanks!
Rafael
v2 cover firmware_class: Fix problems with usermodehelper test
In reply to Rafael J. WysockiHi all,
Quoted 12 lines · Rafael J. Wysocki
The following series of patches fixes two problems with
request_firmware()andrequest_firmware_nowait()resulting from commita144c6a6c924aa1da04dd77fb84b89927354fdffPM: 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
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
@@ -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
PATCH v2 1/8 firmware_class: Rework usermodehelper check
In reply to Rafael J. WysockiFrom: 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
@@ -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
@@ -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
@@ -533,12 +533,10 @@ static int _request_firmware(const strucreturn 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 strucfw_destroy_instance (fw_priv); out: -read_unlock_usermodehelper (); +usermodehelper_read_unlock (); +out_nolock:if (retval) {release_firmware (firmware); *firmware_p =NULL ;
PATCH v2 2/8 firmware_class: Split _request_firmware() into three functions, v2
In reply to Rafael J. WysockiFrom: 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
@@ -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 fdevice_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 strucreturn 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(voreturn 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);
PATCH v2 4/8 PM / Hibernate: Disable usermode helpers right before freezing tasks
In reply to Rafael J. WysockiFrom: 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
@@ -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 ();
PATCH v2 5/8 PM / Sleep: Move disabling of usermode helpers to the freezer
In reply to Rafael J. WysockiFrom: 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
@@ -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
@@ -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_ststatic 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
@@ -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
@@ -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 ;
PATCH v2 6/8 PM / Sleep: Mitigate race between the freezer and request_firmware()
In reply to Rafael J. WysockiFrom: 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
@@ -110,10 +110,27 @@ call_usermodehelper(char *path, char **aextern 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
@@ -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" );
PATCH v2 7/8 firmware_class: Reorganize fw_create_instance()
In reply to Rafael J. WysockiFrom: 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
@@ -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(voreturn 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",
PATCH v2 8/8 firmware_class: Move request_firmware_nowait() to workqueues
In reply to Rafael J. WysockiFrom: 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
@@ -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(vomodule_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 ; }
Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Rafael J. WysockiQuoted 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
Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Kay SieversQuoted 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_nowaitdoes not stall in any system resume paths. Therefore, I think it is perfectly save to userequest_firmware_nowaitfrom 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
Signed-off-by Christian Lamparter <[email protected]>--- drivers/base/firmware_class.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0@@ -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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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
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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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
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/hotplugis 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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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
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/hotplugisn'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 calledsuspend_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
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 theexec()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
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 theexec()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
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 aboutexec(). 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
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 aboutexec(). 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
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 aboutexec(). 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
Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Kay SieversQuoted 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
Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Rafael J. WysockiQuoted 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/hotplugis 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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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/hotplugisn'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 calledsuspend_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
@@ -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;
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/hotplugis 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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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/hotplugisn'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 calledsuspend_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 forusermodehelper_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()tosuspend_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().Signed-off-by Srivatsa S. Bhat <[email protected]>---
drivers/base/firmware_class.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0
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
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/hotplugis 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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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/hotplugisn'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 calledsuspend_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 forusermodehelper_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()tosuspend_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().Signed-off-by Srivatsa S. Bhat <[email protected]>---
drivers/base/firmware_class.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0@@ -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
@@ -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 ;
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/hotplugis 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/hotplugis disabled by default, it is a broken concept, and it cannot work reliably on today's systems.Firmware is not loaded by
/sbin/hotplugsince 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/hotplugisn'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 calledsuspend_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 forusermodehelper_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()tosuspend_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().Signed-off-by Srivatsa S. Bhat <[email protected]>---
drivers/base/firmware_class.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)drivers/base/firmware_class.c +0
-0@@ -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 whyusermodehelper_is_disabled()check is usedRafael 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.Signed-off-by Srivatsa S. Bhat <[email protected]>
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@@ -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 ;
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@@ -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 ;
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
Re: [PATCH] firmware loader: don't cancel _nowait requests when helper is not yet available
In reply to Christian LamparterQuoted 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,
ChrQuoted 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@@ -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 ;
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_nowaitinterface, 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
_nowaitrequests 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@@ -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
-0fw_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
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_nowaitinterface, 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
_nowaitrequests 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@@ -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
-0fw_work->cont (fw, fw_work->context);module_put (fw_work->module);
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_nowaitinterface, 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
_nowaitrequests 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
@@ -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 strucread_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 strucfw_destroy_instance (fw_priv); -out: +unlock:read_unlock_usermodehelper (); +out:if (retval) {release_firmware (firmware); *firmware_p =NULL ;
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_nowaitinterface, 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
_nowaitrequests 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 therequest_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 strucread_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.
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
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 inrequest_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. ThatWARN_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
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 underread_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
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 underread_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 therequest_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
@@ -291,22 +291,74 @@ static atomic_t running_helpers = ATOMICstatic 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
@@ -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
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 ofumhelper_semdown to the callers, ie.request_firmware()andrequest_firmware_work_func().
Looks fairly reasonable to me from a quick read-through, and the code seems to make sense.
Linus
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 ofumhelper_semdown to the callers, ie.request_firmware()andrequest_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
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 ofumhelper_semdown to the callers, ie.request_firmware()andrequest_firmware_work_func().The difference between them is that
request_firmware()fails immediately with aWARN_ON()if it seesusermodehelper_disabledset after acquiringumhelper_sem, whilerequest_firmware_work_func()waits forusermodehelper_disabledto be unset, with a timeout (the wait time is subtracted from the_request_firmware()timeout). The reason whyrequest_firmware_work_func()does it this way is that it can't wait forusermodehelper_disabledto be unset withumhelper_semheld and it has to call_request_firmware()underumhelper_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
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 ofumhelper_semdown to the callers, ie.request_firmware()andrequest_firmware_work_func().The difference between them is that
request_firmware()fails immediately with aWARN_ON()if it seesusermodehelper_disabledset after acquiringumhelper_sem, whilerequest_firmware_work_func()waits forusermodehelper_disabledto be unset, with a timeout (the wait time is subtracted from the_request_firmware()timeout). The reason whyrequest_firmware_work_func()does it this way is that it can't wait forusermodehelper_disabledto be unset withumhelper_semheld and it has to call_request_firmware()underumhelper_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
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
@@ -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
@@ -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
@@ -533,12 +533,10 @@ static int _request_firmware(const strucreturn 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 strucfw_destroy_instance (fw_priv); out: -read_unlock_usermodehelper (); +usermodehelper_read_unlock (); +out_nolock:if (retval) {release_firmware (firmware); *firmware_p =NULL ;
PATCH 2/3 firmware_class: Split _request_firmware() into three functions
In reply to Rafael J. WysockiFrom: 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
@@ -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 fdevice_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 strucreturn 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(voreturn 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);
PATCH 3/3 firmware_class: Do not warn that system is not ready for async loads
In reply to Rafael J. WysockiFrom: 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
@@ -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
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 inrequest_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. ThatWARN_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