summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorrmind <rmind@NetBSD.org>2007-11-04 11:20:34 +0000
committerrmind <rmind@NetBSD.org>2007-11-04 11:20:34 +0000
commitdefb3f5b283d1e595010fa26ed4e2f5d0213e819 (patch)
treeeed93b9465ab8739f3041e2fcb365590d760ff9c /sys
parent6364fdeba528bae7b0bcf7cb4493e2ae090b49d1 (diff)
Support for dynamic changing of SysV message queues and semaphores limits.
Also fixes the alignment problem, which was here forever. Thanks for <uwe> for discussion and <martin> for testing. Reviewed by: <tech-kern>, <gdt>
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/sysv_msg.c336
-rw-r--r--sys/kern/sysv_sem.c310
-rw-r--r--sys/sys/sem.h7
3 files changed, 609 insertions, 44 deletions
diff --git a/sys/kern/sysv_msg.c b/sys/kern/sysv_msg.c
index 1e3e62ec816..ed9a822d067 100644
--- a/sys/kern/sysv_msg.c
+++ b/sys/kern/sysv_msg.c
@@ -1,4 +1,4 @@
-/* $NetBSD: sysv_msg.c,v 1.50 2007/10/09 03:27:37 rmind Exp $ */
+/* $NetBSD: sysv_msg.c,v 1.51 2007/11/04 11:20:34 rmind Exp $ */
/*-
* Copyright (c) 1999, 2006, 2007 The NetBSD Foundation, Inc.
@@ -57,7 +57,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sysv_msg.c,v 1.50 2007/10/09 03:27:37 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sysv_msg.c,v 1.51 2007/11/04 11:20:34 rmind Exp $");
#define SYSVMSG
@@ -88,6 +88,10 @@ static struct __msg *msghdrs; /* MSGTQL msg headers */
kmsq_t *msqs; /* MSGMNI msqid_ds struct's */
kmutex_t msgmutex; /* subsystem lock */
+static u_int msg_waiters = 0; /* total number of msgrcv waiters */
+static bool msg_realloc_state;
+static kcondvar_t msg_realloc_cv;
+
static void msg_freehdr(struct __msg *);
void
@@ -114,19 +118,21 @@ msginit(void)
panic("msginfo.msgseg = %d > 32767", msginfo.msgseg);
}
- /* Allocate pageable memory for our structures */
- sz = msginfo.msgmax +
- msginfo.msgseg * sizeof(struct msgmap) +
- msginfo.msgtql * sizeof(struct __msg) +
- msginfo.msgmni * sizeof(kmsq_t);
+ /* Allocate the wired memory for our structures */
+ sz = ALIGN(msginfo.msgmax) +
+ ALIGN(msginfo.msgseg * sizeof(struct msgmap)) +
+ ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
+ ALIGN(msginfo.msgmni * sizeof(kmsq_t));
v = uvm_km_alloc(kernel_map, round_page(sz), 0,
UVM_KMF_WIRED|UVM_KMF_ZERO);
if (v == 0)
panic("sysv_msg: cannot allocate memory");
msgpool = (void *)v;
- msgmaps = (void *)(msgpool + msginfo.msgmax);
- msghdrs = (void *)(msgmaps + msginfo.msgseg);
- msqs = (void *)(msghdrs + msginfo.msgtql);
+ msgmaps = (void *)(ALIGN(msgpool) + msginfo.msgmax);
+ msghdrs = (void *)(ALIGN(msgmaps) +
+ msginfo.msgseg * sizeof(struct msgmap));
+ msqs = (void *)(ALIGN(msghdrs) +
+ msginfo.msgtql * sizeof(struct __msg));
for (i = 0; i < (msginfo.msgseg - 1); i++)
msgmaps[i].next = i + 1;
@@ -153,6 +159,217 @@ msginit(void)
}
mutex_init(&msgmutex, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&msg_realloc_cv, "msgrealc");
+ msg_realloc_state = false;
+}
+
+static int
+msgrealloc(int newmsgmni, int newmsgseg)
+{
+ struct msgmap *new_msgmaps;
+ struct __msg *new_msghdrs, *new_free_msghdrs;
+ char *old_msgpool, *new_msgpool;
+ kmsq_t *new_msqs;
+ vaddr_t v;
+ int i, sz, msqid, newmsgmax, new_nfree_msgmaps;
+ short new_free_msgmaps;
+
+ if (newmsgmni < 1 || newmsgseg < 1)
+ return EINVAL;
+
+ /* Allocate the wired memory for our structures */
+ newmsgmax = msginfo.msgssz * newmsgseg;
+ sz = ALIGN(newmsgmax) +
+ ALIGN(newmsgseg * sizeof(struct msgmap)) +
+ ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
+ ALIGN(newmsgmni * sizeof(kmsq_t));
+ v = uvm_km_alloc(kernel_map, round_page(sz), 0,
+ UVM_KMF_WIRED|UVM_KMF_ZERO);
+ if (v == 0)
+ return ENOMEM;
+
+ mutex_enter(&msgmutex);
+ if (msg_realloc_state) {
+ mutex_exit(&msgmutex);
+ uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
+ return EBUSY;
+ }
+ msg_realloc_state = true;
+ if (msg_waiters) {
+ /*
+ * Mark reallocation state, wake-up all waiters,
+ * and wait while they will all exit.
+ */
+ for (i = 0; i < msginfo.msgmni; i++)
+ cv_broadcast(&msqs[i].msq_cv);
+ while (msg_waiters)
+ cv_wait(&msg_realloc_cv, &msgmutex);
+ }
+ old_msgpool = msgpool;
+
+ /* We cannot reallocate less memory than we use */
+ i = 0;
+ for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
+ struct msqid_ds *mptr;
+ kmsq_t *msq;
+
+ msq = &msqs[msqid];
+ mptr = &msq->msq_u;
+ if (mptr->msg_qbytes || (mptr->msg_perm.mode & MSG_LOCKED))
+ i = msqid;
+ }
+ if (i >= newmsgmni || (msginfo.msgseg - nfree_msgmaps) > newmsgseg) {
+ mutex_exit(&msgmutex);
+ uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
+ return EBUSY;
+ }
+
+ new_msgpool = (void *)v;
+ new_msgmaps = (void *)(ALIGN(new_msgpool) + newmsgmax);
+ new_msghdrs = (void *)(ALIGN(new_msgmaps) +
+ newmsgseg * sizeof(struct msgmap));
+ new_msqs = (void *)(ALIGN(new_msghdrs) +
+ msginfo.msgtql * sizeof(struct __msg));
+
+ /* Initialize the structures */
+ for (i = 0; i < (newmsgseg - 1); i++)
+ new_msgmaps[i].next = i + 1;
+ new_msgmaps[newmsgseg - 1].next = -1;
+ new_free_msgmaps = 0;
+ new_nfree_msgmaps = newmsgseg;
+
+ for (i = 0; i < (msginfo.msgtql - 1); i++) {
+ new_msghdrs[i].msg_type = 0;
+ new_msghdrs[i].msg_next = &new_msghdrs[i + 1];
+ }
+ i = msginfo.msgtql - 1;
+ new_msghdrs[i].msg_type = 0;
+ new_msghdrs[i].msg_next = NULL;
+ new_free_msghdrs = &new_msghdrs[0];
+
+ for (i = 0; i < newmsgmni; i++) {
+ new_msqs[i].msq_u.msg_qbytes = 0;
+ new_msqs[i].msq_u.msg_perm._seq = 0;
+ cv_init(&new_msqs[i].msq_cv, "msgwait");
+ }
+
+ /*
+ * Copy all message queue identifiers, mesage headers and buffer
+ * pools to the new memory location.
+ */
+ for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
+ struct __msg *nmsghdr, *msghdr, *pmsghdr;
+ struct msqid_ds *nmptr, *mptr;
+ kmsq_t *nmsq, *msq;
+
+ msq = &msqs[msqid];
+ mptr = &msq->msq_u;
+
+ if (mptr->msg_qbytes == 0 &&
+ (mptr->msg_perm.mode & MSG_LOCKED) == 0)
+ continue;
+
+ nmsq = &new_msqs[msqid];
+ nmptr = &nmsq->msq_u;
+ memcpy(nmptr, mptr, sizeof(struct msqid_ds));
+
+ /*
+ * Go through the message headers, and and copy each
+ * one by taking the new ones, and thus defragmenting.
+ */
+ nmsghdr = pmsghdr = NULL;
+ msghdr = mptr->_msg_first;
+ while (msghdr) {
+ short nnext = 0, next;
+ u_short msgsz, segcnt;
+
+ /* Take an entry from the new list of free msghdrs */
+ nmsghdr = new_free_msghdrs;
+ KASSERT(nmsghdr != NULL);
+ new_free_msghdrs = nmsghdr->msg_next;
+
+ nmsghdr->msg_next = NULL;
+ if (pmsghdr) {
+ pmsghdr->msg_next = nmsghdr;
+ } else {
+ nmptr->_msg_first = nmsghdr;
+ pmsghdr = nmsghdr;
+ }
+ nmsghdr->msg_ts = msghdr->msg_ts;
+ nmsghdr->msg_spot = -1;
+
+ /* Compute the amount of segments and reserve them */
+ msgsz = msghdr->msg_ts;
+ segcnt = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
+ if (segcnt == 0)
+ continue;
+ while (segcnt--) {
+ nnext = new_free_msgmaps;
+ new_free_msgmaps = new_msgmaps[nnext].next;
+ new_nfree_msgmaps--;
+ new_msgmaps[nnext].next = nmsghdr->msg_spot;
+ nmsghdr->msg_spot = nnext;
+ }
+
+ /* Copy all segments */
+ KASSERT(nnext == nmsghdr->msg_spot);
+ next = msghdr->msg_spot;
+ while (msgsz > 0) {
+ size_t tlen;
+
+ if (msgsz >= msginfo.msgssz) {
+ tlen = msginfo.msgssz;
+ msgsz -= msginfo.msgssz;
+ } else {
+ tlen = msgsz;
+ msgsz = 0;
+ }
+
+ /* Copy the message buffer */
+ memcpy(&new_msgpool[nnext * msginfo.msgssz],
+ &msgpool[next * msginfo.msgssz], tlen);
+
+ /* Next entry of the map */
+ nnext = msgmaps[nnext].next;
+ next = msgmaps[next].next;
+ }
+
+ /* Next message header */
+ msghdr = msghdr->msg_next;
+ }
+ nmptr->_msg_last = nmsghdr;
+ }
+ KASSERT((msginfo.msgseg - nfree_msgmaps) ==
+ (newmsgseg - new_nfree_msgmaps));
+
+ sz = ALIGN(msginfo.msgmax) +
+ ALIGN(msginfo.msgseg * sizeof(struct msgmap)) +
+ ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
+ ALIGN(msginfo.msgmni * sizeof(kmsq_t));
+
+ for (i = 0; i < msginfo.msgmni; i++)
+ cv_destroy(&msqs[i].msq_cv);
+
+ /* Set the pointers and update the new values */
+ msgpool = new_msgpool;
+ msgmaps = new_msgmaps;
+ msghdrs = new_msghdrs;
+ msqs = new_msqs;
+
+ free_msghdrs = new_free_msghdrs;
+ free_msgmaps = new_free_msgmaps;
+ nfree_msgmaps = new_nfree_msgmaps;
+ msginfo.msgmni = newmsgmni;
+ msginfo.msgseg = newmsgseg;
+ msginfo.msgmax = newmsgmax;
+
+ /* Reallocation completed - notify all waiters, if any */
+ msg_realloc_state = false;
+ cv_broadcast(&msg_realloc_cv);
+ mutex_exit(&msgmutex);
+
+ uvm_km_free(kernel_map, (vaddr_t)old_msgpool, sz, UVM_KMF_WIRED);
+ return 0;
}
static void
@@ -446,6 +663,11 @@ msgsnd1(struct lwp *l, int msqidr, const char *user_msgp, size_t msgsz,
msqid = IPCID_TO_IX(msqidr);
mutex_enter(&msgmutex);
+ if (msg_realloc_state) {
+ /* In case of reallocation, we will wait for completion */
+ while (msg_realloc_state)
+ cv_wait(&msg_realloc_cv, &msgmutex);
+ }
if (msqid < 0 || msqid >= msginfo.msgmni) {
MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
@@ -527,12 +749,20 @@ msgsnd1(struct lwp *l, int msqidr, const char *user_msgp, size_t msgsz,
msqptr->msg_perm.mode |= MSG_LOCKED;
we_own_it = 1;
}
+
+ msg_waiters++;
MSG_PRINTF(("goodnight\n"));
error = cv_wait_sig(&msq->msq_cv, &msgmutex);
MSG_PRINTF(("good morning, error=%d\n", error));
+ msg_waiters--;
+
+ /* Notify reallocator, in case of such state */
+ if (msg_realloc_state)
+ cv_signal(&msg_realloc_cv);
+
if (we_own_it)
msqptr->msg_perm.mode &= ~MSG_LOCKED;
- if (error != 0) {
+ if (error || msg_realloc_state) {
MSG_PRINTF(("msgsnd: interrupted system "
"call\n"));
error = EINTR;
@@ -733,6 +963,11 @@ msgrcv1(struct lwp *l, int msqidr, char *user_msgp, size_t msgsz, long msgtyp,
msqid = IPCID_TO_IX(msqidr);
mutex_enter(&msgmutex);
+ if (msg_realloc_state) {
+ /* In case of reallocation, we will wait for completion */
+ while (msg_realloc_state)
+ cv_wait(&msg_realloc_cv, &msgmutex);
+ }
if (msqid < 0 || msqid >= msginfo.msgmni) {
MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
@@ -849,11 +1084,17 @@ msgrcv1(struct lwp *l, int msqidr, char *user_msgp, size_t msgsz, long msgtyp,
* Wait for something to happen
*/
+ msg_waiters++;
MSG_PRINTF(("msgrcv: goodnight\n"));
error = cv_wait_sig(&msq->msq_cv, &msgmutex);
MSG_PRINTF(("msgrcv: good morning (error=%d)\n", error));
+ msg_waiters--;
- if (error != 0) {
+ /* Notify reallocator, in case of such state */
+ if (msg_realloc_state)
+ cv_signal(&msg_realloc_cv);
+
+ if (error || msg_realloc_state) {
MSG_PRINTF(("msgsnd: interrupted system call\n"));
error = EINTR;
goto unlock;
@@ -922,7 +1163,7 @@ msgrcv1(struct lwp *l, int msqidr, char *user_msgp, size_t msgsz, long msgtyp,
else
tlen = msgsz - len;
mutex_exit(&msgmutex);
- error = copyout(&msgpool[next * msginfo.msgssz],
+ error = (*put_type)(&msgpool[next * msginfo.msgssz],
user_msgp, tlen);
mutex_enter(&msgmutex);
if (error != 0) {
@@ -948,3 +1189,72 @@ unlock:
mutex_exit(&msgmutex);
return error;
}
+
+/*
+ * Sysctl initialization and nodes.
+ */
+
+static int
+sysctl_ipc_msgmni(SYSCTLFN_ARGS)
+{
+ int newsize, error;
+ struct sysctlnode node;
+ node = *rnode;
+ node.sysctl_data = &newsize;
+
+ newsize = msginfo.msgmni;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ return msgrealloc(newsize, msginfo.msgseg);
+}
+
+static int
+sysctl_ipc_msgseg(SYSCTLFN_ARGS)
+{
+ int newsize, error;
+ struct sysctlnode node;
+ node = *rnode;
+ node.sysctl_data = &newsize;
+
+ newsize = msginfo.msgseg;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ return msgrealloc(msginfo.msgmni, newsize);
+}
+
+SYSCTL_SETUP(sysctl_ipc_msg_setup, "sysctl kern.ipc subtree setup")
+{
+ const struct sysctlnode *node = NULL;
+
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "kern", NULL,
+ NULL, 0, NULL, 0,
+ CTL_KERN, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, &node,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "ipc",
+ SYSCTL_DESCR("SysV IPC options"),
+ NULL, 0, NULL, 0,
+ CTL_KERN, KERN_SYSVIPC, CTL_EOL);
+
+ if (node == NULL)
+ return;
+
+ sysctl_createv(clog, 0, &node, NULL,
+ CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
+ CTLTYPE_INT, "msgmni",
+ SYSCTL_DESCR("Max number of message queue identifiers"),
+ sysctl_ipc_msgmni, 0, &msginfo.msgmni, 0,
+ CTL_CREATE, CTL_EOL);
+ sysctl_createv(clog, 0, &node, NULL,
+ CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
+ CTLTYPE_INT, "msgseg",
+ SYSCTL_DESCR("Max number of number of message segments"),
+ sysctl_ipc_msgseg, 0, &msginfo.msgseg, 0,
+ CTL_CREATE, CTL_EOL);
+}
diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c
index 20d77723ff3..173f309a991 100644
--- a/sys/kern/sysv_sem.c
+++ b/sys/kern/sysv_sem.c
@@ -1,4 +1,4 @@
-/* $NetBSD: sysv_sem.c,v 1.73 2007/08/17 23:46:34 ad Exp $ */
+/* $NetBSD: sysv_sem.c,v 1.74 2007/11/04 11:20:34 rmind Exp $ */
/*-
* Copyright (c) 1999, 2007 The NetBSD Foundation, Inc.
@@ -46,7 +46,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sysv_sem.c,v 1.73 2007/08/17 23:46:34 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sysv_sem.c,v 1.74 2007/11/04 11:20:34 rmind Exp $");
#define SYSVSEM
@@ -59,13 +59,28 @@ __KERNEL_RCSID(0, "$NetBSD: sysv_sem.c,v 1.73 2007/08/17 23:46:34 ad Exp $");
#include <sys/syscallargs.h>
#include <sys/kauth.h>
-static int semtot = 0;
-struct semid_ds *sema; /* semaphore id pool */
-static struct __sem *sem; /* semaphore pool */
+/*
+ * Memory areas:
+ * 1st: Pool of semaphore identifiers
+ * 2nd: Semaphores
+ * 3rd: Conditional variables
+ * 4th: Undo structures
+ */
+struct semid_ds *sema;
+static struct __sem *sem;
+static kcondvar_t *semcv;
+static int *semu;
+
+static kmutex_t semlock;
static struct sem_undo *semu_list; /* list of active undo structures */
-static int *semu; /* undo structure pool */
-static kcondvar_t *semcv;
-static kmutex_t semlock;
+static u_int semtot = 0; /* total number of semaphores */
+
+static u_int sem_waiters = 0; /* total number of semop waiters */
+static bool sem_realloc_state;
+static kcondvar_t sem_realloc_cv;
+
+/* Macro to find a particular sem_undo vector */
+#define SEMU(s, ix) ((struct sem_undo *)(((long)s) + ix * seminfo.semusz))
#ifdef SEM_DEBUG
#define SEM_PRINTF(a) printf a
@@ -84,20 +99,25 @@ seminit(void)
vaddr_t v;
mutex_init(&semlock, MUTEX_DEFAULT, IPL_NONE);
-
- /* Allocate pageable memory for our structures */
- sz = seminfo.semmni * sizeof(struct semid_ds) +
- seminfo.semmns * sizeof(struct __sem) +
- seminfo.semmnu * seminfo.semusz +
- seminfo.semmni * sizeof(kcondvar_t);
+ cv_init(&sem_realloc_cv, "semrealc");
+ sem_realloc_state = false;
+
+ /* Allocate the wired memory for our structures */
+ sz = ALIGN(seminfo.semmni * sizeof(struct semid_ds)) +
+ ALIGN(seminfo.semmns * sizeof(struct __sem)) +
+ ALIGN(seminfo.semmni * sizeof(kcondvar_t)) +
+ ALIGN(seminfo.semmnu * seminfo.semusz);
v = uvm_km_alloc(kernel_map, round_page(sz), 0,
UVM_KMF_WIRED|UVM_KMF_ZERO);
if (v == 0)
panic("sysv_sem: cannot allocate memory");
sema = (void *)v;
- sem = (void *)(sema + seminfo.semmni);
- semcv = (void *)(sem + seminfo.semmns);
- semu = (void *)(semcv + seminfo.semmni);
+ sem = (void *)(ALIGN(sema) +
+ seminfo.semmni * sizeof(struct semid_ds));
+ semcv = (void *)(ALIGN(sem) +
+ seminfo.semmns * sizeof(struct __sem));
+ semu = (void *)(ALIGN(semcv) +
+ seminfo.semmni * sizeof(kcondvar_t));
for (i = 0; i < seminfo.semmni; i++) {
sema[i]._sem_base = 0;
@@ -105,13 +125,154 @@ seminit(void)
cv_init(&semcv[i], "semwait");
}
for (i = 0; i < seminfo.semmnu; i++) {
- struct sem_undo *suptr = SEMU(i);
+ struct sem_undo *suptr = SEMU(semu, i);
suptr->un_proc = NULL;
}
semu_list = NULL;
exithook_establish(semexit, NULL);
}
+static int
+semrealloc(int newsemmni, int newsemmns, int newsemmnu)
+{
+ struct semid_ds *new_sema, *old_sema;
+ struct __sem *new_sem;
+ struct sem_undo *new_semu_list, *suptr, *nsuptr;
+ int *new_semu;
+ kcondvar_t *new_semcv;
+ vaddr_t v;
+ int i, j, lsemid, nmnus, sz;
+
+ if (newsemmni < 1 || newsemmns < 1 || newsemmnu < 1)
+ return EINVAL;
+
+ /* Allocate the wired memory for our structures */
+ sz = ALIGN(newsemmni * sizeof(struct semid_ds)) +
+ ALIGN(newsemmns * sizeof(struct __sem)) +
+ ALIGN(newsemmni * sizeof(kcondvar_t)) +
+ ALIGN(newsemmnu * seminfo.semusz);
+ v = uvm_km_alloc(kernel_map, round_page(sz), 0,
+ UVM_KMF_WIRED|UVM_KMF_ZERO);
+ if (v == 0)
+ return ENOMEM;
+
+ mutex_enter(&semlock);
+ if (sem_realloc_state) {
+ mutex_exit(&semlock);
+ uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
+ return EBUSY;
+ }
+ sem_realloc_state = true;
+ if (sem_waiters) {
+ /*
+ * Mark reallocation state, wake-up all waiters,
+ * and wait while they will all exit.
+ */
+ for (i = 0; i < seminfo.semmni; i++)
+ cv_broadcast(&semcv[i]);
+ while (sem_waiters)
+ cv_wait(&sem_realloc_cv, &semlock);
+ }
+ old_sema = sema;
+
+ /* Get the number of last slot */
+ lsemid = 0;
+ for (i = 0; i < seminfo.semmni; i++)
+ if (sema[i].sem_perm.mode & SEM_ALLOC)
+ lsemid = i;
+
+ /* Get the number of currently used undo structures */
+ nmnus = 0;
+ for (i = 0; i < seminfo.semmnu; i++) {
+ suptr = SEMU(semu, i);
+ if (suptr->un_proc == NULL)
+ continue;
+ nmnus++;
+ }
+
+ /* We cannot reallocate less memory than we use */
+ if (lsemid >= newsemmni || semtot > newsemmns || nmnus > newsemmnu) {
+ mutex_exit(&semlock);
+ uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
+ return EBUSY;
+ }
+
+ new_sema = (void *)v;
+ new_sem = (void *)(ALIGN(new_sema) +
+ newsemmni * sizeof(struct semid_ds));
+ new_semcv = (void *)(ALIGN(new_sem) +
+ newsemmns * sizeof(struct __sem));
+ new_semu = (void *)(ALIGN(new_semcv) +
+ newsemmni * sizeof(kcondvar_t));
+
+ /* Initialize all semaphore identifiers and condvars */
+ for (i = 0; i < newsemmni; i++) {
+ new_sema[i]._sem_base = 0;
+ new_sema[i].sem_perm.mode = 0;
+ cv_init(&new_semcv[i], "semwait");
+ }
+ for (i = 0; i < newsemmnu; i++) {
+ nsuptr = SEMU(new_semu, i);
+ nsuptr->un_proc = NULL;
+ }
+
+ /*
+ * Copy all identifiers, semaphores and list of the
+ * undo structures to the new memory allocation.
+ */
+ j = 0;
+ for (i = 0; i <= lsemid; i++) {
+ if ((sema[i].sem_perm.mode & SEM_ALLOC) == 0)
+ continue;
+ memcpy(&new_sema[i], &sema[i], sizeof(struct semid_ds));
+ new_sema[i]._sem_base = &new_sem[j];
+ memcpy(new_sema[i]._sem_base, sema[i]._sem_base,
+ (sizeof(struct __sem) * sema[i].sem_nsems));
+ j += sema[i].sem_nsems;
+ }
+ KASSERT(j == semtot);
+
+ j = 0;
+ new_semu_list = NULL;
+ for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) {
+ KASSERT(j < newsemmnu);
+ nsuptr = SEMU(new_semu, j);
+ memcpy(nsuptr, suptr, SEMUSZ);
+ nsuptr->un_next = new_semu_list;
+ new_semu_list = nsuptr;
+ j++;
+ }
+
+ for (i = 0; i < seminfo.semmni; i++) {
+ KASSERT(cv_has_waiters(&semcv[i]) == false);
+ cv_destroy(&semcv[i]);
+ }
+
+ sz = ALIGN(seminfo.semmni * sizeof(struct semid_ds)) +
+ ALIGN(seminfo.semmns * sizeof(struct __sem)) +
+ ALIGN(seminfo.semmni * sizeof(kcondvar_t)) +
+ ALIGN(seminfo.semmnu * seminfo.semusz);
+
+ /* Set the pointers and update the new values */
+ sema = new_sema;
+ sem = new_sem;
+ semcv = new_semcv;
+ semu = new_semu;
+ semu_list = new_semu_list;
+
+ seminfo.semmni = newsemmni;
+ seminfo.semmns = newsemmns;
+ seminfo.semmnu = newsemmnu;
+
+ /* Reallocation completed - notify all waiters, if any */
+ sem_realloc_state = false;
+ cv_broadcast(&sem_realloc_cv);
+ mutex_exit(&semlock);
+
+ uvm_km_free(kernel_map, (vaddr_t)old_sema, sz, UVM_KMF_WIRED);
+ return 0;
+}
+
/*
* Placebo.
*/
@@ -152,7 +313,7 @@ semu_alloc(struct proc *p)
*/
for (i = 0; i < seminfo.semmnu; i++) {
- suptr = SEMU(i);
+ suptr = SEMU(semu, i);
if (suptr->un_proc == NULL) {
suptr->un_next = semu_list;
semu_list = suptr;
@@ -613,6 +774,11 @@ sys_semop(struct lwp *l, void *v, register_t *retval)
}
mutex_enter(&semlock);
+ if (sem_realloc_state) {
+ /* In case of reallocation, we will wait for completion */
+ while (sem_realloc_state)
+ cv_wait(&sem_realloc_cv, &semlock);
+ }
semid = IPCID_TO_IX(semid); /* Convert back to zero origin */
if (semid < 0 || semid >= seminfo.semmni) {
@@ -727,9 +893,15 @@ sys_semop(struct lwp *l, void *v, register_t *retval)
else
semptr->semncnt++;
+ sem_waiters++;
SEM_PRINTF(("semop: good night!\n"));
error = cv_wait_sig(&semcv[semid], &semlock);
SEM_PRINTF(("semop: good morning (error=%d)!\n", error));
+ sem_waiters--;
+
+ /* Notify reallocator, in case of such state */
+ if (sem_realloc_state)
+ cv_signal(&sem_realloc_cv);
/*
* Make sure that the semaphore still exists
@@ -749,12 +921,9 @@ sys_semop(struct lwp *l, void *v, register_t *retval)
semptr->semzcnt--;
else
semptr->semncnt--;
- /*
- * Is it really morning, or was our sleep interrupted?
- * (Delayed check of tsleep() return code because we
- * need to decrement sem[nz]cnt either way.)
- */
- if (error != 0) {
+
+ /* Is it really morning, or was our sleep interrupted? */
+ if (error || sem_realloc_state) {
error = EINTR;
goto out;
}
@@ -925,3 +1094,94 @@ semexit(struct proc *p, void *v)
*supptr = suptr->un_next;
mutex_exit(&semlock);
}
+
+/*
+ * Sysctl initialization and nodes.
+ */
+
+static int
+sysctl_ipc_semmni(SYSCTLFN_ARGS)
+{
+ int newsize, error;
+ struct sysctlnode node;
+ node = *rnode;
+ node.sysctl_data = &newsize;
+
+ newsize = seminfo.semmni;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ return semrealloc(newsize, seminfo.semmns, seminfo.semmnu);
+}
+
+static int
+sysctl_ipc_semmns(SYSCTLFN_ARGS)
+{
+ int newsize, error;
+ struct sysctlnode node;
+ node = *rnode;
+ node.sysctl_data = &newsize;
+
+ newsize = seminfo.semmns;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ return semrealloc(seminfo.semmni, newsize, seminfo.semmnu);
+}
+
+static int
+sysctl_ipc_semmnu(SYSCTLFN_ARGS)
+{
+ int newsize, error;
+ struct sysctlnode node;
+ node = *rnode;
+ node.sysctl_data = &newsize;
+
+ newsize = seminfo.semmnu;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ return semrealloc(seminfo.semmni, seminfo.semmns, newsize);
+}
+
+SYSCTL_SETUP(sysctl_ipc_sem_setup, "sysctl kern.ipc subtree setup")
+{
+ const struct sysctlnode *node = NULL;
+
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "kern", NULL,
+ NULL, 0, NULL, 0,
+ CTL_KERN, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, &node,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "ipc",
+ SYSCTL_DESCR("SysV IPC options"),
+ NULL, 0, NULL, 0,
+ CTL_KERN, KERN_SYSVIPC, CTL_EOL);
+
+ if (node == NULL)
+ return;
+
+ sysctl_createv(clog, 0, &node, NULL,
+ CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
+ CTLTYPE_INT, "semmni",
+ SYSCTL_DESCR("Max number of number of semaphore identifiers"),
+ sysctl_ipc_semmni, 0, &seminfo.semmni, 0,
+ CTL_CREATE, CTL_EOL);
+ sysctl_createv(clog, 0, &node, NULL,
+ CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
+ CTLTYPE_INT, "semmns",
+ SYSCTL_DESCR("Max number of number of semaphores in system"),
+ sysctl_ipc_semmns, 0, &seminfo.semmns, 0,
+ CTL_CREATE, CTL_EOL);
+ sysctl_createv(clog, 0, &node, NULL,
+ CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
+ CTLTYPE_INT, "semmnu",
+ SYSCTL_DESCR("Max number of undo structures in system"),
+ sysctl_ipc_semmnu, 0, &seminfo.semmnu, 0,
+ CTL_CREATE, CTL_EOL);
+}
diff --git a/sys/sys/sem.h b/sys/sys/sem.h
index 63d62d0d069..969ce2a947c 100644
--- a/sys/sys/sem.h
+++ b/sys/sys/sem.h
@@ -1,4 +1,4 @@
-/* $NetBSD: sem.h,v 1.23 2007/06/17 10:21:25 dsl Exp $ */
+/* $NetBSD: sem.h,v 1.24 2007/11/04 11:20:35 rmind Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -199,11 +199,6 @@ extern struct seminfo seminfo;
extern struct semid_ds *sema; /* semaphore id pool */
/*
- * Macro to find a particular sem_undo vector
- */
-#define SEMU(ix) ((struct sem_undo *)(((long)semu)+ix * SEMUSZ))
-
-/*
* Parameters to the semconfig system call
*/
#define SEM_CONFIG_FREEZE 0 /* Freeze the semaphore facility. */