2015-06-04 12:53:13

by Dan Streetman

[permalink] [raw]
Subject: [RFC] module: add per-module params lock

I sent this as part of a patch series a few days ago, which I was asked to
break up, so I'm sending only this patch as a RFC now, until I work out
the details of the zswap patch that needs this. I'd like to get comments
on this early, since it changes the way module param locking is done.

...

Add a "param_lock" mutex to each module, and add a pointer to the owning
module to struct kernel_param. Then change the param sysfs modification
code to only use the global param_lock for built-ins, and use the
per-module param_lock for all modules. Also simplify the
kernel_[un]block_sysfs_[read|write]() functions, to simply
kernel_[un]block_sysfs().

The kernel param code currently uses a single global mutex to protect
modification of any and all kernel params. While this generally works,
there is one specific problem with it; a module callback function
cannot safely load another module, i.e. with request_module() or even
with indirect calls such as crypto_has_alg(). If the module to be
loaded has any of its params configured (e.g. with a /etc/modprobe.d/*
config file), then the attempt will result in a deadlock between the
first module param callback waiting for modprobe, and modprobe trying to
lock the global kernel param mutex to set the new module's param.

This fixes that by using per-module mutexes, so that each individual module
is protected against concurrent changes in its own kernel params, but is
not blocked by changes to other module params. All built-in modules
continue to use the global mutex, since they will always be loaded at
runtime and references (e.g. request_module(), crypto_has_alg()) to them
will never cause load-time param changing.

This also simplifies the interface used by modules to block sysfs access
to their params; while there are currently functions to block and unblock
sysfs param access which are split up by read and write and expect a single
kernel param to be passed, their actual operation is identical and applies
to all params, not just the one passed to them; they simply lock and unlock
the global param mutex. They are thus replaced with simplified functions
that clearly indicate that all params for a module are being blocked, both
for read and write access.

Signed-off-by: Dan Streetman <[email protected]>
---
arch/um/drivers/hostaudio_kern.c | 20 +++----
drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 6 +--
drivers/net/wireless/libertas_tf/if_usb.c | 6 +--
drivers/usb/atm/ueagle-atm.c | 4 +-
drivers/video/fbdev/vt8623fb.c | 4 +-
include/linux/module.h | 1 +
include/linux/moduleparam.h | 67 +++++++++---------------
kernel/module.c | 1 +
kernel/params.c | 45 ++++++++++------
net/mac80211/rate.c | 4 +-
10 files changed, 77 insertions(+), 81 deletions(-)

diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c
index 9b90fdc..1bcb798 100644
--- a/arch/um/drivers/hostaudio_kern.c
+++ b/arch/um/drivers/hostaudio_kern.c
@@ -185,9 +185,9 @@ static int hostaudio_open(struct inode *inode, struct file *file)
int ret;

#ifdef DEBUG
- kparam_block_sysfs_write(dsp);
+ kparam_block_sysfs();
printk(KERN_DEBUG "hostaudio: open called (host: %s)\n", dsp);
- kparam_unblock_sysfs_write(dsp);
+ kparam_unblock_sysfs();
#endif

state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL);
@@ -199,11 +199,11 @@ static int hostaudio_open(struct inode *inode, struct file *file)
if (file->f_mode & FMODE_WRITE)
w = 1;

- kparam_block_sysfs_write(dsp);
+ kparam_block_sysfs();
mutex_lock(&hostaudio_mutex);
ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0);
mutex_unlock(&hostaudio_mutex);
- kparam_unblock_sysfs_write(dsp);
+ kparam_unblock_sysfs();

if (ret < 0) {
kfree(state);
@@ -260,17 +260,17 @@ static int hostmixer_open_mixdev(struct inode *inode, struct file *file)
if (file->f_mode & FMODE_WRITE)
w = 1;

- kparam_block_sysfs_write(mixer);
+ kparam_block_sysfs();
mutex_lock(&hostaudio_mutex);
ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0);
mutex_unlock(&hostaudio_mutex);
- kparam_unblock_sysfs_write(mixer);
+ kparam_unblock_sysfs();

if (ret < 0) {
- kparam_block_sysfs_write(dsp);
+ kparam_block_sysfs();
printk(KERN_ERR "hostaudio_open_mixdev failed to open '%s', "
"err = %d\n", dsp, -ret);
- kparam_unblock_sysfs_write(dsp);
+ kparam_unblock_sysfs();
kfree(state);
return ret;
}
@@ -326,10 +326,10 @@ MODULE_LICENSE("GPL");

static int __init hostaudio_init_module(void)
{
- __kernel_param_lock();
+ kparam_block_sysfs();
printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n",
dsp, mixer);
- __kernel_param_unlock();
+ kparam_unblock_sysfs();

module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1);
if (module_data.dev_audio < 0) {
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index 2bae502..6d7ac3f 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -279,7 +279,7 @@ MODULE_FIRMWARE("myri10ge_eth_z8e.dat");
MODULE_FIRMWARE("myri10ge_rss_ethp_z8e.dat");
MODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat");

-/* Careful: must be accessed under kparam_block_sysfs_write */
+/* Careful: must be accessed under kparam_block_sysfs */
static char *myri10ge_fw_name = NULL;
module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name");
@@ -3427,7 +3427,7 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
}
}

- kparam_block_sysfs_write(myri10ge_fw_name);
+ kparam_block_sysfs();
if (myri10ge_fw_name != NULL) {
char *fw_name = kstrdup(myri10ge_fw_name, GFP_KERNEL);
if (fw_name) {
@@ -3435,7 +3435,7 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
set_fw_name(mgp, fw_name, true);
}
}
- kparam_unblock_sysfs_write(myri10ge_fw_name);
+ kparam_unblock_sysfs();

