linux-modules.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Petr Pavlu <petr.pavlu@suse.com>
To: mcgrof@kernel.org, Petr Mladek <pmladek@suse.com>
Cc: prarit@redhat.com, david@redhat.com, mwilck@suse.com,
	linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org,
	stable@vger.kernel.org
Subject: Re: [PATCH] module: Don't wait for GOING modules
Date: Sat, 26 Nov 2022 15:43:02 +0100	[thread overview]
Message-ID: <a26ed87f-9e4c-7c1f-515b-edaaff9140fd@suse.com> (raw)
In-Reply-To: <Y348QNmO2AHh3eNr@alley>

On 11/23/22 16:29, Petr Mladek wrote:
> On Wed 2022-11-23 14:12:26, Petr Pavlu wrote:
>> During a system boot, it can happen that the kernel receives a burst of
>> requests to insert the same module but loading it eventually fails
>> during its init call. For instance, udev can make a request to insert
>> a frequency module for each individual CPU when another frequency module
>> is already loaded which causes the init function of the new module to
>> return an error.
>>
>> Since commit 6e6de3dee51a ("kernel/module.c: Only return -EEXIST for
>> modules that have finished loading"), the kernel waits for modules in
>> MODULE_STATE_GOING state to finish unloading before making another
>> attempt to load the same module.
>>
>> This creates unnecessary work in the described scenario and delays the
>> boot. In the worst case, it can prevent udev from loading drivers for
>> other devices and might cause timeouts of services waiting on them and
>> subsequently a failed boot.
>>
>> This patch attempts a different solution for the problem 6e6de3dee51a
>> was trying to solve. Rather than waiting for the unloading to complete,
>> it returns a different error code (-EBUSY) for modules in the GOING
>> state. This should avoid the error situation that was described in
>> 6e6de3dee51a (user space attempting to load a dependent module because
>> the -EEXIST error code would suggest to user space that the first module
>> had been loaded successfully), while avoiding the delay situation too.
>>
>> Fixes: 6e6de3dee51a ("kernel/module.c: Only return -EEXIST for modules that have finished loading")
>> Co-developed-by: Martin Wilck <mwilck@suse.com>
>> Signed-off-by: Martin Wilck <mwilck@suse.com>
>> Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
>> Cc: stable@vger.kernel.org
>> ---
>>
>> Notes:
>>     Sending this alternative patch per the discussion in
>>     https://lore.kernel.org/linux-modules/20220919123233.8538-1-petr.pavlu@suse.com/.
>>     The initial version comes internally from Martin, hence the co-developed tag.
>>
>>  kernel/module/main.c | 8 +++++---
>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>
>> diff --git a/kernel/module/main.c b/kernel/module/main.c
>> index d02d39c7174e..b7e08d1edc27 100644
>> --- a/kernel/module/main.c
>> +++ b/kernel/module/main.c
>> @@ -2386,7 +2386,8 @@ static bool finished_loading(const char *name)
>>  	sched_annotate_sleep();
>>  	mutex_lock(&module_mutex);
>>  	mod = find_module_all(name, strlen(name), true);
>> -	ret = !mod || mod->state == MODULE_STATE_LIVE;
>> +	ret = !mod || mod->state == MODULE_STATE_LIVE
>> +		|| mod->state == MODULE_STATE_GOING;
>>  	mutex_unlock(&module_mutex);
>>  
>>  	return ret;
>> @@ -2566,7 +2567,8 @@ static int add_unformed_module(struct module *mod)
>>  	mutex_lock(&module_mutex);
>>  	old = find_module_all(mod->name, strlen(mod->name), true);
>>  	if (old != NULL) {
>> -		if (old->state != MODULE_STATE_LIVE) {
>> +		if (old->state == MODULE_STATE_COMING
>> +		    || old->state == MODULE_STATE_UNFORMED) {
>>  			/* Wait in case it fails to load. */
>>  			mutex_unlock(&module_mutex);
>>  			err = wait_event_interruptible(module_wq,
>> @@ -2575,7 +2577,7 @@ static int add_unformed_module(struct module *mod)
>>  				goto out_unlocked;
>>  			goto again;
>>  		}
>> -		err = -EEXIST;
>> +		err = old->state != MODULE_STATE_LIVE ? -EBUSY : -EEXIST;
> 
> Hmm, this is not much reliable. It helps only when we manage to read
> the old module state before it is gone.
> 
> A better solution would be to always return when there was a parallel
> load. The older patch from Petr Pavlu was more precise because it
> stored result of the exact parallel load. The below code is easier
> and might be good enough.
> 
> static int add_unformed_module(struct module *mod)
> {
> 	int err;
> 	struct module *old;
> 
> 	mod->state = MODULE_STATE_UNFORMED;
> 
> 	mutex_lock(&module_mutex);
> 	old = find_module_all(mod->name, strlen(mod->name), true);
> 	if (old != NULL) {
> 		if (old->state == MODULE_STATE_COMING
> 		    || old->state == MODULE_STATE_UNFORMED) {
> 			/* Wait for the result of the parallel load. */
> 			mutex_unlock(&module_mutex);
> 			err = wait_event_interruptible(module_wq,
> 					       finished_loading(mod->name));
> 			if (err)
> 				goto out_unlocked;
> 		}
> 
> 		/* The module might have gone in the meantime. */
> 		mutex_lock(&module_mutex);
> 		old = find_module_all(mod->name, strlen(mod->name), true);
> 
> 		/*
> 		 * We are here only when the same module was being loaded.
> 		 * Do not try to load it again right now. It prevents
> 		 * long delays caused by serialized module load failures.
> 		 * It might happen when more devices of the same type trigger
> 		 * load of a particular module.
> 		 */
> 		if (old && old->state == MODULE_STATE_LIVE)
> 			err = -EXIST;
> 		else
> 			err = -EBUSY;
> 		goto out;
> 	}
> 	mod_update_bounds(mod);
> 	list_add_rcu(&mod->list, &modules);
> 	mod_tree_insert(mod);
> 	err = 0;
> 
> out:
> 	mutex_unlock(&module_mutex);
> out_unlocked:
> 	return err;
> }

I think this makes sense. The suggested code only needs to have the second
mutex_lock()+find_module_all() pair moved into the preceding if block to work
correctly. I will wait a bit if there is more feedback and post an updated
patch.

Thanks,
Petr

  reply	other threads:[~2022-11-26 14:43 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-23 13:12 [PATCH] module: Don't wait for GOING modules Petr Pavlu
2022-11-23 15:29 ` Petr Mladek
2022-11-26 14:43   ` Petr Pavlu [this message]
2022-11-27 11:21     ` David Laight
2022-11-30 13:16       ` Petr Mladek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a26ed87f-9e4c-7c1f-515b-edaaff9140fd@suse.com \
    --to=petr.pavlu@suse.com \
    --cc=david@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-modules@vger.kernel.org \
    --cc=mcgrof@kernel.org \
    --cc=mwilck@suse.com \
    --cc=pmladek@suse.com \
    --cc=prarit@redhat.com \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).