From: Arnd Bergmann arnd@arndb.de
[ Upstream commit 43163f4c30f94d2103c948a247cdf2cda5068ca7 ]
In the s390 defconfig, gcc-10 and earlier end up inlining three functions into nldev_stat_get_doit(), and each of them uses some 600 bytes of stack.
The result is a function with an overly large stack frame and a warning:
drivers/infiniband/core/nldev.c:2466:1: error: the frame size of 1720 bytes is larger than 1280 bytes [-Werror=frame-larger-than=]
Mark the three functions noinline_for_stack to prevent this, ensuring that only one copy of the nlattr array is on the stack of each function.
Signed-off-by: Arnd Bergmann arnd@arndb.de Link: https://patch.msgid.link/20250620113335.3776965-1-arnd@kernel.org Signed-off-by: Leon Romanovsky leon@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
**Backport Status: YES**
This commit should be backported to stable kernel trees. Here's my extensive analysis:
## Bug Being Fixed
The commit fixes a **real compilation error** that occurs on s390 architecture with gcc-10 and earlier compilers. The error message shows: ``` drivers/infiniband/core/nldev.c:2466:1: error: the frame size of 1720 bytes is larger than 1280 bytes [-Werror=frame-larger-than=] ```
This is a build failure that prevents the kernel from compiling on s390 systems, which is a significant issue affecting users.
## Nature of the Fix
The fix is extremely minimal and safe: 1. **Three functions are marked with `noinline_for_stack`**: - `res_get_common_doit()` - `stat_get_doit_default_counter()` - `stat_get_doit_qp()`
2. **No functional changes**: The code logic remains identical - only the compiler optimization behavior is modified to prevent inlining.
3. **Stack usage optimization**: Each function uses ~600 bytes of stack (likely from large `struct nlattr` arrays). When inlined into `nldev_stat_get_doit()`, this causes cumulative stack usage of 1720 bytes, exceeding s390's 1280-byte limit.
## Code Analysis
Looking at `/home/sasha/linux/drivers/infiniband/core/nldev.c:2438-2466`, the `nldev_stat_get_doit()` function calls three helper functions: - Line 2450: `stat_get_doit_default_counter()` - Line 2454: `stat_get_doit_qp()` - Line 2457: `res_get_common_doit()`
Each function declares `struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]` which is a large stack array. The `noinline_for_stack` attribute ensures these arrays aren't all allocated on the same stack frame.
## Stable Tree Criteria
This commit meets multiple stable tree criteria: 1. **Fixes a real bug**: Build failure on s390 architecture 2. **Minimal change**: Only adds compiler hints, no logic changes 3. **Low regression risk**: Cannot introduce functional regressions 4. **Architecture-specific fix**: Critical for s390 builds 5. **Clear problem/solution**: Well-documented issue with straightforward fix
## Additional Considerations
- The fix is platform-agnostic despite being triggered on s390, making it safe for all architectures - Similar patterns (`noinline_for_stack`) are used throughout the kernel for stack optimization - The commit message clearly documents the problem and solution - No new features or architectural changes are introduced
This is an ideal stable backport candidate - it fixes a real compilation issue with zero functional risk.
drivers/infiniband/core/nldev.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index a872643e8039..e9b7a6419291 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -1469,10 +1469,11 @@ static const struct nldev_fill_res_entry fill_entries[RDMA_RESTRACK_MAX] = {
};
-static int res_get_common_doit(struct sk_buff *skb, struct nlmsghdr *nlh, - struct netlink_ext_ack *extack, - enum rdma_restrack_type res_type, - res_fill_func_t fill_func) +static noinline_for_stack int +res_get_common_doit(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack, + enum rdma_restrack_type res_type, + res_fill_func_t fill_func) { const struct nldev_fill_res_entry *fe = &fill_entries[res_type]; struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; @@ -2263,10 +2264,10 @@ static int nldev_stat_del_doit(struct sk_buff *skb, struct nlmsghdr *nlh, return ret; }
-static int stat_get_doit_default_counter(struct sk_buff *skb, - struct nlmsghdr *nlh, - struct netlink_ext_ack *extack, - struct nlattr *tb[]) +static noinline_for_stack int +stat_get_doit_default_counter(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack, + struct nlattr *tb[]) { struct rdma_hw_stats *stats; struct nlattr *table_attr; @@ -2356,8 +2357,9 @@ static int stat_get_doit_default_counter(struct sk_buff *skb, return ret; }
-static int stat_get_doit_qp(struct sk_buff *skb, struct nlmsghdr *nlh, - struct netlink_ext_ack *extack, struct nlattr *tb[]) +static noinline_for_stack int +stat_get_doit_qp(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack, struct nlattr *tb[])
{ static enum rdma_nl_counter_mode mode;