diff options
author | NeilBrown <neilb@suse.com> | 2017-12-18 12:13:20 +1100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-01-08 16:03:44 +0100 |
commit | d487fe31f49e78f3cdd826923bf0c340a839ffd8 (patch) | |
tree | eeb88828bdaffd84f114f6174c89e205edc40adf | |
parent | staging: lustre: obdclass: remove vfsmount option from client_fill_super (diff) | |
download | linux-dev-d487fe31f49e78f3cdd826923bf0c340a839ffd8.tar.xz linux-dev-d487fe31f49e78f3cdd826923bf0c340a839ffd8.zip |
staging: lustre: obd_mount: fix possible race with module unload.
lustre_fill_super() calls client_fill_super() without holding a
reference to the module containing client_fill_super. If that
module is unloaded at a bad time, this can crash.
To be able to get a reference to the module using
try_get_module(), we need a pointer to the module.
So replace
lustre_register_client_fill_super() and
lustre_register_kill_super_cb()
with a single
lustre_register_super_ops()
which also passed a module pointer.
Then use a spinlock to ensure the module pointer isn't removed
while try_module_get() is running, and use try_module_get() to
ensure we have a reference before calling client_fill_super().
Signed-off-by: NeilBrown <neilb@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/staging/lustre/lustre/include/lustre_disk.h | 5 | ||||
-rw-r--r-- | drivers/staging/lustre/lustre/llite/super25.c | 6 | ||||
-rw-r--r-- | drivers/staging/lustre/lustre/obdclass/obd_mount.c | 30 |
3 files changed, 24 insertions, 17 deletions
diff --git a/drivers/staging/lustre/lustre/include/lustre_disk.h b/drivers/staging/lustre/lustre/include/lustre_disk.h index 5089ec02dcad..100e993ab00b 100644 --- a/drivers/staging/lustre/lustre/include/lustre_disk.h +++ b/drivers/staging/lustre/lustre/include/lustre_disk.h @@ -141,8 +141,9 @@ struct lustre_sb_info { /* obd_mount.c */ int lustre_start_mgc(struct super_block *sb); -void lustre_register_client_fill_super(int (*cfs)(struct super_block *sb)); -void lustre_register_kill_super_cb(void (*cfs)(struct super_block *sb)); +void lustre_register_super_ops(struct module *mod, + int (*cfs)(struct super_block *sb), + void (*ksc)(struct super_block *sb)); int lustre_common_put_super(struct super_block *sb); int mgc_fsname2resid(char *fsname, struct ldlm_res_id *res_id, int type); diff --git a/drivers/staging/lustre/lustre/llite/super25.c b/drivers/staging/lustre/lustre/llite/super25.c index 0bda111a096e..10105339790e 100644 --- a/drivers/staging/lustre/lustre/llite/super25.c +++ b/drivers/staging/lustre/lustre/llite/super25.c @@ -159,8 +159,7 @@ static int __init lustre_init(void) if (rc != 0) goto out_inode_fini_env; - lustre_register_client_fill_super(ll_fill_super); - lustre_register_kill_super_cb(ll_kill_super); + lustre_register_super_ops(THIS_MODULE, ll_fill_super, ll_kill_super); lustre_register_client_process_config(ll_process_config); return 0; @@ -181,8 +180,7 @@ out_cache: static void __exit lustre_exit(void) { - lustre_register_client_fill_super(NULL); - lustre_register_kill_super_cb(NULL); + lustre_register_super_ops(NULL, NULL, NULL); lustre_register_client_process_config(NULL); debugfs_remove(llite_root); diff --git a/drivers/staging/lustre/lustre/obdclass/obd_mount.c b/drivers/staging/lustre/lustre/obdclass/obd_mount.c index 8068cbbc8390..acc1ea773c9c 100644 --- a/drivers/staging/lustre/lustre/obdclass/obd_mount.c +++ b/drivers/staging/lustre/lustre/obdclass/obd_mount.c @@ -49,8 +49,9 @@ #include <lustre_disk.h> #include <uapi/linux/lustre/lustre_param.h> +static DEFINE_SPINLOCK(client_lock); +static struct module *client_mod; static int (*client_fill_super)(struct super_block *sb); - static void (*kill_super_cb)(struct super_block *sb); /**************** config llog ********************/ @@ -1143,10 +1144,15 @@ static int lustre_fill_super(struct super_block *sb, void *lmd2_data, int silent } if (lmd_is_client(lmd)) { + bool have_client = false; CDEBUG(D_MOUNT, "Mounting client %s\n", lmd->lmd_profile); if (!client_fill_super) request_module("lustre"); - if (!client_fill_super) { + spin_lock(&client_lock); + if (client_fill_super && try_module_get(client_mod)) + have_client = true; + spin_unlock(&client_lock); + if (!have_client) { LCONSOLE_ERROR_MSG(0x165, "Nothing registered for client mount! Is the 'lustre' module loaded?\n"); lustre_put_lsi(sb); rc = -ENODEV; @@ -1159,7 +1165,9 @@ static int lustre_fill_super(struct super_block *sb, void *lmd2_data, int silent /* Connect and start */ /* (should always be ll_fill_super) */ rc = (*client_fill_super)(sb); - /* c_f_s will call lustre_common_put_super on failure */ + /* c_f_s will call lustre_common_put_super on failure, otherwise + * c_f_s will have taken another reference to the module */ + module_put(client_mod); } } else { CERROR("This is client-side-only module, cannot handle server mount.\n"); @@ -1185,17 +1193,17 @@ out: /* We can't call ll_fill_super by name because it lives in a module that * must be loaded after this one. */ -void lustre_register_client_fill_super(int (*cfs)(struct super_block *sb)) +void lustre_register_super_ops(struct module *mod, + int (*cfs)(struct super_block *sb), + void (*ksc)(struct super_block *sb)) { + spin_lock(&client_lock); + client_mod = mod; client_fill_super = cfs; + kill_super_cb = ksc; + spin_unlock(&client_lock); } -EXPORT_SYMBOL(lustre_register_client_fill_super); - -void lustre_register_kill_super_cb(void (*cfs)(struct super_block *sb)) -{ - kill_super_cb = cfs; -} -EXPORT_SYMBOL(lustre_register_kill_super_cb); +EXPORT_SYMBOL(lustre_register_super_ops); /***************** FS registration ******************/ static struct dentry *lustre_mount(struct file_system_type *fs_type, int flags, |