if (mgp->board_number < MYRI10GE_MAX_BOARDS &&
myri10ge_fw_names[mgp->board_number] != NULL &&
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c
index 1a20cee..7cdbbce 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/libertas_tf/if_usb.c
@@ -821,15 +821,15 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp)

lbtf_deb_enter(LBTF_DEB_USB);

- kparam_block_sysfs_write(fw_name);
+ kparam_block_sysfs();
ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev);
if (ret < 0) {
pr_err("request_firmware() failed with %#x\n", ret);
pr_err("firmware %s not found\n", lbtf_fw_name);
- kparam_unblock_sysfs_write(fw_name);
+ kparam_unblock_sysfs();
goto done;
}
- kparam_unblock_sysfs_write(fw_name);
+ kparam_unblock_sysfs();

if (check_fwfile_format(cardp->fw->data, cardp->fw->size))
goto release_fw;
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 888998a..8262989 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -1599,7 +1599,7 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver)
char file_arr[] = "CMVxy.bin";
char *file;

- kparam_block_sysfs_write(cmv_file);
+ kparam_block_sysfs();
/* set proper name corresponding modem version and line type */
if (cmv_file[sc->modem_index] == NULL) {
if (UEA_CHIP_VERSION(sc) == ADI930)
@@ -1618,7 +1618,7 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver)
strlcat(cmv_name, file, UEA_FW_NAME_MAX);
if (ver == 2)
strlcat(cmv_name, ".v2", UEA_FW_NAME_MAX);
- kparam_unblock_sysfs_write(cmv_file);
+ kparam_unblock_sysfs();
}

static int request_cmvs_old(struct uea_softc *sc,
diff --git a/drivers/video/fbdev/vt8623fb.c b/drivers/video/fbdev/vt8623fb.c
index ea7f056..a940626 100644
--- a/drivers/video/fbdev/vt8623fb.c
+++ b/drivers/video/fbdev/vt8623fb.c
@@ -754,9 +754,9 @@ static int vt8623_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)

/* Prepare startup mode */

- kparam_block_sysfs_write(mode_option);
+ kparam_block_sysfs();
rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8);
- kparam_unblock_sysfs_write(mode_option);
+ kparam_unblock_sysfs();
if (! ((rc == 1) || (rc == 2))) {
rc = -EINVAL;
dev_err(info->device, "mode %s not found\n", mode_option);
diff --git a/include/linux/module.h b/include/linux/module.h
index c883b86..e8c24d8 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -232,6 +232,7 @@ struct module {
unsigned int num_syms;

/* Kernel parameters. */
+ struct mutex param_lock;
struct kernel_param *kp;
unsigned int num_kp;

diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 1c9effa..d9f689f 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -67,6 +67,7 @@ enum {

struct kernel_param {
const char *name;
+ struct module *mod;
const struct kernel_param_ops *ops;
u16 perm;
s8 level;
@@ -108,7 +109,7 @@ struct kparam_array
*
* @perm is 0 if the the variable is not to appear in sysfs, or 0444
* for world-readable, 0644 for root-writable, etc. Note that if it
- * is writable, you may need to use kparam_block_sysfs_write() around
+ * is writable, you may need to use kparam_block_sysfs() around
* accesses (esp. charp, which can be kfreed when it changes).
*
* The @type is simply pasted to refer to a param_ops_##type and a
@@ -220,8 +221,8 @@ struct kparam_array
static struct kernel_param __moduleparam_const __param_##name \
__used \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
- = { __param_str_##name, ops, VERIFY_OCTAL_PERMISSIONS(perm), \
- level, flags, { arg } }
+ = { __param_str_##name, THIS_MODULE, ops, \
+ VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }

/* Obsolete - use module_param_cb() */
#define module_param_call(name, set, get, arg, perm) \
@@ -239,57 +240,37 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
}

/**
- * kparam_block_sysfs_write - make sure a parameter isn't written via sysfs.
- * @name: the name of the parameter
+ * kparam_block_sysfs - lock THIS_MODULE's param(s) from being r/w
*
- * There's no point blocking write on a paramter that isn't writable via sysfs!
- */
-#define kparam_block_sysfs_write(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0222)); \
- __kernel_param_lock(); \
- } while (0)
-
-/**
- * kparam_unblock_sysfs_write - allows sysfs to write to a parameter again.
- * @name: the name of the parameter
- */
-#define kparam_unblock_sysfs_write(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0222)); \
- __kernel_param_unlock(); \
- } while (0)
-
-/**
- * kparam_block_sysfs_read - make sure a parameter isn't read via sysfs.
- * @name: the name of the parameter
+ * Each module has its own mutex that protects all of its sysfs params
+ * during read and write from sysfs of any of its params. All built-ins
+ * share a single mutex that similarly protects all built-in sysfs params.
+ * This locks the appropriate mutex; either the built-in mutex if called
+ * from a built-in, or the specific module's mutex if called from a module.
*
- * This also blocks sysfs writes.
+ * As with any mutex, this call will block if any of the corresponding
+ * param(s) are currently being read or written. After calling this, the
+ * @kparam_unblock_sysfs() function must be called to unlock the mutex and
+ * allow the corresponding param(s) to be read and written.
*/
-#define kparam_block_sysfs_read(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0444)); \
- __kernel_param_lock(); \
- } while (0)
+#define kparam_block_sysfs() __kernel_param_lock(THIS_MODULE)

/**
- * kparam_unblock_sysfs_read - allows sysfs to read a parameter again.
- * @name: the name of the parameter
+ * kparam_unblock_sysfs - unlock THIS_MODULE's param(s) from being r/w
+ *
+ * This unlocks the corresponding mutex; see @kparam_block_sysfs() for details.
*/
-#define kparam_unblock_sysfs_read(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0444)); \
- __kernel_param_unlock(); \
- } while (0)
+#define kparam_unblock_sysfs() __kernel_param_unlock(THIS_MODULE)

