Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Copy the kernel module data from user space in chunks

Unlike most (all?) other copies from user space, kernel module loading
is almost unlimited in size. So we do a potentially huge
"copy_from_user()" when we copy the module data from user space to the
kernel buffer, which can be a latency concern when preemption is
disabled (or voluntary).

Also, because 'copy_from_user()' clears the tail of the kernel buffer on
failures, even a *failed* copy can end up wasting a lot of time.

Normally neither of these are concerns in real life, but they do trigger
when doing stress-testing with trinity. Running in a VM seems to add
its own overheadm causing trinity module load testing to even trigger
the watchdog.

The simple fix is to just chunk up the module loading, so that it never
tries to copy insanely big areas in one go. That bounds the latency,
and also the amount of (unnecessarily, in this case) cleared memory for
the failure case.

Reported-by: Sasha Levin <sasha.levin@oracle.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

+18 -1
+18 -1
kernel/module.c
··· 2479 2479 return 0; 2480 2480 } 2481 2481 2482 + #define COPY_CHUNK_SIZE (16*PAGE_SIZE) 2483 + 2484 + static int copy_chunked_from_user(void *dst, const void __user *usrc, unsigned long len) 2485 + { 2486 + do { 2487 + unsigned long n = min(len, COPY_CHUNK_SIZE); 2488 + 2489 + if (copy_from_user(dst, usrc, n) != 0) 2490 + return -EFAULT; 2491 + cond_resched(); 2492 + dst += n; 2493 + usrc += n; 2494 + len -= n; 2495 + } while (len); 2496 + return 0; 2497 + } 2498 + 2482 2499 /* Sets info->hdr and info->len. */ 2483 2500 static int copy_module_from_user(const void __user *umod, unsigned long len, 2484 2501 struct load_info *info) ··· 2515 2498 if (!info->hdr) 2516 2499 return -ENOMEM; 2517 2500 2518 - if (copy_from_user(info->hdr, umod, info->len) != 0) { 2501 + if (copy_chunked_from_user(info->hdr, umod, info->len) != 0) { 2519 2502 vfree(info->hdr); 2520 2503 return -EFAULT; 2521 2504 }