Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Nadia Derbey and committed by
Linus Torvalds
b6b337ad 0c40ba4f

+162 -4
+41 -2
include/linux/ipc_namespace.h
··· 4 4 #include <linux/err.h> 5 5 #include <linux/idr.h> 6 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 19 struct ipc_ids { 9 20 int in_use; ··· 41 30 size_t shm_ctlall; 42 31 int shm_ctlmni; 43 32 int shm_tot; 33 + 34 + #ifdef CONFIG_MEMORY_HOTPLUG 35 + struct notifier_block ipcns_nb; 36 + #endif 44 37 }; 45 38 46 39 extern struct ipc_namespace init_ipc_ns; ··· 52 37 53 38 #ifdef CONFIG_SYSVIPC 54 39 #define INIT_IPC_NS(ns) .ns = &init_ipc_ns, 55 - #else 40 + 41 + #ifdef CONFIG_MEMORY_HOTPLUG 42 + 43 + extern int register_ipcns_notifier(struct ipc_namespace *); 44 + extern int unregister_ipcns_notifier(struct ipc_namespace *); 45 + extern int ipcns_notify(unsigned long); 46 + 47 + #else /* CONFIG_MEMORY_HOTPLUG */ 48 + 49 + static inline int register_ipcns_notifier(struct ipc_namespace *ipcns) 50 + { 51 + return 0; 52 + } 53 + static inline int unregister_ipcns_notifier(struct ipc_namespace *ipcns) 54 + { 55 + return 0; 56 + } 57 + static inline int ipcns_notify(unsigned long ev) 58 + { 59 + return 0; 60 + } 61 + 62 + #endif /* CONFIG_MEMORY_HOTPLUG */ 63 + 64 + #else /* CONFIG_SYSVIPC */ 56 65 #define INIT_IPC_NS(ns) 57 - #endif 66 + #endif /* CONFIG_SYSVIPC */ 58 67 59 68 #if defined(CONFIG_SYSVIPC) && defined(CONFIG_IPC_NS) 60 69 extern void free_ipc_ns(struct kref *kref);
+1
include/linux/memory.h
··· 58 58 * order in the callback chain) 59 59 */ 60 60 #define SLAB_CALLBACK_PRI 1 61 + #define IPC_CALLBACK_PRI 10 61 62 62 63 #ifndef CONFIG_MEMORY_HOTPLUG_SPARSE 63 64 static inline int memory_dev_init(void)
+2 -1
ipc/Makefile
··· 3 3 # 4 4 5 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 8 obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o 8 9 obj_mq-$(CONFIG_COMPAT) += compat_mq.o 9 10 obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y)
+71
ipc/ipcns_notifier.c
··· 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 + }
+1 -1
ipc/msg.c
··· 84 84 * Also take into account the number of nsproxies created so far. 85 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 89 struct sysinfo i; 90 90 unsigned long allowed;
+11
ipc/namespace.c
··· 26 26 msg_init_ns(ns); 27 27 shm_init_ns(ns); 28 28 29 + register_ipcns_notifier(ns); 30 + 29 31 kref_init(&ns->kref); 30 32 return ns; 31 33 } ··· 83 81 struct ipc_namespace *ns; 84 82 85 83 ns = container_of(kref, struct ipc_namespace, kref); 84 + /* 85 + * Unregistering the hotplug notifier at the beginning guarantees 86 + * that the ipc namespace won't be freed while we are inside the 87 + * callback routine. Since the blocking_notifier_chain_XXX routines 88 + * hold a rw lock on the notifier list, unregister_ipcns_notifier() 89 + * won't take the rw lock before blocking_notifier_call_chain() has 90 + * released the rd lock. 91 + */ 92 + unregister_ipcns_notifier(ns); 86 93 sem_exit_ns(ns); 87 94 msg_exit_ns(ns); 88 95 shm_exit_ns(ns);
+33
ipc/util.c
··· 33 33 #include <linux/audit.h> 34 34 #include <linux/nsproxy.h> 35 35 #include <linux/rwsem.h> 36 + #include <linux/memory.h> 36 37 #include <linux/ipc_namespace.h> 37 38 38 39 #include <asm/unistd.h> ··· 56 55 atomic_t nr_ipc_ns = ATOMIC_INIT(1); 57 56 58 57 58 + #ifdef CONFIG_MEMORY_HOTPLUG 59 + 60 + static int ipc_memory_callback(struct notifier_block *self, 61 + unsigned long action, void *arg) 62 + { 63 + switch (action) { 64 + case MEM_ONLINE: /* memory successfully brought online */ 65 + case MEM_OFFLINE: /* or offline: it's time to recompute msgmni */ 66 + /* 67 + * This is done by invoking the ipcns notifier chain with the 68 + * IPC_MEMCHANGED event. 69 + */ 70 + ipcns_notify(IPCNS_MEMCHANGED); 71 + break; 72 + case MEM_GOING_ONLINE: 73 + case MEM_GOING_OFFLINE: 74 + case MEM_CANCEL_ONLINE: 75 + case MEM_CANCEL_OFFLINE: 76 + default: 77 + break; 78 + } 79 + 80 + return NOTIFY_OK; 81 + } 82 + 83 + #endif /* CONFIG_MEMORY_HOTPLUG */ 84 + 59 85 /** 60 86 * ipc_init - initialise IPC subsystem 61 87 * 62 88 * The various system5 IPC resources (semaphores, messages and shared 63 89 * memory) are initialised 90 + * A callback routine is registered into the memory hotplug notifier 91 + * chain: since msgmni scales to lowmem this callback routine will be 92 + * called upon successful memory add / remove to recompute msmgni. 64 93 */ 65 94 66 95 static int __init ipc_init(void) ··· 98 67 sem_init(); 99 68 msg_init(); 100 69 shm_init(); 70 + hotplug_memory_notifier(ipc_memory_callback, IPC_CALLBACK_PRI); 71 + register_ipcns_notifier(&init_ipc_ns); 101 72 return 0; 102 73 } 103 74 __initcall(ipc_init);
+2
ipc/util.h
··· 124 124 extern struct msg_msg *load_msg(const void __user *src, int len); 125 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 129 static inline int ipc_buildid(int id, int seq) 128 130 { 129 131 return SEQ_MULTIPLIER * seq + id;