#ifdef CONFIG_SYSFS
-extern void __kernel_param_lock(void);
-extern void __kernel_param_unlock(void);
+/* don't use these directly, use block/unblock_sysfs above */
+extern void __kernel_param_lock(struct module *);
+extern void __kernel_param_unlock(struct module *);
#else
-static inline void __kernel_param_lock(void)
+static inline void __kernel_param_lock(struct module *)
{
}
-static inline void __kernel_param_unlock(void)
+static inline void __kernel_param_unlock(struct module *)
{
}
#endif
diff --git a/kernel/module.c b/kernel/module.c
index 42a1d2a..d3632e7 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3341,6 +3341,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
goto ddebug_cleanup;

/* Module is ready to execute: parsing args may do that. */
+ mutex_init(&mod->param_lock);
after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
-32768, 32767, unknown_module_param_cb);
if (IS_ERR(after_dashes)) {
diff --git a/kernel/params.c b/kernel/params.c
index a22d6a7..2d1849b 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -25,8 +25,9 @@
#include <linux/slab.h>
#include <linux/ctype.h>

-/* Protects all parameters, and incidentally kmalloced_param list. */
+/* Protects all built-in parameters; modules use their own param_lock */
static DEFINE_MUTEX(param_lock);
+static bool __kernel_param_is_locked(struct module *mod);

/* This just allows us to keep track of which parameters are kmalloced. */
struct kmalloced_param {
@@ -34,6 +35,7 @@ struct kmalloced_param {
char val[];
};
static LIST_HEAD(kmalloced_params);
+static DEFINE_SPINLOCK(kmalloced_params_lock);

static void *kmalloc_parameter(unsigned int size)
{
@@ -43,7 +45,10 @@ static void *kmalloc_parameter(unsigned int size)
if (!p)
return NULL;

+ spin_lock(&kmalloced_params_lock);
list_add(&p->list, &kmalloced_params);
+ spin_unlock(&kmalloced_params_lock);
+
return p->val;
}

@@ -52,6 +57,7 @@ static void maybe_kfree_parameter(void *param)
{
struct kmalloced_param *p;

+ spin_lock(&kmalloced_params_lock);
list_for_each_entry(p, &kmalloced_params, list) {
if (p->val == param) {
list_del(&p->list);
@@ -59,6 +65,7 @@ static void maybe_kfree_parameter(void *param)
break;
}
}
+ spin_unlock(&kmalloced_params_lock);
}

static char dash2underscore(char c)
@@ -118,10 +125,10 @@ static int parse_one(char *param,
return -EINVAL;
pr_debug("handling %s with %p\n", param,
params[i].ops->set);
- mutex_lock(&param_lock);
+ __kernel_param_lock(params[i].mod);
param_check_unsafe(&params[i]);
err = params[i].ops->set(val, &params[i]);
- mutex_unlock(&param_lock);
+ __kernel_param_unlock(params[i].mod);
return err;
}
}
@@ -387,7 +394,8 @@ struct kernel_param_ops param_ops_bint = {
EXPORT_SYMBOL(param_ops_bint);

/* We break the rule and mangle the string. */
-static int param_array(const char *name,
+static int param_array(struct module *mod,
+ const char *name,
const char *val,
unsigned int min, unsigned int max,
void *elem, int elemsize,
@@ -418,7 +426,7 @@ static int param_array(const char *name,
/* nul-terminate and parse */
save = val[len];
((char *)val)[len] = '\0';
- BUG_ON(!mutex_is_locked(&param_lock));
+ BUG_ON(!__kernel_param_is_locked(mod));
ret = set(val, &kp);

if (ret != 0)
@@ -440,7 +448,7 @@ static int param_array_set(const char *val, const struct kernel_param *kp)
const struct kparam_array *arr = kp->arr;
unsigned int temp_num;

- return param_array(kp->name, val, 1, arr->max, arr->elem,
+ return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem,
arr->elemsize, arr->ops->set, kp->level,
arr->num ?: &temp_num);
}
@@ -456,7 +464,7 @@ static int param_array_get(char *buffer, const struct kernel_param *kp)
if (i)
buffer[off++] = ',';
p.arg = arr->elem + arr->elemsize * i;
- BUG_ON(!mutex_is_locked(&param_lock));
+ BUG_ON(!__kernel_param_is_locked(kp->mod));
ret = arr->ops->get(buffer + off, &p);
if (ret < 0)
return ret;
@@ -539,9 +547,9 @@ static ssize_t param_attr_show(struct module_attribute *mattr,
if (!attribute->param->ops->get)
return -EPERM;

- mutex_lock(&param_lock);
+ __kernel_param_lock(mk->mod);
count = attribute->param->ops->get(buf, attribute->param);
- mutex_unlock(&param_lock);
+ __kernel_param_unlock(mk->mod);
if (count > 0) {
strcat(buf, "\n");
++count;
@@ -551,7 +559,7 @@ static ssize_t param_attr_show(struct module_attribute *mattr,

/* sysfs always hands a nul-terminated string in buf. We rely on that. */
static ssize_t param_attr_store(struct module_attribute *mattr,
- struct module_kobject *km,
+ struct module_kobject *mk,
const char *buf, size_t len)
{
int err;
@@ -560,10 +568,10 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
if (!attribute->param->ops->set)
return -EPERM;

- mutex_lock(&param_lock);
+ __kernel_param_lock(mk->mod);
param_check_unsafe(attribute->param);
err = attribute->param->ops->set(buf, attribute->param);
- mutex_unlock(&param_lock);
+ __kernel_param_unlock(mk->mod);
if (!err)
return len;
return err;
@@ -576,16 +584,21 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
#define __modinit __init
#endif

+static bool __kernel_param_is_locked(struct module *mod)
+{
+ return mutex_is_locked(mod ? &mod->param_lock : &param_lock);
+}
+
#ifdef CONFIG_SYSFS
-void __kernel_param_lock(void)
+void __kernel_param_lock(struct module *mod)
{
- mutex_lock(&param_lock);
+ mutex_lock(mod ? &mod->param_lock : &param_lock);
}
EXPORT_SYMBOL(__kernel_param_lock);

-void __kernel_param_unlock(void)
+void __kernel_param_unlock(struct module *mod)
{
- mutex_unlock(&param_lock);
+ mutex_unlock(mod ? &mod->param_lock : &param_lock);
}
EXPORT_SYMBOL(__kernel_param_unlock);

diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index d53355b..ca82d5e 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -103,7 +103,7 @@ ieee80211_rate_control_ops_get(const char *name)
const struct rate_control_ops *ops;
const char *alg_name;

- kparam_block_sysfs_write(ieee80211_default_rc_algo);
+ kparam_block_sysfs();
if (!name)
alg_name = ieee80211_default_rc_algo;
else
@@ -117,7 +117,7 @@ ieee80211_rate_control_ops_get(const char *name)
/* try built-in one if specific alg requested but not found */
if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT))
ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT);
- kparam_unblock_sysfs_write(ieee80211_default_rc_algo);
+ kparam_unblock_sysfs();

return ops;
}
--
2.1.0


2015-06-05 02:58:19

by Rusty Russell

[permalink] [raw]
Subject: Re: [RFC] module: add per-module params lock

Dan Streetman <[email protected]> writes:
> I sent this as part of a patch series a few days ago, which I was asked to
> break up, so I'm sending only this patch as a RFC now, until I work out
> the details of the zswap patch that needs this. I'd like to get comments
> on this early, since it changes the way module param locking is done.

OK, it's not insane, but I'm not entirely convinced.

1) The difference between blocking sysfs for read vs write is mainly
documentation. In theory, it allows a rwsem, though in practice it's
not been a bottleneck to now.

2) Implicit is bad: implying the module rather than the parameter is
weird, and skips the BUG_ON() check which was there before.

And finally, why are you loading a module from a param callback? That's
a first!

Thanks,
Rusty.

2015-06-05 10:48:23

by Dan Streetman

[permalink] [raw]
Subject: Re: [RFC] module: add per-module params lock

On Thu, Jun 4, 2015 at 8:42 PM, Rusty Russell <[email protected]> wrote:
> Dan Streetman <[email protected]> writes:
>> I sent this as part of a patch series a few days ago, which I was asked to
>> break up, so I'm sending only this patch as a RFC now, until I work out
>> the details of the zswap patch that needs this. I'd like to get comments
>> on this early, since it changes the way module param locking is done.
>
> OK, it's not insane, but I'm not entirely convinced.
>
> 1) The difference between blocking sysfs for read vs write is mainly
> documentation. In theory, it allows a rwsem, though in practice it's
> not been a bottleneck to now.

The sysfs block/unblock calls could remain the same, but the downside
there is a module can't block more than 1 of its params. It seemed to
me like the per-param r/w block functions were adding unnecessary
restrictions, since we're not using a rwsem for each individual param.
If the param lock is changed to a rwsem in the future, it won't be
hard to also change all callers. Or, we could change it to a resem
now. But as you said, kernel param locking isn't really a contended
lock.

>
> 2) Implicit is bad: implying the module rather than the parameter is
> weird

