diff options
author | Nadia Derbey <Nadia.Derbey@bull.net> | 2008-04-29 04:00:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-29 11:06:13 -0400 |
commit | 6546bc4279241e8fa432de1bb63a4f6f791fd669 (patch) | |
tree | 308f5c1621b1019a44800622ffc468a372f09434 | |
parent | 91cfb2b4b57816de0c96de417b3238249f0b125f (diff) |
ipc: re-enable msgmni automatic recomputing msgmni if set to negative
The enhancement as asked for by Yasunori: if msgmni is set to a negative
value, register it back into the ipcns notifier chain.
A new interface has been added to the notification mechanism:
notifier_chain_cond_register() registers a notifier block only if not already
registered. With that new interface we avoid taking care of the states
changes in procfs.
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.h | 1 | ||||
-rw-r--r-- | include/linux/notifier.h | 4 | ||||
-rw-r--r-- | ipc/ipc_sysctl.c | 45 | ||||
-rw-r--r-- | ipc/ipcns_notifier.c | 9 | ||||
-rw-r--r-- | kernel/notifier.c | 38 |
5 files changed, 87 insertions, 10 deletions
diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h index c3b1da9e5feb..ea6c18a8b0d4 100644 --- a/include/linux/ipc_namespace.h +++ b/include/linux/ipc_namespace.h | |||
@@ -52,6 +52,7 @@ extern atomic_t nr_ipc_ns; | |||
52 | #define INIT_IPC_NS(ns) .ns = &init_ipc_ns, | 52 | #define INIT_IPC_NS(ns) .ns = &init_ipc_ns, |
53 | 53 | ||
54 | extern int register_ipcns_notifier(struct ipc_namespace *); | 54 | extern int register_ipcns_notifier(struct ipc_namespace *); |
55 | extern int cond_register_ipcns_notifier(struct ipc_namespace *); | ||
55 | extern int unregister_ipcns_notifier(struct ipc_namespace *); | 56 | extern int unregister_ipcns_notifier(struct ipc_namespace *); |
56 | extern int ipcns_notify(unsigned long); | 57 | extern int ipcns_notify(unsigned long); |
57 | 58 | ||
diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 20dfed590183..0ff6224d172a 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h | |||
@@ -121,6 +121,10 @@ extern int raw_notifier_chain_register(struct raw_notifier_head *nh, | |||
121 | extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, | 121 | extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, |
122 | struct notifier_block *nb); | 122 | struct notifier_block *nb); |
123 | 123 | ||
124 | extern int blocking_notifier_chain_cond_register( | ||
125 | struct blocking_notifier_head *nh, | ||
126 | struct notifier_block *nb); | ||
127 | |||
124 | extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, | 128 | extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, |
125 | struct notifier_block *nb); | 129 | struct notifier_block *nb); |
126 | extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, | 130 | extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, |
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c index d12ff5cd2a0b..d3497465cc0a 100644 --- a/ipc/ipc_sysctl.c +++ b/ipc/ipc_sysctl.c | |||
@@ -15,6 +15,8 @@ | |||
15 | #include <linux/sysctl.h> | 15 | #include <linux/sysctl.h> |
16 | #include <linux/uaccess.h> | 16 | #include <linux/uaccess.h> |
17 | #include <linux/ipc_namespace.h> | 17 | #include <linux/ipc_namespace.h> |
18 | #include <linux/msg.h> | ||
19 | #include "util.h" | ||
18 | 20 | ||
19 | static void *get_ipc(ctl_table *table) | 21 | static void *get_ipc(ctl_table *table) |
20 | { | 22 | { |
@@ -24,6 +26,27 @@ static void *get_ipc(ctl_table *table) | |||
24 | return which; | 26 | return which; |
25 | } | 27 | } |
26 | 28 | ||
29 | /* | ||
30 | * Routine that is called when a tunable has successfully been changed by | ||
31 | * hand and it has a callback routine registered on the ipc namespace notifier | ||
32 | * chain: we don't want such tunables to be recomputed anymore upon memory | ||
33 | * add/remove or ipc namespace creation/removal. | ||
34 | * They can come back to a recomputable state by being set to a <0 value. | ||
35 | */ | ||
36 | static void tunable_set_callback(int val) | ||
37 | { | ||
38 | if (val >= 0) | ||
39 | unregister_ipcns_notifier(current->nsproxy->ipc_ns); | ||
40 | else { | ||
41 | /* | ||
42 | * Re-enable automatic recomputing only if not already | ||
43 | * enabled. | ||
44 | */ | ||
45 | recompute_msgmni(current->nsproxy->ipc_ns); | ||
46 | cond_register_ipcns_notifier(current->nsproxy->ipc_ns); | ||
47 | } | ||
48 | } | ||
49 | |||
27 | #ifdef CONFIG_PROC_FS | 50 | #ifdef CONFIG_PROC_FS |
28 | static int proc_ipc_dointvec(ctl_table *table, int write, struct file *filp, | 51 | static int proc_ipc_dointvec(ctl_table *table, int write, struct file *filp, |
29 | void __user *buffer, size_t *lenp, loff_t *ppos) | 52 | void __user *buffer, size_t *lenp, loff_t *ppos) |
@@ -38,17 +61,17 @@ static int proc_ipc_dointvec(ctl_table *table, int write, struct file *filp, | |||
38 | static int proc_ipc_callback_dointvec(ctl_table *table, int write, | 61 | static int proc_ipc_callback_dointvec(ctl_table *table, int write, |
39 | struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) | 62 | struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) |
40 | { | 63 | { |
64 | struct ctl_table ipc_table; | ||
41 | size_t lenp_bef = *lenp; | 65 | size_t lenp_bef = *lenp; |
42 | int rc; | 66 | int rc; |
43 | 67 | ||
44 | rc = proc_ipc_dointvec(table, write, filp, buffer, lenp, ppos); | 68 | memcpy(&ipc_table, table, sizeof(ipc_table)); |
69 | ipc_table.data = get_ipc(table); | ||
70 | |||
71 | rc = proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos); | ||
45 | 72 | ||
46 | if (write && !rc && lenp_bef == *lenp) | 73 | if (write && !rc && lenp_bef == *lenp) |
47 | /* | 74 | tunable_set_callback(*((int *)(ipc_table.data))); |
48 | * Tunable has successfully been changed from userland: | ||
49 | * disable its automatic recomputing. | ||
50 | */ | ||
51 | unregister_ipcns_notifier(current->nsproxy->ipc_ns); | ||
52 | 75 | ||
53 | return rc; | 76 | return rc; |
54 | } | 77 | } |
@@ -119,12 +142,14 @@ static int sysctl_ipc_registered_data(ctl_table *table, int __user *name, | |||
119 | rc = sysctl_ipc_data(table, name, nlen, oldval, oldlenp, newval, | 142 | rc = sysctl_ipc_data(table, name, nlen, oldval, oldlenp, newval, |
120 | newlen); | 143 | newlen); |
121 | 144 | ||
122 | if (newval && newlen && rc > 0) | 145 | if (newval && newlen && rc > 0) { |
123 | /* | 146 | /* |
124 | * Tunable has successfully been changed from userland: | 147 | * Tunable has successfully been changed from userland |
125 | * disable its automatic recomputing. | ||
126 | */ | 148 | */ |
127 | unregister_ipcns_notifier(current->nsproxy->ipc_ns); | 149 | int *data = get_ipc(table); |
150 | |||
151 | tunable_set_callback(*data); | ||
152 | } | ||
128 | 153 | ||
129 | return rc; | 154 | return rc; |
130 | } | 155 | } |
diff --git a/ipc/ipcns_notifier.c b/ipc/ipcns_notifier.c index c7974609defc..70ff09183f7b 100644 --- a/ipc/ipcns_notifier.c +++ b/ipc/ipcns_notifier.c | |||
@@ -61,6 +61,15 @@ int register_ipcns_notifier(struct ipc_namespace *ns) | |||
61 | return blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); | 61 | return blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); |
62 | } | 62 | } |
63 | 63 | ||
64 | int cond_register_ipcns_notifier(struct ipc_namespace *ns) | ||
65 | { | ||
66 | memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); | ||
67 | ns->ipcns_nb.notifier_call = ipcns_callback; | ||
68 | ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; | ||
69 | return blocking_notifier_chain_cond_register(&ipcns_chain, | ||
70 | &ns->ipcns_nb); | ||
71 | } | ||
72 | |||
64 | int unregister_ipcns_notifier(struct ipc_namespace *ns) | 73 | int unregister_ipcns_notifier(struct ipc_namespace *ns) |
65 | { | 74 | { |
66 | return blocking_notifier_chain_unregister(&ipcns_chain, | 75 | return blocking_notifier_chain_unregister(&ipcns_chain, |
diff --git a/kernel/notifier.c b/kernel/notifier.c index 643360d1bb14..823be11584ef 100644 --- a/kernel/notifier.c +++ b/kernel/notifier.c | |||
@@ -31,6 +31,21 @@ static int notifier_chain_register(struct notifier_block **nl, | |||
31 | return 0; | 31 | return 0; |
32 | } | 32 | } |
33 | 33 | ||
34 | static int notifier_chain_cond_register(struct notifier_block **nl, | ||
35 | struct notifier_block *n) | ||
36 | { | ||
37 | while ((*nl) != NULL) { | ||
38 | if ((*nl) == n) | ||
39 | return 0; | ||
40 | if (n->priority > (*nl)->priority) | ||
41 | break; | ||
42 | nl = &((*nl)->next); | ||
43 | } | ||
44 | n->next = *nl; | ||
45 | rcu_assign_pointer(*nl, n); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
34 | static int notifier_chain_unregister(struct notifier_block **nl, | 49 | static int notifier_chain_unregister(struct notifier_block **nl, |
35 | struct notifier_block *n) | 50 | struct notifier_block *n) |
36 | { | 51 | { |
@@ -205,6 +220,29 @@ int blocking_notifier_chain_register(struct blocking_notifier_head *nh, | |||
205 | EXPORT_SYMBOL_GPL(blocking_notifier_chain_register); | 220 | EXPORT_SYMBOL_GPL(blocking_notifier_chain_register); |
206 | 221 | ||
207 | /** | 222 | /** |
223 | * blocking_notifier_chain_cond_register - Cond add notifier to a blocking notifier chain | ||
224 | * @nh: Pointer to head of the blocking notifier chain | ||
225 | * @n: New entry in notifier chain | ||
226 | * | ||
227 | * Adds a notifier to a blocking notifier chain, only if not already | ||
228 | * present in the chain. | ||
229 | * Must be called in process context. | ||
230 | * | ||
231 | * Currently always returns zero. | ||
232 | */ | ||
233 | int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, | ||
234 | struct notifier_block *n) | ||
235 | { | ||
236 | int ret; | ||
237 | |||
238 | down_write(&nh->rwsem); | ||
239 | ret = notifier_chain_cond_register(&nh->head, n); | ||
240 | up_write(&nh->rwsem); | ||
241 | return ret; | ||
242 | } | ||
243 | EXPORT_SYMBOL_GPL(blocking_notifier_chain_cond_register); | ||
244 | |||
245 | /** | ||
208 | * blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain | 246 | * blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain |
209 | * @nh: Pointer to head of the blocking notifier chain | 247 | * @nh: Pointer to head of the blocking notifier chain |
210 | * @n: Entry to remove from notifier chain | 248 | * @n: Entry to remove from notifier chain |