aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNadia Derbey <Nadia.Derbey@bull.net>2008-04-29 04:00:42 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-29 11:06:12 -0400
commitb6b337ad1c1d6fe11b09b35d75464b84b3e11f07 (patch)
treedf174a23a827cc43db76900d1ddbbef3ac9ddc90
parent0c40ba4fd64f98e7a5cba8ffaedbd68642a85700 (diff)
ipc: recompute msgmni on memory add / remove
Introduce the registration of a callback routine that recomputes msg_ctlmni upon memory add / remove. A single notifier block is registered in the hotplug memory chain for all the ipc namespaces. Since the ipc namespaces are not linked together, they have their own notification chain: one notifier_block is defined per ipc namespace. Each time an ipc namespace is created (removed) it registers (unregisters) its notifier block in (from) the ipcns chain. The callback routine registered in the memory chain invokes the ipcns notifier chain with the IPCNS_LOWMEM event. Each callback routine registered in the ipcns namespace, in turn, recomputes msgmni for the owning namespace. Signed-off-by: Nadia Derbey <Nadia.Derbey@bull.net> Cc: Yasunori Goto <y-goto@jp.fujitsu.com> Cc: Matt Helsley <matthltc@us.ibm.com> Cc: Mingming Cao <cmm@us.ibm.com> Cc: Pierre Peiffer <pierre.peiffer@bull.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/ipc_namespace.h43
-rw-r--r--include/linux/memory.h1
-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
8 files changed, 162 insertions, 4 deletions
diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index 878d7ac286fa..cfb2a08b28f5 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -4,6 +4,17 @@
4#include <linux/err.h> 4#include <linux/err.h>
5#include <linux/idr.h> 5#include <linux/idr.h>
6#include <linux/rwsem.h> 6#include <linux/rwsem.h>
7#ifdef CONFIG_MEMORY_HOTPLUG
8#include <linux/notifier.h>
9#endif /* CONFIG_MEMORY_HOTPLUG */
10
11/*
12 * ipc namespace events
13 */
14#define IPCNS_MEMCHANGED 0x00000001 /* Notify lowmem size changed */
15
16#define IPCNS_CALLBACK_PRI 0
17
7 18
8struct ipc_ids { 19struct ipc_ids {
9 int in_use; 20 int in_use;
@@ -30,6 +41,10 @@ struct ipc_namespace {
30 size_t shm_ctlall; 41 size_t shm_ctlall;
31 int shm_ctlmni; 42 int shm_ctlmni;
32 int shm_tot; 43 int shm_tot;
44
45#ifdef CONFIG_MEMORY_HOTPLUG
46 struct notifier_block ipcns_nb;
47#endif
33}; 48};
34 49
35extern struct ipc_namespace init_ipc_ns; 50extern struct ipc_namespace init_ipc_ns;
@@ -37,9 +52,33 @@ extern atomic_t nr_ipc_ns;
37 52
38#ifdef CONFIG_SYSVIPC 53#ifdef CONFIG_SYSVIPC
39#define INIT_IPC_NS(ns) .ns = &init_ipc_ns, 54#define INIT_IPC_NS(ns) .ns = &init_ipc_ns,
40#else 55
56#ifdef CONFIG_MEMORY_HOTPLUG
57
58extern int register_ipcns_notifier(struct ipc_namespace *);
59extern int unregister_ipcns_notifier(struct ipc_namespace *);
60extern int ipcns_notify(unsigned long);
61
62#else /* CONFIG_MEMORY_HOTPLUG */
63
64static inline int register_ipcns_notifier(struct ipc_namespace *ipcns)
65{
66 return 0;
67}
68static inline int unregister_ipcns_notifier(struct ipc_namespace *ipcns)
69{
70 return 0;
71}
72static inline int ipcns_notify(unsigned long ev)
73{
74 return 0;
75}
76
77#endif /* CONFIG_MEMORY_HOTPLUG */
78
79#else /* CONFIG_SYSVIPC */
41#define INIT_IPC_NS(ns) 80#define INIT_IPC_NS(ns)
42#endif 81#endif /* CONFIG_SYSVIPC */
43 82
44#if defined(CONFIG_SYSVIPC) && defined(CONFIG_IPC_NS) 83#if defined(CONFIG_SYSVIPC) && defined(CONFIG_IPC_NS)
45extern void free_ipc_ns(struct kref *kref); 84extern void free_ipc_ns(struct kref *kref);
diff --git a/include/linux/memory.h b/include/linux/memory.h
index 39628dfe4a4c..2f5f8a5ef2a0 100644
--- a/include/linux/memory.h
+++ b/include/linux/memory.h
@@ -58,6 +58,7 @@ struct mem_section;
58 * order in the callback chain) 58 * order in the callback chain)
59 */ 59 */
60#define SLAB_CALLBACK_PRI 1 60#define SLAB_CALLBACK_PRI 1
61#define IPC_CALLBACK_PRI 10
61 62
62#ifndef CONFIG_MEMORY_HOTPLUG_SPARSE 63#ifndef CONFIG_MEMORY_HOTPLUG_SPARSE
63static inline int memory_dev_init(void) 64static inline int memory_dev_init(void)
diff --git a/ipc/Makefile b/ipc/Makefile
index 5fc5e33ea047..388e4d259f02 100644
--- a/ipc/Makefile
+++ b/ipc/Makefile
@@ -3,7 +3,8 @@
3# 3#
4 4
5obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o 5obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o
6obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o 6obj_mem-$(CONFIG_MEMORY_HOTPLUG) += ipcns_notifier.o
7obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o $(obj_mem-y)
7obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o 8obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o
8obj_mq-$(CONFIG_COMPAT) += compat_mq.o 9obj_mq-$(CONFIG_COMPAT) += compat_mq.o
9obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y) 10obj-$(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 @@
1/*
2 * linux/ipc/ipcns_notifier.c
3 * Copyright (C) 2007 BULL SA. Nadia Derbey
4 *
5 * Notification mechanism for ipc namespaces:
6 * The callback routine registered in the memory chain invokes the ipcns
7 * notifier chain with the IPCNS_MEMCHANGED event.
8 * Each callback routine registered in the ipcns namespace recomputes msgmni
9 * for the owning namespace.
10 */
11
12#include <linux/msg.h>
13#include <linux/rcupdate.h>
14#include <linux/notifier.h>
15#include <linux/nsproxy.h>
16#include <linux/ipc_namespace.h>
17
18#include "util.h"
19
20
21
22static BLOCKING_NOTIFIER_HEAD(ipcns_chain);
23
24
25static int ipcns_callback(struct notifier_block *self,
26 unsigned long action, void *arg)
27{
28 struct ipc_namespace *ns;
29
30 switch (action) {
31 case IPCNS_MEMCHANGED: /* amount of lowmem has changed */
32 /*
33 * It's time to recompute msgmni
34 */
35 ns = container_of(self, struct ipc_namespace, ipcns_nb);
36 /*
37 * No need to get a reference on the ns: the 1st job of
38 * free_ipc_ns() is to unregister the callback routine.
39 * blocking_notifier_chain_unregister takes the wr lock to do
40 * it.
41 * When this callback routine is called the rd lock is held by
42 * blocking_notifier_call_chain.
43 * So the ipc ns cannot be freed while we are here.
44 */
45 recompute_msgmni(ns);
46 break;
47 default:
48 break;
49 }
50
51 return NOTIFY_OK;
52}
53
54int register_ipcns_notifier(struct ipc_namespace *ns)
55{
56 memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb));
57 ns->ipcns_nb.notifier_call = ipcns_callback;
58 ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI;
59 return blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb);
60}
61
62int unregister_ipcns_notifier(struct ipc_namespace *ns)
63{
64 return blocking_notifier_chain_unregister(&ipcns_chain,
65 &ns->ipcns_nb);
66}
67
68int ipcns_notify(unsigned long val)
69{
70 return blocking_notifier_call_chain(&ipcns_chain, val, NULL);
71}
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);
84 * Also take into account the number of nsproxies created so far. 84 * Also take into account the number of nsproxies created so far.
85 * This should be done staying within the (MSGMNI , IPCMNI/nr_ipc_ns) range. 85 * This should be done staying within the (MSGMNI , IPCMNI/nr_ipc_ns) range.
86 */ 86 */
87static void recompute_msgmni(struct ipc_namespace *ns) 87void recompute_msgmni(struct ipc_namespace *ns)
88{ 88{
89 struct sysinfo i; 89 struct sysinfo i;
90 unsigned long allowed; 90 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)
26 msg_init_ns(ns); 26 msg_init_ns(ns);
27 shm_init_ns(ns); 27 shm_init_ns(ns);
28 28
29 register_ipcns_notifier(ns);
30
29 kref_init(&ns->kref); 31 kref_init(&ns->kref);
30 return ns; 32 return ns;
31} 33}
@@ -81,6 +83,15 @@ void free_ipc_ns(struct kref *kref)
81 struct ipc_namespace *ns; 83 struct ipc_namespace *ns;
82 84
83 ns = container_of(kref, struct ipc_namespace, kref); 85 ns = container_of(kref, struct ipc_namespace, kref);
86 /*
87 * Unregistering the hotplug notifier at the beginning guarantees
88 * that the ipc namespace won't be freed while we are inside the
89 * callback routine. Since the blocking_notifier_chain_XXX routines
90 * hold a rw lock on the notifier list, unregister_ipcns_notifier()
91 * won't take the rw lock before blocking_notifier_call_chain() has
92 * released the rd lock.
93 */
94 unregister_ipcns_notifier(ns);
84 sem_exit_ns(ns); 95 sem_exit_ns(ns);
85 msg_exit_ns(ns); 96 msg_exit_ns(ns);
86 shm_exit_ns(ns); 97 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 @@
33#include <linux/audit.h> 33#include <linux/audit.h>
34#include <linux/nsproxy.h> 34#include <linux/nsproxy.h>
35#include <linux/rwsem.h> 35#include <linux/rwsem.h>
36#include <linux/memory.h>
36#include <linux/ipc_namespace.h> 37#include <linux/ipc_namespace.h>
37 38
38#include <asm/unistd.h> 39#include <asm/unistd.h>
@@ -55,11 +56,41 @@ struct ipc_namespace init_ipc_ns = {
55atomic_t nr_ipc_ns = ATOMIC_INIT(1); 56atomic_t nr_ipc_ns = ATOMIC_INIT(1);
56 57
57 58
59#ifdef CONFIG_MEMORY_HOTPLUG
60
61static int ipc_memory_callback(struct notifier_block *self,
62 unsigned long action, void *arg)
63{
64 switch (action) {
65 case MEM_ONLINE: /* memory successfully brought online */
66 case MEM_OFFLINE: /* or offline: it's time to recompute msgmni */
67 /*
68 * This is done by invoking the ipcns notifier chain with the
69 * IPC_MEMCHANGED event.
70 */
71 ipcns_notify(IPCNS_MEMCHANGED);
72 break;
73 case MEM_GOING_ONLINE:
74 case MEM_GOING_OFFLINE:
75 case MEM_CANCEL_ONLINE:
76 case MEM_CANCEL_OFFLINE:
77 default:
78 break;
79 }
80
81 return NOTIFY_OK;
82}
83
84#endif /* CONFIG_MEMORY_HOTPLUG */
85
58/** 86/**
59 * ipc_init - initialise IPC subsystem 87 * ipc_init - initialise IPC subsystem
60 * 88 *
61 * The various system5 IPC resources (semaphores, messages and shared 89 * The various system5 IPC resources (semaphores, messages and shared
62 * memory) are initialised 90 * memory) are initialised
91 * A callback routine is registered into the memory hotplug notifier
92 * chain: since msgmni scales to lowmem this callback routine will be
93 * called upon successful memory add / remove to recompute msmgni.
63 */ 94 */
64 95
65static int __init ipc_init(void) 96static int __init ipc_init(void)
@@ -67,6 +98,8 @@ static int __init ipc_init(void)
67 sem_init(); 98 sem_init();
68 msg_init(); 99 msg_init();
69 shm_init(); 100 shm_init();
101 hotplug_memory_notifier(ipc_memory_callback, IPC_CALLBACK_PRI);
102 register_ipcns_notifier(&init_ipc_ns);
70 return 0; 103 return 0;
71} 104}
72__initcall(ipc_init); 105__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);
124extern struct msg_msg *load_msg(const void __user *src, int len); 124extern struct msg_msg *load_msg(const void __user *src, int len);
125extern int store_msg(void __user *dest, struct msg_msg *msg, int len); 125extern int store_msg(void __user *dest, struct msg_msg *msg, int len);
126 126
127extern void recompute_msgmni(struct ipc_namespace *);
128
127static inline int ipc_buildid(int id, int seq) 129static inline int ipc_buildid(int id, int seq)
128{ 130{
129 return SEQ_MULTIPLIER * seq + id; 131 return SEQ_MULTIPLIER * seq + id;