Well implying it enforces only blocking your own module params; should
module A be blocking and accessing module B's params? Isn't requiring
each module to pass THIS_MODULE unnecessary, at least in the vast
majority of cases? There is still __kernel_param_lock(module) that
can be used if it really is necessary anywhere to block some other
module's params. Or, I can change it to require passing THIS_MODULE
if you think that's a better api.

> , and skips the BUG_ON() check which was there before.

those made absolutely no sense to me; why in the world is it necessary
to BUG() simply because the param permissions don't match r or w? At
*most* that deserves a WARN_ON() but more likely a pr_warn() is
appropriate. And even then, who cares? Does it actually cause any
harm? No. Especially since the underlying lock isn't a rwsem. But
even if it *was* a rwsem, it still wouldn't hurt anything. Maybe the
param's permissions change at runtime by the module for whatever
reason. Should the module really check the current permissions before
deciding to block the param or not? No, it's simpler and harmless to
just block it unconditionally, and avoids a race with the permissions
being changed again and the param accessed. And what if the user
changes the permissions from sysfs? Certainly we don't want to BUG(),
and even printing a WARN() or pr_warn() isn't really appropriate.

In any case, IMHO, those permission checks aren't needed and shouldn't be done.

>
> And finally, why are you loading a module from a param callback? That's
> a first!

Yeah :)

So zswap uses a crypto compressor. The crypto api allows selecting
different compressor functions via crypto_alloc_comp(), and it will
internally load any crypto module that's requested if it's missing (it
also loads the module via crypto_has_comp()/crypto_has_alg()). So,
when someone requests to change zswap's compressor function, zswap
tries to create the crypto compressor transform, and the crypto api
then does request_module() to load the new compressor.

The configuration and module loading is done in the param callback so
that zswap can fail the param setting if there is no such
implementation available.

>
> Thanks,
> Rusty.

2015-06-08 21:14:13

by Rusty Russell

[permalink] [raw]
Subject: Re: [RFC] module: add per-module params lock

