kmod: few simple enhancements
Subsystems (5)
Luis R. Rodriguez
maintainercover kmod: few simple enhancements
I've been working on making module loading more deterministic, these are some of the more straight forward changes I've come up with so far. The others depend on some further sysctl changes so I'll wait to introduce those, and also on a new kmod stress driver loader, which I'll also hold off on introducing.
I believe these are pretty straight forward, but please let me know if there are any questions. In this re-spin I've dropped the kref / refcount proposals given its just overkill for what we need here.
Luis R. Rodriguez (6):
kmod: add dynamic max concurrent thread count
module: use list_for_each_entry_rcu() on find_module_all()
kmod: provide wrappers for kmod_concurrent inc/dec
kmod: return -EBUSY if modprobe limit is reached
kmod: preempt on kmod_umh_threads_get()
kmod: use simplified rate limit printk
Documentation/admin-guide/kernel-parameters.txt | 7 ++ include/linux/kmod.h | 3 +- init/Kconfig | 23 ++++++ init/main.c | 1 + kernel/kmod.c | 98 +++++++++++++++++-------- kernel/module.c | 2 +- 6 files changed, 103 insertions(+), 31 deletions(-)
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH 1/6 kmod: add dynamic max concurrent thread count
We currently statically limit the number of modprobe threads which we allow to run concurrently to 50. As per Keith Owens, this was a completely arbitrary value, and it was set in the 2.3.38 days [0] over 16 years ago in year 2000.
Although we haven't yet hit our lower limits, experimentation [1] shows that when and if we hit this limit in the worst case, will be fatal -- consider get_fs_type() failures upon mount on a system which has many partitions, some of which might even be with the same filesystem. Its best to be prudent and increase and set this value to something more sensible which ensures we're far from hitting the limit and also allows default build/user run time override.
The worst case is fatal given that once a module fails to load there is a period of time during which subsequent request for the same module will fail, so in the case of partitions its not just one request that could fail, but whole series of partitions. This later issue of a module request failure domino effect can be addressed later, but increasing the limit to something more meaninful should at least give us enough cushion to avoid this for a while.
Set this value up with a bit more meaninful modern limits:
Bump this up to 64 max for small systems (CONFIG_BASE_SMALL) Bump this up to 128 max for larger systems (!CONFIG_BASE_SMALL)
Also allow the default max limit to be further fine tuned at compile time and at initialization at run time at boot up using the kernel
parameter: max_modprobes.
[0] https://git.kernel.org/cgit/linux/kernel/git/history/history.git/commit/?id=ab1c4ec7410f6ec64e1511d1a7d850fc99c09b44
[1] https://github.com/mcgrof/test_request_module
--- Documentation/admin-guide/kernel-parameters.txt | 7 ++++ include/linux/kmod.h | 3 +- init/Kconfig | 23 +++++++++++++ init/main.c | 1 + kernel/kmod.c | 43 ++++++++++++++++--------- 5 files changed, 61 insertions(+), 16 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +7 -0
@@ -1748,6 +1748,13 @@ keepinitrd [HW,ARM] + kmod.max_modprobes [KNL] + This lets you set the max allowed of concurrent + modprobes threads possible on a system overriding the + default heuristic of: + + min(max_threads/2, 1 << CONFIG_MAX_KMOD_CONCURRENT) + kernelcore= [KNL,X86,IA-64,PPC] Format: nn[KMGTPE] | "mirror" This parameter
include/linux/kmod.h +2 -1
@@ -38,13 +38,14 @@ int __request_module(bool wait, const char *name, ...); #define request_module_nowait(mod...) __request_module(false, mod) #define try_then_request_module(x, mod...) \ ((x) ?: (__request_module(true, mod), (x))) +void init_kmod_umh (void );#else static inline int request_module (const char *name, ...) {return -ENOSYS ; }static inline int request_module_nowait (const char *name, ...) {return -ENOSYS ; } +static inline void init_kmod_umh (void ) { } #define try_then_request_module(x, mod...) (x)#endif -struct cred ;struct file ;
init/Kconfig +23 -0
@@ -2191,6 +2191,29 @@ config TRIM_UNUSED_KSYMS If unsure, or if you need to build out-of-tree modules, say N. +config MAX_KMOD_CONCURRENT + int "Max allowed concurrent request_module() calls (6=>64, 10=>1024)" + range 6 14 + default 7 if !BASE_SMALL + default 6 if BASE_SMALL + help + The kernel restricts the number of possible concurrent calls to + request_module() to help avoid a recursive loop possible with + modules. The default maximum number of concurrent threads allowed + to run request_module() will be: + + max_modprobes = min(max_threads/2, 1 << CONFIG_MAX_KMOD_CONCURRENT); + + The value set in CONFIG_MAX_KMOD_CONCURRENT represents then the power + of 2 value used at boot time for the above computation. You can + override the default built value using the kernel parameter: + + kmod.max_modprobes=4096 + + We set this to default to 64 (2^6) concurrent modprobe threads for + small systems, for larger systems this defaults to 128 (2^7) + concurrent modprobe threads. + endif # MODULES config MODULES_TREE_LOOKUP
init/main.c +1 -0
@@ -650,6 +650,7 @@ asmlinkage __visible void __init start_kernel(void)thread_stack_cache_init ();cred_init ();fork_init (); +init_kmod_umh ();proc_caches_init ();buffer_init ();key_init ();
kernel/kmod.c +28 -15
69 lines not on this page · raw message
Signature · 1 line
2.11.0
Re: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 26 lines · Luis R. Rodriguez
We currently statically limit the number of modprobe threads which we allow to run concurrently to 50. As per Keith Owens, this was a completely arbitrary value, and it was set in the 2.3.38 days [0] over 16 years ago in year 2000.
Although we haven't yet hit our lower limits, experimentation [1] shows that when and if we hit this limit in the worst case, will be fatal -- consider
get_fs_type()failures upon mount on a system which has many partitions, some of which might even be with the same filesystem. Its best to be prudent and increase and set this value to something more sensible which ensures we're far from hitting the limit and also allows default build/user run time override.The worst case is fatal given that once a module fails to load there is a period of time during which subsequent request for the same module will fail, so in the case of partitions its not just one request that could fail, but whole series of partitions. This later issue of a module request failure domino effect can be addressed later, but increasing the limit to something more meaninful should at least give us enough cushion to avoid this for a while.
Set this value up with a bit more meaninful modern limits:
Bump this up to 64 max for small systems (
CONFIG_BASE_SMALL) Bump this up to 128 max for larger systems (!CONFIG_BASE_SMALL)
Quoted 6 lines
Also allow the default max limit to be further fine tuned at compile time and at initialization at run time at boot up using the kernel
parameter:max_modprobes.[0] https://git.kernel.org/cgit/linux/kernel/git/history/history.git/commit/?id=ab1c4ec7410f6ec64e1511d1a7d850fc99c09b44
[1] https://github.com/mcgrof/test_request_module
If we actually run into this issue, instead of slamming the system with bazillion concurrent requests, can we wait for the other modprobes to finish and then continue?
Thanks.
Signature · 1 line
Dmitry
A message that is not in the archive (sent off-list or not yet received): CAB=NE6XGL24O+JfTNUG0HO4obhDc-v+HyL0SCrQELiZrj2-qNw@mail.gmail.com
A message that is not in the archive (sent off-list or not yet received): CAB=NE6Wa4Nemh80yaCCwbjrNRLPD+GJMncg12APg9Vq63AWVng@mail.gmail.com
A message that is not in the archive (sent off-list or not yet received): CAB=NE6Vc6RDAytn2Pkv2V58HFo8ncR0eOHZ3===kbZ2NF78ubg@mail.gmail.com
A message that is not in the archive (sent off-list or not yet received): CAB=NE6Vqmx=y6muenpuQKynTP=pGWMF8tzoCA0BXD6d63q9wPg@mail.gmail.com
Re: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 38 lines · Luis R. Rodriguez
On May 19, 2017 1:45 PM, "Dmitry Torokhov" <[email protected]> wrote:
Quoted 23 lines · Luis R. Rodriguez
We currently statically limit the number of modprobe threads which we allow to run concurrently to 50. As per Keith Owens, this was a completely arbitrary value, and it was set in the 2.3.38 days [0] over 16 years ago in year 2000.
Although we haven't yet hit our lower limits, experimentation [1] shows that when and if we hit this limit in the worst case, will be fatal -- consider
get_fs_type()failures upon mount on a system which has many partitions, some of which might even be with the same filesystem. Its best to be prudent and increase and set this value to something more sensible which ensures we're far from hitting the limit and also allows default build/user run time override.The worst case is fatal given that once a module fails to load there is a period of time during which subsequent request for the same module will fail, so in the case of partitions its not just one request that could fail, but whole series of partitions. This later issue of a module request failure domino effect can be addressed later, but increasing the limit to something more meaninful should at least give us enough cushion to avoid this for a while.
Set this value up with a bit more meaninful modern limits:
Quoted 8 lines
Bump this up to 64 max for small systems (
CONFIG_BASE_SMALL) Bump this up to 128 max for larger systems (!CONFIG_BASE_SMALL)Also allow the default max limit to be further fine tuned at compile time and at initialization at run time at boot up using the kernel
parameter:max_modprobes.history.git/commit/?id=ab1c4ec7410f6ec64e1511d1a7d850fc99c09b44
Quoted 1 line
Quoted 9 lines
If we actually run into this issue, instead of slamming the system with bazillion concurrent requests, can we wait for the other modprobes to finish and then continue?
Yes ! That I have a patch that does precisely that ! That is actually still *not enough* to not fail fatally but this would be subject of another series with more debatable approaches.
Then please post it.
Quoted 2 lines
This at least pushes us to closer safer limits for now while also making it configurable.
Making it configurable depending on how big/little box is makes no sense, especially if the above is implemented, as depth of modprobe invocations depends on configuration and not computing power of the hardware the system is running on.
Thanks.
Signature · 1 line
Dmitry
Luis R. Rodriguez
maintainerRe: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 44 lines · Dmitry Torokhov
Quoted 43 lines · Luis R. Rodriguez
On May 19, 2017 1:45 PM, "Dmitry Torokhov" <[email protected]> wrote:
Quoted 23 lines · Luis R. Rodriguez
We currently statically limit the number of modprobe threads which we allow to run concurrently to 50. As per Keith Owens, this was a completely arbitrary value, and it was set in the 2.3.38 days [0] over 16 years ago in year 2000.
Although we haven't yet hit our lower limits, experimentation [1] shows that when and if we hit this limit in the worst case, will be fatal -- consider
get_fs_type()failures upon mount on a system which has many partitions, some of which might even be with the same filesystem. Its best to be prudent and increase and set this value to something more sensible which ensures we're far from hitting the limit and also allows default build/user run time override.The worst case is fatal given that once a module fails to load there is a period of time during which subsequent request for the same module will fail, so in the case of partitions its not just one request that could fail, but whole series of partitions. This later issue of a module request failure domino effect can be addressed later, but increasing the limit to something more meaninful should at least give us enough cushion to avoid this for a while.
Set this value up with a bit more meaninful modern limits:
Quoted 8 lines
Bump this up to 64 max for small systems (
CONFIG_BASE_SMALL) Bump this up to 128 max for larger systems (!CONFIG_BASE_SMALL)Also allow the default max limit to be further fine tuned at compile time and at initialization at run time at boot up using the kernel
parameter:max_modprobes.history.git/commit/?id=ab1c4ec7410f6ec64e1511d1a7d850fc99c09b44
Quoted 1 line
If we actually run into this issue, instead of slamming the system with bazillion concurrent requests, can we wait for the other modprobes to finish and then continue?
Quoted 6 lines
Quoted 4 lines
Yes ! That I have a patch that does precisely that ! That is actually still *not enough* to not fail fatally but this would be subject of another series with more debatable approaches.
Then please post it.
Will do.
Quoted 5 lines
Quoted 2 lines
This at least pushes us to closer safer limits for now while also making it configurable.
Making it configurable depending on how big/little box is makes no sense,
If we set a hard limit then we need to patch a system if we need to increment it. This is rather stupid given we have no current heuristics to make kmod loading deterministic from userspace, and in the worst case this can be fatal. General system size is a good first guess, but making it configurable is really key given current limitations. I'll post further patches which reveals some of these issues more clearly.
Quoted 3 lines
especially if the above is implemented, as depth of modprobe invocations depends on configuration and not computing power of the hardware the system is running on.
You seem to agree making it configurable is sensible , but not depending on the system size ?
Luis
Re: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 67 lines · Luis R. Rodriguez
Quoted 44 lines · Dmitry Torokhov
Quoted 43 lines · Luis R. Rodriguez
On May 19, 2017 1:45 PM, "Dmitry Torokhov" <[email protected]> wrote:
Quoted 23 lines · Luis R. Rodriguez
We currently statically limit the number of modprobe threads which we allow to run concurrently to 50. As per Keith Owens, this was a completely arbitrary value, and it was set in the 2.3.38 days [0] over 16 years ago in year 2000.
Although we haven't yet hit our lower limits, experimentation [1] shows that when and if we hit this limit in the worst case, will be fatal -- consider
get_fs_type()failures upon mount on a system which has many partitions, some of which might even be with the same filesystem. Its best to be prudent and increase and set this value to something more sensible which ensures we're far from hitting the limit and also allows default build/user run time override.The worst case is fatal given that once a module fails to load there is a period of time during which subsequent request for the same module will fail, so in the case of partitions its not just one request that could fail, but whole series of partitions. This later issue of a module request failure domino effect can be addressed later, but increasing the limit to something more meaninful should at least give us enough cushion to avoid this for a while.
Set this value up with a bit more meaninful modern limits:
Quoted 8 lines
Bump this up to 64 max for small systems (
CONFIG_BASE_SMALL) Bump this up to 128 max for larger systems (!CONFIG_BASE_SMALL)Also allow the default max limit to be further fine tuned at compile time and at initialization at run time at boot up using the kernel
parameter:max_modprobes.history.git/commit/?id=ab1c4ec7410f6ec64e1511d1a7d850fc99c09b44
Quoted 1 line
If we actually run into this issue, instead of slamming the system with bazillion concurrent requests, can we wait for the other modprobes to finish and then continue?
Quoted 6 lines
Quoted 4 lines
Yes ! That I have a patch that does precisely that ! That is actually still *not enough* to not fail fatally but this would be subject of another series with more debatable approaches.
Then please post it.
Will do.
Quoted 5 lines
Quoted 2 lines
This at least pushes us to closer safer limits for now while also making it configurable.
Making it configurable depending on how big/little box is makes no sense,
If we set a hard limit then we need to patch a system if we need to increment it. This is rather stupid given we have no current heuristics to make kmod loading deterministic from userspace, and in the worst case this can be fatal. General system size is a good first guess, but making it configurable is really key given current limitations. I'll post further patches which reveals some of these issues more clearly.
Quoted 6 lines
Quoted 3 lines
especially if the above is implemented, as depth of modprobe invocations depends on configuration and not computing power of the hardware the system is running on.
You seem to agree making it configurable is sensible , but not depending on the system size ?
No, I am saying that making it configurable based on system size makes no sense at all, and making it configurable given you already have patches removing hard failures gives no benefit.
Thanks.
Signature · 1 line
Dmitry
Luis R. Rodriguez
maintainerRe: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 72 lines · Dmitry Torokhov
Quoted 71 lines · Luis R. Rodriguez
Quoted 44 lines · Dmitry Torokhov
Quoted 43 lines · Luis R. Rodriguez
On May 19, 2017 1:45 PM, "Dmitry Torokhov" <[email protected]> wrote:
Quoted 23 lines · Luis R. Rodriguez
We currently statically limit the number of modprobe threads which we allow to run concurrently to 50. As per Keith Owens, this was a completely arbitrary value, and it was set in the 2.3.38 days [0] over 16 years ago in year 2000.
Although we haven't yet hit our lower limits, experimentation [1] shows that when and if we hit this limit in the worst case, will be fatal -- consider
get_fs_type()failures upon mount on a system which has many partitions, some of which might even be with the same filesystem. Its best to be prudent and increase and set this value to something more sensible which ensures we're far from hitting the limit and also allows default build/user run time override.The worst case is fatal given that once a module fails to load there is a period of time during which subsequent request for the same module will fail, so in the case of partitions its not just one request that could fail, but whole series of partitions. This later issue of a module request failure domino effect can be addressed later, but increasing the limit to something more meaninful should at least give us enough cushion to avoid this for a while.
Set this value up with a bit more meaninful modern limits:
Quoted 8 lines
Bump this up to 64 max for small systems (
CONFIG_BASE_SMALL) Bump this up to 128 max for larger systems (!CONFIG_BASE_SMALL)Also allow the default max limit to be further fine tuned at compile time and at initialization at run time at boot up using the kernel
parameter:max_modprobes.history.git/commit/?id=ab1c4ec7410f6ec64e1511d1a7d850fc99c09b44
Quoted 1 line
If we actually run into this issue, instead of slamming the system with bazillion concurrent requests, can we wait for the other modprobes to finish and then continue?
Quoted 6 lines
Quoted 4 lines
Yes ! That I have a patch that does precisely that ! That is actually still *not enough* to not fail fatally but this would be subject of another series with more debatable approaches.
Then please post it.
Will do.
Quoted 5 lines
Quoted 2 lines
This at least pushes us to closer safer limits for now while also making it configurable.
Making it configurable depending on how big/little box is makes no sense,
If we set a hard limit then we need to patch a system if we need to increment it. This is rather stupid given we have no current heuristics to make kmod loading deterministic from userspace, and in the worst case this can be fatal. General system size is a good first guess, but making it configurable is really key given current limitations. I'll post further patches which reveals some of these issues more clearly.
Quoted 3 lines
especially if the above is implemented, as depth of modprobe invocations depends on configuration and not computing power of the hardware the system is running on.
Quoted 6 lines
Quoted 2 lines
You seem to agree making it configurable is sensible , but not depending on the system size ?
No, I am saying that making it configurable based on system size makes no sense at all, and making it configurable given you already have patches removing hard failures gives no benefit.
Ah no, the problem is that hard failures are not yet removed in this patch set at all! This series only contains the things I thought were non-radical really.
In fact even with the subsequent patches from my 2nd series I'll eventually post post -- these fatal issues are not cured at all unless we dance with userspace a bit, or unless as you suggest we have *all* pending theads wait without killing any.
*This* small patch should enable folks to move the needle to a more *fair* limit, its also useful to backport a simple fix even if the other stuff is not merged, *but* it *also* provides a way for systems to move away from the slippery slope if they know what they are doing.
I'll post the 2nd series next.
Luis
Re: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 77 lines · Luis R. Rodriguez
Quoted 75 lines · Dmitry Torokhov
Quoted 73 lines · Luis R. Rodriguez
Quoted 44 lines · Dmitry Torokhov
Quoted 43 lines · Luis R. Rodriguez
On May 19, 2017 1:45 PM, "Dmitry Torokhov" <[email protected]> wrote:
Quoted 23 lines · Luis R. Rodriguez
We currently statically limit the number of modprobe threads which we allow to run concurrently to 50. As per Keith Owens, this was a completely arbitrary value, and it was set in the 2.3.38 days [0] over 16 years ago in year 2000.
Although we haven't yet hit our lower limits, experimentation [1] shows that when and if we hit this limit in the worst case, will be fatal -- consider
get_fs_type()failures upon mount on a system which has many partitions, some of which might even be with the same filesystem. Its best to be prudent and increase and set this value to something more sensible which ensures we're far from hitting the limit and also allows default build/user run time override.The worst case is fatal given that once a module fails to load there is a period of time during which subsequent request for the same module will fail, so in the case of partitions its not just one request that could fail, but whole series of partitions. This later issue of a module request failure domino effect can be addressed later, but increasing the limit to something more meaninful should at least give us enough cushion to avoid this for a while.
Set this value up with a bit more meaninful modern limits:
Quoted 8 lines
Bump this up to 64 max for small systems (
CONFIG_BASE_SMALL) Bump this up to 128 max for larger systems (!CONFIG_BASE_SMALL)Also allow the default max limit to be further fine tuned at compile time and at initialization at run time at boot up using the kernel
parameter:max_modprobes.history.git/commit/?id=ab1c4ec7410f6ec64e1511d1a7d850fc99c09b44
Quoted 1 line
If we actually run into this issue, instead of slamming the system with bazillion concurrent requests, can we wait for the other modprobes to finish and then continue?
Quoted 6 lines
Quoted 4 lines
Yes ! That I have a patch that does precisely that ! That is actually still *not enough* to not fail fatally but this would be subject of another series with more debatable approaches.
Then please post it.
Will do.
Quoted 5 lines
Quoted 2 lines
This at least pushes us to closer safer limits for now while also making it configurable.
Making it configurable depending on how big/little box is makes no sense,
If we set a hard limit then we need to patch a system if we need to increment it. This is rather stupid given we have no current heuristics to make kmod loading deterministic from userspace, and in the worst case this can be fatal. General system size is a good first guess, but making it configurable is really key given current limitations. I'll post further patches which reveals some of these issues more clearly.
Quoted 3 lines
especially if the above is implemented, as depth of modprobe invocations depends on configuration and not computing power of the hardware the system is running on.
You seem to agree making it configurable is sensible , but not depending on the system size ?
Quoted 7 lines
Quoted 3 lines
No, I am saying that making it configurable based on system size makes no sense at all, and making it configurable given you already have patches removing hard failures gives no benefit.
Ah no, the problem is that hard failures are not yet removed in this patch set at all! This series only contains the things I thought were non-radical really.
I know they are not removed in this patch set.
Quoted 5 lines
In fact even with the subsequent patches from my 2nd series I'll eventually post post -- these fatal issues are not cured at all unless we dance with userspace a bit, or unless as you suggest we have *all* pending theads wait without killing any.
Well, that is too bad, I understood you already implemented what I suggested.
Quoted 5 lines
*This* small patch should enable folks to move the needle to a more *fair* limit, its also useful to backport a simple fix even if the other stuff is not merged, *but* it *also* provides a way for systems to move away from the slippery slope if they know what they are doing.
Look, you are trying to push a band-aid solution for a problem that is purely theoretical (as you say in your patch description we are not hitting this problem in practice, only your test does). There is no slippery slope for systems to move away, no need to backport anything. We seem to agree that a better solution is possible (throttle number of concurrently running modprobes without killing requesters), and with that solution the band-aid will no longer be needed.
So please implement and post the proper fix for the issue.
Thanks.
Signature · 1 line
Dmitry
Luis R. Rodriguez
maintainerRe: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 89 lines · Dmitry Torokhov
Quoted 77 lines · Luis R. Rodriguez
Quoted 75 lines · Dmitry Torokhov
Quoted 73 lines · Luis R. Rodriguez
Quoted 44 lines · Dmitry Torokhov
Quoted 43 lines · Luis R. Rodriguez
On May 19, 2017 1:45 PM, "Dmitry Torokhov" <[email protected]> wrote:
Quoted 23 lines · Luis R. Rodriguez
We currently statically limit the number of modprobe threads which we allow to run concurrently to 50. As per Keith Owens, this was a completely arbitrary value, and it was set in the 2.3.38 days [0] over 16 years ago in year 2000.
Although we haven't yet hit our lower limits, experimentation [1] shows that when and if we hit this limit in the worst case, will be fatal -- consider
get_fs_type()failures upon mount on a system which has many partitions, some of which might even be with the same filesystem. Its best to be prudent and increase and set this value to something more sensible which ensures we're far from hitting the limit and also allows default build/user run time override.The worst case is fatal given that once a module fails to load there is a period of time during which subsequent request for the same module will fail, so in the case of partitions its not just one request that could fail, but whole series of partitions. This later issue of a module request failure domino effect can be addressed later, but increasing the limit to something more meaninful should at least give us enough cushion to avoid this for a while.
Set this value up with a bit more meaninful modern limits:
Quoted 8 lines
Bump this up to 64 max for small systems (
CONFIG_BASE_SMALL) Bump this up to 128 max for larger systems (!CONFIG_BASE_SMALL)Also allow the default max limit to be further fine tuned at compile time and at initialization at run time at boot up using the kernel
parameter:max_modprobes.history.git/commit/?id=ab1c4ec7410f6ec64e1511d1a7d850fc99c09b44
Quoted 1 line
If we actually run into this issue, instead of slamming the system with bazillion concurrent requests, can we wait for the other modprobes to finish and then continue?
Quoted 6 lines
Quoted 4 lines
Yes ! That I have a patch that does precisely that ! That is actually still *not enough* to not fail fatally but this would be subject of another series with more debatable approaches.
Then please post it.
Will do.
Quoted 5 lines
Quoted 2 lines
This at least pushes us to closer safer limits for now while also making it configurable.
Making it configurable depending on how big/little box is makes no sense,
If we set a hard limit then we need to patch a system if we need to increment it. This is rather stupid given we have no current heuristics to make kmod loading deterministic from userspace, and in the worst case this can be fatal. General system size is a good first guess, but making it configurable is really key given current limitations. I'll post further patches which reveals some of these issues more clearly.
Quoted 3 lines
especially if the above is implemented, as depth of modprobe invocations depends on configuration and not computing power of the hardware the system is running on.
You seem to agree making it configurable is sensible , but not depending on the system size ?
Quoted 7 lines
Quoted 3 lines
No, I am saying that making it configurable based on system size makes no sense at all, and making it configurable given you already have patches removing hard failures gives no benefit.
Ah no, the problem is that hard failures are not yet removed in this patch set at all! This series only contains the things I thought were non-radical really.
I know they are not removed in this patch set.
Quoted 1 line
Quoted 7 lines
Quoted 4 lines
In fact even with the subsequent patches from my 2nd series I'll eventually post post -- these fatal issues are not cured at all unless we dance with userspace a bit, or unless as you suggest we have *all* pending theads wait without killing any.
Well, that is too bad, I understood you already implemented what I suggested.
We seem to want the same so that is good actually.
Quoted 8 lines
Quoted 4 lines
*This* small patch should enable folks to move the needle to a more *fair* limit, its also useful to backport a simple fix even if the other stuff is not merged, *but* it *also* provides a way for systems to move away from the slippery slope if they know what they are doing.
Look, you are trying to push a band-aid solution for a problem that is purely theoretical (as you say in your patch description we are not hitting this problem in practice, only your test does).
I have a stress test driver which reveals we can easily hit it. In practice the only way to know if we hit the limit is a:
"request_module: runaway loop modprobe %s" message on dmesg, however its fatal, how often people inspect a kernel log to see if that came up though... not sure. So a module could not be loaded and we may not realize it.
Quoted 7 lines
There is
no slippery slope for systems to move away, no need to backport anything. We seem to agree that a better solution is possible (throttle number of concurrently running modprobes without killing requesters), and with that solution the band-aid will no longer be needed.So please implement and post the proper fix for the issue.
Alright, will do away with this patch and just go for the jugular of the issue.
Luis
Luis R. Rodriguez
maintainerRe: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 10 lines · Luis R. Rodriguez
On Thu, May 25, 2017 at 10:30 AM, Dmitry Torokhov
Quoted 7 lines
There is
no slippery slope for systems to move away, no need to backport anything. We seem to agree that a better solution is possible (throttle number of concurrently running modprobes without killing requesters), and with that solution the band-aid will no longer be needed.So please implement and post the proper fix for the issue.
Alright, will do away with this patch and just go for the jugular of the issue.
I gave this some more thought, even if we go with the throttling right away in practice you'll end up with a dmesg notice of a throttle kicking in once you *do* reach this. We are forcing only 50 concurrent threads and making this a static limit with no good reason than 2.3.38 days evaluation from 16 years ago (2000). If we throttle we are going to throttle with a 2.3.38 days limit. And you advocate that ?
Luis
Re: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 8 lines · Luis R. Rodriguez
Quoted 7 lines · Luis R. Rodriguez
On Thu, May 25, 2017 at 10:30 AM, Dmitry Torokhov
Quoted 6 lines
There is
no slippery slope for systems to move away, no need to backport anything. We seem to agree that a better solution is possible (throttle number of concurrently running modprobes without killing requesters), and with that solution the band-aid will no longer be needed.
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
So please implement and post the proper fix for the issue.
Alright, will do away with this patch and just go for the jugular of the issue.
I gave this some more thought, even if we go with the throttling right away in practice you'll end up with a dmesg notice of a throttle kicking in once you *do*
So remove it. The warning was meaningful when we rejected requests, now it is not.
Quoted 4 lines
reach this. We are forcing only 50 concurrent threads and making this a static limit with no good reason than 2.3.38 days evaluation from 16 years ago (2000). If we throttle we are going to throttle with a 2.3.38 days limit. And you advocate that ?
Yes. Can you give me reason why slamming the system with more than 50 modprobes is a good idea in 4.12 days? Does the increased limit decreases boot time? By how much?
Signature · 1 line
Dmitry
Luis R. Rodriguez
maintainerRe: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 11 lines · Dmitry Torokhov
Quoted 10 lines · Luis R. Rodriguez
Quoted 9 lines · Luis R. Rodriguez
On Thu, May 25, 2017 at 10:30 AM, Dmitry Torokhov
Quoted 7 lines
There is
no slippery slope for systems to move away, no need to backport anything. We seem to agree that a better solution is possible (throttle number of concurrently running modprobes without killing requesters), and with that solution the band-aid will no longer be needed.So please implement and post the proper fix for the issue.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
Alright, will do away with this patch and just go for the jugular of the issue.
I gave this some more thought, even if we go with the throttling right away in practice you'll end up with a dmesg notice of a throttle kicking in once you *do*
So remove it. The warning was meaningful when we rejected requests, now it is not.
Great.
Quoted 8 lines
Quoted 4 lines
reach this. We are forcing only 50 concurrent threads and making this a static limit with no good reason than 2.3.38 days evaluation from 16 years ago (2000). If we throttle we are going to throttle with a 2.3.38 days limit. And you advocate that ?
Yes. Can you give me reason why slamming the system with more than 50 modprobes is a good idea in 4.12 days? Does the increased limit decreases boot time? By how much?
If in practice we are not hitting the limit the point is moot, and when we do I agree we can re-evaluate. With my stress test driver on a test case we can push as hard as bringing out the OOM killer even if we throttle, fun.
Luis
Luis R. Rodriguez
maintainerRe: [PATCH 1/6] kmod: add dynamic max concurrent thread count
Quoted 28 lines · Luis R. Rodriguez
Quoted 11 lines · Dmitry Torokhov
Quoted 10 lines · Luis R. Rodriguez
Quoted 9 lines · Luis R. Rodriguez
On Thu, May 25, 2017 at 10:30 AM, Dmitry Torokhov
Quoted 7 lines
There is
no slippery slope for systems to move away, no need to backport anything. We seem to agree that a better solution is possible (throttle number of concurrently running modprobes without killing requesters), and with that solution the band-aid will no longer be needed.So please implement and post the proper fix for the issue.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
Alright, will do away with this patch and just go for the jugular of the issue.
I gave this some more thought, even if we go with the throttling right away in practice you'll end up with a dmesg notice of a throttle kicking in once you *do*
So remove it. The warning was meaningful when we rejected requests, now it is not.
Great.
Quoted 5 lines
Quoted 4 lines
reach this. We are forcing only 50 concurrent threads and making this a static limit with no good reason than 2.3.38 days evaluation from 16 years ago (2000). If we throttle we are going to throttle with a 2.3.38 days limit. And you advocate that ?
Quoted 8 lines
Quoted 3 lines
Yes. Can you give me reason why slamming the system with more than 50 modprobes is a good idea in 4.12 days? Does the increased limit decreases boot time? By how much?
If in practice we are not hitting the limit the point is moot, and when we do I agree we can re-evaluate. With my stress test driver on a test case we can push as hard as bringing out the OOM killer even if we throttle, fun.
Alright, I don't see these OOMs anymore *after* I actually nuked that patch which incremented the kmod limit. The reason we can OOM is finit_module() can consume gobs of memory, the current value then seems fair, and work well for my tests provided we do use the proper throttle. Will respin.
Luis
Luis R. Rodriguez
maintainerPATCH 2/6 module: use list_for_each_entry_rcu() on find_module_all()
In reply to Luis R. RodriguezThe module list has been using RCU in a lot of other calls for a while now, we just overlooked changing this one over to use RCU.
--- kernel/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/module.c +1 -1
@@ -602,7 +602,7 @@ static struct module *find_module_all(const char *name, size_t len,module_assert_mutex_or_preempt (); -list_for_each_entry (mod, &modules, list) { +list_for_each_entry_rcu (mod, &modules, list) {if (!even_unformed && mod->state ==MODULE_STATE_UNFORMED )continue ;if (strlen (mod->name) == len && !memcmp (mod->name, name, len))
Signature · 1 line
2.11.0
Re: [PATCH 2/6] module: use list_for_each_entry_rcu() on find_module_all()
Quoted 14 lines · Luis R. Rodriguez
The module list has been using RCU in a lot of other calls for a while now, we just overlooked changing this one over to use RCU.
Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)kernel/module.c +0
-0
kernel/module.c static struct module *find_module_all(const char *name, size_t len, · line 608
Quoted 8 lines
kernel/module.c +1
-1@@ -602,7 +602,7 @@ static struct module *find_module_all(const char *name, size_t len,module_assert_mutex_or_preempt (); -list_for_each_entry (mod, &modules, list) { +list_for_each_entry_rcu (mod, &modules, list) {if (!even_unformed && mod->state ==MODULE_STATE_UNFORMED )continue ;if (strlen (mod->name) == len && !memcmp (mod->name, name, len))
This makes sense. It would not be an issue if all callers of find_module_all() held module_mutex, but module_kallsyms_lookup_name() does not.
There is one more occurrence of just list_for_each_entry(mod, &modules, list) in kernel/module.c -- in module_kallsyms_on_each_symbol(). But that one is safe because we have to hold the mutex there.
Miroslav
Luis R. Rodriguez
maintainerPATCH 3/6 kmod: provide wrappers for kmod_concurrent inc/dec
In reply to Luis R. Rodriguezkmod_concurrent is used as an atomic counter for enabling the allowed limit of modprobe calls, provide wrappers for it to enable this to be expanded on more easily. This will be done later.
--- kernel/kmod.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-)
kernel/kmod.c +19 -6
@@ -59,6 +59,7 @@ static DEFINE_SPINLOCK(umh_sysctl_lock);static DECLARE_RWSEM (umhelper_sem);#ifdef CONFIG_MODULES +static atomic_t kmod_concurrent =ATOMIC_INIT (0 );/* modprobe_path is set via /proc/sys.
@@ -110,6 +111,20 @@ static int call_modprobe(char *module_name, int wait)return -ENOMEM ; } +static int kmod_umh_threads_get (void ) +{ +atomic_inc (&kmod_concurrent); +if (atomic_read (&kmod_concurrent) <= max_modprobes) +return 0 ; +atomic_dec (&kmod_concurrent); +return -ENOMEM ; +} + +static void kmod_umh_threads_put (void ) +{ +atomic_dec (&kmod_concurrent); +} +/** * __request_module - try to load a kernel module * @wait: wait (or not) for the operation to complete
@@ -131,7 +146,6 @@ int __request_module(bool wait, const char *fmt, ...)va_list args;char module_name[MODULE_NAME_LEN ];int ret; -static atomic_t kmod_concurrent =ATOMIC_INIT (0 );static int kmod_loop_msg;/*
@@ -155,8 +169,8 @@ int __request_module(bool wait, const char *fmt, ...)if (ret)return ret; -atomic_inc (&kmod_concurrent); -if (atomic_read (&kmod_concurrent) > max_modprobes) { + ret =kmod_umh_threads_get (); +if (ret) {/* We may be blaming an innocent here, but unlikely */ if (kmod_loop_msg <5 ) { printk(KERN_ERR
@@ -164,15 +178,14 @@ int __request_module(bool wait, const char *fmt, ...) module_name); kmod_loop_msg++; } -atomic_dec (&kmod_concurrent); -return -ENOMEM ; +return ret; }trace_module_request (module_name, wait, _RET_IP_); ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC ); -atomic_dec (&kmod_concurrent); +kmod_umh_threads_put ();return ret; }EXPORT_SYMBOL (__request_module);
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH 4/6 kmod: return -EBUSY if modprobe limit is reached
In reply to Luis R. RodriguezRunning out of our modprobe limit is not a memory limit but a system specific established limitation set to avoid a possible recursive issue with modprobe. This gives userspace a better idea of what happened if we can't load a module, it could use this to wait and try again.
--- kernel/kmod.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/kmod.c +1 -1
@@ -117,7 +117,7 @@ static int kmod_umh_threads_get(void)if (atomic_read (&kmod_concurrent) <= max_modprobes)return 0 ;atomic_dec (&kmod_concurrent); -return -ENOMEM ; +return -EBUSY ; }static void kmod_umh_threads_put (void )
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH 5/6 kmod: preempt on kmod_umh_threads_get()
In reply to Luis R. RodriguezIn theory it is possible multiple concurrent threads will try to kmod_umh_threads_get() and as such atomic_inc(&kmod_concurrent) at the same time, therefore enabling a small time during which we've bumped kmod_concurrent but have not really enabled work. By using preemption we mitigate this a bit.
Preemption is not needed when we kmod_umh_threads_put().
--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
kernel/kmod.c +22 -2
@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here +* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes) -return 0 ; + goto out; +atomic_dec (&kmod_concurrent); -return -EBUSY ; + ret = -EBUSY ; +out: +preempt_enable (); +return ret; }static void kmod_umh_threads_put (void ) { +/* +* Preemption is not needed given once work is done we can +* pace ourselves on our way out. +*/ atomic_dec (&kmod_concurrent); }
Signature · 1 line
2.11.0
Re: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
Quoted 25 lines · Luis R. Rodriguez
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here
kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)
That is very "fancy" way to basically say:
if (atomic_inc_return(&kmod_concurrent) <= max_modprobes) ...
Quoted 23 lines
kernel/kmod.c +10
-2-return 0 ; + goto out; +atomic_dec (&kmod_concurrent); -return -EBUSY ; + ret = -EBUSY ; +out: +preempt_enable (); +return ret; }static void kmod_umh_threads_put (void ) { +/* +* Preemption is not needed given once work is done we can +* pace ourselves on our way out. +*/ atomic_dec (&kmod_concurrent); }Signature · 1 line
2.11.0
Thanks.
Signature · 1 line
Dmitry
Luis R. Rodriguez
maintainerRe: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
Quoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Luis R. Rodriguez
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)That is very "fancy" way to basically say:
Quoted 1 line
if (
atomic_inc_return(&kmod_concurrent)<=max_modprobes)
Do you mean to combine the atomic_inc() and atomic_read() in one as you noted (as that is not a change in this patch), *or* that using a memory barrier here with atomic_inc_return() should suffice to address the same and avoid an explicit preemption enable / disable ?
Luis
Re: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
Quoted 40 lines · Luis R. Rodriguez
Quoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Luis R. Rodriguez
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)That is very "fancy" way to basically say:
Quoted 6 lines
Quoted 1 line
if (
atomic_inc_return(&kmod_concurrent)<=max_modprobes)Do you mean to combine the
atomic_inc()andatomic_read()in one as you noted (as that is not a change in this patch), *or* that using a memory barrier here withatomic_inc_return()should suffice to address the same and avoid an explicit preemption enable / disable ?
I am saying that atomic_inc_return() will avoid situation where you have more than one threads incrementing the counter and believing that they are [not] allowed to start modprobe.
I have no idea why you think preempt_disable() would help here. It only ensures that current thread will not be preempted between the point where you update the counter and where you check the result. It does not stop interrupts nor does it affect other threads that might be updating the same counter.
Thanks.
Signature · 1 line
Dmitry
Luis R. Rodriguez
maintainerRe: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
Quoted 52 lines · Dmitry Torokhov
Quoted 40 lines · Luis R. Rodriguez
Quoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Luis R. Rodriguez
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)That is very "fancy" way to basically say:
Quoted 6 lines
Quoted 1 line
if (
atomic_inc_return(&kmod_concurrent)<=max_modprobes)Do you mean to combine the
atomic_inc()andatomic_read()in one as you noted (as that is not a change in this patch), *or* that using a memory barrier here withatomic_inc_return()should suffice to address the same and avoid an explicit preemption enable / disable ?I am saying that
atomic_inc_return()will avoid situation where you have more than one threads incrementing the counter and believing that they are [not] allowed to start modprobe.
Quoted 5 lines
I have no idea why you think
preempt_disable()would help here. It only ensures that current thread will not be preempted between the point where you update the counter and where you check the result. It does not stop interrupts nor does it affect other threads that might be updating the same counter.
The preemption was inspired by __module_get() and try_module_get(), was that rather silly ?
Luis
Re: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
Quoted 53 lines · Luis R. Rodriguez
Quoted 52 lines · Dmitry Torokhov
Quoted 40 lines · Luis R. Rodriguez
Quoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Luis R. Rodriguez
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)That is very "fancy" way to basically say:
Quoted 6 lines
Quoted 1 line
if (
atomic_inc_return(&kmod_concurrent)<=max_modprobes)Do you mean to combine the
atomic_inc()andatomic_read()in one as you noted (as that is not a change in this patch), *or* that using a memory barrier here withatomic_inc_return()should suffice to address the same and avoid an explicit preemption enable / disable ?I am saying that
atomic_inc_return()will avoid situation where you have more than one threads incrementing the counter and believing that they are [not] allowed to start modprobe.
Quoted 8 lines
Quoted 5 lines
I have no idea why you think
preempt_disable()would help here. It only ensures that current thread will not be preempted between the point where you update the counter and where you check the result. It does not stop interrupts nor does it affect other threads that might be updating the same counter.The preemption was inspired by
__module_get()andtry_module_get(), was that rather silly ?
As far as I can see prrempt_disable() was needed in __module_get() when modules user per-cpu refcounts: you did not want to move away from CPU while manipulating refcount.
Now that modules use simple atomics for refcounting I think these preempt_disable() and preempt_enable() can be removed.
Thanks.
Signature · 1 line
Dmitry
Re: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
Quoted 63 lines · Dmitry Torokhov
Quoted 53 lines · Luis R. Rodriguez
Quoted 52 lines · Dmitry Torokhov
Quoted 40 lines · Luis R. Rodriguez
Quoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Luis R. Rodriguez
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)That is very "fancy" way to basically say:
Quoted 6 lines
Quoted 1 line
if (
atomic_inc_return(&kmod_concurrent)<=max_modprobes)Do you mean to combine the
atomic_inc()andatomic_read()in one as you noted (as that is not a change in this patch), *or* that using a memory barrier here withatomic_inc_return()should suffice to address the same and avoid an explicit preemption enable / disable ?I am saying that
atomic_inc_return()will avoid situation where you have more than one threads incrementing the counter and believing that they are [not] allowed to start modprobe.Quoted 8 lines
Quoted 5 lines
I have no idea why you think
preempt_disable()would help here. It only ensures that current thread will not be preempted between the point where you update the counter and where you check the result. It does not stop interrupts nor does it affect other threads that might be updating the same counter.The preemption was inspired by
__module_get()andtry_module_get(), was that rather silly ?
Quoted 6 lines
As far as I can see
prrempt_disable()was needed in__module_get()when modules user per-cpu refcounts: you did not want to move away from CPU while manipulating refcount.Now that modules use simple atomics for refcounting I think these
preempt_disable()andpreempt_enable()can be removed.
preempt_disable() still might be useful because you do the atomic_dec() when you reach the limit.
By other words, you have three operations that should be atomic:
inc, read, and dec. atomic_inc_return() covers only two of them.
Hmm, a solution might be to use atomic_dec_if_positive(). I would kmod_concurrent to something like kmod_concurrent_allowed, intialize it with the maximum allowed number. Then you could do:
static int kmod_umh_threads_get(void)
{
if (atomic_dec_if_positive(kmod_concurrent_available) < 0)
return -EBUSY;
return 0;
}Best Regards,
Petr
Luis R. Rodriguez
maintainerRe: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
Quoted 81 lines · Petr Mladek
Quoted 63 lines · Dmitry Torokhov
Quoted 53 lines · Luis R. Rodriguez
Quoted 52 lines · Dmitry Torokhov
Quoted 40 lines · Luis R. Rodriguez
Quoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Luis R. Rodriguez
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)That is very "fancy" way to basically say:
Quoted 6 lines
Quoted 1 line
if (
atomic_inc_return(&kmod_concurrent)<=max_modprobes)Do you mean to combine the
atomic_inc()andatomic_read()in one as you noted (as that is not a change in this patch), *or* that using a memory barrier here withatomic_inc_return()should suffice to address the same and avoid an explicit preemption enable / disable ?I am saying that
atomic_inc_return()will avoid situation where you have more than one threads incrementing the counter and believing that they are [not] allowed to start modprobe.Quoted 8 lines
Quoted 5 lines
I have no idea why you think
preempt_disable()would help here. It only ensures that current thread will not be preempted between the point where you update the counter and where you check the result. It does not stop interrupts nor does it affect other threads that might be updating the same counter.The preemption was inspired by
__module_get()andtry_module_get(), was that rather silly ?Quoted 6 lines
As far as I can see
prrempt_disable()was needed in__module_get()when modules user per-cpu refcounts: you did not want to move away from CPU while manipulating refcount.Now that modules use simple atomics for refcounting I think these
preempt_disable()andpreempt_enable()can be removed.
preempt_disable()still might be useful because you do theatomic_dec()when you reach the limit.By other words, you have three operations that should be atomic:
inc, read, and dec.atomic_inc_return()covers only two of them.Hmm, a solution might be to use
atomic_dec_if_positive(). I wouldkmod_concurrentto something likekmod_concurrent_allowed, intialize it with the maximum allowed number. Then you could do:
Quoted 6 lines
static int
kmod_umh_threads_get(void){ if (atomic_dec_if_positive(kmod_concurrent_available) < 0) return -EBUSY; return 0; }
I like this much more, thanks!
Luis
Re: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
In reply to Petr MladekQuoted 68 lines · Petr Mladek
Quoted 67 lines · Dmitry Torokhov
Quoted 53 lines · Luis R. Rodriguez
Quoted 52 lines · Dmitry Torokhov
Quoted 40 lines · Luis R. Rodriguez
Quoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Luis R. Rodriguez
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)That is very "fancy" way to basically say:
Quoted 6 lines
Quoted 1 line
if (
atomic_inc_return(&kmod_concurrent)<=max_modprobes)Do you mean to combine the
atomic_inc()andatomic_read()in one as you noted (as that is not a change in this patch), *or* that using a memory barrier here withatomic_inc_return()should suffice to address the same and avoid an explicit preemption enable / disable ?I am saying that
atomic_inc_return()will avoid situation where you have more than one threads incrementing the counter and believing that they are [not] allowed to start modprobe.Quoted 8 lines
Quoted 5 lines
I have no idea why you think
preempt_disable()would help here. It only ensures that current thread will not be preempted between the point where you update the counter and where you check the result. It does not stop interrupts nor does it affect other threads that might be updating the same counter.The preemption was inspired by
__module_get()andtry_module_get(), was that rather silly ?As far as I can see
prrempt_disable()was needed in__module_get()when modules user per-cpu refcounts: you did not want to move away from CPU while manipulating refcount.
Quoted 5 lines
Quoted 2 lines
Now that modules use simple atomics for refcounting I think these
preempt_disable()andpreempt_enable()can be removed.
preempt_disable()still might be useful because you do theatomic_dec()when you reach the limit.
No, not really, because even if you disallow process to migrate to another CPU, it will not help with another thread modifying the counter.
Quoted 8 lines
By other words, you have three operations that should be atomic:
inc, read, and dec.atomic_inc_return()covers only two of them.Hmm, a solution might be to use
atomic_dec_if_positive(). I wouldkmod_concurrentto something likekmod_concurrent_allowed, intialize it with the maximum allowed number. Then you could do:
Quoted 6 lines
static int
kmod_umh_threads_get(void){ if (atomic_dec_if_positive(kmod_concurrent_available) < 0) return -EBUSY; return 0; }
Yes, this looks like optimal solution. And we won't need kmod_umh_threads_get() wrapper anymore I think.
Signature · 1 line
Dmitry
Re: [PATCH 5/6] kmod: preempt on kmod_umh_threads_get()
In reply to Dmitry Torokhov+++ Dmitry Torokhov [24/05/17 19:27 -0700]:
Quoted 63 lines · Luis R. Rodriguez
Quoted 53 lines · Dmitry Torokhov
Quoted 52 lines · Luis R. Rodriguez
Quoted 40 lines · Dmitry Torokhov
Quoted 39 lines · Luis R. Rodriguez
Quoted 25 lines
In theory it is possible multiple concurrent threads will try to
kmod_umh_threads_get()and as suchatomic_inc(&kmod_concurrent)at the same time, therefore enabling a small time during which we've bumpedkmod_concurrentbut have not really enabled work. By using preemption we mitigate this a bit.Preemption is not needed when we
kmod_umh_threads_put().Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)kernel/kmod.c +4
-0@@ -113,15 +113,35 @@ static int call_modprobe(char *module_name, int wait)static int kmod_umh_threads_get (void ) { +int ret =0 ; + +/* +* Disabling preemption makes sure that we are not rescheduled here kernel/kmod.c static int call_modprobe(char *module_name, int wait) · line 129
Quoted 10 lines
kernel/kmod.c +8
-0+* +* Also preemption helps kmod_concurrent is not increased by mistake +* for too long given in theory two concurrent threads could race on +* atomic_inc() before we atomic_read() -- we know that's possible +* and but we don't care, this is not used for object accounting and +* is just a subjective threshold. The alternative is a lock. +*/ +preempt_disable ();atomic_inc (&kmod_concurrent);if (atomic_read (&kmod_concurrent) <= max_modprobes)That is very "fancy" way to basically say:
Quoted 6 lines
Quoted 1 line
if (
atomic_inc_return(&kmod_concurrent)<=max_modprobes)Do you mean to combine the
atomic_inc()andatomic_read()in one as you noted (as that is not a change in this patch), *or* that using a memory barrier here withatomic_inc_return()should suffice to address the same and avoid an explicit preemption enable / disable ?I am saying that
atomic_inc_return()will avoid situation where you have more than one threads incrementing the counter and believing that they are [not] allowed to start modprobe.Quoted 8 lines
Quoted 5 lines
I have no idea why you think
preempt_disable()would help here. It only ensures that current thread will not be preempted between the point where you update the counter and where you check the result. It does not stop interrupts nor does it affect other threads that might be updating the same counter.The preemption was inspired by
__module_get()andtry_module_get(), was that rather silly ?
Quoted 6 lines
As far as I can see
prrempt_disable()was needed in__module_get()when modules user per-cpu refcounts: you did not want to move away from CPU while manipulating refcount.Now that modules use simple atomics for refcounting I think these
preempt_disable()andpreempt_enable()can be removed.
Yup, preempt_disable/enable was originally used for percpu module refcounting. AFAIK they are artifacts that remained from commit e1783a240f4 "use this_cpu_xx to dynamically allocate counters" and subsequently commit 2f35c41f58a "Replace module_ref with atomic_t refcnt" removed the need for it.
Jessica
Luis R. Rodriguez
maintainerPATCH 6/6 kmod: use simplified rate limit printk
In reply to Luis R. RodriguezJust use the simplified rate limit printk when the max modprobe limit is reached, while at it throw out a bone should the error be triggered.
--- kernel/kmod.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-)
kernel/kmod.c +2 -8
@@ -166,7 +166,6 @@ int __request_module(bool wait, const char *fmt, ...)va_list args;char module_name[MODULE_NAME_LEN ];int ret; -static int kmod_loop_msg;/* * We don't allow synchronous module loading from async. Module
@@ -191,13 +190,8 @@ int __request_module(bool wait, const char *fmt, ...) ret =kmod_umh_threads_get ();if (ret) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } +pr_err_ratelimited ("%s: module \"%s\" reached limit (%u) of concurrent modprobe calls\n" , + __func__, module_name, max_modprobes);return ret; }
Signature · 1 line
2.11.0
Re: [PATCH 6/6] kmod: use simplified rate limit printk
Quoted 25 lines · Luis R. Rodriguez
Just use the simplified rate limit printk when the max modprobe limit is reached, while at it throw out a bone should the error be triggered.
Reviewed-by Petr Mladek <[email protected]>Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-)kernel/kmod.c +0
-1@@ -166,7 +166,6 @@ int __request_module(bool wait, const char *fmt, ...)va_list args;char module_name[MODULE_NAME_LEN ];int ret; -static int kmod_loop_msg;/* * We don't allow synchronous module loading from async. Module @@ -191,13 +190,8 @@ int __request_module(bool wait, const char *fmt, ...) ret =kmod_umh_threads_get ();
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 194
Quoted 10 lines
kernel/kmod.c +2
-7if (ret) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } +pr_err_ratelimited ("%s: module \"%s\" reached limit (%u) of concurrent modprobe calls\n" , + __func__, module_name, max_modprobes);
This is completely different behavior, isn't it? Instead of reporting first 5 occurrences we now reporting every once in a while. Why is this new behavior better?
Signature · 1 line
Dmitry
Re: [PATCH 6/6] kmod: use simplified rate limit printk
Quoted 25 lines · Dmitry Torokhov
Quoted 24 lines · Luis R. Rodriguez
Just use the simplified rate limit printk when the max modprobe limit is reached, while at it throw out a bone should the error be triggered.
Reviewed-by Petr Mladek <[email protected]>Signed-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-)kernel/kmod.c +0
-1@@ -166,7 +166,6 @@ int __request_module(bool wait, const char *fmt, ...)va_list args;char module_name[MODULE_NAME_LEN ];int ret; -static int kmod_loop_msg;/* * We don't allow synchronous module loading from async. Module @@ -191,13 +190,8 @@ int __request_module(bool wait, const char *fmt, ...)
Quoted 15 lines
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 194
Quoted 11 lines
kernel/kmod.c +2
-7ret =kmod_umh_threads_get ();if (ret) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } +pr_err_ratelimited ("%s: module \"%s\" reached limit (%u) of concurrent modprobe calls\n" , + __func__, module_name, max_modprobes);This is completely different behavior, isn't it? Instead of reporting first 5 occurrences we now reporting every once in a while. Why is this new behavior better?
pr_err_ratelimited() shows the first 10 messages by default, see DEFAULT_RATELIMIT_BURST. In addition, it allows to see the messages again after some time (5 sec by default). Therefore you could see if the bad situation persists or if the limit was reached more times during the system lifetime.
Best Regards,
Petr
Luis R. Rodriguez
maintainerv2 cover kmod: help make deterministic
In reply to Luis R. RodriguezOn this v2 I'm following Dmitry Torokhov's recommendation from the v1 series
[0] and I stop dancing around with work arounds and try to go straight for what I think should be a proper fix for kmod: throttle instead of failing when kmod max concurrent threshold is reached.
This patch series depends on the unsigned int range proc sysctl changes which Andrew Morton recently merged into his -mm tree [1].
The kmod stress test driver uses a new license (GPL on Linux, copyleft-next outside of Linux). Linus was fine with the copyleft-next so long as it was clear GPL applies to Linux [2] and an or clause was used if I wanted to use copyleft-next. Later based on discussions with Alan and Ted ironed out an "or" language clause to use [3].
All code is also available on my 20170525-kmod-throttle branch of my linux-next tree based on tag next-20170525 [4].
If there are any questions please let me know.
[0] https://lkml.kernel.org/r/[email protected]
[1] https://lkml.kernel.org/r/[email protected]
[2] https://lkml.kernel.org/r/CA+55aFyhxcvD+q7tp+-yrSFDKfR0mOHgyEAe=f_94aKLsOu0Og@mail.gmail.com
[3] https://lkml.kernel.org/r/[email protected]
[4] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20170525-kmod-throttle
Luis
Luis R. Rodriguez (5):
module: use list_for_each_entry_rcu() on find_module_all()
kmod: reduce atomic operations on kmod_concurrent
kmod: add test driver to stress test the module loader
kmod: add helpers for getting kmod limit
kmod: throttle kmod thread limit
Documentation/sysctl/kernel.txt | 20 + include/linux/kmod.h | 7 + init/main.c | 1 + kernel/kmod.c | 92 ++- kernel/module.c | 2 +- kernel/sysctl.c | 7 + lib/Kconfig.debug | 25 + lib/Makefile | 1 + lib/test_kmod.c | 1246 +++++++++++++++++++++++++++++++++ tools/testing/selftests/kmod/Makefile | 11 + tools/testing/selftests/kmod/config | 7 + tools/testing/selftests/kmod/kmod.sh | 615 ++++++++++++++++ 12 files changed, 2004 insertions(+), 30 deletions(-) create mode 100644 lib/test_kmod.c create mode 100644 tools/testing/selftests/kmod/Makefile create mode 100644 tools/testing/selftests/kmod/config create mode 100755 tools/testing/selftests/kmod/kmod.sh
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v2 1/5 module: use list_for_each_entry_rcu() on find_module_all()
The module list has been using RCU in a lot of other calls for a while now, we just overlooked changing this one over to use RCU.
--- kernel/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/module.c +1 -1
@@ -603,7 +603,7 @@ static struct module *find_module_all(const char *name, size_t len,module_assert_mutex_or_preempt (); -list_for_each_entry (mod, &modules, list) { +list_for_each_entry_rcu (mod, &modules, list) {if (!even_unformed && mod->state ==MODULE_STATE_UNFORMED )continue ;if (strlen (mod->name) == len && !memcmp (mod->name, name, len))
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v2 2/5 kmod: reduce atomic operations on kmod_concurrent
In reply to Luis R. RodriguezWhen checking if we want to allow a kmod thread to kick off we increment, then read to see if we should enable a thread. If we were over the allowed limit limit we decrement. Splitting the increment far apart from decrement means there could be a time where two increments happen potentially giving a false failure on a thread which should have been allowed.
CPU1 CPU2atomic_inc()
atomic_inc()atomic_read()
atomic_read()atomic_dec()
atomic_dec()
In this case a read on CPU1 gets the atomic_inc()'s and we could negate it from getting a kmod thread. We could try to prevent this with a lock or preemption but that is overkill. We can fix by reducing the number of atomic operations. We do this by inverting the logic of of the enabler, instead of incrementing kmod_concurrent as we get new kmod users, define the variable kmod_concurrent_max as the max number of currently allowed kmod users and as we get new kmod users just decrement it if its still positive. This combines the dec and read in one atomic operation.
In this case we no longer get the same false failure:
CPU1 CPU2atomic_dec_if_positive()
atomic_dec_if_positive()atomic_inc()
atomic_inc()
--- include/linux/kmod.h | 2 ++ init/main.c | 1 + kernel/kmod.c | 44 +++++++++++++++++++++++++------------------- 3 files changed, 28 insertions(+), 19 deletions(-)
include/linux/kmod.h +2 -0
@@ -38,10 +38,12 @@ int __request_module(bool wait, const char *name, ...); #define request_module_nowait(mod...) __request_module(false, mod) #define try_then_request_module(x, mod...) \ ((x) ?: (__request_module(true, mod), (x))) +void init_kmod_umh (void );#else static inline int request_module (const char *name, ...) {return -ENOSYS ; }static inline int request_module_nowait (const char *name, ...) {return -ENOSYS ; } #define try_then_request_module(x, mod...) (x) +static inline void init_kmod_umh (void ) { }#endif
init/main.c +1 -0
@@ -650,6 +650,7 @@ asmlinkage __visible void __init start_kernel(void)thread_stack_cache_init ();cred_init ();fork_init (); +init_kmod_umh ();proc_caches_init ();buffer_init ();key_init ();
kernel/kmod.c +25 -19
78 lines not on this page · raw message
Signature · 1 line
2.11.0
Re: [PATCH v2 2/5] kmod: reduce atomic operations on kmod_concurrent
Quoted 140 lines · Luis R. Rodriguez
When checking if we want to allow a kmod thread to kick off we increment, then read to see if we should enable a thread. If we were over the allowed limit limit we decrement. Splitting the increment far apart from decrement means there could be a time where two increments happen potentially giving a false failure on a thread which should have been allowed.
CPU1 CPU2
atomic_inc()
atomic_inc()atomic_read()
atomic_read()atomic_dec()
atomic_dec()In this case a read on CPU1 gets the atomic_inc()'s and we could negate it from getting a kmod thread. We could try to prevent this with a lock or preemption but that is overkill. We can fix by reducing the number of atomic operations. We do this by inverting the logic of of the enabler, instead of incrementing
kmod_concurrentas we get new kmod users, define the variablekmod_concurrent_maxas the max number of currently allowed kmod users and as we get new kmod users just decrement it if its still positive. This combines the dec and read in one atomic operation.In this case we no longer get the same false failure:
CPU1 CPU2
atomic_dec_if_positive()
atomic_dec_if_positive()atomic_inc()
atomic_inc()Suggested-by Petr Mladek <[email protected]>Signed-off-by Luis R. Rodriguez <[email protected]>--- include/linux/kmod.h | 2 ++ init/main.c | 1 + kernel/kmod.c | 44 +++++++++++++++++++++++++------------------- 3 files changed, 28 insertions(+), 19 deletions(-)include/linux/kmod.h +2
-0@@ -38,10 +38,12 @@ int __request_module(bool wait, const char *name, ...); #define request_module_nowait(mod...) __request_module(false, mod) #define try_then_request_module(x, mod...) \ ((x) ?: (__request_module(true, mod), (x))) +void init_kmod_umh (void );#else static inline int request_module (const char *name, ...) {return -ENOSYS ; }static inline int request_module_nowait (const char *name, ...) {return -ENOSYS ; } #define try_then_request_module(x, mod...) (x) +static inline void init_kmod_umh (void ) { }#endif init/main.c +1
-0@@ -650,6 +650,7 @@ asmlinkage __visible void __init start_kernel(void)thread_stack_cache_init ();cred_init ();fork_init (); +init_kmod_umh ();proc_caches_init ();buffer_init ();key_init ();kernel/kmod.c +13
-19@@ -46,6 +46,7 @@#include <trace/events/module.h> extern int max_threads; +unsigned int max_modprobes;#define CAP_BSET (void *)1#define CAP_PI (void *)2@@ -56,6 +57,8 @@ static DEFINE_SPINLOCK(umh_sysctl_lock);static DECLARE_RWSEM (umhelper_sem);#ifdef CONFIG_MODULES +static atomic_t kmod_concurrent_max =ATOMIC_INIT (0 ); +#define MAX_KMOD_CONCURRENT 50/* Completely arbitrary value - KAO */ /* modprobe_path is set via /proc/sys. @@ -127,10 +130,7 @@ int __request_module(bool wait, const char *fmt, ...) {va_list args;char module_name[MODULE_NAME_LEN ]; -unsigned int max_modprobes;int ret; -static atomic_t kmod_concurrent =ATOMIC_INIT (0 ); -#define MAX_KMOD_CONCURRENT 50/* Completely arbitrary value - KAO */ static int kmod_loop_msg;/* @@ -154,21 +154,7 @@ int __request_module(bool wait, const char *fmt, ...)if (ret)return ret; -/* If modprobe needs a service that is in a module, we get a recursive -* loop. Limit the number of running kmod threads to max_threads/2 or -* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method -* would be to run the parents of this process, counting how many times -* kmod was invoked. That would mean accessing the internals of the -* process tables to get the command line, proc_pid_cmdline is static -* and it is not worth changing the proc code just to handle this case. -* KAO. -* -* "trace the ppid" is simple, but will fail if someone's -* parent exits. I think this is as good as it gets. --RR -*/ - max_modprobes =min (max_threads/2 ,MAX_KMOD_CONCURRENT ); -atomic_inc (&kmod_concurrent); -if (atomic_read (&kmod_concurrent) > max_modprobes) { +if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) {/* We may be blaming an innocent here, but unlikely */ if (kmod_loop_msg <5 ) { printk(KERN_ERR@@ -184,10 +170,30 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC ); -atomic_dec (&kmod_concurrent); +atomic_inc (&kmod_concurrent_max); +return ret; }EXPORT_SYMBOL (__request_module); + +/* +* If modprobe needs a service that is in a module, we get a recursive +* loop. Limit the number of running kmod threads to max_threads/2 or +* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method +* would be to run the parents of this process, counting how many times +* kmod was invoked. That would mean accessing the internals of the
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 194
Quoted 10 lines
kernel/kmod.c +10
-0+* process tables to get the command line, proc_pid_cmdline is static +* and it is not worth changing the proc code just to handle this case. +* +* "trace the ppid" is simple, but will fail if someone's +* parent exits. I think this is as good as it gets. +*/ +void __init init_kmod_umh (void ) +{ + max_modprobes =min (max_threads/2 ,MAX_KMOD_CONCURRENT ); +atomic_set (&kmod_concurrent_max, max_modprobes);
I would love if we could initialize atomic statically. So the trouble we are trying to solve here is we create more threads than kernel supports, with thread count being calculated as:
threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE, (u64) THREAD_SIZE * 8UL);
So to not being serve 50 threads we need to deal with system smaller than 3200 pages, or ~13M memory (assume thread size is 8 pages - 64 bit with kasan, smaller page sizes reduce memory even more). Can you run 4.12 with modules support on machine with such memory?
So maybe we shoudl simply say:
static atomic_t kmod_concurrent_max = ATOMIC_INIT(MAX_KMOD_CONCURRENT_MAX);
and call it a day? So we do not need init_kmod_umh() and don't need to call it from init/main.c.
Thanks.
Signature · 1 line
Dmitry
Luis R. Rodriguez
maintainerRe: [PATCH v2 2/5] kmod: reduce atomic operations on kmod_concurrent
Quoted 164 lines · Dmitry Torokhov
Quoted 140 lines · Luis R. Rodriguez
When checking if we want to allow a kmod thread to kick off we increment, then read to see if we should enable a thread. If we were over the allowed limit limit we decrement. Splitting the increment far apart from decrement means there could be a time where two increments happen potentially giving a false failure on a thread which should have been allowed.
CPU1 CPU2
atomic_inc()
atomic_inc()atomic_read()
atomic_read()atomic_dec()
atomic_dec()In this case a read on CPU1 gets the atomic_inc()'s and we could negate it from getting a kmod thread. We could try to prevent this with a lock or preemption but that is overkill. We can fix by reducing the number of atomic operations. We do this by inverting the logic of of the enabler, instead of incrementing
kmod_concurrentas we get new kmod users, define the variablekmod_concurrent_maxas the max number of currently allowed kmod users and as we get new kmod users just decrement it if its still positive. This combines the dec and read in one atomic operation.In this case we no longer get the same false failure:
CPU1 CPU2
atomic_dec_if_positive()
atomic_dec_if_positive()atomic_inc()
atomic_inc()Suggested-by Petr Mladek <[email protected]>Signed-off-by Luis R. Rodriguez <[email protected]>--- include/linux/kmod.h | 2 ++ init/main.c | 1 + kernel/kmod.c | 44 +++++++++++++++++++++++++------------------- 3 files changed, 28 insertions(+), 19 deletions(-)include/linux/kmod.h +2
-0@@ -38,10 +38,12 @@ int __request_module(bool wait, const char *name, ...); #define request_module_nowait(mod...) __request_module(false, mod) #define try_then_request_module(x, mod...) \ ((x) ?: (__request_module(true, mod), (x))) +void init_kmod_umh (void );#else static inline int request_module (const char *name, ...) {return -ENOSYS ; }static inline int request_module_nowait (const char *name, ...) {return -ENOSYS ; } #define try_then_request_module(x, mod...) (x) +static inline void init_kmod_umh (void ) { }#endif init/main.c +1
-0@@ -650,6 +650,7 @@ asmlinkage __visible void __init start_kernel(void)thread_stack_cache_init ();cred_init ();fork_init (); +init_kmod_umh ();proc_caches_init ();buffer_init ();key_init ();kernel/kmod.c +13
-19@@ -46,6 +46,7 @@#include <trace/events/module.h> extern int max_threads; +unsigned int max_modprobes;#define CAP_BSET (void *)1#define CAP_PI (void *)2@@ -56,6 +57,8 @@ static DEFINE_SPINLOCK(umh_sysctl_lock);static DECLARE_RWSEM (umhelper_sem);#ifdef CONFIG_MODULES +static atomic_t kmod_concurrent_max =ATOMIC_INIT (0 ); +#define MAX_KMOD_CONCURRENT 50/* Completely arbitrary value - KAO */ /* modprobe_path is set via /proc/sys. @@ -127,10 +130,7 @@ int __request_module(bool wait, const char *fmt, ...) {va_list args;char module_name[MODULE_NAME_LEN ]; -unsigned int max_modprobes;int ret; -static atomic_t kmod_concurrent =ATOMIC_INIT (0 ); -#define MAX_KMOD_CONCURRENT 50/* Completely arbitrary value - KAO */ static int kmod_loop_msg;/* @@ -154,21 +154,7 @@ int __request_module(bool wait, const char *fmt, ...)if (ret)return ret; -/* If modprobe needs a service that is in a module, we get a recursive -* loop. Limit the number of running kmod threads to max_threads/2 or -* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method -* would be to run the parents of this process, counting how many times -* kmod was invoked. That would mean accessing the internals of the -* process tables to get the command line, proc_pid_cmdline is static -* and it is not worth changing the proc code just to handle this case. -* KAO. -* -* "trace the ppid" is simple, but will fail if someone's -* parent exits. I think this is as good as it gets. --RR -*/ - max_modprobes =min (max_threads/2 ,MAX_KMOD_CONCURRENT ); -atomic_inc (&kmod_concurrent); -if (atomic_read (&kmod_concurrent) > max_modprobes) { +if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) {/* We may be blaming an innocent here, but unlikely */ if (kmod_loop_msg <5 ) { printk(KERN_ERR@@ -184,10 +170,30 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC ); -atomic_dec (&kmod_concurrent); +atomic_inc (&kmod_concurrent_max); +return ret; }EXPORT_SYMBOL (__request_module); + +/* +* If modprobe needs a service that is in a module, we get a recursive +* loop. Limit the number of running kmod threads to max_threads/2 or +* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method +* would be to run the parents of this process, counting how many times +* kmod was invoked. That would mean accessing the internals of the kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 194
Quoted 10 lines
kernel/kmod.c +10
-0+* process tables to get the command line, proc_pid_cmdline is static +* and it is not worth changing the proc code just to handle this case. +* +* "trace the ppid" is simple, but will fail if someone's +* parent exits. I think this is as good as it gets. +*/ +void __init init_kmod_umh (void ) +{ + max_modprobes =min (max_threads/2 ,MAX_KMOD_CONCURRENT ); +atomic_set (&kmod_concurrent_max, max_modprobes);I would love if we could initialize atomic statically. So the trouble we are trying to solve here is we create more threads than kernel supports, with thread count being calculated as:
threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE, (u64) THREAD_SIZE * 8UL);So to not being serve 50 threads we need to deal with system smaller than 3200 pages, or ~13M memory (assume thread size is 8 pages - 64 bit with kasan, smaller page sizes reduce memory even more). Can you run 4.12 with modules support on machine with such memory?
Quoted 6 lines
So maybe we shoudl simply say:
static
atomic_tkmod_concurrent_max=ATOMIC_INIT(MAX_KMOD_CONCURRENT_MAX);and call it a day? So we do not need
init_kmod_umh()and don't need to call it frominit/main.c.
I like this very much. If shit blows up we can simply use the kconfig thing I had proposed earlier, however it would have to accept less than 50 threads given we'd be computing this at build time, not at run time.
Luis
Luis R. Rodriguez
maintainerPATCH v2 3/5 kmod: add test driver to stress test the module loader
In reply to Luis R. RodriguezThis adds a new stress test driver for kmod: the kernel module loader. The new stress test driver, test_kmod, is only enabled as a module right now. It should be possible to load this as built-in and load tests early (refer to the force_init_test module parameter), however since a lot of test can get a system out of memory fast we leave this disabled for now.
Using a system with 1024 MiB of RAM can *easily* get your kernel OOM fast with this test driver.
The test_kmod driver exposes API knobs for us to fine tune simple request_module() and get_fs_type() calls. Since these API calls only allow each one parameter a test driver for these is rather simple. Other factors that can help out test driver though are the number of calls we issue and knowing current limitations of each. This exposes configuration as much as possible through userspace to be able to build tests directly from userspace.
Since it allows multiple misc devices its will eventually (once we add a knob to let us create new devices at will) also be possible to perform more tests in parallel, provided you have enough memory.
We only enable tests we know work as of right now.
Demo screenshots:
# tools/testing/selftests/kmod/kmod.sh kmod_test_0001_driver: OK! - loading kmod test kmod_test_0001_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0001_fs: OK! - loading kmod test kmod_test_0001_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL kmod_test_0002_driver: OK! - loading kmod test kmod_test_0002_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0002_fs: OK! - loading kmod test kmod_test_0002_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL kmod_test_0003: OK! - loading kmod test kmod_test_0003: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0004: OK! - loading kmod test kmod_test_0004: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0005: OK! - loading kmod test kmod_test_0005: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0006: OK! - loading kmod test kmod_test_0006: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0005: OK! - loading kmod test kmod_test_0005: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0006: OK! - loading kmod test kmod_test_0006: OK! - Return value: 0 (SUCCESS), expected SUCCESS XXX: add test restult for 0007 Test completed
You can also request for specific tests:
# tools/testing/selftests/kmod/kmod.sh -t 0001 kmod_test_0001_driver: OK! - loading kmod test kmod_test_0001_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0001_fs: OK! - loading kmod test kmod_test_0001_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL Test completed
Lastly, the current available number of tests:
# tools/testing/selftests/kmod/kmod.sh --help Usage: tools/testing/selftests/kmod/kmod.sh [ -t <4-number-digit> ] Valid tests: 0001-0009
0001 - Simple test - 1 thread for empty string 0002 - Simple test - 1 thread for modules/filesystems that do not exist 0003 - Simple test - 1 thread for get_fs_type() only
0004 - Simple test - 2 threads for get_fs_type() only
0005 - multithreaded tests with default setup - request_module() only 0006 - multithreaded tests with default setup - get_fs_type() only
0007 - multithreaded tests with default setup test request_module() and get_fs_type() 0008 - multithreaded - push kmod_concurrent over max_modprobes for request_module() 0009 - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()
The following test cases currently fail, as such they are not currently enabled by default:
# tools/testing/selftests/kmod/kmod.sh -t 0008 # tools/testing/selftests/kmod/kmod.sh -t 0009
To be sure to run them as intended please unload both of the modules:
o test_module
o xfs
And ensure they are not loaded on your system prior to testing them. If you use these paritions for your rootfs you can change the default test driver used for get_fs_type() by exporting it into your environment. For example of other test defaults you can override refer to kmod.sh allow_user_defaults().
Behind the scenes this is how we fine tune at a test case prior to hitting a trigger to run it:
cat /sys/devices/virtual/misc/test_kmod0/config
echo -n "2" > /sys/devices/virtual/misc/test_kmod0/config_test_case echo -n "ext4" > /sys/devices/virtual/misc/test_kmod0/config_test_fs echo -n "80" > /sys/devices/virtual/misc/test_kmod0/config_num_threads cat /sys/devices/virtual/misc/test_kmod0/config
echo -n "1" > /sys/devices/virtual/misc/test_kmod0/config_num_threads
Finally to trigger:
echo -n "1" > /sys/devices/virtual/misc/test_kmod0/trigger_config
The kmod.sh script uses the above constructs to build different test cases.
A bit of interpretation of the current failures follows, first two premises:
a) When request_module() is used userspace figures out an optimized version of module order for us. Once it finds the modules it needs, as per depmod symbol dep map, it will finit_module() the respective modules which are needed for the original request_module() request.
b) We have an optimization in place whereby if a kernel uses request_module() on a module already loaded we never bother userspace as the module already is loaded. This is all handled by kernel/kmod.c.
A few things to consider to help identify root causes of issues:
0) kmod 19 has a broken heuristic for modules being assumed to be built-in to your kernel and will return 0 even though request_module() failed. Upgrade to a newer version of kmod.
1) A get_fs_type() call for "xfs" will request_module() for "fs-xfs", not for "xfs". The optimization in kernel described in b) fails to catch if we have a lot of consecutive get_fs_type() calls. The reason is the optimization in place does not look for aliases. This means two consecutive get_fs_type() calls will bump kmod_concurrent, whereas request_module() will not.
This one explanation why test case 0009 fails at least once for get_fs_type().
2) If a module fails to load --- for whatever reason (kmod_concurrent limit reached, file not yet present due to rootfs switch, out of memory) we have a period of time during which module request for the same name either with request_module() or get_fs_type() will *also* fail to load even if the file for the module is ready.
This explains why *multiple* NULLs are possible on test 0009.
3) finit_module() consumes quite a bit of memory.
4) Filesystems typically also have more dependent modules than other modules, its important to note though that even though a get_fs_type() call does not incur additional kmod_concurrent bumps, since userspace loads dependencies it finds it needs via finit_module_fd(), it *will* take much more memory to load a module with a lot of dependencies.
Because of 3) and 4) we will easily run into out of memory failures with certain tests. For instance test 0006 fails on qemu with 1024 MiB of RAM. It panics a box after reaping all userspace processes and still not having enough memory to reap.
--- lib/Kconfig.debug | 25 + lib/Makefile | 1 + lib/test_kmod.c | 1246 +++++++++++++++++++++++++++++++++ tools/testing/selftests/kmod/Makefile | 11 + tools/testing/selftests/kmod/config | 7 + tools/testing/selftests/kmod/kmod.sh | 635 +++++++++++++++++ 6 files changed, 1925 insertions(+) create mode 100644 lib/test_kmod.c create mode 100644 tools/testing/selftests/kmod/Makefile create mode 100644 tools/testing/selftests/kmod/config create mode 100755 tools/testing/selftests/kmod/kmod.sh
lib/Kconfig.debug +25 -0
@@ -1833,6 +1833,31 @@ config BUG_ON_DATA_CORRUPTION If unsure, say N. +config TEST_KMOD + tristate "kmod stress tester" + default n + depends on m + select TEST_LKM + select XFS_FS + select TUN + select BTRFS_FS + help + Test the kernel's module loading mechanism: kmod. kmod implements + support to load modules using the Linux kernel's usermode helper. + This test provides a series of tests against kmod. + + Although technically you can either build test_kmod as a module or + into the kernel we disallow building it into the kernel since + it stress tests request_module() and this will very likely cause + some issues by taking over precious threads available from other + module load requests, ultimately this could be fatal. + + To run tests run: + + tools/testing/selftests/kmod/kmod.sh --help + + If unsure, say N. + source "samples/Kconfig" source "lib/Kconfig.kgdb"
lib/Makefile +1 -0
@@ -61,6 +61,7 @@ obj-$(CONFIG_TEST_PRINTF) += test_printf.o obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_PARMAN) += test_parman.o +obj-$(CONFIG_TEST_KMOD) += test_kmod.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG
lib/test_kmod.c new file +1246 -0
1,246 lines not on this page · raw message
tools/testing/selftests/kmod/Makefile new file +11 -0
@@ -0,0 +1,11 @@ +# Makefile for kmod loading selftests + +# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" +all: + +TEST_PROGS := kmod.sh + +include ../lib.mk + +# Nothing to clean up. +clean:
tools/testing/selftests/kmod/config new file +7 -0
@@ -0,0 +1,7 @@ +CONFIG_TEST_KMOD=m +CONFIG_TEST_LKM=m +CONFIG_XFS_FS=m + +# For the module parameter force_init_test is used +CONFIG_TUN=m +CONFIG_BTRFS_FS=m
tools/testing/selftests/kmod/kmod.sh new file +635 -0
635 lines not on this page · raw message
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v2 4/5 kmod: add helpers for getting kmod limit
In reply to Luis R. RodriguezThis adds helpers for getting access to the kmod limit from userspace. This knob should help userspace more gracefully and deterministically handle module loading.
--- Documentation/sysctl/kernel.txt | 20 ++++++++++++++++++++ include/linux/kmod.h | 5 +++++ kernel/kmod.c | 33 +++++++++++++++++++++++++++++++++ kernel/sysctl.c | 7 +++++++ 4 files changed, 65 insertions(+)
Documentation/sysctl/kernel.txt +20 -0
@@ -370,6 +370,26 @@ with the "modules_disabled" sysctl. ============================================================== +kmod-limit: + +Get the max amount of concurrent requests (kmod_concurrent) the kernel can +make out to userspace to call 'modprobe'. This limit is known internally to the +kernel as max_modprobes. This interface is designed to enable userspace to +query the kernel for the max_modprobes limit so userspace can more +deterministically handle module loading by only enabling max_modprobes +'modprobe' calls at a time. + +Dependencies are resolved in userspace through depmod, so one modprobe +call only bumps the number of concurrent threads (kmod_concurrent) by one. +Dependencies for a module then are loaded directly in userspace using +init_module() / finit_module() skipping bumping kmod_concurrent or being +affected by max_modprobes. + +The max_modprobes value is set at build time with CONFIG_MAX_KMOD_CONCURRENT. +You can override at initialization with the module parameter max_modprobes. + +============================================================== + kptr_restrict: This toggle indicates whether restrictions are placed on
include/linux/kmod.h +5 -0
@@ -39,13 +39,18 @@ int __request_module(bool wait, const char *name, ...); #define try_then_request_module(x, mod...) \ ((x) ?: (__request_module(true, mod), (x)))void init_kmod_umh (void ); +unsigned int get_kmod_umh_limit (void ); +int sysctl_kmod_limit (struct ctl_table *table,int write, +void __user *buffer,size_t *lenp,loff_t *ppos);#else static inline int request_module (const char *name, ...) {return -ENOSYS ; }static inline int request_module_nowait (const char *name, ...) {return -ENOSYS ; } #define try_then_request_module(x, mod...) (x)static inline void init_kmod_umh (void ) { } +static inline unsigned int get_kmod_umh_limit (void ) {return 0 ; }#endif +#define get_kmod_umh_limit get_kmod_umh_limitstruct cred ;struct file ;
kernel/kmod.c +33 -0
@@ -111,6 +111,17 @@ static int call_modprobe(char *module_name, int wait) }/** +* get_kmod_umh_limit - get concurrent modprobe thread limit +* +* Returns the number of allowed concurrent modprobe calls. +*/ +unsigned int get_kmod_umh_limit (void ) +{ +return max_modprobes; +} +EXPORT_SYMBOL_GPL (get_kmod_umh_limit); + +/** * __request_module - try to load a kernel module * @wait: wait (or not) for the operation to complete * @fmt: printf style format string for the name of the module
@@ -194,6 +205,28 @@ void __init init_kmod_umh(void) atomic_set(&kmod_concurrent_max, max_modprobes); } +int sysctl_kmod_limit (struct ctl_table *table,int write, +void __user *buffer,size_t *lenp,loff_t *ppos) +{ +struct ctl_table t; +int ret; +unsigned int local_max_modprobes = max_modprobes; +unsigned int min =0 ; +unsigned int max = max_threads/2 ; + + t = *table; + t.data = &local_max_modprobes; + t.extra1 = &min; + t.extra2 = &max; + +if (write) +return -EPERM ; + + ret =proc_douintvec_minmax (&t, write, buffer, lenp, ppos); + +return ret; +} +#endif /* CONFIG_MODULES */ static void call_usermodehelper_freeinfo (struct subprocess_info *info)
kernel/sysctl.c +7 -0
@@ -682,6 +682,13 @@ static struct ctl_table kern_table[] = { .extra1 = &one, .extra2 = &one, }, + { + .procname = "kmod-limit", + .data = NULL, /* filled in by handler */ + .maxlen = sizeof(unsigned int), + .mode = 0444, + .proc_handler = sysctl_kmod_limit, + },#endif #ifdef CONFIG_UEVENT_HELPER {
Signature · 1 line
2.11.0
Re: [PATCH v2 4/5] kmod: add helpers for getting kmod limit
Quoted 3 lines · Luis R. Rodriguez
This adds helpers for getting access to the kmod limit from userspace. This knob should help userspace more gracefully and deterministically handle module loading.
I think more details is needed before we add a new ABI to the kernel. Why can't userspace submit as much as it wants and the kernel decide how much it will service at once?
Quoted 137 lines
Cc Tom Gundersen <[email protected]>Cc Petr Mladek <[email protected]>Signed-off-by Luis R. Rodriguez <[email protected]>--- Documentation/sysctl/kernel.txt | 20 ++++++++++++++++++++ include/linux/kmod.h | 5 +++++ kernel/kmod.c | 33 +++++++++++++++++++++++++++++++++ kernel/sysctl.c | 7 +++++++ 4 files changed, 65 insertions(+)Documentation/sysctl/kernel.txt +20
-0@@ -370,6 +370,26 @@ with the "modules_disabled" sysctl. ============================================================== +kmod-limit: + +Get the max amount of concurrent requests (kmod_concurrent) the kernel can +make out to userspace to call 'modprobe'. This limit is known internally to the +kernel as max_modprobes. This interface is designed to enable userspace to +query the kernel for the max_modprobes limit so userspace can more +deterministically handle module loading by only enabling max_modprobes +'modprobe' calls at a time. + +Dependencies are resolved in userspace through depmod, so one modprobe +call only bumps the number of concurrent threads (kmod_concurrent) by one. +Dependencies for a module then are loaded directly in userspace using +init_module() / finit_module() skipping bumping kmod_concurrent or being +affected by max_modprobes. + +The max_modprobes value is set at build time with CONFIG_MAX_KMOD_CONCURRENT. +You can override at initialization with the module parameter max_modprobes. + +============================================================== + kptr_restrict: This toggle indicates whether restrictions are placed oninclude/linux/kmod.h +5
-0@@ -39,13 +39,18 @@ int __request_module(bool wait, const char *name, ...); #define try_then_request_module(x, mod...) \ ((x) ?: (__request_module(true, mod), (x)))void init_kmod_umh (void ); +unsigned int get_kmod_umh_limit (void ); +int sysctl_kmod_limit (struct ctl_table *table,int write, +void __user *buffer,size_t *lenp,loff_t *ppos);#else static inline int request_module (const char *name, ...) {return -ENOSYS ; }static inline int request_module_nowait (const char *name, ...) {return -ENOSYS ; } #define try_then_request_module(x, mod...) (x)static inline void init_kmod_umh (void ) { } +static inline unsigned int get_kmod_umh_limit (void ) {return 0 ; }#endif +#define get_kmod_umh_limit get_kmod_umh_limitstruct cred ;struct file ;kernel/kmod.c +33
-0@@ -111,6 +111,17 @@ static int call_modprobe(char *module_name, int wait) }/** +* get_kmod_umh_limit - get concurrent modprobe thread limit +* +* Returns the number of allowed concurrent modprobe calls. +*/ +unsigned int get_kmod_umh_limit (void ) +{ +return max_modprobes; +} +EXPORT_SYMBOL_GPL (get_kmod_umh_limit); + +/** * __request_module - try to load a kernel module * @wait: wait (or not) for the operation to complete * @fmt: printf style format string for the name of the module @@ -194,6 +205,28 @@ void __init init_kmod_umh(void) atomic_set(&kmod_concurrent_max, max_modprobes); } +int sysctl_kmod_limit (struct ctl_table *table,int write, +void __user *buffer,size_t *lenp,loff_t *ppos) +{ +struct ctl_table t; +int ret; +unsigned int local_max_modprobes = max_modprobes; +unsigned int min =0 ; +unsigned int max = max_threads/2 ; + + t = *table; + t.data = &local_max_modprobes; + t.extra1 = &min; + t.extra2 = &max; + +if (write) +return -EPERM ; + + ret =proc_douintvec_minmax (&t, write, buffer, lenp, ppos); + +return ret; +} +#endif /* CONFIG_MODULES */ static void call_usermodehelper_freeinfo (struct subprocess_info *info)kernel/sysctl.c +7
-0@@ -682,6 +682,13 @@ static struct ctl_table kern_table[] = { .extra1 = &one, .extra2 = &one, }, + { + .procname = "kmod-limit", + .data = NULL, /* filled in by handler */ + .maxlen = sizeof(unsigned int), + .mode = 0444, + .proc_handler = sysctl_kmod_limit, + },#endif #ifdef CONFIG_UEVENT_HELPER {Signature · 1 line
2.11.0
Signature · 1 line
Dmitry
Luis R. Rodriguez
maintainerRe: [PATCH v2 4/5] kmod: add helpers for getting kmod limit
Quoted 8 lines · Dmitry Torokhov
Quoted 3 lines · Luis R. Rodriguez
This adds helpers for getting access to the kmod limit from userspace. This knob should help userspace more gracefully and deterministically handle module loading.
I think more details is needed before we add a new ABI to the kernel. Why can't userspace submit as much as it wants and the kernel decide how much it will service at once?
I suppose I should clarify on the commit log then, that without this heuristic of #ifdef get_kmod_umh_limit -- *iff* userspace today is allowing more than 50 threads it can mean userspace is allowing a modprobe to fail. Check the results of test 0008 and 0009 on dmesg, you can end up with really unexpected results.
This knob enables userspace to gracefully send in as many requests as allowed. I guess this could just be a kernel revision check... given that once throttling go in it can be as crazy as it wants.
Also I suppose one could argue that *since* this has not been a real issue *yet* (we assume), the old userspace of sending as many requests as it needs is fine.
Will drop this!
Luis
Luis R. Rodriguez
maintainerPATCH v2 5/5 kmod: throttle kmod thread limit
In reply to Luis R. RodriguezIf we reach the limit of modprobe_limit threads running the next request_module() call will fail. The original reason for adding a kill was to do away with possible issues with in old circumstances which would create a recursive series of request_module() calls. We can do better than just be super aggressive and reject calls once we've reached the limit by simply making pending callers wait until the threshold has been reduced.
The only difference is the clutch helps with avoiding making request_module() requests fatal more often. With x86_64 qemu, with 4 cores, 4 GiB of RAM it takes the following run time to run both tests:
time kmod.sh -t 0008
real 0m14.066s user 0m1.403s sys 0m5.837s
time kmod.sh -t 0009
real 0m53.928s user 0m1.271s sys 0m7.343s
--- kernel/kmod.c | 15 +++++---------- tools/testing/selftests/kmod/kmod.sh | 24 ++---------------------- 2 files changed, 7 insertions(+), 32 deletions(-)
kernel/kmod.c +5 -10
@@ -59,6 +59,7 @@ static DECLARE_RWSEM(umhelper_sem);#ifdef CONFIG_MODULES static atomic_t kmod_concurrent_max =ATOMIC_INIT (0 );#define MAX_KMOD_CONCURRENT 50/* Completely arbitrary value - KAO */ +static DECLARE_WAIT_QUEUE_HEAD (kmod_wq);/* modprobe_path is set via /proc/sys.
@@ -142,7 +143,6 @@ int __request_module(bool wait, const char *fmt, ...)va_list args;char module_name[MODULE_NAME_LEN ];int ret; -static int kmod_loop_msg;/* * We don't allow synchronous module loading from async. Module
@@ -166,15 +166,9 @@ int __request_module(bool wait, const char *fmt, ...)return ret;if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } -atomic_dec (&kmod_concurrent); -return -ENOMEM ; +pr_warn_ratelimited ("request_module: kmod_concurrent_max (%u) close to 0 (max_modprobes: %u), for module %s\n, throttling..." , +atomic_read (&kmod_concurrent_max), max_modprobes, module_name); +wait_event_interruptible (kmod_wq,atomic_dec_if_positive (&kmod_concurrent_max) >=0 ); }trace_module_request (module_name, wait, _RET_IP_);
@@ -182,6 +176,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );atomic_inc (&kmod_concurrent_max); +wake_up_all (&kmod_wq);return ret; }
tools/testing/selftests/kmod/kmod.sh +2 -22
@@ -59,28 +59,8 @@ ALL_TESTS="$ALL_TESTS 0004:1:1" ALL_TESTS="$ALL_TESTS 0005:10:1" ALL_TESTS="$ALL_TESTS 0006:10:1" ALL_TESTS="$ALL_TESTS 0007:5:1" - -# Disabled tests: -# -# 0008 x 150 - multithreaded - push kmod_concurrent over max_modprobes for request_module()" -# Current best-effort failure interpretation: -# Enough module requests get loaded in place fast enough to reach over the -# max_modprobes limit and trigger a failure -- before we're even able to -# start processing pending requests. -ALL_TESTS="$ALL_TESTS 0008:150:0" - -# 0009 x 150 - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()" -# Current best-effort failure interpretation: -# -# get_fs_type() requests modules using aliases as such the optimization in -# place today to look for already loaded modules will not take effect and -# we end up requesting a new module to load, this bumps the kmod_concurrent, -# and in certain circumstances can lead to pushing the kmod_concurrent over -# the max_modprobe limit. -# -# This test fails much easier than test 0008 since the alias optimizations -# are not in place. -ALL_TESTS="$ALL_TESTS 0009:150:0" +ALL_TESTS="$ALL_TESTS 0008:150:1" +ALL_TESTS="$ALL_TESTS 0009:150:1" test_modprobe() {
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerv3 cover kmod: help make deterministic
In reply to Luis R. RodriguezThis v3 nukes the proc sysctl interface in favor for just letting userspace just check kernel revision. Prior to whenever this is merged userspace should try to avoid hammering more than 50 kmod threads as they can fail and it'd get -ENOMEM.
We do away with the old heuristics on assuming you could end up with less than max_threads/2 < 50 threads as Dmitry notes this would mean having a system with 16 MiB of RAM with modules enabled. It simplifies our patch "kmod: reduce atomic operations on kmod_concurrent" considerbly.
Since the sysctl interface is gone, this no longer depends on any other patches, the series is independent. As usual the series is available on my linux-next 20170526-kmod-only branch which is based on next-20170526.
[0] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20170526-kmod-only
Luis
Luis R. Rodriguez (4):
module: use list_for_each_entry_rcu() on find_module_all()
kmod: reduce atomic operations on kmod_concurrent and simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limit
kernel/kmod.c | 55 +- kernel/module.c | 2 +- lib/Kconfig.debug | 25 + lib/Makefile | 1 + lib/test_kmod.c | 1246 +++++++++++++++++++++++++++++++++ tools/testing/selftests/kmod/Makefile | 11 + tools/testing/selftests/kmod/config | 7 + tools/testing/selftests/kmod/kmod.sh | 615 ++++++++++++++++ 8 files changed, 1930 insertions(+), 32 deletions(-) create mode 100644 lib/test_kmod.c create mode 100644 tools/testing/selftests/kmod/Makefile create mode 100644 tools/testing/selftests/kmod/config create mode 100755 tools/testing/selftests/kmod/kmod.sh
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v3 1/4 module: use list_for_each_entry_rcu() on find_module_all()
The module list has been using RCU in a lot of other calls for a while now, we just overlooked changing this one over to use RCU.
--- kernel/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/module.c +1 -1
@@ -603,7 +603,7 @@ static struct module *find_module_all(const char *name, size_t len,module_assert_mutex_or_preempt (); -list_for_each_entry (mod, &modules, list) { +list_for_each_entry_rcu (mod, &modules, list) {if (!even_unformed && mod->state ==MODULE_STATE_UNFORMED )continue ;if (strlen (mod->name) == len && !memcmp (mod->name, name, len))
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v3 2/4 kmod: reduce atomic operations on kmod_concurrent and simplify
In reply to Luis R. RodriguezWhen checking if we want to allow a kmod thread to kick off we increment, then read to see if we should enable a thread. If we were over the allowed limit limit we decrement. Splitting the increment far apart from decrement means there could be a time where two increments happen potentially giving a false failure on a thread which should have been allowed.
CPU1 CPU2atomic_inc()
atomic_inc()atomic_read()
atomic_read()atomic_dec()
atomic_dec()
In this case a read on CPU1 gets the atomic_inc()'s and we could negate it from getting a kmod thread. We could try to prevent this with a lock or preemption but that is overkill. We can fix by reducing the number of atomic operations. We do this by inverting the logic of of the enabler, instead of incrementing kmod_concurrent as we get new kmod users, define the variable kmod_concurrent_max as the max number of currently allowed kmod users and as we get new kmod users just decrement it if its still positive. This combines the dec and read in one atomic operation.
In this case we no longer get the same false failure:
CPU1 CPU2atomic_dec_if_positive()
atomic_dec_if_positive()atomic_inc()
atomic_inc()
The number of threads is computed at init, and since the current computation of kmod_concurrent includes the thread count we can avoid setting kmod_concurrent_max later in boot through an init call by simply sticking to 50 as the kmod_concurrent_max. The assumption here is a system with modules must at least have ~16 MiB of RAM.
--- kernel/kmod.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-)
kernel/kmod.c +17 -22
@@ -45,8 +45,6 @@#include <trace/events/module.h> -extern int max_threads; -#define CAP_BSET (void *)1#define CAP_PI (void *)2
@@ -56,6 +54,19 @@ static DEFINE_SPINLOCK(umh_sysctl_lock);static DECLARE_RWSEM (umhelper_sem);#ifdef CONFIG_MODULES +/* +* Assuming: +* +* threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE, +* (u64) THREAD_SIZE * 8UL); +* +* If you need less than 50 threads would mean we're dealing with systems +* smaller than 3200 pages. This assuems you are capable of having ~13M memory, +* and this would only be an be an upper limit, after which the OOM killer +* would take effect. Systems like these are very unlikely if modules are +* enabled. +* */ +static atomic_t kmod_concurrent_max =ATOMIC_INIT (50 );/* modprobe_path is set via /proc/sys.
@@ -127,10 +138,7 @@ int __request_module(bool wait, const char *fmt, ...) {va_list args;char module_name[MODULE_NAME_LEN ]; -unsigned int max_modprobes;int ret; -static atomic_t kmod_concurrent =ATOMIC_INIT (0 ); -#define MAX_KMOD_CONCURRENT 50/* Completely arbitrary value - KAO */ static int kmod_loop_msg;/*
@@ -154,21 +162,7 @@ int __request_module(bool wait, const char *fmt, ...)if (ret)return ret; -/* If modprobe needs a service that is in a module, we get a recursive -* loop. Limit the number of running kmod threads to max_threads/2 or -* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method -* would be to run the parents of this process, counting how many times -* kmod was invoked. That would mean accessing the internals of the -* process tables to get the command line, proc_pid_cmdline is static -* and it is not worth changing the proc code just to handle this case. -* KAO. -* -* "trace the ppid" is simple, but will fail if someone's -* parent exits. I think this is as good as it gets. --RR -*/ - max_modprobes =min (max_threads/2 ,MAX_KMOD_CONCURRENT ); -atomic_inc (&kmod_concurrent); -if (atomic_read (&kmod_concurrent) > max_modprobes) { +if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) {/* We may be blaming an innocent here, but unlikely */ if (kmod_loop_msg <5 ) { printk(KERN_ERR
@@ -176,7 +170,6 @@ int __request_module(bool wait, const char *fmt, ...) module_name); kmod_loop_msg++; } -atomic_dec (&kmod_concurrent);return -ENOMEM ; }
@@ -184,10 +177,12 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC ); -atomic_dec (&kmod_concurrent); +atomic_inc (&kmod_concurrent_max); + return ret; }EXPORT_SYMBOL (__request_module); +#endif /* CONFIG_MODULES */ static void call_usermodehelper_freeinfo (struct subprocess_info *info)
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v4 2/4 kmod: reduce atomic operations on kmod_concurrent and simplify
When checking if we want to allow a kmod thread to kick off we increment, then read to see if we should enable a thread. If we were over the allowed limit limit we decrement. Splitting the increment far apart from decrement means there could be a time where two increments happen potentially giving a false failure on a thread which should have been allowed.
CPU1 CPU2atomic_inc()
atomic_inc()atomic_read()
atomic_read()atomic_dec()
atomic_dec()
In this case a read on CPU1 gets the atomic_inc()'s and we could negate it from getting a kmod thread. We could try to prevent this with a lock or preemption but that is overkill. We can fix by reducing the number of atomic operations. We do this by inverting the logic of of the enabler, instead of incrementing kmod_concurrent as we get new kmod users, define the variable kmod_concurrent_max as the max number of currently allowed kmod users and as we get new kmod users just decrement it if its still positive. This combines the dec and read in one atomic operation.
In this case we no longer get the same false failure:
CPU1 CPU2atomic_dec_if_positive()
atomic_dec_if_positive()atomic_inc()
atomic_inc()
The number of threads is computed at init, and since the current computation of kmod_concurrent includes the thread count we can avoid setting kmod_concurrent_max later in boot through an init call by simply sticking to 50 as the kmod_concurrent_max. The assumption here is a system with modules must at least have ~16 MiB of RAM.
--- kernel/kmod.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-)
kernel/kmod.c +18 -22
@@ -45,8 +45,6 @@#include <trace/events/module.h> -extern int max_threads; -#define CAP_BSET (void *)1#define CAP_PI (void *)2
@@ -56,6 +54,20 @@ static DEFINE_SPINLOCK(umh_sysctl_lock);static DECLARE_RWSEM (umhelper_sem);#ifdef CONFIG_MODULES +/* +* Assuming: +* +* threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE, +* (u64) THREAD_SIZE * 8UL); +* +* If you need less than 50 threads would mean we're dealing with systems +* smaller than 3200 pages. This assuems you are capable of having ~13M memory, +* and this would only be an be an upper limit, after which the OOM killer +* would take effect. Systems like these are very unlikely if modules are +* enabled. +*/ +#define MAX_KMOD_CONCURRENT 50 +static atomic_t kmod_concurrent_max =ATOMIC_INIT (MAX_KMOD_CONCURRENT );/* modprobe_path is set via /proc/sys.
@@ -127,10 +139,7 @@ int __request_module(bool wait, const char *fmt, ...) {va_list args;char module_name[MODULE_NAME_LEN ]; -unsigned int max_modprobes;int ret; -static atomic_t kmod_concurrent =ATOMIC_INIT (0 ); -#define MAX_KMOD_CONCURRENT 50/* Completely arbitrary value - KAO */ static int kmod_loop_msg;/*
@@ -154,21 +163,7 @@ int __request_module(bool wait, const char *fmt, ...)if (ret)return ret; -/* If modprobe needs a service that is in a module, we get a recursive -* loop. Limit the number of running kmod threads to max_threads/2 or -* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method -* would be to run the parents of this process, counting how many times -* kmod was invoked. That would mean accessing the internals of the -* process tables to get the command line, proc_pid_cmdline is static -* and it is not worth changing the proc code just to handle this case. -* KAO. -* -* "trace the ppid" is simple, but will fail if someone's -* parent exits. I think this is as good as it gets. --RR -*/ - max_modprobes =min (max_threads/2 ,MAX_KMOD_CONCURRENT ); -atomic_inc (&kmod_concurrent); -if (atomic_read (&kmod_concurrent) > max_modprobes) { +if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) {/* We may be blaming an innocent here, but unlikely */ if (kmod_loop_msg <5 ) { printk(KERN_ERR
@@ -176,7 +171,6 @@ int __request_module(bool wait, const char *fmt, ...) module_name); kmod_loop_msg++; } -atomic_dec (&kmod_concurrent);return -ENOMEM ; }
@@ -184,10 +178,12 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC ); -atomic_dec (&kmod_concurrent); +atomic_inc (&kmod_concurrent_max); + return ret; }EXPORT_SYMBOL (__request_module); +#endif /* CONFIG_MODULES */ static void call_usermodehelper_freeinfo (struct subprocess_info *info)
Signature · 1 line
2.11.0
Re: [PATCH v3 2/4] kmod: reduce atomic operations on kmod_concurrent and simplify
In reply to Luis R. RodriguezQuoted 14 lines · Luis R. Rodriguez
atomic operations. We do this by inverting the logic of of the enabler, instead of incrementing
kmod_concurrentas we get new kmod users, define the variablekmod_concurrent_maxas the max number of currently allowed kmod users and as we get new kmod users just decrement it if its still positive. This combines the dec and read in one atomic operation.In this case we no longer get the same false failure:
CPU1 CPU2
atomic_dec_if_positive()
atomic_dec_if_positive()atomic_inc()
atomic_inc()
Quoted 3 lines
Suggested-by Petr Mladek <[email protected]>Suggested-by Dmitry Torokhov <[email protected]>Signed-off-by Luis R. Rodriguez <[email protected]>
The change looks fine to me. The code is much easier and less hacky.
Best Regards,
Petr
Luis R. Rodriguez
maintainerPATCH v3 3/4 kmod: add test driver to stress test the module loader
In reply to Luis R. RodriguezThis adds a new stress test driver for kmod: the kernel module loader. The new stress test driver, test_kmod, is only enabled as a module right now. It should be possible to load this as built-in and load tests early (refer to the force_init_test module parameter), however since a lot of test can get a system out of memory fast we leave this disabled for now.
Using a system with 1024 MiB of RAM can *easily* get your kernel OOM fast with this test driver.
The test_kmod driver exposes API knobs for us to fine tune simple request_module() and get_fs_type() calls. Since these API calls only allow each one parameter a test driver for these is rather simple. Other factors that can help out test driver though are the number of calls we issue and knowing current limitations of each. This exposes configuration as much as possible through userspace to be able to build tests directly from userspace.
Since it allows multiple misc devices its will eventually (once we add a knob to let us create new devices at will) also be possible to perform more tests in parallel, provided you have enough memory.
We only enable tests we know work as of right now.
Demo screenshots:
# tools/testing/selftests/kmod/kmod.sh kmod_test_0001_driver: OK! - loading kmod test kmod_test_0001_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0001_fs: OK! - loading kmod test kmod_test_0001_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL kmod_test_0002_driver: OK! - loading kmod test kmod_test_0002_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0002_fs: OK! - loading kmod test kmod_test_0002_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL kmod_test_0003: OK! - loading kmod test kmod_test_0003: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0004: OK! - loading kmod test kmod_test_0004: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0005: OK! - loading kmod test kmod_test_0005: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0006: OK! - loading kmod test kmod_test_0006: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0005: OK! - loading kmod test kmod_test_0005: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0006: OK! - loading kmod test kmod_test_0006: OK! - Return value: 0 (SUCCESS), expected SUCCESS XXX: add test restult for 0007 Test completed
You can also request for specific tests:
# tools/testing/selftests/kmod/kmod.sh -t 0001 kmod_test_0001_driver: OK! - loading kmod test kmod_test_0001_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0001_fs: OK! - loading kmod test kmod_test_0001_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL Test completed
Lastly, the current available number of tests:
# tools/testing/selftests/kmod/kmod.sh --help Usage: tools/testing/selftests/kmod/kmod.sh [ -t <4-number-digit> ] Valid tests: 0001-0009
0001 - Simple test - 1 thread for empty string 0002 - Simple test - 1 thread for modules/filesystems that do not exist 0003 - Simple test - 1 thread for get_fs_type() only
0004 - Simple test - 2 threads for get_fs_type() only
0005 - multithreaded tests with default setup - request_module() only 0006 - multithreaded tests with default setup - get_fs_type() only
0007 - multithreaded tests with default setup test request_module() and get_fs_type() 0008 - multithreaded - push kmod_concurrent over max_modprobes for request_module() 0009 - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()
The following test cases currently fail, as such they are not currently enabled by default:
# tools/testing/selftests/kmod/kmod.sh -t 0008 # tools/testing/selftests/kmod/kmod.sh -t 0009
To be sure to run them as intended please unload both of the modules:
o test_module
o xfs
And ensure they are not loaded on your system prior to testing them. If you use these paritions for your rootfs you can change the default test driver used for get_fs_type() by exporting it into your environment. For example of other test defaults you can override refer to kmod.sh allow_user_defaults().
Behind the scenes this is how we fine tune at a test case prior to hitting a trigger to run it:
cat /sys/devices/virtual/misc/test_kmod0/config
echo -n "2" > /sys/devices/virtual/misc/test_kmod0/config_test_case echo -n "ext4" > /sys/devices/virtual/misc/test_kmod0/config_test_fs echo -n "80" > /sys/devices/virtual/misc/test_kmod0/config_num_threads cat /sys/devices/virtual/misc/test_kmod0/config
echo -n "1" > /sys/devices/virtual/misc/test_kmod0/config_num_threads
Finally to trigger:
echo -n "1" > /sys/devices/virtual/misc/test_kmod0/trigger_config
The kmod.sh script uses the above constructs to build different test cases.
A bit of interpretation of the current failures follows, first two premises:
a) When request_module() is used userspace figures out an optimized version of module order for us. Once it finds the modules it needs, as per depmod symbol dep map, it will finit_module() the respective modules which are needed for the original request_module() request.
b) We have an optimization in place whereby if a kernel uses request_module() on a module already loaded we never bother userspace as the module already is loaded. This is all handled by kernel/kmod.c.
A few things to consider to help identify root causes of issues:
0) kmod 19 has a broken heuristic for modules being assumed to be built-in to your kernel and will return 0 even though request_module() failed. Upgrade to a newer version of kmod.
1) A get_fs_type() call for "xfs" will request_module() for "fs-xfs", not for "xfs". The optimization in kernel described in b) fails to catch if we have a lot of consecutive get_fs_type() calls. The reason is the optimization in place does not look for aliases. This means two consecutive get_fs_type() calls will bump kmod_concurrent, whereas request_module() will not.
This one explanation why test case 0009 fails at least once for get_fs_type().
2) If a module fails to load --- for whatever reason (kmod_concurrent limit reached, file not yet present due to rootfs switch, out of memory) we have a period of time during which module request for the same name either with request_module() or get_fs_type() will *also* fail to load even if the file for the module is ready.
This explains why *multiple* NULLs are possible on test 0009.
3) finit_module() consumes quite a bit of memory.
4) Filesystems typically also have more dependent modules than other modules, its important to note though that even though a get_fs_type() call does not incur additional kmod_concurrent bumps, since userspace loads dependencies it finds it needs via finit_module_fd(), it *will* take much more memory to load a module with a lot of dependencies.
Because of 3) and 4) we will easily run into out of memory failures with certain tests. For instance test 0006 fails on qemu with 1024 MiB of RAM. It panics a box after reaping all userspace processes and still not having enough memory to reap.
--- lib/Kconfig.debug | 25 + lib/Makefile | 1 + lib/test_kmod.c | 1246 +++++++++++++++++++++++++++++++++ tools/testing/selftests/kmod/Makefile | 11 + tools/testing/selftests/kmod/config | 7 + tools/testing/selftests/kmod/kmod.sh | 635 +++++++++++++++++ 6 files changed, 1925 insertions(+) create mode 100644 lib/test_kmod.c create mode 100644 tools/testing/selftests/kmod/Makefile create mode 100644 tools/testing/selftests/kmod/config create mode 100755 tools/testing/selftests/kmod/kmod.sh
lib/Kconfig.debug +25 -0
@@ -1822,6 +1822,31 @@ config BUG_ON_DATA_CORRUPTION If unsure, say N. +config TEST_KMOD + tristate "kmod stress tester" + default n + depends on m + select TEST_LKM + select XFS_FS + select TUN + select BTRFS_FS + help + Test the kernel's module loading mechanism: kmod. kmod implements + support to load modules using the Linux kernel's usermode helper. + This test provides a series of tests against kmod. + + Although technically you can either build test_kmod as a module or + into the kernel we disallow building it into the kernel since + it stress tests request_module() and this will very likely cause + some issues by taking over precious threads available from other + module load requests, ultimately this could be fatal. + + To run tests run: + + tools/testing/selftests/kmod/kmod.sh --help + + If unsure, say N. + source "samples/Kconfig" source "lib/Kconfig.kgdb"
lib/Makefile +1 -0
@@ -60,6 +60,7 @@ obj-$(CONFIG_TEST_PRINTF) += test_printf.o obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_PARMAN) += test_parman.o +obj-$(CONFIG_TEST_KMOD) += test_kmod.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG
lib/test_kmod.c new file +1246 -0
1,246 lines not on this page · raw message
tools/testing/selftests/kmod/Makefile new file +11 -0
@@ -0,0 +1,11 @@ +# Makefile for kmod loading selftests + +# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" +all: + +TEST_PROGS := kmod.sh + +include ../lib.mk + +# Nothing to clean up. +clean:
tools/testing/selftests/kmod/config new file +7 -0
@@ -0,0 +1,7 @@ +CONFIG_TEST_KMOD=m +CONFIG_TEST_LKM=m +CONFIG_XFS_FS=m + +# For the module parameter force_init_test is used +CONFIG_TUN=m +CONFIG_BTRFS_FS=m
tools/testing/selftests/kmod/kmod.sh new file +635 -0
635 lines not on this page · raw message
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v3 4/4 kmod: throttle kmod thread limit
In reply to Luis R. RodriguezIf we reach the limit of modprobe_limit threads running the next request_module() call will fail. The original reason for adding a kill was to do away with possible issues with in old circumstances which would create a recursive series of request_module() calls. We can do better than just be super aggressive and reject calls once we've reached the limit by simply making pending callers wait until the threshold has been reduced.
The only difference is the clutch helps with avoiding making request_module() requests fatal more often. With x86_64 qemu, with 4 cores, 4 GiB of RAM it takes the following run time to run both tests:
time ./kmod.sh -t 0008
real 0m12.364s user 0m0.704s sys 0m5.373s
time ./kmod.sh -t 0009
real 0m47.638s user 0m1.033s sys 0m5.425s
--- kernel/kmod.c | 16 +++++++--------- tools/testing/selftests/kmod/kmod.sh | 24 ++---------------------- 2 files changed, 9 insertions(+), 31 deletions(-)
kernel/kmod.c +7 -9
@@ -67,6 +67,7 @@ static DECLARE_RWSEM(umhelper_sem);* enabled. * */ static atomic_t kmod_concurrent_max =ATOMIC_INIT (50 ); +static DECLARE_WAIT_QUEUE_HEAD (kmod_wq);/* modprobe_path is set via /proc/sys.
@@ -139,7 +140,6 @@ int __request_module(bool wait, const char *fmt, ...)va_list args;char module_name[MODULE_NAME_LEN ];int ret; -static int kmod_loop_msg;/* * We don't allow synchronous module loading from async. Module
@@ -163,14 +163,11 @@ int __request_module(bool wait, const char *fmt, ...)return ret;if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } -return -ENOMEM ; +pr_warn_ratelimited ("request_module: kmod_concurrent_max (%u) close to 0 (max_modprobes: %u), for module %s\n, throttling..." , +atomic_read (&kmod_concurrent_max), +50 , module_name); +wait_event_interruptible (kmod_wq, +atomic_dec_if_positive (&kmod_concurrent_max) >=0 ); }trace_module_request (module_name, wait, _RET_IP_);
@@ -178,6 +175,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );atomic_inc (&kmod_concurrent_max); +wake_up_all (&kmod_wq);return ret; }
tools/testing/selftests/kmod/kmod.sh +2 -22
@@ -59,28 +59,8 @@ ALL_TESTS="$ALL_TESTS 0004:1:1" ALL_TESTS="$ALL_TESTS 0005:10:1" ALL_TESTS="$ALL_TESTS 0006:10:1" ALL_TESTS="$ALL_TESTS 0007:5:1" - -# Disabled tests: -# -# 0008 x 150 - multithreaded - push kmod_concurrent over max_modprobes for request_module()" -# Current best-effort failure interpretation: -# Enough module requests get loaded in place fast enough to reach over the -# max_modprobes limit and trigger a failure -- before we're even able to -# start processing pending requests. -ALL_TESTS="$ALL_TESTS 0008:150:0" - -# 0009 x 150 - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()" -# Current best-effort failure interpretation: -# -# get_fs_type() requests modules using aliases as such the optimization in -# place today to look for already loaded modules will not take effect and -# we end up requesting a new module to load, this bumps the kmod_concurrent, -# and in certain circumstances can lead to pushing the kmod_concurrent over -# the max_modprobe limit. -# -# This test fails much easier than test 0008 since the alias optimizations -# are not in place. -ALL_TESTS="$ALL_TESTS 0009:150:0" +ALL_TESTS="$ALL_TESTS 0008:150:1" +ALL_TESTS="$ALL_TESTS 0009:150:1" test_modprobe() {
Signature · 1 line
2.11.0
Re: [PATCH v3 4/4] kmod: throttle kmod thread limit
Quoted 38 lines · Luis R. Rodriguez
If we reach the limit of
modprobe_limitthreads running the nextrequest_module()call will fail. The original reason for adding a kill was to do away with possible issues with in old circumstances which would create a recursive series ofrequest_module()calls. We can do better than just be super aggressive and reject calls once we've reached the limit by simply making pending callers wait until the threshold has been reduced.The only difference is the clutch helps with avoiding making
request_module()requests fatal more often. Withx86_64qemu, with 4 cores, 4 GiB of RAM it takes the following run time to run both tests:time ./kmod.sh -t 0008
real 0m12.364s user 0m0.704s sys 0m5.373stime ./kmod.sh -t 0009
real 0m47.638s user 0m1.033s sys 0m5.425sSigned-off-by Luis R. Rodriguez <[email protected]>--- kernel/kmod.c | 16 +++++++--------- tools/testing/selftests/kmod/kmod.sh | 24 ++---------------------- 2 files changed, 9 insertions(+), 31 deletions(-)kernel/kmod.c +0
-1@@ -163,14 +163,11 @@ int __request_module(bool wait, const char *fmt, ...)return ret;if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) { -/* We may be blaming an innocent here, but unlikely */
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 168
Quoted 10 lines
kernel/kmod.c +3
-7-if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } -return -ENOMEM ; +pr_warn_ratelimited ("request_module: kmod_concurrent_max (%u) close to 0 (max_modprobes: %u), for module %s\n, throttling..." , +atomic_read (&kmod_concurrent_max), +50 , module_name);
It is weird to pass the constant '50' via %s. Also a #define should be used to keep it in sync with the kmod_concurrent_max initialization.
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 178
Quoted 10 lines
kernel/kmod.c +3
-0+wait_event_interruptible (kmod_wq, +atomic_dec_if_positive (&kmod_concurrent_max) >=0 ); }trace_module_request (module_name, wait, _RET_IP_);@@ -178,6 +175,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );atomic_inc (&kmod_concurrent_max); +wake_up_all (&kmod_wq);
Does it make sense to wake up all waiters when we released the resource only for one? IMHO, a simple wake_up() should be here.
I am sorry for the late review. The month ran really fast.
Best Regards,
Petr
Luis R. Rodriguez
maintainerRe: [PATCH v3 4/4] kmod: throttle kmod thread limit
Quoted 6 lines · Petr Mladek
Quoted 5 lines · Luis R. Rodriguez
kernel/kmod.c +0
-0@@ -163,14 +163,11 @@ int __request_module(bool wait, const char *fmt, ...)return ret;
Quoted 14 lines
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 168
Quoted 12 lines
kernel/kmod.c +3
-8if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } -return -ENOMEM ; +pr_warn_ratelimited ("request_module: kmod_concurrent_max (%u) close to 0 (max_modprobes: %u), for module %s\n, throttling..." , +atomic_read (&kmod_concurrent_max), +50 , module_name);It is weird to pass the constant '50' via %s.
The 50 was passed with %u, so I take it you meant it is odd to use a parameter for it.
Quoted 2 lines
Also a #define should be
used to keep it in sync with thekmod_concurrent_maxinitialization.
OK.
Quoted 8 lines
Quoted 8 lines
kernel/kmod.c +2
-0+wait_event_interruptible (kmod_wq, +atomic_dec_if_positive (&kmod_concurrent_max) >=0 ); }trace_module_request (module_name, wait, _RET_IP_);@@ -178,6 +175,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );
Quoted 5 lines
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 178
Quoted 2 lines
kernel/kmod.c +1
-0atomic_inc (&kmod_concurrent_max); +wake_up_all (&kmod_wq);Does it make sense to wake up all waiters when we released the resource only for one? IMHO, a simple
wake_up()should be here.
Then we should wake_up() also on failure, otherwise we have the potential to not wake some in a proper time.
Quoted 1 line
I am sorry for the late review. The month ran really fast.
No worries!
Luis
Luis R. Rodriguez
maintainerRe: [PATCH v3 4/4] kmod: throttle kmod thread limit
Quoted 7 lines · Luis R. Rodriguez
Quoted 6 lines · Petr Mladek
Quoted 5 lines · Luis R. Rodriguez
kernel/kmod.c +0
-0@@ -178,6 +175,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );
Quoted 8 lines
Quoted 5 lines
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 178
Quoted 2 lines
kernel/kmod.c +1
-0atomic_inc (&kmod_concurrent_max); +wake_up_all (&kmod_wq);Does it make sense to wake up all waiters when we released the resource only for one? IMHO, a simple
wake_up()should be here.Then we should
wake_up()also on failure, otherwise we have the potential to not wake some in a proper time.
I checked and it turns out we have no error paths after we consume a kmod ticket, if you will. Once we bump with atomic_dec_if_positive() we assume we're moving forward with an attempt, and the only failure path is already bundled with a wake at the end of the __request_module() call.
Then the next question would be *who* exactly gets woken up next if we just use wake_up() ? The common core wake up code varies depending on use and all this reminded me of the complexity we just don't need, so I have now converted to use swait. swait uses list_add() if empty and then iterates with list_first_entry() on wakeup, so that should get the first item added to the wait list.
Works with me. Will run a test a before v4 is sent, but since only 2 patches are modified will only send a respective update for these 2 patches.
Luis
Luis R. Rodriguez
maintainerRe: [PATCH v3 4/4] kmod: throttle kmod thread limit
Quoted 29 lines · Luis R. Rodriguez
Quoted 7 lines · Luis R. Rodriguez
Quoted 6 lines · Petr Mladek
Quoted 5 lines · Luis R. Rodriguez
kernel/kmod.c +0
-0@@ -178,6 +175,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );Quoted 8 lines
Quoted 5 lines
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 178
Quoted 2 lines
kernel/kmod.c +1
-0atomic_inc (&kmod_concurrent_max); +wake_up_all (&kmod_wq);Does it make sense to wake up all waiters when we released the resource only for one? IMHO, a simple
wake_up()should be here.Then we should
wake_up()also on failure, otherwise we have the potential to not wake some in a proper time.I checked and it turns out we have no error paths after we consume a kmod ticket, if you will. Once we bump with
atomic_dec_if_positive()we assume we're moving forward with an attempt, and the only failure path is already bundled with a wake at the end of the__request_module()call.Then the next question would be *who* exactly gets woken up next if we just use
wake_up()? The common core wake up code varies depending on use and all this reminded me of the complexity we just don't need, so I have now converted to use swait. swait useslist_add()if empty and then iterates withlist_first_entry()on wakeup, so that should get the first item added to the wait list.
Quoted 2 lines
Works with me. Will run a test a before v4 is sent, but since only 2 patches are modified will only send a respective update for these 2 patches.
Alright, this worked out well! Its just a tiny bit slower on test cases 0008 and 0009 (few seconds) but that's fine, its natural due to the lack of the swake_up_all().
Luis
Re: [PATCH v3 4/4] kmod: throttle kmod thread limit
Quoted 30 lines · Luis R. Rodriguez
Quoted 29 lines · Luis R. Rodriguez
Quoted 7 lines · Luis R. Rodriguez
Quoted 6 lines · Petr Mladek
Quoted 5 lines · Luis R. Rodriguez
kernel/kmod.c +0
-0@@ -178,6 +175,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );Quoted 8 lines
Quoted 5 lines
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 178
Quoted 2 lines
kernel/kmod.c +1
-0atomic_inc (&kmod_concurrent_max); +wake_up_all (&kmod_wq);Does it make sense to wake up all waiters when we released the resource only for one? IMHO, a simple
wake_up()should be here.Then we should
wake_up()also on failure, otherwise we have the potential to not wake some in a proper time.I checked and it turns out we have no error paths after we consume a kmod ticket, if you will. Once we bump with
atomic_dec_if_positive()we assume we're moving forward with an attempt, and the only failure path is already bundled with a wake at the end of the__request_module()call.Then the next question would be *who* exactly gets woken up next if we just use
wake_up()? The common core wake up code varies depending on use and all this reminded me of the complexity we just don't need, so I have now converted to use swait. swait useslist_add()if empty and then iterates withlist_first_entry()on wakeup, so that should get the first item added to the wait list.
Quoted 6 lines
Quoted 2 lines
Works with me. Will run a test a before v4 is sent, but since only 2 patches are modified will only send a respective update for these 2 patches.
Alright, this worked out well! Its just a tiny bit slower on test cases 0008 and 0009 (few seconds) but that's fine, its natural due to the lack of the
swake_up_all().
This is interesting. I guess that it was faster with swake_up_all() because it worked as a speculative pre-wake. I mean that it takes some time between adding a process into run-queue and really running it. IMHO, swake_up_all() caused that __request_module() callers were more often really running and trying to pass that atomic_dec_if_positive(&kmod_concurrent_max) >= 0).
Best Regards,
Petr
Re: [PATCH v3 4/4] kmod: throttle kmod thread limit
In reply to Luis R. RodriguezQuoted 22 lines · Luis R. Rodriguez
Quoted 6 lines · Petr Mladek
Quoted 5 lines · Luis R. Rodriguez
kernel/kmod.c +0
-0@@ -163,14 +163,11 @@ int __request_module(bool wait, const char *fmt, ...)return ret;Quoted 14 lines
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 168
Quoted 12 lines
kernel/kmod.c +3
-8if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } -return -ENOMEM ; +pr_warn_ratelimited ("request_module: kmod_concurrent_max (%u) close to 0 (max_modprobes: %u), for module %s\n, throttling..." , +atomic_read (&kmod_concurrent_max), +50 , module_name);It is weird to pass the constant '50' via %s.
Quoted 2 lines
The 50 was passed with %u, so I take it you meant it is odd to use a parameter for it.
Yeah, I meant %u and not %s.
Quoted 13 lines
Quoted 2 lines
Also a #define should be
used to keep it in sync with thekmod_concurrent_maxinitialization.OK.
Quoted 8 lines
Quoted 8 lines
kernel/kmod.c +2
-0+wait_event_interruptible (kmod_wq, +atomic_dec_if_positive (&kmod_concurrent_max) >=0 ); }trace_module_request (module_name, wait, _RET_IP_);@@ -178,6 +175,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );
Quoted 8 lines
Quoted 5 lines
kernel/kmod.c int __request_module(bool wait, const char *fmt, ...) · line 178
Quoted 2 lines
kernel/kmod.c +1
-0atomic_inc (&kmod_concurrent_max); +wake_up_all (&kmod_wq);Does it make sense to wake up all waiters when we released the resource only for one? IMHO, a simple
wake_up()should be here.Then we should
wake_up()also on failure, otherwise we have the potential to not wake some in a proper time.
I think that we must wake_up() always when we increment kmod_concurrent_max. If the value was negative, the increment will allow exactly one process to pass that atomic_dec_if_positive(&kmod_concurrent_max) >= 0). It the value is positive, there must have been other wake_up() calls or there is no waiter.
IMHO, this works because kmod_concurrent_max handling is atomic and race-less now. Also (s)wait_event_interruptible() is safe and does not allow to get into sleep when the resource is available.
Anyway, it is great that you have double checked this.
Best Regards,
Petr
Luis R. Rodriguez
maintainerPATCH v4 4/4 kmod: throttle kmod thread limit
In reply to Luis R. RodriguezIf we reach the limit of modprobe_limit threads running the next request_module() call will fail. The original reason for adding a kill was to do away with possible issues with in old circumstances which would create a recursive series of request_module() calls.
We can do better than just be super aggressive and reject calls once we've reached the limit by simply making pending callers wait until the threshold has been reduced, and then throttling them in, one by one.
This throttling enables requests over the kmod concurrent limit to be processed once a pending request completes. Only the first item queued up to wait is woken up. The assumption here is once a task is woken it will have no other option to also kick the queue to check if there are more pending tasks -- regardless of whether or not it was successful.
By throttling and processing only max kmod concurrent tasks we ensure we avoid unexpected fatal request_module() calls, and we keep memory consumption on module loading to a minimum.
With x86_64 qemu, with 4 cores, 4 GiB of RAM it takes the following run time to run both tests:
time ./kmod.sh -t 0008
real 0m16.523s user 0m0.879s sys 0m8.977s
time ./kmod.sh -t 0009
real 0m56.080s user 0m0.717s sys 0m10.324s
--- kernel/kmod.c | 17 ++++++++--------- tools/testing/selftests/kmod/kmod.sh | 24 ++---------------------- 2 files changed, 10 insertions(+), 31 deletions(-)
kernel/kmod.c +8 -9
@@ -42,6 +42,7 @@#include <linux/ptrace.h> #include <linux/async.h> #include <linux/uaccess.h> +#include <linux/swait.h> #include <trace/events/module.h>
@@ -68,6 +69,7 @@ static DECLARE_RWSEM(umhelper_sem);*/ #define MAX_KMOD_CONCURRENT 50static atomic_t kmod_concurrent_max =ATOMIC_INIT (MAX_KMOD_CONCURRENT ); +static DECLARE_SWAIT_QUEUE_HEAD (kmod_wq);/* modprobe_path is set via /proc/sys.
@@ -140,7 +142,6 @@ int __request_module(bool wait, const char *fmt, ...)va_list args;char module_name[MODULE_NAME_LEN ];int ret; -static int kmod_loop_msg;/* * We don't allow synchronous module loading from async. Module
@@ -164,14 +165,11 @@ int __request_module(bool wait, const char *fmt, ...)return ret;if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } -return -ENOMEM ; +pr_warn_ratelimited ("request_module: kmod_concurrent_max (%u) close to 0 (max_modprobes: %u), for module %s, throttling..." , +atomic_read (&kmod_concurrent_max), +MAX_KMOD_CONCURRENT , module_name); +swait_event_interruptible (kmod_wq, +atomic_dec_if_positive (&kmod_concurrent_max) >=0 ); }trace_module_request (module_name, wait, _RET_IP_);
@@ -179,6 +177,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );atomic_inc (&kmod_concurrent_max); +swake_up (&kmod_wq);return ret; }
tools/testing/selftests/kmod/kmod.sh +2 -22
@@ -59,28 +59,8 @@ ALL_TESTS="$ALL_TESTS 0004:1:1" ALL_TESTS="$ALL_TESTS 0005:10:1" ALL_TESTS="$ALL_TESTS 0006:10:1" ALL_TESTS="$ALL_TESTS 0007:5:1" - -# Disabled tests: -# -# 0008 x 150 - multithreaded - push kmod_concurrent over max_modprobes for request_module()" -# Current best-effort failure interpretation: -# Enough module requests get loaded in place fast enough to reach over the -# max_modprobes limit and trigger a failure -- before we're even able to -# start processing pending requests. -ALL_TESTS="$ALL_TESTS 0008:150:0" - -# 0009 x 150 - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()" -# Current best-effort failure interpretation: -# -# get_fs_type() requests modules using aliases as such the optimization in -# place today to look for already loaded modules will not take effect and -# we end up requesting a new module to load, this bumps the kmod_concurrent, -# and in certain circumstances can lead to pushing the kmod_concurrent over -# the max_modprobe limit. -# -# This test fails much easier than test 0008 since the alias optimizations -# are not in place. -ALL_TESTS="$ALL_TESTS 0009:150:0" +ALL_TESTS="$ALL_TESTS 0008:150:1" +ALL_TESTS="$ALL_TESTS 0009:150:1" test_modprobe() {
Signature · 1 line
2.11.0
Re: [PATCH v4 4/4] kmod: throttle kmod thread limit
Quoted 29 lines · Luis R. Rodriguez
If we reach the limit of
modprobe_limitthreads running the nextrequest_module()call will fail. The original reason for adding a kill was to do away with possible issues with in old circumstances which would create a recursive series ofrequest_module()calls.We can do better than just be super aggressive and reject calls once we've reached the limit by simply making pending callers wait until the threshold has been reduced, and then throttling them in, one by one.
This throttling enables requests over the kmod concurrent limit to be processed once a pending request completes. Only the first item queued up to wait is woken up. The assumption here is once a task is woken it will have no other option to also kick the queue to check if there are more pending tasks -- regardless of whether or not it was successful.
By throttling and processing only max kmod concurrent tasks we ensure we avoid unexpected fatal
request_module()calls, and we keep memory consumption on module loading to a minimum.With
x86_64qemu, with 4 cores, 4 GiB of RAM it takes the following run time to run both tests:time ./kmod.sh -t 0008
real 0m16.523s user 0m0.879s sys 0m8.977s
Quoted 6 lines
time ./kmod.sh -t 0009
real 0m56.080s user 0m0.717s sys 0m10.324sSigned-off-by Luis R. Rodriguez <[email protected]>
All the changes look fine to me. They make perfect sense.
Best Regards,
Petr
Re: [PATCH v4 4/4] kmod: throttle kmod thread limit
Quoted 30 lines · Petr Mladek
Quoted 29 lines · Luis R. Rodriguez
If we reach the limit of
modprobe_limitthreads running the nextrequest_module()call will fail. The original reason for adding a kill was to do away with possible issues with in old circumstances which would create a recursive series ofrequest_module()calls.We can do better than just be super aggressive and reject calls once we've reached the limit by simply making pending callers wait until the threshold has been reduced, and then throttling them in, one by one.
This throttling enables requests over the kmod concurrent limit to be processed once a pending request completes. Only the first item queued up to wait is woken up. The assumption here is once a task is woken it will have no other option to also kick the queue to check if there are more pending tasks -- regardless of whether or not it was successful.
By throttling and processing only max kmod concurrent tasks we ensure we avoid unexpected fatal
request_module()calls, and we keep memory consumption on module loading to a minimum.With
x86_64qemu, with 4 cores, 4 GiB of RAM it takes the following run time to run both tests:time ./kmod.sh -t 0008
real 0m16.523s user 0m0.879s sys 0m8.977s
Quoted 8 lines
Quoted 6 lines
time ./kmod.sh -t 0009
real 0m56.080s user 0m0.717s sys 0m10.324sSigned-off-by Luis R. Rodriguez <[email protected]>All the changes look fine to me. They make perfect sense.
Thanks, I'll peg a
Luis
Luis R. Rodriguez
maintainerRe: [PATCH v3 0/4] kmod: help make deterministic
In reply to Luis R. RodriguezQuoted 17 lines · Luis R. Rodriguez
This v3 nukes the proc sysctl interface in favor for just letting userspace just check kernel revision. Prior to whenever this is merged userspace should try to avoid hammering more than 50 kmod threads as they can fail and it'd get
-ENOMEM.We do away with the old heuristics on assuming you could end up with less than max_threads/2 < 50 threads as Dmitry notes this would mean having a system with 16 MiB of RAM with modules enabled. It simplifies our patch "kmod: reduce atomic operations on
kmod_concurrent" considerbly.Since the sysctl interface is gone, this no longer depends on any other patches, the series is independent. As usual the series is available on my linux-next 20170526-kmod-only branch which is based on next-20170526.
[0] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20170526-kmod-only
Quoted 7 lines
Luis
Luis R. Rodriguez (4):
module: uselist_for_each_entry_rcu()onfind_module_all()
kmod: reduce atomic operations onkmod_concurrentand simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limit
About a month now with no further nitpicks. What tree should these changes go through if there are no issues? Andrew's, Jessica's ?
Luis
Re: [PATCH v3 0/4] kmod: help make deterministic
Quoted 20 lines · Luis R. Rodriguez
Quoted 19 lines · Luis R. Rodriguez
This v3 nukes the proc sysctl interface in favor for just letting userspace just check kernel revision. Prior to whenever this is merged userspace should try to avoid hammering more than 50 kmod threads as they can fail and it'd get
-ENOMEM.We do away with the old heuristics on assuming you could end up with less than max_threads/2 < 50 threads as Dmitry notes this would mean having a system with 16 MiB of RAM with modules enabled. It simplifies our patch "kmod: reduce atomic operations on
kmod_concurrent" considerbly.Since the sysctl interface is gone, this no longer depends on any other patches, the series is independent. As usual the series is available on my linux-next 20170526-kmod-only branch which is based on next-20170526.
[0] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20170526-kmod-only
Luis
Quoted 8 lines
Quoted 5 lines
Luis R. Rodriguez (4):
module: uselist_for_each_entry_rcu()onfind_module_all()
kmod: reduce atomic operations onkmod_concurrentand simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limitAbout a month now with no further nitpicks. What tree should these changes go through if there are no issues? Andrew's, Jessica's ?
Seems like going through Jessica's would make the most sense?
-Kees
Signature · 2 lines
Kees Cook Pixel Security
Re: [PATCH v3 0/4] kmod: help make deterministic
+++ Kees Cook [20/06/17 17:23 -0700]:
Quoted 27 lines · Luis R. Rodriguez
Quoted 26 lines · Luis R. Rodriguez
Quoted 24 lines
This v3 nukes the proc sysctl interface in favor for just letting userspace just check kernel revision. Prior to whenever this is merged userspace should try to avoid hammering more than 50 kmod threads as they can fail and it'd get
-ENOMEM.We do away with the old heuristics on assuming you could end up with less than max_threads/2 < 50 threads as Dmitry notes this would mean having a system with 16 MiB of RAM with modules enabled. It simplifies our patch "kmod: reduce atomic operations on
kmod_concurrent" considerbly.Since the sysctl interface is gone, this no longer depends on any other patches, the series is independent. As usual the series is available on my linux-next 20170526-kmod-only branch which is based on next-20170526.
[0] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20170526-kmod-only
Luis
Luis R. Rodriguez (4):
module: uselist_for_each_entry_rcu()onfind_module_all()
kmod: reduce atomic operations onkmod_concurrentand simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limit
Quoted 4 lines
Quoted 2 lines
About a month now with no further nitpicks. What tree should these changes go through if there are no issues? Andrew's, Jessica's ?
Seems like going through Jessica's would make the most sense?
Would be happy to take patches 01 (which I need to anyway), 02, possibly 04 if decoupled from the test driver (03). I can't take patch 03 through my tree just yet, as I haven't had time to give it a look yet :-/
[ Side comment, it seems that kmod.c isn't directly maintained by anyone right now, perhaps Luis would be interested in picking it up? :-) ]
Thanks,
Jessica
Luis R. Rodriguez
maintainerRe: [PATCH v3 0/4] kmod: help make deterministic
Quoted 28 lines · Jessica Yu
+++ Kees Cook [20/06/17 17:23 -0700]:
Quoted 27 lines · Luis R. Rodriguez
Quoted 26 lines · Luis R. Rodriguez
Quoted 24 lines
This v3 nukes the proc sysctl interface in favor for just letting userspace just check kernel revision. Prior to whenever this is merged userspace should try to avoid hammering more than 50 kmod threads as they can fail and it'd get
-ENOMEM.We do away with the old heuristics on assuming you could end up with less than max_threads/2 < 50 threads as Dmitry notes this would mean having a system with 16 MiB of RAM with modules enabled. It simplifies our patch "kmod: reduce atomic operations on
kmod_concurrent" considerbly.Since the sysctl interface is gone, this no longer depends on any other patches, the series is independent. As usual the series is available on my linux-next 20170526-kmod-only branch which is based on next-20170526.
[0] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20170526-kmod-only
Luis
Luis R. Rodriguez (4):
module: uselist_for_each_entry_rcu()onfind_module_all()
kmod: reduce atomic operations onkmod_concurrentand simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limit
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
About a month now with no further nitpicks. What tree should these changes go through if there are no issues? Andrew's, Jessica's ?
Seems like going through Jessica's would make the most sense?
Would be happy to take patches 01 (which I need to anyway), 02, possibly 04 if decoupled from the test driver (03).
Feel free to decouple it, but note that then the commit log must then be changed. My own take is this fix is not so critical as it is a corner case, so I have instead preferred to couple in the test case and respective fix together. I'll leave it up to you how to proceed.
Quoted 2 lines
I can't take patch 03 through my tree just yet, as I haven't had time to give it a look yet :-/
Understood. I'd appreciate at least a review though.
Quoted 2 lines
[ Side comment, it seems that
kmod.cisn't directly maintained by anyone right now, perhaps Luis would be interested in picking it up? :-) ]
Sure thing, I'm not sure if it makes sense to decouple kernel/kmod.c on MAINTAINERS though, if you do let me know what you'd prefer to call it, "KMOD MODULE USERMODE HELPER" ?
If you prefer to keep them together I can certainly volunteer to review all kmod changes and can send a patch to add kmod and myself under "MODULE SUPPORT".
Either way, let me know!
Luis
Luis R. Rodriguez
maintainerRe: [PATCH v3 0/4] kmod: help make deterministic
Quoted 15 lines · Luis R. Rodriguez
Quoted 14 lines · Jessica Yu
+++ Kees Cook [20/06/17 17:23 -0700]:
Quoted 12 lines · Luis R. Rodriguez
Quoted 9 lines · Luis R. Rodriguez
Quoted 5 lines
Luis R. Rodriguez (4):
module: uselist_for_each_entry_rcu()onfind_module_all()
kmod: reduce atomic operations onkmod_concurrentand simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limitAbout a month now with no further nitpicks. What tree should these changes go through if there are no issues? Andrew's, Jessica's ?
Seems like going through Jessica's would make the most sense?
Quoted 7 lines
Quoted 2 lines
Would be happy to take patches 01 (which I need to anyway), 02, possibly 04 if decoupled from the test driver (03).
Feel free to decouple it, but note that then the commit log must then be changed. My own take is this fix is not so critical as it is a corner case, so I have instead preferred to couple in the test case and respective fix together. I'll leave it up to you how to proceed.
Note: Linus noted swait is actually very special use-case [0] so I'd hate to add a new use case not vetted for. This use case on kmod.c really does *not* require anything but a simple wait though, so really am inclined to let that through unless I hear back...
[0] https://lkml.kernel.org/r/[email protected]
Luis
Re: [PATCH v3 0/4] kmod: help make deterministic
Quoted 24 lines · Luis R. Rodriguez
Quoted 15 lines · Luis R. Rodriguez
Quoted 14 lines · Jessica Yu
+++ Kees Cook [20/06/17 17:23 -0700]:
Quoted 12 lines · Luis R. Rodriguez
Quoted 9 lines · Luis R. Rodriguez
Quoted 5 lines
Luis R. Rodriguez (4):
module: uselist_for_each_entry_rcu()onfind_module_all()
kmod: reduce atomic operations onkmod_concurrentand simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limitAbout a month now with no further nitpicks. What tree should these changes go through if there are no issues? Andrew's, Jessica's ?
Seems like going through Jessica's would make the most sense?
Quoted 7 lines
Quoted 2 lines
Would be happy to take patches 01 (which I need to anyway), 02, possibly 04 if decoupled from the test driver (03).
Feel free to decouple it, but note that then the commit log must then be changed. My own take is this fix is not so critical as it is a corner case, so I have instead preferred to couple in the test case and respective fix together. I'll leave it up to you how to proceed.
Quoted 6 lines
Note: Linus noted swait is actually very special use-case [0] so I'd hate to add a new use case not vetted for. This use case on
kmod.creally does *not* require anything but a simple wait though, so really am inclined to let that through unless I hear back...
Heh, I was not aware of this special case either. The welcoming comment of the swait API confused me as well.
In this light, I suggest to switch the patch to using the normal wait API.
Best Regards,
Petr
Re: [PATCH v3 0/4] kmod: help make deterministic
+++ Petr Mladek [27/06/17 10:13 +0200]:
Quoted 30 lines · Luis R. Rodriguez
Quoted 29 lines · Luis R. Rodriguez
Quoted 15 lines · Jessica Yu
Quoted 14 lines
+++ Kees Cook [20/06/17 17:23 -0700]:
Quoted 12 lines · Luis R. Rodriguez
Quoted 9 lines · Luis R. Rodriguez
Quoted 5 lines
Luis R. Rodriguez (4):
module: uselist_for_each_entry_rcu()onfind_module_all()
kmod: reduce atomic operations onkmod_concurrentand simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limitAbout a month now with no further nitpicks. What tree should these changes go through if there are no issues? Andrew's, Jessica's ?
Seems like going through Jessica's would make the most sense?
Quoted 7 lines
Quoted 2 lines
Would be happy to take patches 01 (which I need to anyway), 02, possibly 04 if decoupled from the test driver (03).
Feel free to decouple it, but note that then the commit log must then be changed. My own take is this fix is not so critical as it is a corner case, so I have instead preferred to couple in the test case and respective fix together. I'll leave it up to you how to proceed.
Note: Linus noted swait is actually very special use-case [0] so I'd hate to add a new use case not vetted for. This use case on
kmod.creally does *not* require anything but a simple wait though, so really am inclined to let that through unless I hear back...
Quoted 6 lines
Quoted 1 line
Heh, I was not aware of this special case either. The welcoming comment of the swait API confused me as well.
In this light, I suggest to switch the patch to using the normal wait API.
Huh, I wasn't aware either :-/ But I agree, judging from Linus' response [0], it's probably best to use the well established wait_* variants. I'm not sure I understood why the patch switched to swait, but in any case I don't think we'd be hitting the "thundering herd" problem very often here (and if we do, we could just use exclusive wait. But in that scenario I'd be more interested in why a normal system would be battered with more than 50 in-kernel modprobe requests at a time).
Re: [PATCH v3 0/4] kmod: help make deterministic
In reply to Luis R. Rodriguez+++ Luis R. Rodriguez [27/06/17 00:44 +0200]:
Quoted 34 lines · Jessica Yu
Quoted 33 lines
+++ Kees Cook [20/06/17 17:23 -0700]:
Quoted 31 lines · Luis R. Rodriguez
Quoted 20 lines · Luis R. Rodriguez
Quoted 19 lines
This v3 nukes the proc sysctl interface in favor for just letting userspace just check kernel revision. Prior to whenever this is merged userspace should try to avoid hammering more than 50 kmod threads as they can fail and it'd get
-ENOMEM.We do away with the old heuristics on assuming you could end up with less than max_threads/2 < 50 threads as Dmitry notes this would mean having a system with 16 MiB of RAM with modules enabled. It simplifies our patch "kmod: reduce atomic operations on
kmod_concurrent" considerbly.Since the sysctl interface is gone, this no longer depends on any other patches, the series is independent. As usual the series is available on my linux-next 20170526-kmod-only branch which is based on next-20170526.
[0] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20170526-kmod-only
Luis
Quoted 8 lines
Quoted 5 lines
Luis R. Rodriguez (4):
module: uselist_for_each_entry_rcu()onfind_module_all()
kmod: reduce atomic operations onkmod_concurrentand simplify
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limitAbout a month now with no further nitpicks. What tree should these changes go through if there are no issues? Andrew's, Jessica's ?
Seems like going through Jessica's would make the most sense?
Quoted 7 lines
Quoted 2 lines
Would be happy to take patches 01 (which I need to anyway), 02, possibly 04 if decoupled from the test driver (03).
Feel free to decouple it, but note that then the commit log must then be changed. My own take is this fix is not so critical as it is a corner case, so I have instead preferred to couple in the test case and respective fix together. I'll leave it up to you how to proceed.
I'll take 01 and 02 for the next merge window, as they are straightforward. 03/04 can stay together, and as I understand it 04 may need to switch back to using the normal wait_* api.
Quoted 4 lines
Quoted 2 lines
I can't take patch 03 through my tree just yet, as I haven't had time to give it a look yet :-/
Understood. I'd appreciate at least a review though.
Of course! I should have rephrased and said *by this upcoming merge window.
Quoted 10 lines
Quoted 2 lines
[ Side comment, it seems that
kmod.cisn't directly maintained by anyone right now, perhaps Luis would be interested in picking it up? :-) ]Sure thing, I'm not sure if it makes sense to decouple
kernel/kmod.conMAINTAINERSthough, if you do let me know what you'd prefer to call it, "KMOD MODULE USERMODE HELPER" ?If you prefer to keep them together I can certainly volunteer to review all kmod changes and can send a patch to add kmod and myself under "MODULE SUPPORT".
I'm not the maintainer for kmod.c, if that's what you mean by decoupling. But I don't think it has one, which is why I'm suggesting adding it to MAINTAINERS, since you've been actively working on it :) (looking at git log, it looks like Andrew did most of the sign-off's for kmod.c in the past). I think a separate entry in MAINTAINERS is good, with the name you suggested.
Thanks!
Jessica
Luis R. Rodriguez
maintainerRe: [PATCH v3 0/4] kmod: help make deterministic
Quoted 9 lines · Jessica Yu
+++ Luis R. Rodriguez [27/06/17 00:44 +0200]:
Quoted 4 lines
Feel free to decouple it, but note that then the commit log must then be changed. My own take is this fix is not so critical as it is a corner case, so I have instead preferred to couple in the test case and respective fix together. I'll leave it up to you how to proceed.
I'll take 01 and 02 for the next merge window, as they are straightforward. 03/04 can stay together, and as I understand it 04 may need to switch back to using the normal wait_* api.
OK, I'll rework 03-04 with the regular wait and submit aiming towards Andrew's tree.
Quoted 2 lines
I'm not the maintainer for
kmod.c, if that's what you mean by decoupling.
I was suggesting adding kmod.c to MODULE SUPPORT list as its all module related, adding my self to the list and I'd just take on helping kmod.c stuff. My point was that it feels odd to decouple kmod.c from module stuff. But lets give it a shot with them separated first and see how that goes.
Quoted 5 lines
But I don't think it has one, which is why I'm suggesting
adding it toMAINTAINERS, since you've been actively working on it :) (looking at git log, it looks like Andrew did most of the sign-off's forkmod.cin the past). I think a separate entry inMAINTAINERSis good, with the name you suggested.
Sure.
Luis
Luis R. Rodriguez
maintainerv4 cover kmod: help make deterministic
In reply to Luis R. RodriguezAndrew,
I'm submitting the last few patches of my kmod series through your tree and as suggested by Jessica I am picking up maintenance on kmod. These last changes can either wait until the next release or can be merged now provided someone reviews the kmod test driver and the merge conflict is addressed with Jessica's tree with the kmod patch she picked up.
This v4 moves back again from swait to regular waitqueue as suggested generally by Linus as swait is very specialized [0], and it also drops two patches picked up by Jessica. Jessica picked the following two patches visible on linux-next tag next-20170628 as follows:
165d1cc0074b kmod: reduce atomic operations on kmod_concurrent and simplify 93437353daef module: use list_for_each_entry_rcu() on find_module_all()
FWIW, the swait --> wait change proved to just help speed up tests only marginally, even over a spread of tests these results were generally consistent. Figured some peformance folks might be interested in that tidbit, should they wish to go play.
With wait:
time ./kmod.sh -t 0008
real 0m16.366s user 0m0.883s sys 0m8.916s
time ./kmod.sh -t 0009
real 0m50.803s user 0m0.791s sys 0m9.852s
With swait:
time ./kmod.sh -t 0008
real 0m16.523s user 0m0.879s sys 0m8.977s
time ./kmod.sh -t 0009
real 0m51.258s user 0m0.812s sys 0m10.133s
If anyone wants these in a git tree you can check out the 20170628-kmod-only branch from my linux-next tree [1], based on next-20170628. If there are any questions or issues please let me know.
[0] https://marc.info/?l=linux-kernel&m=149851347228696&w=2
[1] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20170628-kmod-only
Luis R. Rodriguez (3):
MAINTAINERS: give kmod some maintainer love
kmod: add test driver to stress test the module loader
kmod: throttle kmod thread limit
MAINTAINERS | 9 + kernel/kmod.c | 16 +- lib/Kconfig.debug | 25 + lib/Makefile | 1 + lib/test_kmod.c | 1246 +++++++++++++++++++++++++++++++++ tools/testing/selftests/kmod/Makefile | 11 + tools/testing/selftests/kmod/config | 7 + tools/testing/selftests/kmod/kmod.sh | 615 ++++++++++++++++ 8 files changed, 1921 insertions(+), 9 deletions(-) create mode 100644 lib/test_kmod.c create mode 100644 tools/testing/selftests/kmod/Makefile create mode 100644 tools/testing/selftests/kmod/config create mode 100755 tools/testing/selftests/kmod/kmod.sh
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v4 1/3 MAINTAINERS: give kmod some maintainer love
As suggested by Jessica, I've been actively working on kmod, so might as well reflect its maintained status.
Changes are expected to go through akpm's tree.
--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
@@ -7561,6 +7561,13 @@ F: include/linux/kmemleak.h F: mm/kmemleak.c F: mm/kmemleak-test.c +KMOD MODULE USERMODE HELPER +M: "Luis R. Rodriguez" <[email protected]> +L: [email protected] +S: Maintained +F: kernel/kmod.c +F: include/linux/kmod.h + KPROBES M: Ananth N Mavinakayanahalli <[email protected]> M: Anil S Keshavamurthy <[email protected]>
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v4 2/3 kmod: add test driver to stress test the module loader
In reply to Luis R. RodriguezThis adds a new stress test driver for kmod: the kernel module loader. The new stress test driver, test_kmod, is only enabled as a module right now. It should be possible to load this as built-in and load tests early (refer to the force_init_test module parameter), however since a lot of test can get a system out of memory fast we leave this disabled for now.
Using a system with 1024 MiB of RAM can *easily* get your kernel OOM fast with this test driver.
The test_kmod driver exposes API knobs for us to fine tune simple request_module() and get_fs_type() calls. Since these API calls only allow each one parameter a test driver for these is rather simple. Other factors that can help out test driver though are the number of calls we issue and knowing current limitations of each. This exposes configuration as much as possible through userspace to be able to build tests directly from userspace.
Since it allows multiple misc devices its will eventually (once we add a knob to let us create new devices at will) also be possible to perform more tests in parallel, provided you have enough memory.
We only enable tests we know work as of right now.
Demo screenshots:
# tools/testing/selftests/kmod/kmod.sh kmod_test_0001_driver: OK! - loading kmod test kmod_test_0001_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0001_fs: OK! - loading kmod test kmod_test_0001_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL kmod_test_0002_driver: OK! - loading kmod test kmod_test_0002_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0002_fs: OK! - loading kmod test kmod_test_0002_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL kmod_test_0003: OK! - loading kmod test kmod_test_0003: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0004: OK! - loading kmod test kmod_test_0004: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0005: OK! - loading kmod test kmod_test_0005: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0006: OK! - loading kmod test kmod_test_0006: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0005: OK! - loading kmod test kmod_test_0005: OK! - Return value: 0 (SUCCESS), expected SUCCESS kmod_test_0006: OK! - loading kmod test kmod_test_0006: OK! - Return value: 0 (SUCCESS), expected SUCCESS XXX: add test restult for 0007 Test completed
You can also request for specific tests:
# tools/testing/selftests/kmod/kmod.sh -t 0001 kmod_test_0001_driver: OK! - loading kmod test kmod_test_0001_driver: OK! - Return value: 256 (MODULE_NOT_FOUND), expected MODULE_NOT_FOUND kmod_test_0001_fs: OK! - loading kmod test kmod_test_0001_fs: OK! - Return value: -22 (-EINVAL), expected -EINVAL Test completed
Lastly, the current available number of tests:
# tools/testing/selftests/kmod/kmod.sh --help Usage: tools/testing/selftests/kmod/kmod.sh [ -t <4-number-digit> ] Valid tests: 0001-0009
0001 - Simple test - 1 thread for empty string 0002 - Simple test - 1 thread for modules/filesystems that do not exist 0003 - Simple test - 1 thread for get_fs_type() only
0004 - Simple test - 2 threads for get_fs_type() only
0005 - multithreaded tests with default setup - request_module() only 0006 - multithreaded tests with default setup - get_fs_type() only
0007 - multithreaded tests with default setup test request_module() and get_fs_type() 0008 - multithreaded - push kmod_concurrent over max_modprobes for request_module() 0009 - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()
The following test cases currently fail, as such they are not currently enabled by default:
# tools/testing/selftests/kmod/kmod.sh -t 0008 # tools/testing/selftests/kmod/kmod.sh -t 0009
To be sure to run them as intended please unload both of the modules:
o test_module
o xfs
And ensure they are not loaded on your system prior to testing them. If you use these paritions for your rootfs you can change the default test driver used for get_fs_type() by exporting it into your environment. For example of other test defaults you can override refer to kmod.sh allow_user_defaults().
Behind the scenes this is how we fine tune at a test case prior to hitting a trigger to run it:
cat /sys/devices/virtual/misc/test_kmod0/config
echo -n "2" > /sys/devices/virtual/misc/test_kmod0/config_test_case echo -n "ext4" > /sys/devices/virtual/misc/test_kmod0/config_test_fs echo -n "80" > /sys/devices/virtual/misc/test_kmod0/config_num_threads cat /sys/devices/virtual/misc/test_kmod0/config
echo -n "1" > /sys/devices/virtual/misc/test_kmod0/config_num_threads
Finally to trigger:
echo -n "1" > /sys/devices/virtual/misc/test_kmod0/trigger_config
The kmod.sh script uses the above constructs to build different test cases.
A bit of interpretation of the current failures follows, first two premises:
a) When request_module() is used userspace figures out an optimized version of module order for us. Once it finds the modules it needs, as per depmod symbol dep map, it will finit_module() the respective modules which are needed for the original request_module() request.
b) We have an optimization in place whereby if a kernel uses request_module() on a module already loaded we never bother userspace as the module already is loaded. This is all handled by kernel/kmod.c.
A few things to consider to help identify root causes of issues:
0) kmod 19 has a broken heuristic for modules being assumed to be built-in to your kernel and will return 0 even though request_module() failed. Upgrade to a newer version of kmod.
1) A get_fs_type() call for "xfs" will request_module() for "fs-xfs", not for "xfs". The optimization in kernel described in b) fails to catch if we have a lot of consecutive get_fs_type() calls. The reason is the optimization in place does not look for aliases. This means two consecutive get_fs_type() calls will bump kmod_concurrent, whereas request_module() will not.
This one explanation why test case 0009 fails at least once for get_fs_type().
2) If a module fails to load --- for whatever reason (kmod_concurrent limit reached, file not yet present due to rootfs switch, out of memory) we have a period of time during which module request for the same name either with request_module() or get_fs_type() will *also* fail to load even if the file for the module is ready.
This explains why *multiple* NULLs are possible on test 0009.
3) finit_module() consumes quite a bit of memory.
4) Filesystems typically also have more dependent modules than other modules, its important to note though that even though a get_fs_type() call does not incur additional kmod_concurrent bumps, since userspace loads dependencies it finds it needs via finit_module_fd(), it *will* take much more memory to load a module with a lot of dependencies.
Because of 3) and 4) we will easily run into out of memory failures with certain tests. For instance test 0006 fails on qemu with 1024 MiB of RAM. It panics a box after reaping all userspace processes and still not having enough memory to reap.
--- MAINTAINERS | 2 + lib/Kconfig.debug | 25 + lib/Makefile | 1 + lib/test_kmod.c | 1246 +++++++++++++++++++++++++++++++++ tools/testing/selftests/kmod/Makefile | 11 + tools/testing/selftests/kmod/config | 7 + tools/testing/selftests/kmod/kmod.sh | 635 +++++++++++++++++ 7 files changed, 1927 insertions(+) create mode 100644 lib/test_kmod.c create mode 100644 tools/testing/selftests/kmod/Makefile create mode 100644 tools/testing/selftests/kmod/config create mode 100755 tools/testing/selftests/kmod/kmod.sh
MAINTAINERS +2 -0
@@ -7567,6 +7567,8 @@ L: [email protected] S: Maintained F: kernel/kmod.c F: include/linux/kmod.h +F: lib/test_kmod.c +F: tools/testing/selftests/kmod/ KPROBES M: Ananth N Mavinakayanahalli <[email protected]>
lib/Kconfig.debug +25 -0
@@ -1875,6 +1875,31 @@ config BUG_ON_DATA_CORRUPTION If unsure, say N. +config TEST_KMOD + tristate "kmod stress tester" + default n + depends on m + select TEST_LKM + select XFS_FS + select TUN + select BTRFS_FS + help + Test the kernel's module loading mechanism: kmod. kmod implements + support to load modules using the Linux kernel's usermode helper. + This test provides a series of tests against kmod. + + Although technically you can either build test_kmod as a module or + into the kernel we disallow building it into the kernel since + it stress tests request_module() and this will very likely cause + some issues by taking over precious threads available from other + module load requests, ultimately this could be fatal. + + To run tests run: + + tools/testing/selftests/kmod/kmod.sh --help + + If unsure, say N. + source "samples/Kconfig" source "lib/Kconfig.kgdb"
lib/Makefile +1 -0
@@ -61,6 +61,7 @@ obj-$(CONFIG_TEST_PRINTF) += test_printf.o obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_PARMAN) += test_parman.o +obj-$(CONFIG_TEST_KMOD) += test_kmod.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG
lib/test_kmod.c new file +1246 -0
1,246 lines not on this page · raw message
tools/testing/selftests/kmod/Makefile new file +11 -0
@@ -0,0 +1,11 @@ +# Makefile for kmod loading selftests + +# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" +all: + +TEST_PROGS := kmod.sh + +include ../lib.mk + +# Nothing to clean up. +clean:
tools/testing/selftests/kmod/config new file +7 -0
@@ -0,0 +1,7 @@ +CONFIG_TEST_KMOD=m +CONFIG_TEST_LKM=m +CONFIG_XFS_FS=m + +# For the module parameter force_init_test is used +CONFIG_TUN=m +CONFIG_BTRFS_FS=m
tools/testing/selftests/kmod/kmod.sh new file +635 -0
635 lines not on this page · raw message
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v4 3/3 kmod: throttle kmod thread limit
In reply to Luis R. RodriguezIf we reach the limit of modprobe_limit threads running the next request_module() call will fail. The original reason for adding a kill was to do away with possible issues with in old circumstances which would create a recursive series of request_module() calls.
We can do better than just be super aggressive and reject calls once we've reached the limit by simply making pending callers wait until the threshold has been reduced, and then throttling them in, one by one.
This throttling enables requests over the kmod concurrent limit to be processed once a pending request completes. Only the first item queued up to wait is woken up. The assumption here is once a task is woken it will have no other option to also kick the queue to check if there are more pending tasks -- regardless of whether or not it was successful.
By throttling and processing only max kmod concurrent tasks we ensure we avoid unexpected fatal request_module() calls, and we keep memory consumption on module loading to a minimum.
With x86_64 qemu, with 4 cores, 4 GiB of RAM it takes the following run time to run both tests:
time ./kmod.sh -t 0008
real 0m16.366s user 0m0.883s sys 0m8.916s
time ./kmod.sh -t 0009
real 0m50.803s user 0m0.791s sys 0m9.852s
--- kernel/kmod.c | 16 +++++++--------- tools/testing/selftests/kmod/kmod.sh | 24 ++---------------------- 2 files changed, 9 insertions(+), 31 deletions(-)
kernel/kmod.c +7 -9
@@ -68,6 +68,7 @@ static DECLARE_RWSEM(umhelper_sem);*/ #define MAX_KMOD_CONCURRENT 50static atomic_t kmod_concurrent_max =ATOMIC_INIT (MAX_KMOD_CONCURRENT ); +static DECLARE_WAIT_QUEUE_HEAD (kmod_wq);/* modprobe_path is set via /proc/sys.
@@ -140,7 +141,6 @@ int __request_module(bool wait, const char *fmt, ...)va_list args;char module_name[MODULE_NAME_LEN ];int ret; -static int kmod_loop_msg;/* * We don't allow synchronous module loading from async. Module
@@ -164,14 +164,11 @@ int __request_module(bool wait, const char *fmt, ...)return ret;if (atomic_dec_if_positive (&kmod_concurrent_max) <0 ) { -/* We may be blaming an innocent here, but unlikely */ -if (kmod_loop_msg <5 ) { -printk (KERN_ERR -"request_module: runaway loop modprobe %s\n" , - module_name); - kmod_loop_msg++; - } -return -ENOMEM ; +pr_warn_ratelimited ("request_module: kmod_concurrent_max (%u) close to 0 (max_modprobes: %u), for module %s, throttling..." , +atomic_read (&kmod_concurrent_max), +MAX_KMOD_CONCURRENT , module_name); +wait_event_interruptible (kmod_wq, +atomic_dec_if_positive (&kmod_concurrent_max) >=0 ); }trace_module_request (module_name, wait, _RET_IP_);
@@ -179,6 +176,7 @@ int __request_module(bool wait, const char *fmt, ...) ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );atomic_inc (&kmod_concurrent_max); +wake_up (&kmod_wq);return ret; }
tools/testing/selftests/kmod/kmod.sh +2 -22
@@ -59,28 +59,8 @@ ALL_TESTS="$ALL_TESTS 0004:1:1" ALL_TESTS="$ALL_TESTS 0005:10:1" ALL_TESTS="$ALL_TESTS 0006:10:1" ALL_TESTS="$ALL_TESTS 0007:5:1" - -# Disabled tests: -# -# 0008 x 150 - multithreaded - push kmod_concurrent over max_modprobes for request_module()" -# Current best-effort failure interpretation: -# Enough module requests get loaded in place fast enough to reach over the -# max_modprobes limit and trigger a failure -- before we're even able to -# start processing pending requests. -ALL_TESTS="$ALL_TESTS 0008:150:0" - -# 0009 x 150 - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()" -# Current best-effort failure interpretation: -# -# get_fs_type() requests modules using aliases as such the optimization in -# place today to look for already loaded modules will not take effect and -# we end up requesting a new module to load, this bumps the kmod_concurrent, -# and in certain circumstances can lead to pushing the kmod_concurrent over -# the max_modprobe limit. -# -# This test fails much easier than test 0008 since the alias optimizations -# are not in place. -ALL_TESTS="$ALL_TESTS 0009:150:0" +ALL_TESTS="$ALL_TESTS 0008:150:1" +ALL_TESTS="$ALL_TESTS 0009:150:1" test_modprobe() {
Signature · 1 line
2.11.0