sem.c revision 005d3feb53a9a10272d4a24b03991575d6a9bcb3
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Inter-Process Communication Semaphore Facility.
*
*
* Resource controls
* -----------------
*
* Control: zone.max-sem-ids (rc_zone_semmni)
* Description: Maximum number of semaphore ids allowed a zone.
*
* When semget() is used to allocate a semaphore set, one id is
* allocated. If the id allocation doesn't succeed, semget() fails
* and errno is set to ENOSPC. Upon successful semctl(, IPC_RMID)
* the id is deallocated.
*
* Control: project.max-sem-ids (rc_project_semmni)
* Description: Maximum number of semaphore ids allowed a project.
*
* When semget() is used to allocate a semaphore set, one id is
* allocated. If the id allocation doesn't succeed, semget() fails
* and errno is set to ENOSPC. Upon successful semctl(, IPC_RMID)
* the id is deallocated.
*
* Control: process.max-sem-nsems (rc_process_semmsl)
* Description: Maximum number of semaphores allowed per semaphore set.
*
* When semget() is used to allocate a semaphore set, the size of the
* set is compared with this limit. If the number of semaphores
* exceeds the limit, semget() fails and errno is set to EINVAL.
*
* Control: process.max-sem-ops (rc_process_semopm)
* Description: Maximum number of semaphore operations allowed per
* semop call.
*
* When semget() successfully allocates a semaphore set, the minimum
* enforced value of this limit is used to initialize the
* "system-imposed maximum" number of operations a semop() call for
* this set can perform.
*
* Undo structures
* ---------------
*
* Removing the undo structure tunables involved a serious redesign of
* how they were implemented. There is now one undo structure for
* course), and each is equal in size to the semaphore it corresponds
* to. To avoid scalability and performance problems, the undo
* structures are stored in two places: a per-process AVL tree sorted
* by ksemid pointer (p_semacct, protected by p_lock) and an unsorted
* per-semaphore linked list (sem_undos, protected by the semaphore's
* ID lock). The former is used by semop, where a lookup is performed
* once and cached if SEM_UNDO is specified for any of the operations,
* and at process exit where the undoable operations are rolled back.
* The latter is used when removing the semaphore, so the undo
* structures can be removed from the appropriate processes' trees.
*
* The undo structure itself contains pointers to the ksemid and proc
* to which it corresponds, a list node, an AVL node, and an array of
* adjust-on-exit (AOE) values. When an undo structure is allocated it
* is immediately added to both the process's tree and the semaphore's
* list. Lastly, the reference count on the semaphore is increased.
*
* Avoiding a lock ordering violation between p_lock and the ID lock,
* wont to occur when there is a race between a process exiting and the
* removal of a semaphore, mandates the delicate dance that exists
* between semexit and sem_rmid.
*
* sem_rmid, holding the ID lock, iterates through all undo structures
* and for each takes the appropriate process's p_lock and checks to
* see if p_semacct is NULL. If it is, it skips that undo structure
* and continues to the next. Otherwise, it removes the undo structure
* from both the AVL tree and the semaphore's list, and releases the
* hold that the undo structure had on the semaphore.
*
* The important other half of this is semexit, which will immediately
* take p_lock, obtain the AVL pointer, clear p_semacct, and drop
* p_lock. From this point on it is semexit's responsibility to clean
* up all undo structures found in the tree -- a coexecuting sem_rmid
* will see the NULL p_semacct and skip that undo structure. It walks
* the AVL tree (using avl_destroy_nodes) and for each undo structure
* takes the appropriate semaphore's ID lock (always legal since the
* undo structure has a hold on the semaphore), updates all semaphores
* with non-zero AOE values, and removes the structure from the
* semaphore's list. It then drops the structure's reference on the
* semaphore, drops the ID lock, and frees the undo structure.
*/
#include <sys/sysmacros.h>
#include <sys/ipc_impl.h>
#include <sys/sem_impl.h>
extern rctl_hndl_t rc_zone_semmni;
extern rctl_hndl_t rc_project_semmni;
extern rctl_hndl_t rc_process_semmsl;
extern rctl_hndl_t rc_process_semopm;
static ipc_service_t *sem_svc;
static zone_key_t sem_zone_key;
/*
* The following tunables are obsolete. Though for compatibility we
* still read and interpret seminfo_semmsl, seminfo_semopm and
* seminfo_semmni (see os/project.c and os/rctl_proc.c), the preferred
* mechanism for administrating the IPC Semaphore facility is through
* the resource controls described at the top of this file.
*/
static void sem_dtor(kipc_perm_t *);
static void sem_rmid(kipc_perm_t *);
static void sem_remove_zone(zoneid_t, void *);
static struct sysent ipcsem_sysent = {
5,
};
/*
* Module linkage information for the kernel.
*/
};
#ifdef _SYSCALL32_IMPL
};
#endif
static struct modlinkage modlinkage = {
&modlsys,
#ifdef _SYSCALL32_IMPL
#endif
};
int
_init(void)
{
int result;
return (0);
(void) zone_key_delete(sem_zone_key);
return (result);
}
int
_fini(void)
{
return (EBUSY);
}
int
{
}
static void
{
}
/*
* sem_undo_add - Create or update adjust on exit entry.
*/
static int
{
return (ERANGE);
return (0);
}
/*
* sem_undo_clear - clears all undo entries for specified semaphores
*
* Used when semaphores are reset by SETVAL or SETALL.
*/
static void
{
int i;
}
/*
* sem_rollback - roll back work done so far if unable to complete operation
*/
static void
{
continue;
}
}
}
static void
{
int i;
/*LINTED*/
continue;
}
}
}
}
}
}
/*
* semctl - Semctl system call.
*/
static int
{
struct sem *p; /* ptr to semaphore */
unsigned int i; /* loop control */
int error = 0;
int retval = 0;
struct semid_ds64 ds64;
/*
* Perform pre- or non-lookup actions (e.g. copyins, RMID).
*/
switch (cmd) {
case IPC_SET:
break;
case IPC_SET64:
break;
case SETALL:
/* allocate space to hold all semaphore values */
}
break;
case IPC_RMID:
return (0);
}
if (vsize != 0)
}
switch (cmd) {
/* Set ownership and permissions. */
case IPC_SET:
}
return (0);
/* Get semaphore data structure. */
case IPC_STAT:
}
return (0);
case IPC_SET64:
}
return (0);
case IPC_STAT64:
return (0);
/* Get # of processes sleeping for greater semval. */
case GETNCNT:
}
}
return (retval);
/* Get pid of last process to operate on semaphore. */
case GETPID:
}
}
return (retval);
/* Get semval of one semaphore. */
case GETVAL:
}
}
return (retval);
/* Get all semvals in set. */
case GETALL:
}
/* allocate space to hold all semaphore values */
}
return (0);
/* Get # of processes sleeping for semval to become zero. */
case GETZCNT:
}
}
return (retval);
/* Set semval of one semaphore. */
case SETVAL:
}
}
}
if (p->semncnt) {
cv_broadcast(&p->semncnt_cv);
}
} else if (p->semzcnt) {
cv_broadcast(&p->semzcnt_cv);
}
return (0);
/* Set semvals of all semaphores in set. */
case SETALL:
/* Check if semaphore set has been deleted and reallocated. */
goto seterr;
}
goto seterr;
}
if (p->semncnt) {
cv_broadcast(&p->semncnt_cv);
}
} else if (p->semzcnt) {
cv_broadcast(&p->semzcnt_cv);
}
}
return (error);
default:
}
/* NOTREACHED */
}
/*
* semexit - Called by exit() to clean up on process exit.
*/
void
{
int i;
if (adj) {
if (v < 0 || v > USHRT_MAX)
continue;
}
}
}
}
}
/*
* Remove all semaphores associated with a given zone. Called by
* zone_shutdown when the zone is halted.
*/
/*ARGSUSED1*/
static void
{
}
/*
* semget - Semget system call.
*/
static int
{
top:
/*
* A semaphore with the requested key exists.
*/
}
} else {
/*
* This is a new semaphore set. Finish initialization.
*/
}
/*
* We round the allocation up to coherency granularity
* so that multiple semaphore allocations won't result
* in the false sharing of their sem structures.
*/
KM_SLEEP);
(kipc_perm_t *)sp)) {
goto top;
}
sp->sem_maxops =
}
}
if (AU_AUDITING())
return (id);
}
/*
* semids system call.
*/
static int
{
int error;
return (0);
}
/*
* Helper function for semop - copies in the provided timespec and
* computes the absolute future time after which we must return.
*/
static int
{
if (datamodel == DATAMODEL_NATIVE) {
return (EFAULT);
} else {
return (EFAULT);
}
if (itimerspecfix(ts))
return (EINVAL);
/*
* Convert the timespec value into absolute time.
*/
return (0);
}
/*
* Undo structure comparator. We sort based on ksemid_t pointer.
*/
static int
sem_undo_compar(const void *x, const void *y)
{
return (-1);
return (1);
return (0);
}
/*
* Helper function for semop - creates an undo structure and adds it to
* the process's avl tree and the semaphore's list.
*/
static int
{
mutex_exit(*lock);
if (tree)
return (EIDRM);
}
if (tree) {
sizeof (struct sem_undo),
} else {
}
}
} else {
}
return (0);
}
/*
* semop - Semop system call.
*/
static int
{
int i; /* loop control */
int error = 0;
int timecheck = 0;
int held = 0;
/*
* To avoid the cost of copying in 'timeout' in the common
* case, we could only grab the time here and defer the copyin
* and associated computations until we are about to block.
*
* The down side to this is that we would then have to spin
* some goto top nonsense to avoid the copyin behind the semid
* lock. As a common use of timed semaphores is as an explicit
* blocking mechanism, this could incur a greater penalty.
*
* If we eventually decide that this would be a wise route to
* take, the deferrable functionality is completely contained
* in 'compute_timeout', and the interface is defined such that
* we can legally not validate 'timeout' if it is unused.
*/
gethrestime(&now);
}
/*
* Allocate space to hold the vector of semaphore ops. If
* there is only 1 operation we use a preallocated buffer on
* the stack for speed.
*
* Since we don't want to allow the user to allocate an
* arbitrary amount of kernel memory, we need to check against
* the number of operations allowed by the semaphore. We only
* bother doing this if the number of operations is larger than
* SEM_MAXUCOPS.
*/
if (nsops == 1)
else if (nsops == 0)
return (0);
else if (nsops <= SEM_MAXUCOPS)
if (nsops > SEM_MAXUCOPS) {
}
held = 1;
goto semoperr;
}
goto semoperr;
}
} else {
/*
* This could be interleaved with the above code, but
* keeping them separate improves readability.
*/
goto semoperr_unlocked;
}
goto semoperr_unlocked;
}
goto semoperr;
}
}
/*
* Scan all operations. Verify that sem #s are in range and
* this process is allowed the requested operations. If any
* operations are marked SEM_UNDO, find (or allocate) the undo
* structure for this process and semaphore.
*/
needundo = 0;
mode = 0;
goto semoperr;
}
needundo = 1;
}
goto semoperr;
if (needundo) {
else
if (!held) {
held = 1;
}
&undo))
goto semoperr;
/* sem_undo_alloc unlocks the semaphore */
goto semoperr;
}
}
/*
* Loop waiting for the operations to be satisfied atomically.
* Actually, do the operations and undo them if a wait is needed
* or an error is detected.
*/
for (i = 0; i < nsops; i++) {
/*
* Raise the semaphore (i.e. sema_v)
*/
undo)))) {
if (i)
if (error == 0)
goto semoperr;
}
/*
* If we are only incrementing the semaphore value
* by one on a binary semaphore, we can cv_signal.
*/
else
}
continue;
}
/*
* Lower the semaphore (i.e. sema_p)
*/
if (i)
goto semoperr;
}
continue;
}
if (i)
goto semoperr;
}
/*
* Mark the semaphore set as not a binary type
* if we are decrementing the value by more than 1.
*
* V operations will resort to cv_broadcast
* for this set because there are too many weird
* cases that have to be caught.
*/
sp->sem_binary = 0;
if (!held) {
held = 1;
}
if (cvres > 0) /* normal wakeup */
goto check;
}
/* EINTR or EAGAIN overrides EIDRM */
if (cvres == 0)
else if (cvres < 0)
else
goto semoperr;
}
/*
* Wait for zero value
*/
if (i)
goto semoperr;
}
if (!held) {
held = 1;
}
/*
* Don't touch semp if the semaphores have been removed.
*/
if (cvres > 0) /* normal wakeup */
goto check;
}
/* EINTR or EAGAIN overrides EIDRM */
if (cvres == 0)
else if (cvres < 0)
else
goto semoperr;
}
}
/* All operations succeeded. Update sempid for accessed semaphores. */
;
if (held)
else
/* Before leaving, deallocate the buffer that held the user semops */
if (nsops != 1)
return (0);
/*
* Error return labels
*/
if (held)
else
/* Before leaving, deallocate the buffer that held the user semops */
if (nsops != 1)
}
/*
* semsys - System entry point for semctl, semget, and semop system calls.
*/
static int
{
int error;
switch (opcode) {
case SEMCTL:
break;
case SEMGET:
break;
case SEMOP:
break;
case SEMIDS:
break;
case SEMTIMEDOP:
(timespec_t *)a4);
break;
default:
break;
}
return (error);
}