Dan Streetman <[email protected]> writes:
> On Thu, Jun 4, 2015 at 8:42 PM, Rusty Russell <[email protected]> wrote:
>> Dan Streetman <[email protected]> writes:
>>> I sent this as part of a patch series a few days ago, which I was asked to
>>> break up, so I'm sending only this patch as a RFC now, until I work out
>>> the details of the zswap patch that needs this. I'd like to get comments
>>> on this early, since it changes the way module param locking is done.
>>
>> OK, it's not insane, but I'm not entirely convinced.
>>
>> 1) The difference between blocking sysfs for read vs write is mainly
>> documentation. In theory, it allows a rwsem, though in practice it's
>> not been a bottleneck to now.
>
> The sysfs block/unblock calls could remain the same, but the downside
> there is a module can't block more than 1 of its params. It seemed to
> me like the per-param r/w block functions were adding unnecessary
> restrictions, since we're not using a rwsem for each individual param.
> If the param lock is changed to a rwsem in the future, it won't be
> hard to also change all callers. Or, we could change it to a resem
> now. But as you said, kernel param locking isn't really a contended
> lock.
>
>>
>> 2) Implicit is bad: implying the module rather than the parameter is
>> weird
>
> Well implying it enforces only blocking your own module params; should
> module A be blocking and accessing module B's params? Isn't requiring
> each module to pass THIS_MODULE unnecessary, at least in the vast
> majority of cases? There is still __kernel_param_lock(module) that
> can be used if it really is necessary anywhere to block some other
> module's params. Or, I can change it to require passing THIS_MODULE
> if you think that's a better api.

It's weird to do anything other than lock the actual parameter you're
talking about. Nobody actually seems to want multi param locks (and if
they try it they'll quickly discover it deadlocks).

>> , and skips the BUG_ON() check which was there before.
>
> those made absolutely no sense to me; why in the world is it necessary
> to BUG() simply because the param permissions don't match r or w? At
> *most* that deserves a WARN_ON() but more likely a pr_warn() is
> appropriate. And even then, who cares? Does it actually cause any
> harm? No. Especially since the underlying lock isn't a rwsem. But
> even if it *was* a rwsem, it still wouldn't hurt anything.

Because you're locking something that can't change. I really want that
not to happen: I'd make it BUILD_BUG_ON() if I could.

You're confusing the API (which explicitly DOESN'T talk about locking,
just blocking sysfs access), and the implementation. For the majority
of users, the API is more important.

