diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h index 878d7ac286fa6220f38047698305466cc357467c..cfb2a08b28f5c244b4cce36e3b3f1292eefd627b 100644 --- a/include/linux/ipc_namespace.h +++ b/include/linux/ipc_namespace.h @@ -4,6 +4,17 @@ #include #include #include +#ifdef CONFIG_MEMORY_HOTPLUG +#include +#endif /* CONFIG_MEMORY_HOTPLUG */ + +/* + * ipc namespace events + */ +#define IPCNS_MEMCHANGED 0x00000001 /* Notify lowmem size changed */ + +#define IPCNS_CALLBACK_PRI 0 + struct ipc_ids { int in_use; @@ -30,6 +41,10 @@ struct ipc_namespace { size_t shm_ctlall; int shm_ctlmni; int shm_tot; + +#ifdef CONFIG_MEMORY_HOTPLUG + struct notifier_block ipcns_nb; +#endif }; extern struct ipc_namespace init_ipc_ns; @@ -37,9 +52,33 @@ extern atomic_t nr_ipc_ns; #ifdef CONFIG_SYSVIPC #define INIT_IPC_NS(ns) .ns = &init_ipc_ns, -#else + +#ifdef CONFIG_MEMORY_HOTPLUG + +extern int register_ipcns_notifier(struct ipc_namespace *); +extern int unregister_ipcns_notifier(struct ipc_namespace *); +extern int ipcns_notify(unsigned long); + +#else /* CONFIG_MEMORY_HOTPLUG */ + +static inline int register_ipcns_notifier(struct ipc_namespace *ipcns) +{ + return 0; +} +static inline int unregister_ipcns_notifier(struct ipc_namespace *ipcns) +{ + return 0; +} +static inline int ipcns_notify(unsigned long ev) +{ + return 0; +} + +#endif /* CONFIG_MEMORY_HOTPLUG */ + +#else /* CONFIG_SYSVIPC */ #define INIT_IPC_NS(ns) -#endif +#endif /* CONFIG_SYSVIPC */ #if defined(CONFIG_SYSVIPC) && defined(CONFIG_IPC_NS) extern void free_ipc_ns(struct kref *kref); diff --git a/include/linux/memory.h b/include/linux/memory.h index 39628dfe4a4c16d5be661d5cad7075df9e334e8c..2f5f8a5ef2a08a5e796294275ee095cae2319fd5 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -58,6 +58,7 @@ struct mem_section; * order in the callback chain) */ #define SLAB_CALLBACK_PRI 1 +#define IPC_CALLBACK_PRI 10 #ifndef CONFIG_MEMORY_HOTPLUG_SPARSE static inline int memory_dev_init(void) diff --git a/ipc/Makefile b/ipc/Makefile index 5fc5e33ea047c9f46b18948d06e0133733dba8dc..388e4d259f02cacc28d0eecb9157efefdd7348d6 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 0000000000000000000000000000000000000000..0786af6ce3ecdc36c63cb998363730b2b0c7ec42 --- /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 +#include +#include +#include +#include + +#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 be8449d48a8e648d1208116a48d27844f50cf871..7d9b0694c7432d38ddab35812726f9635a9cfa42 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 fe3c97aa99dc191f69c9febf058fc2b876204523..f7a35be2e7718ce72a1510fb80f301d7a7d77832 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 c27f0e92f48988016875461ffd5e5db0a8abd08c..2d545d7144a7b9cfe04edaeaedefd2472314000d 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -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 f37d160c98fe7c5c42b44d9611979a4a79d793fc..0e3d79037a2aef3ffd9323f0940d8e2ba3670227 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;