Re: [BUG] More issues with arm/aes-neonbs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 7 Aug 2024 at 23:17, Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx> wrote:
>
> OK I tracked it down to a recursive module load that hangs because
> of this commit:
>
> commit 9b9879fc03275ffe0da328cf5b864d9e694167c8
> Author: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
> Date:   Mon May 29 21:39:51 2023 -0400
>
>     modules: catch concurrent module loads, treat them as idempotent
>
> So what's happening here is that the first modprobe tries to load
> a fallback CBC implementation, in doing so it triggers a load of
> the exact same module due to module aliases.

Ahh. That would indeed be very wrong, but yes, the fact that it now
just ends up hanging is very annoying and not helpful for debugging.

Sadly, the "return -EBUSY" that the code initially did caused problems
because when the loading isn't recursive, but just concurrent (because
two separate users show up at the same time), you really do want to
wait for the original loader.

> Now I presume this used to just fail immediately which is OK because
> user-space would then try to load other aliases of ecb(aes).  But it
> now hangs which causes the whole thing to freeze until a timeout
> hits somwhere along the line.

What used to happen is that the recursive loader would *also* load the
module into memory (so it's loaded twice), but then the two loaders at
the end get serialized by the 'module_mutex' when it does the
add_unformed_module()

So at that point the module_patient_check_exists() would notice that
one or the other loaded module is a duplicate, and would exit with an
error (EBUSY or EEXIST depending on whether the "winning" module got
to MODULE_STATE_LIVE in the meantime).

The new code exists exactly because on big machines, the "load X
modules concurrently" was a huge cost, and would use literally
gigabytes of memory loading the same duplicated module multiple times
concurrently, only for all but one of them to fail.

So now we catch that "we're already loading this module" early, and
wait for the first loader to do it all.

But yes, it does mean that you can't recursively load the same module
from the kernel.

... which was obviously always bogus, but now it's actively very wrong.

Sadly, loading modules recursively is very much required in general
(because modules depend on each other). So we do need to deal with
that. It's only loading the *same* module recurively that is very very
bad.

I guess we could at least add a timeout and a big fat warning when it
triggers. But what's the right timeout? Sometimes module loading can
really be very slow, if it problems hardware.

Let me think about this, because the new behavior is obviously not
great for this situation, even if it was triggered by a different
kernel bug / misfeature. From a debugging standpoint, that "silent
hang" is most definitely bad.

It did apparently take a long time for people to notice (that module
loading behavior is over a year old by now). How hard is it to just
fix the recursive load?

ANYWAY.

While I think some more about this, does this attached patch at least
give you an error printout? It won't fix the situation, but at least
the "silently wait forever" should turn into a "wait forever but with
a warning".

Which isn't perfect, but is better, and would presumably have made it
a whole lot easier to debug this nasty situation.

Hmm?

(Please note: ENTIRELY UNTESTED! It compiles for me, but I might have
done something incredibly stupid and maybe there's some silly and
fatal bug in what _appears_ trivially correct).

                 Linus
 kernel/module/main.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/kernel/module/main.c b/kernel/module/main.c
index d9592195c5bb..4150a546a00a 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3183,15 +3183,29 @@ static int idempotent_init_module(struct file *f, const char __user * uargs, int
 	if (!f || !(f->f_mode & FMODE_READ))
 		return -EBADF;
 
-	/* See if somebody else is doing the operation? */
-	if (idempotent(&idem, file_inode(f))) {
-		wait_for_completion(&idem.complete);
-		return idem.ret;
+	/* Are we the winners of the race and get to do this? */
+	if (!idempotent(&idem, file_inode(f))) {
+		int ret = init_module_from_file(f, uargs, flags);
+		return idempotent_complete(&idem, ret);
 	}
 
-	/* Otherwise, we'll do it and complete others */
-	return idempotent_complete(&idem,
-		init_module_from_file(f, uargs, flags));
+	/*
+	 * Somebody else won the race and is loading the module.
+	 *
+	 * We have to wait for it forever, since our 'idem' is
+	 * on the stack and the list entry stays there until
+	 * completed (but we could fix it under the idem_lock)
+	 *
+	 * It's also unclear what a real timeout might be,
+	 * but we could maybe at least make this killable
+	 * and remove the idem entry in that case?
+	 */
+	for (;;) {
+		int ret = wait_for_completion_timeout(&idem.complete, 10*HZ);
+		if (likely(!ret))
+			return idem.ret;
+		pr_warn_once("module '%pD' taking a long time to load", f);
+	}
 }
 
 SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)

[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]
  Powered by Linux