mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 16:53:20 -04:00
Merge tag 'modules-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/modules/linux
Pull module updates from Petr Pavlu: - Remove unnecessary module::args. Nowadays, no parameter-handling code points into the module::args buffer. The last user of module::args in xtensa/simdisk is updated and the data is then removed - Add Rust support for boolean parameters. This will initially be used by the Rust null block driver - Fix clearing the current charp parameter value when setting a new one fails due to an allocation failure - Improve the debugging code for kmod (request_module()) duplicates. Fix a potential use-after-free when waiting on a duplicate request and make several general improvements to the code - Fix the symbol size returned when looking up a data symbol through kallsyms - Smaller fixes and cleanups * tag 'modules-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/modules/linux: params: fix charp corruption on allocation failure module: validate string table section types module/dups: Clean up includes module/dups: Use strcmp() to compare module names module/dups: Use scope-based cleanup helpers module/dups: Avoid unnecessary kmod_dup_req allocations module/dups: Fix use-after-free in kmod_dup_req lifetime handling module/dups: Inform duplicate requests about the result directly rust: module_param: support bool parameters rust: module_param: return value by copy from `value` module: Remove unnecessary module::args xtensa/simdisk: Avoid referring to module::args module: Remove unused DISCARD_EH_FRAME definition from module.lds.S module: procfs: use matching type for accumulator in module_total_size() module: use strscpy() to copy module names in stats and dup tracking params: fix path of /sys/module/XYZ/parameters/ in comment module/kallsyms: fix nextval for data symbol lookup
This commit is contained in:
@@ -41,7 +41,7 @@ module_param(simdisk_count, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(simdisk_count, "Number of simdisk units.");
|
||||
|
||||
static int n_files;
|
||||
static const char *filename[MAX_SIMDISK_COUNT] = {
|
||||
static char *filename[MAX_SIMDISK_COUNT] = {
|
||||
#ifdef CONFIG_SIMDISK0_FILENAME
|
||||
CONFIG_SIMDISK0_FILENAME,
|
||||
#ifdef CONFIG_SIMDISK1_FILENAME
|
||||
@@ -50,20 +50,48 @@ static const char *filename[MAX_SIMDISK_COUNT] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* The simdisk code can be built either into the kernel or as a loadable module.
|
||||
* When built-in, CONFIG_SIMDISK{0,1}_FILENAME can be used to specify the
|
||||
* initial simdisk filenames and additional filenames can be provided on the
|
||||
* kernel command line. These arguments are parsed during early boot when slab
|
||||
* is not yet available, but the command line itself is preserved for the
|
||||
* lifetime of the kernel, so the incoming pointer is stored directly.
|
||||
* When built as a loadable module, each value is copied with kstrdup() and all
|
||||
* allocated memory is freed in simdisk_param_free_filename() when the module is
|
||||
* unloaded.
|
||||
*/
|
||||
static int simdisk_param_set_filename(const char *val,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
if (n_files < ARRAY_SIZE(filename))
|
||||
filename[n_files++] = val;
|
||||
else
|
||||
char *str;
|
||||
|
||||
if (n_files >= ARRAY_SIZE(filename))
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef MODULE
|
||||
str = kstrdup(val, GFP_KERNEL);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
#else
|
||||
str = (char *)val;
|
||||
#endif
|
||||
|
||||
filename[n_files++] = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void simdisk_param_free_filename(void *arg)
|
||||
{
|
||||
for (int i = 0; i < n_files; i++)
|
||||
kfree(filename[i]);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops simdisk_param_ops_filename = {
|
||||
.set = simdisk_param_set_filename,
|
||||
.free = simdisk_param_free_filename,
|
||||
};
|
||||
module_param_cb(filename, &simdisk_param_ops_filename, &n_files, 0);
|
||||
module_param_cb(filename, &simdisk_param_ops_filename, NULL, 0);
|
||||
MODULE_PARM_DESC(filename, "Backing storage filename.");
|
||||
|
||||
static int simdisk_major = SIMDISK_MAJOR;
|
||||
|
||||
@@ -477,10 +477,6 @@ struct module {
|
||||
struct module_notes_attrs *notes_attrs;
|
||||
#endif
|
||||
|
||||
/* The command line arguments (may be mangled). People like
|
||||
keeping pointers to this stuff */
|
||||
char *args;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Per-cpu data. */
|
||||
void __percpu *percpu;
|
||||
|
||||
@@ -7,29 +7,22 @@
|
||||
|
||||
#define pr_fmt(fmt) "module: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -38,32 +31,42 @@
|
||||
static bool enable_dups_trace = IS_ENABLED(CONFIG_MODULE_DEBUG_AUTOLOAD_DUPS_TRACE);
|
||||
module_param(enable_dups_trace, bool_enable_only, 0644);
|
||||
|
||||
/*
|
||||
* Protects dup_kmod_reqs list, adds / removals with RCU.
|
||||
*/
|
||||
/* A mutex-protected list of active kmod requests. */
|
||||
static DEFINE_MUTEX(kmod_dup_mutex);
|
||||
static LIST_HEAD(dup_kmod_reqs);
|
||||
|
||||
struct kmod_dup_req {
|
||||
refcount_t refcount;
|
||||
struct list_head list;
|
||||
char name[MODULE_NAME_LEN];
|
||||
struct completion first_req_done;
|
||||
struct work_struct complete_work;
|
||||
struct delayed_work delete_work;
|
||||
int dup_ret;
|
||||
};
|
||||
|
||||
static void get_kmod_req(struct kmod_dup_req *kmod_req)
|
||||
{
|
||||
refcount_inc(&kmod_req->refcount);
|
||||
}
|
||||
|
||||
static void put_kmod_req(struct kmod_dup_req *kmod_req)
|
||||
{
|
||||
if (refcount_dec_and_test(&kmod_req->refcount))
|
||||
kfree(kmod_req);
|
||||
}
|
||||
|
||||
DEFINE_FREE(put_kmod_req, struct kmod_dup_req *, if (_T) put_kmod_req(_T))
|
||||
|
||||
static struct kmod_dup_req *kmod_dup_request_lookup(char *module_name)
|
||||
{
|
||||
struct kmod_dup_req *kmod_req;
|
||||
|
||||
list_for_each_entry_rcu(kmod_req, &dup_kmod_reqs, list,
|
||||
lockdep_is_held(&kmod_dup_mutex)) {
|
||||
if (strlen(kmod_req->name) == strlen(module_name) &&
|
||||
!memcmp(kmod_req->name, module_name, strlen(module_name))) {
|
||||
lockdep_assert_held(&kmod_dup_mutex);
|
||||
|
||||
list_for_each_entry(kmod_req, &dup_kmod_reqs, list) {
|
||||
if (!strcmp(kmod_req->name, module_name))
|
||||
return kmod_req;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -86,58 +89,40 @@ static void kmod_dup_request_delete(struct work_struct *work)
|
||||
* kmod. The inneficies there are a call to modprobe and modprobe
|
||||
* just returning 0.
|
||||
*/
|
||||
mutex_lock(&kmod_dup_mutex);
|
||||
list_del_rcu(&kmod_req->list);
|
||||
synchronize_rcu();
|
||||
mutex_unlock(&kmod_dup_mutex);
|
||||
kfree(kmod_req);
|
||||
scoped_guard(mutex, &kmod_dup_mutex)
|
||||
list_del(&kmod_req->list);
|
||||
|
||||
put_kmod_req(kmod_req);
|
||||
}
|
||||
|
||||
static void kmod_dup_request_complete(struct work_struct *work)
|
||||
static struct kmod_dup_req *alloc_kmod_req(const char *module_name)
|
||||
{
|
||||
struct kmod_dup_req *kmod_req;
|
||||
struct kmod_dup_req *kmod_req = kzalloc_obj(*kmod_req);
|
||||
|
||||
kmod_req = container_of(work, struct kmod_dup_req, complete_work);
|
||||
if (!kmod_req)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* This will ensure that the kernel will let all the waiters get
|
||||
* informed its time to check the return value. It's time to
|
||||
* go home.
|
||||
*/
|
||||
complete_all(&kmod_req->first_req_done);
|
||||
|
||||
/*
|
||||
* Now that we have allowed prior request_module() calls to go on
|
||||
* with life, let's schedule deleting this entry. We don't have
|
||||
* to do it right away, but we *eventually* want to do it so to not
|
||||
* let this linger forever as this is just a boot optimization for
|
||||
* possible abuses of vmalloc() incurred by finit_module() thrashing.
|
||||
*/
|
||||
queue_delayed_work(system_dfl_wq, &kmod_req->delete_work, 60 * HZ);
|
||||
refcount_set(&kmod_req->refcount, 1);
|
||||
strscpy(kmod_req->name, module_name);
|
||||
INIT_DELAYED_WORK(&kmod_req->delete_work, kmod_dup_request_delete);
|
||||
init_completion(&kmod_req->first_req_done);
|
||||
return kmod_req;
|
||||
}
|
||||
|
||||
bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
|
||||
{
|
||||
struct kmod_dup_req *kmod_req, *new_kmod_req;
|
||||
struct kmod_dup_req *kmod_req __free(put_kmod_req) = NULL;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Pre-allocate the entry in case we have to use it later
|
||||
* to avoid contention with the mutex.
|
||||
*/
|
||||
new_kmod_req = kzalloc_obj(*new_kmod_req);
|
||||
if (!new_kmod_req)
|
||||
return false;
|
||||
scoped_guard(mutex, &kmod_dup_mutex) {
|
||||
struct kmod_dup_req *new_kmod_req;
|
||||
|
||||
memcpy(new_kmod_req->name, module_name, strlen(module_name));
|
||||
INIT_WORK(&new_kmod_req->complete_work, kmod_dup_request_complete);
|
||||
INIT_DELAYED_WORK(&new_kmod_req->delete_work, kmod_dup_request_delete);
|
||||
init_completion(&new_kmod_req->first_req_done);
|
||||
kmod_req = kmod_dup_request_lookup(module_name);
|
||||
if (kmod_req) {
|
||||
get_kmod_req(kmod_req);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_lock(&kmod_dup_mutex);
|
||||
|
||||
kmod_req = kmod_dup_request_lookup(module_name);
|
||||
if (!kmod_req) {
|
||||
/*
|
||||
* If the first request that came through for a module
|
||||
* was with request_module_nowait() we cannot wait for it
|
||||
@@ -150,9 +135,7 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
|
||||
* would benefit from duplicate detection.
|
||||
*/
|
||||
if (!wait) {
|
||||
kfree(new_kmod_req);
|
||||
pr_debug("New request_module_nowait() for %s -- cannot track duplicates for this request\n", module_name);
|
||||
mutex_unlock(&kmod_dup_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -161,14 +144,14 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
|
||||
* keep tab on duplicates later.
|
||||
*/
|
||||
pr_debug("New request_module() for %s\n", module_name);
|
||||
list_add_rcu(&new_kmod_req->list, &dup_kmod_reqs);
|
||||
mutex_unlock(&kmod_dup_mutex);
|
||||
new_kmod_req = alloc_kmod_req(module_name);
|
||||
if (!new_kmod_req)
|
||||
return false;
|
||||
list_add(&new_kmod_req->list, &dup_kmod_reqs);
|
||||
return false;
|
||||
}
|
||||
mutex_unlock(&kmod_dup_mutex);
|
||||
|
||||
/* We are dealing with a duplicate request now */
|
||||
kfree(new_kmod_req);
|
||||
|
||||
/*
|
||||
* To fix these try to use try_then_request_module() instead as that
|
||||
@@ -214,7 +197,6 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
|
||||
|
||||
/* Now the duplicate request has the same exact return value as the first request */
|
||||
*dup_ret = kmod_req->dup_ret;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -222,26 +204,29 @@ void kmod_dup_request_announce(char *module_name, int ret)
|
||||
{
|
||||
struct kmod_dup_req *kmod_req;
|
||||
|
||||
mutex_lock(&kmod_dup_mutex);
|
||||
/*
|
||||
* Look for a kmod_dup_req previously added in
|
||||
* kmod_dup_request_exists_wait(). Note that a request_module_nowait()
|
||||
* without its own kmod_dup_req entry can announce a result of
|
||||
* a concurrent request_module() call.
|
||||
*/
|
||||
scoped_guard(mutex, &kmod_dup_mutex) {
|
||||
kmod_req = kmod_dup_request_lookup(module_name);
|
||||
if (!kmod_req || completion_done(&kmod_req->first_req_done))
|
||||
return;
|
||||
|
||||
kmod_req = kmod_dup_request_lookup(module_name);
|
||||
if (!kmod_req)
|
||||
goto out;
|
||||
kmod_req->dup_ret = ret;
|
||||
|
||||
kmod_req->dup_ret = ret;
|
||||
/* Inform all duplicate waiters to check the return value. */
|
||||
complete_all(&kmod_req->first_req_done);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we complete() here we may allow duplicate threads
|
||||
* to continue before the first one that submitted the
|
||||
* request. We're in no rush also, given that each and
|
||||
* every bounce back to userspace is slow we avoid that
|
||||
* with a slight delay here. So queueue up the completion
|
||||
* and let duplicates suffer, just wait a tad bit longer.
|
||||
* There is no rush. But we also don't want to hold the
|
||||
* caller up forever or introduce any boot delays.
|
||||
* Now that we have allowed prior request_module() calls to go on
|
||||
* with life, let's schedule deleting this entry. We don't have
|
||||
* to do it right away, but we *eventually* want to do it so to not
|
||||
* let this linger forever as this is just a boot optimization for
|
||||
* possible abuses of vmalloc() incurred by finit_module() thrashing.
|
||||
*/
|
||||
queue_work(system_dfl_wq, &kmod_req->complete_work);
|
||||
|
||||
out:
|
||||
mutex_unlock(&kmod_dup_mutex);
|
||||
queue_delayed_work(system_dfl_wq, &kmod_req->delete_work, 60 * HZ);
|
||||
}
|
||||
|
||||
@@ -258,17 +258,25 @@ static const char *find_kallsyms_symbol(struct module *mod,
|
||||
unsigned int i, best = 0;
|
||||
unsigned long nextval, bestval;
|
||||
struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);
|
||||
struct module_memory *mod_mem;
|
||||
struct module_memory *mod_mem = NULL;
|
||||
|
||||
/* At worse, next value is at end of module */
|
||||
if (within_module_init(addr, mod))
|
||||
mod_mem = &mod->mem[MOD_INIT_TEXT];
|
||||
else
|
||||
mod_mem = &mod->mem[MOD_TEXT];
|
||||
for_each_mod_mem_type(type) {
|
||||
#ifndef CONFIG_KALLSYMS_ALL
|
||||
if (!mod_mem_type_is_text(type))
|
||||
continue;
|
||||
#endif
|
||||
if (within_module_mem_type(addr, mod, type)) {
|
||||
mod_mem = &mod->mem[type];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mod_mem)
|
||||
return NULL;
|
||||
|
||||
/* Initialize bounds within memory region the address belongs to. */
|
||||
nextval = (unsigned long)mod_mem->base + mod_mem->size;
|
||||
|
||||
bestval = kallsyms_symbol_value(&kallsyms->symtab[best]);
|
||||
bestval = (unsigned long)mod_mem->base - 1;
|
||||
|
||||
/*
|
||||
* Scan for closest preceding symbol, and next symbol. (ELF
|
||||
|
||||
@@ -1458,7 +1458,6 @@ static void free_module(struct module *mod)
|
||||
|
||||
/* This may be empty, but that's OK */
|
||||
module_arch_freeing_init(mod);
|
||||
kfree(mod->args);
|
||||
percpu_modfree(mod);
|
||||
|
||||
free_mod_mem(mod);
|
||||
@@ -2011,6 +2010,7 @@ static int elf_validity_cache_sechdrs(struct load_info *info)
|
||||
* Specifically checks:
|
||||
*
|
||||
* * Section name table index is inbounds of section headers
|
||||
* * Section name table type is SHT_STRTAB
|
||||
* * Section name table is not empty
|
||||
* * Section name table is NUL terminated
|
||||
* * All section name offsets are inbounds of the section
|
||||
@@ -2038,6 +2038,11 @@ static int elf_validity_cache_secstrings(struct load_info *info)
|
||||
|
||||
strhdr = &info->sechdrs[info->hdr->e_shstrndx];
|
||||
|
||||
if (strhdr->sh_type != SHT_STRTAB) {
|
||||
pr_err("Invalid ELF section name table type: %u\n", strhdr->sh_type);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
/*
|
||||
* The section name table must be NUL-terminated, as required
|
||||
* by the spec. This makes strcmp and pr_* calls that access
|
||||
@@ -2204,7 +2209,7 @@ static int elf_validity_cache_index_sym(struct load_info *info)
|
||||
* Must have &load_info->index.sym populated.
|
||||
*
|
||||
* Looks at the symbol table's associated string table, makes sure it is
|
||||
* in-bounds, and caches it.
|
||||
* in-bounds and of type SHT_STRTAB, and caches it.
|
||||
*
|
||||
* Return: %0 if valid, %-ENOEXEC on failure.
|
||||
*/
|
||||
@@ -2218,6 +2223,12 @@ static int elf_validity_cache_index_str(struct load_info *info)
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
if (info->sechdrs[str_idx].sh_type != SHT_STRTAB) {
|
||||
pr_err("Invalid ELF symbol string table type: %u\n",
|
||||
info->sechdrs[str_idx].sh_type);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
info->index.str = str_idx;
|
||||
return 0;
|
||||
}
|
||||
@@ -3425,7 +3436,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
struct module *mod;
|
||||
bool module_allocated = false;
|
||||
long err = 0;
|
||||
char *after_dashes;
|
||||
char *args = NULL, *after_dashes;
|
||||
|
||||
/*
|
||||
* Do the signature check (if any) first. All that
|
||||
@@ -3523,9 +3534,9 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
flush_module_icache(mod);
|
||||
|
||||
/* Now copy in args */
|
||||
mod->args = strndup_user(uargs, ~0UL >> 1);
|
||||
if (IS_ERR(mod->args)) {
|
||||
err = PTR_ERR(mod->args);
|
||||
args = strndup_user(uargs, ~0UL >> 1);
|
||||
if (IS_ERR(args)) {
|
||||
err = PTR_ERR(args);
|
||||
goto free_arch_cleanup;
|
||||
}
|
||||
|
||||
@@ -3546,7 +3557,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
mod->async_probe_requested = async_probe;
|
||||
|
||||
/* Module is ready to execute: parsing args may do that. */
|
||||
after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
|
||||
after_dashes = parse_args(mod->name, args, mod->kp, mod->num_kp,
|
||||
-32768, 32767, mod,
|
||||
unknown_module_param_cb);
|
||||
if (IS_ERR(after_dashes)) {
|
||||
@@ -3556,6 +3567,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
pr_warn("%s: parameters '%s' after `--' ignored\n",
|
||||
mod->name, after_dashes);
|
||||
}
|
||||
kfree(args);
|
||||
args = NULL;
|
||||
|
||||
/* Link in to sysfs. */
|
||||
err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
|
||||
@@ -3597,7 +3610,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
ddebug_cleanup:
|
||||
ftrace_release_mod(mod);
|
||||
synchronize_rcu();
|
||||
kfree(mod->args);
|
||||
kfree(args);
|
||||
free_arch_cleanup:
|
||||
module_arch_cleanup(mod);
|
||||
free_modinfo:
|
||||
|
||||
@@ -64,7 +64,7 @@ static void m_stop(struct seq_file *m, void *p)
|
||||
|
||||
static unsigned int module_total_size(struct module *mod)
|
||||
{
|
||||
int size = 0;
|
||||
unsigned int size = 0;
|
||||
|
||||
for_each_mod_mem_type(type)
|
||||
size += mod->mem[type].size;
|
||||
|
||||
@@ -253,7 +253,7 @@ int try_add_failed_module(const char *name, enum fail_dup_mod_reason reason)
|
||||
mod_fail = kzalloc_obj(*mod_fail);
|
||||
if (!mod_fail)
|
||||
return -ENOMEM;
|
||||
memcpy(mod_fail->name, name, strlen(name));
|
||||
strscpy(mod_fail->name, name);
|
||||
__set_bit(reason, &mod_fail->dup_fail_mask);
|
||||
atomic_long_inc(&mod_fail->count);
|
||||
list_add_rcu(&mod_fail->list, &dup_failed_modules);
|
||||
|
||||
@@ -261,6 +261,7 @@ EXPORT_SYMBOL_GPL(param_set_uint_minmax);
|
||||
|
||||
int param_set_charp(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
char *tmp;
|
||||
size_t len, maxlen = 1024;
|
||||
|
||||
len = strnlen(val, maxlen + 1);
|
||||
@@ -269,19 +270,20 @@ int param_set_charp(const char *val, const struct kernel_param *kp)
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
maybe_kfree_parameter(*(char **)kp->arg);
|
||||
|
||||
/*
|
||||
* This is a hack. We can't kmalloc() in early boot, and we
|
||||
* don't need to; this mangled commandline is preserved.
|
||||
*/
|
||||
if (slab_is_available()) {
|
||||
*(char **)kp->arg = kmalloc_parameter(len + 1);
|
||||
if (!*(char **)kp->arg)
|
||||
tmp = kmalloc_parameter(len + 1);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
strcpy(*(char **)kp->arg, val);
|
||||
memcpy(tmp, val, len + 1);
|
||||
} else
|
||||
*(const char **)kp->arg = val;
|
||||
tmp = (char *)val;
|
||||
|
||||
maybe_kfree_parameter(*(char **)kp->arg);
|
||||
*(char **)kp->arg = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -538,7 +540,7 @@ const struct kernel_param_ops param_ops_string = {
|
||||
};
|
||||
EXPORT_SYMBOL(param_ops_string);
|
||||
|
||||
/* sysfs output in /sys/modules/XYZ/parameters/ */
|
||||
/* sysfs output in /sys/module/XYZ/parameters/ */
|
||||
#define to_module_attr(n) container_of_const(n, struct module_attribute, attr)
|
||||
#define to_module_kobject(n) container_of(n, struct module_kobject, kobj)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h)
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::str::BStr;
|
||||
use crate::str::{kstrtobool_bytes, BStr};
|
||||
use bindings;
|
||||
use kernel::sync::SetOnce;
|
||||
|
||||
@@ -105,6 +105,12 @@ fn try_from_param_arg(arg: &BStr) -> Result<Self> {
|
||||
impl_int_module_param!(isize);
|
||||
impl_int_module_param!(usize);
|
||||
|
||||
impl ModuleParam for bool {
|
||||
fn try_from_param_arg(arg: &BStr) -> Result<Self> {
|
||||
kstrtobool_bytes(arg)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for kernel parameters.
|
||||
///
|
||||
/// This type is instantiated by the [`module!`] macro when module parameters are
|
||||
@@ -130,10 +136,26 @@ pub const fn new(default: T) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a copy of the parameter value.
|
||||
///
|
||||
/// Returns the value supplied at module load time, or the default value
|
||||
/// if the parameter has not been set.
|
||||
#[inline]
|
||||
pub fn value(&self) -> T
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
self.value.copy().unwrap_or(self.default)
|
||||
}
|
||||
|
||||
/// Get a shared reference to the parameter value.
|
||||
///
|
||||
/// Returns a reference to the value supplied at module load time, or a
|
||||
/// reference to the default value if the parameter has not been set.
|
||||
// Note: When sysfs access to parameters are enabled, we have to pass in a
|
||||
// held lock guard here.
|
||||
pub fn value(&self) -> &T {
|
||||
#[inline]
|
||||
pub fn value_ref(&self) -> &T {
|
||||
self.value.as_ref().unwrap_or(&self.default)
|
||||
}
|
||||
|
||||
@@ -179,3 +201,4 @@ macro_rules! make_param_ops {
|
||||
make_param_ops!(PARAM_OPS_U64, u64);
|
||||
make_param_ops!(PARAM_OPS_ISIZE, isize);
|
||||
make_param_ops!(PARAM_OPS_USIZE, usize);
|
||||
make_param_ops!(PARAM_OPS_BOOL, bool);
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
/// - [`u64`]
|
||||
/// - [`isize`]
|
||||
/// - [`usize`]
|
||||
/// - [`bool`]
|
||||
///
|
||||
/// C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h)
|
||||
///
|
||||
|
||||
@@ -192,6 +192,7 @@ fn param_ops_path(param_type: &str) -> Path {
|
||||
"u64" => parse_quote!(::kernel::module_param::PARAM_OPS_U64),
|
||||
"isize" => parse_quote!(::kernel::module_param::PARAM_OPS_ISIZE),
|
||||
"usize" => parse_quote!(::kernel::module_param::PARAM_OPS_USIZE),
|
||||
"bool" => parse_quote!(::kernel::module_param::PARAM_OPS_BOOL),
|
||||
t => panic!("Unsupported parameter type {}", t),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
default: 1,
|
||||
description: "This parameter has a default of 1",
|
||||
},
|
||||
test_bool_parameter: bool {
|
||||
default: false,
|
||||
description: "This boolean parameter defaults to false",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -28,7 +32,11 @@ fn init(_module: &'static ThisModule) -> Result<Self> {
|
||||
pr_info!("Am I built-in? {}\n", !cfg!(MODULE));
|
||||
pr_info!(
|
||||
"test_parameter: {}\n",
|
||||
*module_parameters::test_parameter.value()
|
||||
module_parameters::test_parameter.value()
|
||||
);
|
||||
pr_info!(
|
||||
"test_bool_parameter: {}\n",
|
||||
module_parameters::test_bool_parameter.value()
|
||||
);
|
||||
|
||||
let mut numbers = KVec::new();
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
* Archs are free to supply their own linker scripts. ld will
|
||||
* combine them automatically.
|
||||
*/
|
||||
#ifdef CONFIG_UNWIND_TABLES
|
||||
#define DISCARD_EH_FRAME
|
||||
#else
|
||||
#define DISCARD_EH_FRAME *(.eh_frame)
|
||||
#endif
|
||||
|
||||
#include <asm-generic/codetag.lds.h>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user