diff options
-rw-r--r-- | include/linux/ipc_namespace.h | 43 | ||||
-rw-r--r-- | include/linux/memory.h | 1 | ||||
-rw-r--r-- | ipc/Makefile | 3 | ||||
-rw-r--r-- | ipc/ipcns_notifier.c | 71 | ||||
-rw-r--r-- | ipc/msg.c | 2 | ||||
-rw-r--r-- | ipc/namespace.c | 11 | ||||
-rw-r--r-- | ipc/util.c | 33 | ||||
-rw-r--r-- | ipc/util.h | 2 |
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 | ||
8 | struct ipc_ids { | 19 | struct 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 | ||
35 | extern struct ipc_namespace init_ipc_ns; | 50 | extern 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 | |||
58 | extern int register_ipcns_notifier(struct ipc_namespace *); | ||
59 | extern int unregister_ipcns_notifier(struct ipc_namespace *); | ||
60 | extern int ipcns_notify(unsigned long); | ||
61 | |||
62 | #else /* CONFIG_MEMORY_HOTPLUG */ | ||
63 | |||
64 | static inline int register_ipcns_notifier(struct ipc_namespace *ipcns) | ||
65 | { | ||
66 | return 0; | ||
67 | } | ||
68 | static inline int unregister_ipcns_notifier(struct ipc_namespace *ipcns) | ||
69 | { | ||
70 | return 0; | ||
71 | } | ||
72 | static 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) |
45 | extern void free_ipc_ns(struct kref *kref); | 84 | extern 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 |
63 | static inline int memory_dev_init(void) | 64 | static 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 | ||
5 | obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o | 5 | obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o |
6 | obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o | 6 | obj_mem-$(CONFIG_MEMORY_HOTPLUG) += ipcns_notifier.o |
7 | obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o $(obj_mem-y) | ||
7 | obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o | 8 | obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o |
8 | obj_mq-$(CONFIG_COMPAT) += compat_mq.o | 9 | obj_mq-$(CONFIG_COMPAT) += compat_mq.o |
9 | obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y) | 10 | 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 @@ | |||
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 | |||
22 | static BLOCKING_NOTIFIER_HEAD(ipcns_chain); | ||
23 | |||
24 | |||
25 | static 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 | |||
54 | int 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 | |||
62 | int unregister_ipcns_notifier(struct ipc_namespace *ns) | ||
63 | { | ||
64 | return blocking_notifier_chain_unregister(&ipcns_chain, | ||
65 | &ns->ipcns_nb); | ||
66 | } | ||
67 | |||
68 | int ipcns_notify(unsigned long val) | ||
69 | { | ||
70 | return blocking_notifier_call_chain(&ipcns_chain, val, NULL); | ||
71 | } | ||
@@ -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 | */ |
87 | static void recompute_msgmni(struct ipc_namespace *ns) | 87 | void 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 = { | |||
55 | atomic_t nr_ipc_ns = ATOMIC_INIT(1); | 56 | atomic_t nr_ipc_ns = ATOMIC_INIT(1); |
56 | 57 | ||
57 | 58 | ||
59 | #ifdef CONFIG_MEMORY_HOTPLUG | ||
60 | |||
61 | static 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 | ||
65 | static int __init ipc_init(void) | 96 | static 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); | |||
124 | extern struct msg_msg *load_msg(const void __user *src, int len); | 124 | extern struct msg_msg *load_msg(const void __user *src, int len); |
125 | extern int store_msg(void __user *dest, struct msg_msg *msg, int len); | 125 | extern int store_msg(void __user *dest, struct msg_msg *msg, int len); |
126 | 126 | ||
127 | extern void recompute_msgmni(struct ipc_namespace *); | ||
128 | |||
127 | static inline int ipc_buildid(int id, int seq) | 129 | static inline int ipc_buildid(int id, int seq) |
128 | { | 130 | { |
129 | return SEQ_MULTIPLIER * seq + id; | 131 | return SEQ_MULTIPLIER * seq + id; |