If you really dislike the API (and it's such a minor one), perhaps it's
better to expose the lock explicitly:

/* Stops sysfs updates to the module's parameters */
static inline struct mutex *module_param_mutex(struct module *mod)
{
return mod ? mod->param_lock : param_lock;
}

And make the callers do the obvious thing with it?

Cheers,
Rusty.

2015-06-10 01:02:05

by Dan Streetman

[permalink] [raw]
Subject: Re: [RFC] module: add per-module params lock

On Mon, Jun 8, 2015 at 5:13 PM, Rusty Russell <[email protected]> wrote:
> Dan Streetman <[email protected]> writes:
>> On Thu, Jun 4, 2015 at 8:42 PM, Rusty Russell <[email protected]> wrote:
>>> Dan Streetman <[email protected]> writes:
>>>> I sent this as part of a patch series a few days ago, which I was asked to
>>>> break up, so I'm sending only this patch as a RFC now, until I work out
>>>> the details of the zswap patch that needs this. I'd like to get comments
>>>> on this early, since it changes the way module param locking is done.
>>>
>>> OK, it's not insane, but I'm not entirely convinced.
>>>
>>> 1) The difference between blocking sysfs for read vs write is mainly
>>> documentation. In theory, it allows a rwsem, though in practice it's
>>> not been a bottleneck to now.
>>
>> The sysfs block/unblock calls could remain the same, but the downside
>> there is a module can't block more than 1 of its params. It seemed to
>> me like the per-param r/w block functions were adding unnecessary
>> restrictions, since we're not using a rwsem for each individual param.
>> If the param lock is changed to a rwsem in the future, it won't be
>> hard to also change all callers. Or, we could change it to a resem
>> now. But as you said, kernel param locking isn't really a contended
>> lock.
>>
>>>
>>> 2) Implicit is bad: implying the module rather than the parameter is
>>> weird
>>
>> Well implying it enforces only blocking your own module params; should
>> module A be blocking and accessing module B's params? Isn't requiring
>> each module to pass THIS_MODULE unnecessary, at least in the vast
>> majority of cases? There is still __kernel_param_lock(module) that
>> can be used if it really is necessary anywhere to block some other
>> module's params. Or, I can change it to require passing THIS_MODULE
>> if you think that's a better api.
>
> It's weird to do anything other than lock the actual parameter you're
> talking about. Nobody actually seems to want multi param locks (and if
> they try it they'll quickly discover it deadlocks).
>
>>> , and skips the BUG_ON() check which was there before.
>>
>> those made absolutely no sense to me; why in the world is it necessary
>> to BUG() simply because the param permissions don't match r or w? At
>> *most* that deserves a WARN_ON() but more likely a pr_warn() is
>> appropriate. And even then, who cares? Does it actually cause any
>> harm? No. Especially since the underlying lock isn't a rwsem. But
>> even if it *was* a rwsem, it still wouldn't hurt anything.
>
> Because you're locking something that can't change. I really want that
> not to happen: I'd make it BUILD_BUG_ON() if I could.

Ah, ok. I dug in there and I see the perm field isn't actually
changeable from sysfs, it's only the initial setting; so chmod will
only change the sysfs file perm, not the kernel_param.perm field that
the block_sysfs macros check.

If that's the case and perm *really* should never change, why not just
make it const and use BUILD_BUG_ON()? I'll send a patch...

>
> You're confusing the API (which explicitly DOESN'T talk about locking,
> just blocking sysfs access), and the implementation. For the majority
> of users, the API is more important.
>
> If you really dislike the API (and it's such a minor one), perhaps it's
> better to expose the lock explicitly:

I got to this point only because I was getting deadlocked when loading
a module from a param callback, so the API was really secondary to me;
I don't hate the API; and BUILD_BUG_ON seems a lot better. If the
global mutex -> per module mutex seems ok, I can send reduce the patch
to only do that without changing the API.

2015-06-10 17:44:42

by Dan Streetman

[permalink] [raw]
Subject: [PATCH] module: make perm const, change BUG_ON to BUILD_BUG_ON

Change the struct kernel_param.perm field to a const, as it should never
be changed. Add inline functions that return a const value, which are
compiled out, for kparam_[un]block_sysfs_r/w() macros to use; and change
the BUG_ON() in those macros to BUILD_BUG_ON().

This will enforce that the kernel_param permissions are never changed,
and it will fail at build instead of runtime for any misuse of
sysfs param blocking.

I initially wanted to just use the const param.perm in the block_sysfs
checks, but unfortunately that also requires the param itself to be
const, in order to do const folding into a constant compiletime
condition to BUILD_BUG_ON(), and on some archs the params aren't defined
as const:

/* On alpha, ia64 and ppc64 relocations to global data cannot go into
read-only sections (which is part of respective UNIX ABI on these
platforms). So 'const' makes no sense and even causes compile failures
with some compilers. */

So instead, this copies the idea from __param_check() to add inline
constant functions to check the param permissions. That way no extra
bits are needed.

Signed-off-by: Dan Streetman <[email protected]>
---
include/linux/moduleparam.h | 41 +++++++++++++++++++++++------------------
kernel/params.c | 8 +++-----
2 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 1c9effa..032f1ce 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -68,7 +68,7 @@ enum {
struct kernel_param {
const char *name;
const struct kernel_param_ops *ops;
- u16 perm;
+ const u16 perm;
s8 level;
u8 flags;
union {
@@ -215,8 +215,13 @@ struct kparam_array
/* This is the fundamental function for registering boot/module
parameters. */
#define __module_param_call(prefix, name, ops, arg, perm, level, flags) \
+ /* These allow kparam_block_sysfs_*() to use BUILD_BUG_ON() */ \
+ static __attribute__((unused)) __always_inline const u16 \
+ __param_perm_rcheck_##name(void) { return (perm) & 0444; }; \
+ static __attribute__((unused)) __always_inline const u16 \
+ __param_perm_wcheck_##name(void) { return (perm) & 0222; }; \
/* Default value instead of permissions? */ \
- static const char __param_str_##name[] = prefix #name; \
+ static const char __param_str_##name[] = prefix #name; \
static struct kernel_param __moduleparam_const __param_##name \
__used \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
@@ -244,20 +249,20 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
*
* There's no point blocking write on a paramter that isn't writable via sysfs!
*/
-#define kparam_block_sysfs_write(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0222)); \
- __kernel_param_lock(); \
+#define kparam_block_sysfs_write(name) \
+ do { \
+ BUILD_BUG_ON(!__param_perm_wcheck_##name()); \
+ __kernel_param_lock(); \
} while (0)

/**
* kparam_unblock_sysfs_write - allows sysfs to write to a parameter again.
* @name: the name of the parameter
*/
-#define kparam_unblock_sysfs_write(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0222)); \
- __kernel_param_unlock(); \
+#define kparam_unblock_sysfs_write(name) \
+ do { \
+ BUILD_BUG_ON(!__param_perm_wcheck_##name()); \
+ __kernel_param_unlock(); \
} while (0)

/**
@@ -266,20 +271,20 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
*
* This also blocks sysfs writes.
*/
-#define kparam_block_sysfs_read(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0444)); \
- __kernel_param_lock(); \
+#define kparam_block_sysfs_read(name) \
+ do { \
+ BUILD_BUG_ON(!__param_perm_rcheck_##name()); \
+ __kernel_param_lock(); \
} while (0)

/**
* kparam_unblock_sysfs_read - allows sysfs to read a parameter again.
* @name: the name of the parameter
*/
-#define kparam_unblock_sysfs_read(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0444)); \
- __kernel_param_unlock(); \
+#define kparam_unblock_sysfs_read(name) \
+ do { \
+ BUILD_BUG_ON(!__param_perm_rcheck_##name()); \
+ __kernel_param_unlock(); \
} while (0)

#ifdef CONFIG_SYSFS
diff --git a/kernel/params.c b/kernel/params.c
index a22d6a7..4e28ff1 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -364,12 +364,11 @@ EXPORT_SYMBOL(param_ops_invbool);

int param_set_bint(const char *val, const struct kernel_param *kp)
{
- struct kernel_param boolkp;
+ /* Match bool exactly, by re-using it. */
+ struct kernel_param boolkp = *kp;
bool v;
int ret;

- /* Match bool exactly, by re-using it. */
- boolkp = *kp;
boolkp.arg = &v;

ret = param_set_bool(val, &boolkp);
@@ -449,9 +448,8 @@ static int param_array_get(char *buffer, const struct kernel_param *kp)
{
int i, off, ret;
const struct kparam_array *arr = kp->arr;
- struct kernel_param p;
+ struct kernel_param p = *kp;

- p = *kp;
for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
if (i)
buffer[off++] = ',';
--
2.1.0

2015-06-12 01:44:14

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH] module: make perm const, change BUG_ON to BUILD_BUG_ON

Dan Streetman <[email protected]> writes:
> Change the struct kernel_param.perm field to a const, as it should never
> be changed. Add inline functions that return a const value, which are
> compiled out, for kparam_[un]block_sysfs_r/w() macros to use; and change
> the BUG_ON() in those macros to BUILD_BUG_ON().

Nice win!

I think it's slightly clearer if we just declare:

static __always_unused const u16 __param_perm_##name = (perm).

And indeed, gcc (at least 4.9) eliminates it correctly.

So, here's your patch with that mod, and the param.c noise removed.
It's in my pending queue for the moment.

I still am leaning towards just removing these wrappers and exposing
the (new) per-module mutex though. Happy for that patch to replace this
one, if you send it.

Thanks,
Rusty.

From: Dan Streetman <[email protected]>
Subject: module: make perm const, change BUG_ON to BUILD_BUG_ON

Change the struct kernel_param.perm field to a const, as it should never
be changed. Add a static const name for this (which gcc compiles out)
for kparam_[un]block_sysfs_r/w() macros to use; and change the BUG_ON()
in those macros to BUILD_BUG_ON().

This will enforce that the kernel_param permissions are never changed,
and it will fail at build instead of runtime for any misuse of
sysfs param blocking.

I initially wanted to just use the const param.perm in the block_sysfs
checks, but unfortunately that also requires the param itself to be
const, in order to do const folding into a constant compiletime
condition to BUILD_BUG_ON(), and on some archs the params aren't defined
as const:

/* On alpha, ia64 and ppc64 relocations to global data cannot go into
read-only sections (which is part of respective UNIX ABI on these
platforms). So 'const' makes no sense and even causes compile failures
with some compilers. */

Signed-off-by: Dan Streetman <[email protected]>
Signed-off-by: Rusty Russell <[email protected]> (slight tweak)

diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 7e0079936396..347e11f3bc32 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -68,7 +68,7 @@ enum {
struct kernel_param {
const char *name;
const struct kernel_param_ops *ops;
- u16 perm;
+ const u16 perm;
s8 level;
u8 flags;
union {
@@ -215,8 +215,10 @@ struct kparam_array
/* This is the fundamental function for registering boot/module
parameters. */
#define __module_param_call(prefix, name, ops, arg, perm, level, flags) \
+ /* These allow kparam_block_sysfs_*() to use BUILD_BUG_ON() */ \
+ static __always_unused const u16 __param_perm_##name = (perm); \
/* Default value instead of permissions? */ \
- static const char __param_str_##name[] = prefix #name; \
+ static const char __param_str_##name[] = prefix #name; \
static struct kernel_param __moduleparam_const __param_##name \
__used \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
@@ -244,20 +246,20 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
*
* There's no point blocking write on a paramter that isn't writable via sysfs!
*/
-#define kparam_block_sysfs_write(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0222)); \
- __kernel_param_lock(); \
+#define kparam_block_sysfs_write(name) \
+ do { \
+ BUILD_BUG_ON(!(__param_perm_##name & 0222)); \
+ __kernel_param_lock(); \
} while (0)

/**
* kparam_unblock_sysfs_write - allows sysfs to write to a parameter again.
* @name: the name of the parameter
*/
-#define kparam_unblock_sysfs_write(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0222)); \
- __kernel_param_unlock(); \
+#define kparam_unblock_sysfs_write(name) \
+ do { \
+ BUILD_BUG_ON(!(__param_perm_##name & 0222)); \
+ __kernel_param_unlock(); \
} while (0)

/**
@@ -266,20 +268,20 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
*
* This also blocks sysfs writes.
*/
-#define kparam_block_sysfs_read(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0444)); \
- __kernel_param_lock(); \
+#define kparam_block_sysfs_read(name) \
+ do { \
+ BUILD_BUG_ON(!(__param_perm_##name & 0444)); \
+ __kernel_param_lock(); \
} while (0)

/**
* kparam_unblock_sysfs_read - allows sysfs to read a parameter again.
* @name: the name of the parameter
*/
-#define kparam_unblock_sysfs_read(name) \
- do { \
- BUG_ON(!(__param_##name.perm & 0444)); \
- __kernel_param_unlock(); \
+#define kparam_unblock_sysfs_read(name) \
+ do { \
+ BUILD_BUG_ON(!(__param_perm_##name & 0444)); \
+ __kernel_param_unlock(); \
} while (0)

#ifdef CONFIG_SYSFS
diff --git a/kernel/params.c b/kernel/params.c
index 0b9bbdf830cb..15715a3efb50 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -395,12 +395,11 @@ EXPORT_SYMBOL(param_ops_invbool);

int param_set_bint(const char *val, const struct kernel_param *kp)
{
- struct kernel_param boolkp;
+ /* Match bool exactly, by re-using it. */
+ struct kernel_param boolkp = *kp;
bool v;
int ret;

- /* Match bool exactly, by re-using it. */
- boolkp = *kp;
boolkp.arg = &v;

ret = param_set_bool(val, &boolkp);
@@ -480,9 +479,8 @@ static int param_array_get(char *buffer, const struct kernel_param *kp)
{
int i, off, ret;
const struct kparam_array *arr = kp->arr;
- struct kernel_param p;
+ struct kernel_param p = *kp;

- p = *kp;
for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
if (i)
buffer[off++] = ',';

2015-06-12 03:24:06

by Dan Streetman

[permalink] [raw]
Subject: Re: [PATCH] module: make perm const, change BUG_ON to BUILD_BUG_ON

On Thu, Jun 11, 2015 at 9:43 PM, Rusty Russell <[email protected]> wrote:
> Dan Streetman <[email protected]> writes:
>> Change the struct kernel_param.perm field to a const, as it should never
>> be changed. Add inline functions that return a const value, which are
>> compiled out, for kparam_[un]block_sysfs_r/w() macros to use; and change
>> the BUG_ON() in those macros to BUILD_BUG_ON().
>
> Nice win!
>
> I think it's slightly clearer if we just declare:
>
> static __always_unused const u16 __param_perm_##name = (perm).
>
> And indeed, gcc (at least 4.9) eliminates it correctly.
>
> So, here's your patch with that mod, and the param.c noise removed.
> It's in my pending queue for the moment.
>
> I still am leaning towards just removing these wrappers and exposing
> the (new) per-module mutex though. Happy for that patch to replace this
> one, if you send it.

sure ok, i'll update it and send it tomorrow. Thanks!

>
> Thanks,
> Rusty.
>
> From: Dan Streetman <[email protected]>
> Subject: module: make perm const, change BUG_ON to BUILD_BUG_ON
>
> Change the struct kernel_param.perm field to a const, as it should never
> be changed. Add a static const name for this (which gcc compiles out)
> for kparam_[un]block_sysfs_r/w() macros to use; and change the BUG_ON()
> in those macros to BUILD_BUG_ON().
>
> This will enforce that the kernel_param permissions are never changed,
> and it will fail at build instead of runtime for any misuse of
> sysfs param blocking.
>
> I initially wanted to just use the const param.perm in the block_sysfs
> checks, but unfortunately that also requires the param itself to be
> const, in order to do const folding into a constant compiletime
> condition to BUILD_BUG_ON(), and on some archs the params aren't defined
> as const:
>
> /* On alpha, ia64 and ppc64 relocations to global data cannot go into
> read-only sections (which is part of respective UNIX ABI on these
> platforms). So 'const' makes no sense and even causes compile failures
> with some compilers. */
>
> Signed-off-by: Dan Streetman <[email protected]>
> Signed-off-by: Rusty Russell <[email protected]> (slight tweak)
>
> diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
> index 7e0079936396..347e11f3bc32 100644
> --- a/include/linux/moduleparam.h
> +++ b/include/linux/moduleparam.h
> @@ -68,7 +68,7 @@ enum {
> struct kernel_param {
> const char *name;
> const struct kernel_param_ops *ops;
> - u16 perm;
> + const u16 perm;
> s8 level;
> u8 flags;
> union {
> @@ -215,8 +215,10 @@ struct kparam_array
> /* This is the fundamental function for registering boot/module
> parameters. */
> #define __module_param_call(prefix, name, ops, arg, perm, level, flags) \
> + /* These allow kparam_block_sysfs_*() to use BUILD_BUG_ON() */ \
> + static __always_unused const u16 __param_perm_##name = (perm); \
> /* Default value instead of permissions? */ \
> - static const char __param_str_##name[] = prefix #name; \
> + static const char __param_str_##name[] = prefix #name; \
> static struct kernel_param __moduleparam_const __param_##name \
> __used \
> __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
> @@ -244,20 +246,20 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
> *
> * There's no point blocking write on a paramter that isn't writable via sysfs!
> */
> -#define kparam_block_sysfs_write(name) \
> - do { \
> - BUG_ON(!(__param_##name.perm & 0222)); \
> - __kernel_param_lock(); \
> +#define kparam_block_sysfs_write(name) \
> + do { \
> + BUILD_BUG_ON(!(__param_perm_##name & 0222)); \
> + __kernel_param_lock(); \
> } while (0)
>
> /**
> * kparam_unblock_sysfs_write - allows sysfs to write to a parameter again.
> * @name: the name of the parameter
> */
> -#define kparam_unblock_sysfs_write(name) \
> - do { \
> - BUG_ON(!(__param_##name.perm & 0222)); \
> - __kernel_param_unlock(); \
> +#define kparam_unblock_sysfs_write(name) \
> + do { \
> + BUILD_BUG_ON(!(__param_perm_##name & 0222)); \
> + __kernel_param_unlock(); \
> } while (0)
>
> /**
> @@ -266,20 +268,20 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
> *
> * This also blocks sysfs writes.
> */
> -#define kparam_block_sysfs_read(name) \
> - do { \
> - BUG_ON(!(__param_##name.perm & 0444)); \
> - __kernel_param_lock(); \
> +#define kparam_block_sysfs_read(name) \
> + do { \
> + BUILD_BUG_ON(!(__param_perm_##name & 0444)); \
> + __kernel_param_lock(); \
> } while (0)
>
> /**
> * kparam_unblock_sysfs_read - allows sysfs to read a parameter again.
> * @name: the name of the parameter
> */
> -#define kparam_unblock_sysfs_read(name) \
> - do { \
> - BUG_ON(!(__param_##name.perm & 0444)); \
> - __kernel_param_unlock(); \
> +#define kparam_unblock_sysfs_read(name) \
> + do { \
> + BUILD_BUG_ON(!(__param_perm_##name & 0444)); \
> + __kernel_param_unlock(); \
> } while (0)
>
> #ifdef CONFIG_SYSFS
> diff --git a/kernel/params.c b/kernel/params.c
> index 0b9bbdf830cb..15715a3efb50 100644
> --- a/kernel/params.c
> +++ b/kernel/params.c
> @@ -395,12 +395,11 @@ EXPORT_SYMBOL(param_ops_invbool);
>
> int param_set_bint(const char *val, const struct kernel_param *kp)
> {
> - struct kernel_param boolkp;
> + /* Match bool exactly, by re-using it. */
> + struct kernel_param boolkp = *kp;
> bool v;
> int ret;
>
> - /* Match bool exactly, by re-using it. */
> - boolkp = *kp;
> boolkp.arg = &v;
>
> ret = param_set_bool(val, &boolkp);
> @@ -480,9 +479,8 @@ static int param_array_get(char *buffer, const struct kernel_param *kp)
> {
> int i, off, ret;
> const struct kparam_array *arr = kp->arr;
> - struct kernel_param p;
> + struct kernel_param p = *kp;
>
> - p = *kp;
> for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
> if (i)
> buffer[off++] = ',';