summaryrefslogtreecommitdiff
path: root/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc')
-rw-r--r--ipc/Makefile3
-rw-r--r--ipc/ipcns_notifier.c71
-rw-r--r--ipc/msg.c2
-rw-r--r--ipc/namespace.c11
-rw-r--r--ipc/util.c33
-rw-r--r--ipc/util.h2
6 files changed, 120 insertions, 2 deletions
diff --git a/ipc/Makefile b/ipc/Makefile
index 5fc5e33ea047..388e4d259f02 100644
--- a/ipc/Makefile
+++ b/ipc/Makefile
@@ -3,7 +3,8 @@
#
obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o
-obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o
+obj_mem-$(CONFIG_MEMORY_HOTPLUG) += ipcns_notifier.o
+obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o $(obj_mem-y)
obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o
obj_mq-$(CONFIG_COMPAT) += compat_mq.o
obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y)
diff --git a/ipc/ipcns_notifier.c b/ipc/ipcns_notifier.c
new file mode 100644
index 000000000000..0786af6ce3ec
--- /dev/null
+++ b/ipc/ipcns_notifier.c
@@ -0,0 +1,71 @@
+/*
+ * linux/ipc/ipcns_notifier.c
+ * Copyright (C) 2007 BULL SA. Nadia Derbey
+ *
+ * Notification mechanism for ipc namespaces:
+ * The callback routine registered in the memory chain invokes the ipcns
+ * notifier chain with the IPCNS_MEMCHANGED event.
+ * Each callback routine registered in the ipcns namespace recomputes msgmni
+ * for the owning namespace.
+ */
+
+#include <linux/msg.h>
+#include <linux/rcupdate.h>
+#include <linux/notifier.h>
+#include <linux/nsproxy.h>
+#include <linux/ipc_namespace.h>
+
+#include "util.h"
+
+
+
+static BLOCKING_NOTIFIER_HEAD(ipcns_chain);
+
+
+static int ipcns_callback(struct notifier_block *self,
+ unsigned long action, void *arg)
+{
+ struct ipc_namespace *ns;
+
+ switch (action) {
+ case IPCNS_MEMCHANGED: /* amount of lowmem has changed */
+ /*
+ * It's time to recompute msgmni
+ */
+ ns = container_of(self, struct ipc_namespace, ipcns_nb);
+ /*
+ * No need to get a reference on the ns: the 1st job of
+ * free_ipc_ns() is to unregister the callback routine.
+ * blocking_notifier_chain_unregister takes the wr lock to do
+ * it.
+ * When this callback routine is called the rd lock is held by
+ * blocking_notifier_call_chain.
+ * So the ipc ns cannot be freed while we are here.
+ */
+ recompute_msgmni(ns);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+int register_ipcns_notifier(struct ipc_namespace *ns)
+{
+ memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb));
+ ns->ipcns_nb.notifier_call = ipcns_callback;
+ ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI;
+ return blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb);
+}
+
+int unregister_ipcns_notifier(struct ipc_namespace *ns)
+{
+ return blocking_notifier_chain_unregister(&ipcns_chain,
+ &ns->ipcns_nb);
+}
+
+int ipcns_notify(unsigned long val)
+{
+ return blocking_notifier_call_chain(&ipcns_chain, val, NULL);
+}
diff --git a/ipc/msg.c b/ipc/msg.c
index be8449d48a8e..7d9b0694c743 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -84,7 +84,7 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it);
* Also take into account the number of nsproxies created so far.
* This should be done staying within the (MSGMNI , IPCMNI/nr_ipc_ns) range.
*/
-static void recompute_msgmni(struct ipc_namespace *ns)
+void recompute_msgmni(struct ipc_namespace *ns)
{
struct sysinfo i;
unsigned long allowed;
diff --git a/ipc/namespace.c b/ipc/namespace.c
index fe3c97aa99dc..f7a35be2e771 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -26,6 +26,8 @@ static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
msg_init_ns(ns);
shm_init_ns(ns);
+ register_ipcns_notifier(ns);
+
kref_init(&ns->kref);
return ns;
}
@@ -81,6 +83,15 @@ void free_ipc_ns(struct kref *kref)
struct ipc_namespace *ns;
ns = container_of(kref, struct ipc_namespace, kref);
+ /*
+ * Unregistering the hotplug notifier at the beginning guarantees
+ * that the ipc namespace won't be freed while we are inside the
+ * callback routine. Since the blocking_notifier_chain_XXX routines
+ * hold a rw lock on the notifier list, unregister_ipcns_notifier()
+ * won't take the rw lock before blocking_notifier_call_chain() has
+ * released the rd lock.
+ */
+ unregister_ipcns_notifier(ns);
sem_exit_ns(ns);
msg_exit_ns(ns);
shm_exit_ns(ns);
diff --git a/ipc/util.c b/ipc/util.c
index c27f0e92f489..2d545d7144a7 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -33,6 +33,7 @@
#include <linux/audit.h>
#include <linux/nsproxy.h>
#include <linux/rwsem.h>
+#include <linux/memory.h>
#include <linux/ipc_namespace.h>
#include <asm/unistd.h>
@@ -55,11 +56,41 @@ struct ipc_namespace init_ipc_ns = {
atomic_t nr_ipc_ns = ATOMIC_INIT(1);
+#ifdef CONFIG_MEMORY_HOTPLUG
+
+static int ipc_memory_callback(struct notifier_block *self,
+ unsigned long action, void *arg)
+{
+ switch (action) {
+ case MEM_ONLINE: /* memory successfully brought online */
+ case MEM_OFFLINE: /* or offline: it's time to recompute msgmni */
+ /*
+ * This is done by invoking the ipcns notifier chain with the
+ * IPC_MEMCHANGED event.
+ */
+ ipcns_notify(IPCNS_MEMCHANGED);
+ break;
+ case MEM_GOING_ONLINE:
+ case MEM_GOING_OFFLINE:
+ case MEM_CANCEL_ONLINE:
+ case MEM_CANCEL_OFFLINE:
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+#endif /* CONFIG_MEMORY_HOTPLUG */
+
/**
* ipc_init - initialise IPC subsystem
*
* The various system5 IPC resources (semaphores, messages and shared
* memory) are initialised
+ * A callback routine is registered into the memory hotplug notifier
+ * chain: since msgmni scales to lowmem this callback routine will be
+ * called upon successful memory add / remove to recompute msmgni.
*/
static int __init ipc_init(void)
@@ -67,6 +98,8 @@ static int __init ipc_init(void)
sem_init();
msg_init();
shm_init();
+ hotplug_memory_notifier(ipc_memory_callback, IPC_CALLBACK_PRI);
+ register_ipcns_notifier(&init_ipc_ns);
return 0;
}
__initcall(ipc_init);
diff --git a/ipc/util.h b/ipc/util.h
index f37d160c98fe..0e3d79037a2a 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -124,6 +124,8 @@ extern void free_msg(struct msg_msg *msg);
extern struct msg_msg *load_msg(const void __user *src, int len);
extern int store_msg(void __user *dest, struct msg_msg *msg, int len);
+extern void recompute_msgmni(struct ipc_namespace *);
+
static inline int ipc_buildid(int id, int seq)
{
return SEQ_MULTIPLIER * seq + id;