2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * plock - lock "segments" in physical memory.
2N/A *
2N/A * Supports SVID-compatible plock, taking into account dynamically linked
2N/A * objects (such as shared libraries).
2N/A */
2N/A
2N/A#include "lint.h"
2N/A#include <mtlib.h>
2N/A#include <sys/types.h>
2N/A#include <sys/mman.h>
2N/A#include <sys/lock.h>
2N/A#include <errno.h>
2N/A#include <stddef.h>
2N/A#include <unistd.h>
2N/A#include <thread.h>
2N/A#include <synch.h>
2N/A
2N/A/*
2N/A * Module-scope variables.
2N/A */
2N/Astatic int lock_state = 0; /* lock state */
2N/Astatic pid_t state_pid = -1; /* pid to which state belongs */
2N/Astatic mutex_t plock_lock = DEFAULTMUTEX;
2N/A
2N/A/*
2N/A * plock
2N/A */
2N/Aint
2N/Aplock(int op) /* desired operation */
2N/A{
2N/A int e; /* return value */
2N/A pid_t pid; /* current pid */
2N/A
2N/A lmutex_lock(&plock_lock);
2N/A
2N/A /*
2N/A * Validate state of lock's. If parent has forked, then
2N/A * the lock state needs to be reset (children do not inherit
2N/A * memory locks, and thus do not inherit their state).
2N/A */
2N/A if ((pid = getpid()) != state_pid) {
2N/A lock_state = 0;
2N/A state_pid = pid;
2N/A }
2N/A
2N/A /*
2N/A * Dispatch on operation. Note: plock and its relatives depend
2N/A * upon "op" being bit encoded.
2N/A */
2N/A switch (op) {
2N/A
2N/A /*
2N/A * UNLOCK: remove all memory locks. Requires that some be set!
2N/A */
2N/A case UNLOCK:
2N/A if (lock_state == 0) {
2N/A errno = EINVAL;
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A }
2N/A e = munlockall();
2N/A if (e) {
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A } else {
2N/A lock_state = 0;
2N/A lmutex_unlock(&plock_lock);
2N/A return (0);
2N/A }
2N/A /*NOTREACHED*/
2N/A
2N/A /*
2N/A * TXTLOCK: locks text segments.
2N/A */
2N/A case TXTLOCK:
2N/A
2N/A /*
2N/A * If a text or process lock is already set, then fail.
2N/A */
2N/A if ((lock_state & TXTLOCK) || (lock_state & PROCLOCK)) {
2N/A errno = EINVAL;
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Try to apply the lock(s). If a failure occurs,
2N/A * memcntl backs them out automatically.
2N/A */
2N/A e = memcntl(NULL, 0, MC_LOCKAS, (caddr_t)MCL_CURRENT,
2N/A PROC_TEXT|PRIVATE, (int)NULL);
2N/A if (!e)
2N/A lock_state |= TXTLOCK;
2N/A lmutex_unlock(&plock_lock);
2N/A return (e);
2N/A /*NOTREACHED*/
2N/A
2N/A /*
2N/A * DATLOCK: locks data segment(s), including the stack and all
2N/A * future growth in the address space.
2N/A */
2N/A case DATLOCK:
2N/A
2N/A /*
2N/A * If a data or process lock is already set, then fail.
2N/A */
2N/A if ((lock_state & DATLOCK) || (lock_state & PROCLOCK)) {
2N/A errno = EINVAL;
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Try to lock the data and stack segments. On failure
2N/A * memcntl undoes the locks internally.
2N/A */
2N/A e = memcntl(NULL, 0, MC_LOCKAS, (caddr_t)MCL_CURRENT,
2N/A PROC_DATA|PRIVATE, (int)NULL);
2N/A if (e) {
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A /* try to set a lock for all future mappings. */
2N/A e = mlockall(MCL_FUTURE);
2N/A
2N/A /*
2N/A * If failures have occurred, back out the locks
2N/A * and return failure.
2N/A */
2N/A if (e) {
2N/A e = errno;
2N/A (void) memcntl(NULL, 0, MC_UNLOCKAS,
2N/A (caddr_t)MCL_CURRENT, PROC_DATA|PRIVATE, (int)NULL);
2N/A errno = e;
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Data, stack, and growth have been locked. Set state
2N/A * and return success.
2N/A */
2N/A lock_state |= DATLOCK;
2N/A lmutex_unlock(&plock_lock);
2N/A return (0);
2N/A /*NOTREACHED*/
2N/A
2N/A /*
2N/A * PROCLOCK: lock everything, and all future things as well.
2N/A * There should be nothing locked when this is called.
2N/A */
2N/A case PROCLOCK:
2N/A if (lock_state) {
2N/A errno = EINVAL;
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A }
2N/A if (mlockall(MCL_CURRENT | MCL_FUTURE) == 0) {
2N/A lock_state |= PROCLOCK;
2N/A lmutex_unlock(&plock_lock);
2N/A return (0);
2N/A } else {
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A }
2N/A /*NOTREACHED*/
2N/A
2N/A /*
2N/A * Invalid operation.
2N/A */
2N/A default:
2N/A errno = EINVAL;
2N/A lmutex_unlock(&plock_lock);
2N/A return (-1);
2N/A /*NOTREACHED*/
2N/A }
2N/A}