diff options
| author | rmind <rmind@NetBSD.org> | 2007-11-04 11:20:34 +0000 |
|---|---|---|
| committer | rmind <rmind@NetBSD.org> | 2007-11-04 11:20:34 +0000 |
| commit | defb3f5b283d1e595010fa26ed4e2f5d0213e819 (patch) | |
| tree | eed93b9465ab8739f3041e2fcb365590d760ff9c /sys | |
| parent | 6364fdeba528bae7b0bcf7cb4493e2ae090b49d1 (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.c | 336 | ||||
| -rw-r--r-- | sys/kern/sysv_sem.c | 310 | ||||
| -rw-r--r-- | sys/sys/sem.h | 7 |
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. */ |
