/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "lint.h"
#include "thr_uberdata.h"
#include "libc.h"
#include <alloca.h>
#include <unistd.h>
#include <thread.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <door.h>
#include <signal.h>
#include <ucred.h>
#include <strings.h>
#include <ucontext.h>
#include <atomic.h>
/*
* Global state -- the non-statics are accessed from the __door_return()
* syscall wrapper.
*/
/*
* The raw system call interfaces
*/
size_t);
extern int __door_ucred(ucred_t *);
extern int __door_unref(void);
extern int __door_unbind(void);
/*
* Key for per-door data for doors created with door_xcreate.
*/
/*
* Each door_xcreate'd door has a struct privdoor_data allocated for it,
* and each of the initial pool of service threads for the door
* has TSD for the privdoor_key set to point to this structure.
* When a thread in door_return decides it is time to perform a
* thread depletion callback we can retrieve this door information
* via a TSD lookup on the privdoor key.
*/
struct privdoor_data {
int pd_dfd;
void *pd_crcookie;
};
/*
* door_create_cmn holds the privdoor data before kicking off server
* thread creation, all of which must succeed; if they don't then
* they return leaving the refcnt unchanged overall, and door_create_cmn
* releases its hold after revoking the door and we're done. Otherwise
* all n threads created add one each to the refcnt, and door_create_cmn
* drops its hold. If and when a server thread exits the key destructor
* function will be called, and we use that to decrement the reference
* count. We also decrement the reference count on door_unbind().
* If ever we get the reference count to 0 then we will free that data.
*/
static void
{
}
static void
{
}
void
{
}
/*
* We park the ourselves in the kernel to serve as the "caller" for
* unreferenced upcalls for this process. If the call returns with
* EINTR (e.g., someone did a forkall), we repeat as long as we're still
* in the parent. If the child creates an unref door it will create
* a new thread.
*/
static void *
{
/* mask signals before diving into the kernel */
(void) sigfillset(&fillset);
continue;
return (NULL);
}
static int
{
int d;
int do_create_first = 0;
int do_create_unref = 0;
return (-1);
}
if (crf)
flags |= DOOR_PRIVCREATE;
/*
* Doors are associated with the processes which created them. In
* the face of forkall(), this gets quite complicated. To simplify
* it somewhat, we include the call to __door_create() in a critical
* section, and figure out what additional actions to take while
* still in the critical section.
*/
return (-1); /* errno is set */
}
if (mypid != door_create_pid ||
do_create_first = 1;
}
do_create_unref = 1;
}
}
if (do_create_unref) {
/*
* Create an unref thread the first time we create an
* unref door for this process. Create it as a daemon
* thread, so that it doesn't interfere with normal exit
* processing.
*/
}
if (is_private) {
/*
* Create the first thread(s) for this private door.
*/
if (__door_info(d, &di) < 0)
return (-1); /* errno is set */
/*
* This key must be available for lookup for all private
* door threads, whether associated with a door created via
* door_create or door_xcreate.
*/
(void) pthread_key_create_once_np(&privdoor_key,
(*door_server_func)(&di);
} else {
(void) door_revoke(d);
return (-1);
}
(void) door_revoke(d);
return (-1);
} else {
}
}
} else if (do_create_first) {
/* First non-private door created in the process */
(*door_server_func)(NULL);
}
return (d);
}
int
{
return (-1);
}
}
int
{
return (-1);
}
}
int
{
ucp = _ucred_alloc();
return (-1);
}
if (__door_ucred(ucp) != 0) {
return (-1);
}
return (0);
}
int
{
/*
* Ucred size is small and alloca is fast
* and cannot fail.
*/
int ret;
}
return (ret);
}
int
door_unbind(void)
{
/*
* If we were indeed bound to the door then check to see whether
* we are part of a door_xcreate'd door by checking for our TSD.
* If so, then clear the TSD for this key to avoid destructor
* callback on future thread exit, and release the private door data.
*/
}
return (rv);
}
int
{
{
stack_t s;
if (thr_stksegment(&s) != 0) {
return (-1);
}
}
if (!self->ul_door_noreserve) {
/*
* When we return from the kernel, we must have enough stack
* available to handle the request. Since the creator of
* the thread has control over its stack size, and larger
* stacks generally indicate bigger request queues, we
* use the heuristic of reserving 1/32nd of the stack size
* (up to the default stack size), with a minimum of 1/8th
* of MINSTACK. Currently, this translates to:
*
* _ILP32 _LP64
* min resv 512 bytes 1024 bytes
* max resv 32k bytes 64k bytes
*
* This reservation can be disabled by setting
* _THREAD_DOOR_NORESERVE=1
* in the environment, but shouldn't be.
*/
else if (ssize < DEFAULTSTACK)
else
else
ssize = 0;
}
/*
* Historically, the __door_return() syscall wrapper subtracted
* some "slop" from the stack pointer before trapping into the
* kernel. We now do this here, so that ssize can be adjusted
* correctly. Eventually, this should be removed, since it is
* unnecessary. (note that TNF on x86 currently relies upon this
* idiocy)
*/
#if defined(__sparc)
#else
#endif
#ifdef _STACK_GROWS_DOWNWARD
#else
#endif
else
ssize = 0;
/*
* Normally, the above will leave plenty of space in sp for a
* request. Just in case some bozo overrides thr_stksegment() to
* return an uncommonly small stack size, we turn off stack size
* checking if there is less than 1k remaining.
*/
if (ssize < MIN_DOOR_STACK)
ssize = 0;
/*
* We have to wrap the desc_* arguments for the syscall. If there are
* no descriptors being returned, we can skip the wrapping.
*/
if (num_desc != 0) {
}
}
/*
* To start and synchronize a number of door service threads at once
* we use a struct door_xsync_shared shared by all threads, and
* a struct door_xsync for each thread. While each thread
* has its own startup state, all such state are protected by the same
* shared lock. This could cause a little contention but it is a one-off
* cost at door creation.
*/
enum door_xsync_state {
};
/* These stats are incremented non-atomically - indicative only */
DOOR_XSYNC_CREATEWAIT + 1];
struct door_xsync_shared {
};
struct door_xsync {
};
/*
* Thread start function that xcreated private doors must use in
* thr_create or pthread_create. They must also use the argument we
* provide. We:
*
* o call a thread setup function if supplied, or apply sensible defaults
* o bind the newly-created thread to the door it will service
* o synchronize with door_xcreate to indicate that we have successfully
* bound to the door; door_xcreate will not return until all
* requested threads have at least bound
* o enter service with door_return quoting magic sentinel args
*/
void *
{
goto handshake;
}
} else {
}
else
if (next_state != DOOR_XSYNC_BOUND) {
return (NULL); /* thread exits, key destructor called */
}
if (next_state == DOOR_XSYNC_ABORT)
return (NULL); /* thread exits, key destructor called */
return (NULL);
}
static int
{
int isdepcb = 0;
int failerrno;
int bound = 0;
#ifdef _STACK_GROWS_DOWNWARD
#else
#endif
int rv = 0;
/*
* If we're called during door creation then we have the
* privdoor_data. If we're called as part of a depletion callback
* then the current thread has the privdoor_data as TSD.
*/
isdepcb = 1;
thr_panic("door_xcreate_n - no privdoor_data "
"on existing server thread");
}
/*
* Allocate on our stack. We'll pass pointers to this to the
* newly-created threads, therefore this function must not return until
* we have synced with server threads that are created.
* We do not limit the number of threads so begin by checking
* that we have space on the stack for this.
*/
{
char dummy;
return (0);
}
}
return (0);
}
for (i = 0; failidx == -1 && i < n; i++) {
membar_producer(); /* xssp and xsp[i] for new thread */
case 1:
/*
* Thread successfully created. Set mailbox
* state and increment the number we have to
* sync with.
*/
break;
case 0:
/*
* Elected to create no further threads. OK for
* a depletion callback, but not during door_xcreate.
*/
if (!isdepcb) {
failidx = i;
}
break;
case -1:
/*
* Thread creation was attempted but failed.
*/
failidx = i;
break;
default:
/*
* The application-supplied function did not return
* -1/0/1 - best we can do is panic because anything
* else is harder to debug.
*/
thr_panic("door server create function illegal return");
/*NOTREACHED*/
}
}
/*
* On initial creation all must succeed; if not then abort
*/
for (i = 0; i < failidx; i++)
}
/*
* Wait for thread startup handshake to complete for all threads
*/
/*
* If we are aborting for a failed thread create in door_xcreate
* then we're done.
*/
rv = 0;
goto out; /* lock held, failerrno is set */
}
/*
* Did we all succeed in binding?
*/
for (i = 0; i < n; i++) {
bound++;
}
if (bound == n) {
rv = 1;
} else {
rv = 0;
}
/*
* During door_xcreate all must succeed in binding - if not then
* we command even those that did bind to abort. Threads that
* did not get as far as binding have already exited.
*/
for (i = 0; i < n; i++) {
}
}
out:
if (rv == 0)
return (rv);
}
/*
* Call the server creation function to give it the opportunity to
* create more threads. Called during a door invocation when we
* return from door_return(NULL,0, NULL, 0) and notice that we're
* running on the last available thread.
*/
void
{
/*
* Non-private doors always use door_server_func.
*/
(*door_server_func)(NULL);
return;
}
/*
* Private, door_xcreate'd door specified no callbacks.
*/
return;
/*
*/
(*door_server_func)(dip);
return;
} else {
/*
* Private, door_xcreate'd door.
*/
}
}
/*
* Install a new server creation function. The appointed function
* will receieve depletion callbacks for non-private doors and private
* doors created with door_create(..., DOOR_PRIVATE).
*/
{
return (prev);
}
/*
* Thread start function for door_create_server() below.
* Create door server threads with cancellation(5) disabled.
*/
static void *
{
return (arg);
}
/*
* The default door_server_func_t.
*/
/* ARGSUSED */
static void
{
yield(); /* Gives server thread a chance to run */
}