lkml.org 
[lkml]   [2009]   [Mar]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[RFC PATCH 2/3] sysfs: add blocking notifier to prohibit module unload
Date
As its name suggests, module_inhibit_unload() inhibits all module
unloading till the matching module_allow_unload() is called. This
unload inhibition doesn't affect whether a module can be unloaded or
not. It just stalls the final module free till the inhibition is
lifted.

This sledgehammer mechanism is to be used briefly in obscure cases
where identifying or getting the module to prevent from unloading is
difficult or not worth the effort. Note that module unloading is
siberia-cold path. If the inhibtion is relatively brief in human
scale, that is, upto a few secs at maximum, it should be fine.

Even if something goes wrong with unload inhibition (e.g. someone
forgets to lift the inhibition), it doesn't prevent modules from being
loaded.

Originally written by Tejun Heo. Refreshed and implemented as a
blocking notifier that registers with the module core.

Cc: Tejun Heo <htejun@gmail.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Alex Chiang <achiang@hp.com>
---

fs/sysfs/file.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/sysfs/mount.c | 8 ++++++
fs/sysfs/sysfs.h | 2 ++
3 files changed, 79 insertions(+), 0 deletions(-)

diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 7cc4dc0..bf93b58 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -21,6 +21,7 @@
#include <linux/mutex.h>
#include <linux/limits.h>
#include <asm/uaccess.h>
+#include <asm/atomic.h>

#include "sysfs.h"

@@ -60,6 +61,74 @@ struct sysfs_buffer {
struct list_head list;
};

+static atomic_t module_unload_inhibit_cnt = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(module_unload_wait);
+
+/**
+ * sysfs_module_callback - block until module unload allowed again
+ *
+ * (ab)use the blocking notifier call chain in delete_module()
+ * and prevent module unload for our own purposes, namely a
+ * suicidal sysfs attribute has finished killing itself.
+ *
+ * This prevents a module from freeing its code section before
+ * we are done accessing it.
+ */
+int sysfs_module_callback(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (val == MODULE_STATE_COMING)
+ return NOTIFY_DONE;
+
+ add_wait_queue(&module_unload_wait, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (atomic_read(&module_unload_inhibit_cnt))
+ schedule();
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&module_unload_wait, &wait);
+
+ return NOTIFY_DONE;
+}
+
+/**
+ * module_inhibit_unload - inhibit module unload
+ *
+ * Inhibit module unload until allowed again. All module unload
+ * operations which reach zero reference count after this call
+ * has returned are guaranteed to be stalled till inhibition is
+ * lifted.
+ *
+ * This is a simple mechanism to prevent premature unload while
+ * code on a to-be-unloaded module is still executing. Unload
+ * inhibitions must be finite and relatively short.
+ *
+ * LOCKING:
+ * None.
+ */
+static void module_inhibit_unload(void)
+{
+ atomic_inc(&module_unload_inhibit_cnt);
+}
+
+/**
+ * module_allow_unload - allow module unload
+ *
+ * Allow module unload. Must be balanced with calls to
+ * module_inhibit_unload().
+ *
+ * LOCKING:
+ * None.
+ */
+static void module_allow_unload(void)
+{
+ if (atomic_dec_and_test(&module_unload_inhibit_cnt))
+ wake_up_all(&module_unload_wait);
+
+ BUG_ON(atomic_read(&module_unload_inhibit_cnt) < 0);
+}
+
/**
* fill_read_buffer - allocate and fill buffer from object.
* @dentry: dentry pointer.
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index ab343e3..c805bec 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -83,6 +83,11 @@ static struct file_system_type sysfs_fs_type = {
.kill_sb = kill_anon_super,
};

+static struct notifier_block sysfs_module_nb = {
+ .notifier_call = sysfs_module_callback,
+ .priority = 0
+};
+
int __init sysfs_init(void)
{
int err = -ENOMEM;
@@ -109,6 +114,9 @@ int __init sysfs_init(void)
}
} else
goto out_err;
+
+ register_module_notifier(&sysfs_module_nb);
+
out:
return err;
out_err:
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index cb8ac65..5d9b340 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -153,6 +153,8 @@ int sysfs_inode_init(void);
* file.c
*/
extern const struct file_operations sysfs_file_operations;
+extern int sysfs_module_callback(struct notifier_block *self, unsigned long val,
+ void *data);

int sysfs_add_file(struct sysfs_dirent *dir_sd,
const struct attribute *attr, int type);


\
 
 \ /
  Last update: 2009-03-25 05:21    [W:0.125 / U:0.532 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site