The original issue, https://lore.kernel.org/netdev/1562959401-19815-1-git-send-email-cai@xxxxxx/ The debugging so far seems point to that the compilers get confused by the module sections. During module_param(), it stores “__param_rx_frag_size" as a “struct kernel_param” into the __param section. Later, load_module() obtains all “kernel_param” from the __param section and compare against the user-input module parameters from the command-line. If there is a match, it calls params[i].ops->set(¶ms[I]) to replace the value. If compilers can’t see that params[i].ops->set(¶ms[I]) could potentially change the value of rx_frag_size, it will wrongly optimize it as a constant. For example (it is not compilable yet as I have not able to extract variable from the __param section like find_module_sections()), #include <stdio.h> #include <string.h> #define __module_param_call(name, ops, arg) \ static struct kernel_param __param_##name \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) = { \ #name, ops, { arg } } struct kernel_param { const char *name; const struct kernel_param_ops *ops; union { int *arg; }; }; struct kernel_param_ops { int (*set)(const struct kernel_param *kp); }; #define STANDARD_PARAM_DEF(name) \ int param_set_##name(const struct kernel_param *kp) \ { \ *kp->arg = 2; \ } \ const struct kernel_param_ops param_ops_##name = { \ .set = param_set_##name, \ }; STANDARD_PARAM_DEF(ushort); static int rx = 1; __module_param_call(rx_frag_siz, ¶m_ops_ushort, &rx_frag_size); int main(int argc, char *argv[]) { const struct kernel_param *params = <<< Get all kernel_param from the __param section >>>; int i; if (__builtin_constant_p(rx_frag_size)) printf("rx_frag_size is a const.\n"); for (i = 0; i < num_param; i++) { if (!strcmp(params[I].name, argv[1])) { params[i].ops->set(¶ms[i]); break; } } printf("rx_frag_size = %d\n", rx_frag_size); return 0; }