File:  [DragonFly] / src / lib / libc / sys / upc_register.2
Revision 1.1: download - view: text, annotated - select for diffs
Fri Nov 21 06:41:02 2003 UTC (11 years, 1 month ago) by dillon
Branches: MAIN
CVS tags: HEAD
Add a big whopping manual page for the upcall syscalls, upc_register and
friends.

.\" Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\"    notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\"    notice, this list of conditions and the following disclaimer in the
.\"    documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $DragonFly: src/lib/libc/sys/upc_register.2,v 1.1 2003/11/21 06:41:02 dillon Exp $
.\"
.Dd November 20, 2003
.Dt UPC_REGISTER 2
.Os
.Sh NAME
.Nm upc_register ,
.Nm upc_control
.Nd configure and control upcalls
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In sys/upcall.h
.Ft int
.Fn upc_register "struct upcall *upc" "upcall_func_t ctxfunc" "upcall_func_t cfunc" "void *data"
.Ft int
.Fn upc_control "int command" "int upcall_id" "void *data"
.Sh DESCRIPTION
.Fn Upc_register
registers an upcall.  Note that the specified upcall structural pointer
is per-process, not per-upcall.  It points to a structure in user memory
that both the user and kernel manipulate to deal with upcall/critical-section
interlocks.
.Fa ctxfunc
is a pointer to context save and restore code.  The native upcall interface
does not necessarily save and restore call-used registers.  This function
is typically written in assembly and supplied by
.Lb libc .
.Fa cfunc
is a pointer to a C style function and
.Fa data
is the data passed to it as an argument.  A positive upcall identifier
will be returned or -1 if an error occured.
.Pp
When an upcall is executed the
kernel will add LWKT_CRITPRI to the critical section count in the upcall
structure, push a minimal context (not even call-used), and pass the C
function and data pointers to
.Fa ctxfunc
in registers.
.Fa ctxfunc 
will then proceed to save appropriate state, call the C function, and 
deal with cleanup.  Cleanup typically involves an interlocking operation
and a call to
.Fn Upc_control
in order to undo the critical section count and process any additional
pending upcalls atomically.
.Fa ctxfunc
will typically pop most of its state, set upcall->pending to 1,
subtract LWKT_CRITPRI from upcall->crit_count, and call  
.Fn upc_control "UPC_CONTROL_NEXT" "-1" "stack_pointer"
which atomically handles any further pending upcalls and/or pops the
original stack context supplied to
.Fn upc_control
and resumes the originally interrupted code.  This is all very complex
which is why
.Lb libc
typically supplies
.Fa ctxfunc .
.Pp
Note that upcalls can only occur if the target process is not in a critical
section.  If an upcall cannot be dispatched it will instead be made pending
and the pending field in the user-supplied upcall structure will be set to
non-zero.  Userland critical-section-exiting code must check the pending
bit and call the appropriate
.Fn upc_control
function to handle any pending upcalls.
.Pp
The upcall identifier space is shared amoungst all processes sharing the
same VM space.  That is, all the processes created through clone() or
rfork(RFMEM).  Through appropriate
.Fn upc_control
calls any process within this domain can generate an upcall on any other
process within this domain, including itself.  Each process typically 
installs a different (per-process) upcall data structure.
.Pp
.Fn Upc_control
is a multi-function system call capable of dispatching upcalls, handling
the context assembly function's interlocks, deleting upcalls, and polling
for upcalls.
.Pp
.Ft int
.Fn upc_control "UPC_CONTROL_DISPATCH" "upcid" "NULL"
.Pp
.Bd -offset indent
Dispatch a particular upcall.  0 is returned on success, ENOENT if
.Fa upcid
does not exist.  You can dispatch upcalls belonging to your process or
to another process.  You may specify a
.Fa upcid
of -1 to re-dispatch the first upcall owned by your own process that is
pending from a previous operation.  Note that that critical section and
pending rules apply, and an actual dispatch will pushdown the stack.
.Pp
This command is most often used to alert a target process to a change in
a shared structure, queue, etc.
.Ed
.Pp
.Ft int
.Fn upc_control "UPC_CONTROL_NEXT" "-1" "stack_pointer"
.Pp
.Bd -offset indent
Do interlocking and stack munging to process additional upcalls.  This
system call should never be made directly by C code because it expects
all registers not saved by the operating system in entering a context
function to have been popped and a pointer to the base of the OS-supplied
stack context on entry to the context routine to be supplied.  This routine
does not return to the caller but instead either regenerates the stack
context for the next pending upcall or it restores the original context,
resuming whomever was interrupted by the original upcall that entered the
context routine.
.Ed
.Pp
.Ft int
.Fn upc_control "UPC_CONTROL_DELETE" "upcid" "NULL"
.Pp
.Bd -offset indent
Delete the specified
.Fa upcid
or, if -1 is specified, delete all upcall registered by the current process.
If -1 is specified, the upcalls registered by another process will not be
deleted.  If a particular
.Fa upcid
is specified, it will be deleted regardless of which process registered it.
The upcall structural pointer registered with this or any other process is 
not effected.
.Ed
.Pp
.Ft int
.Fn upc_control "UPC_CONTROL_POLL" "upcid" "NULL"
.Pp
.Ft int
.Fn upc_control "UPC_CONTROL_POLLANDCLEAR" "upcid" "NULL"
.Pp
.Bd -offset indent
Poll or poll-and-clear the pending status for a particular upcall.  0 or 1
is returned, or -1 if an error occured (e.g. ENOENT).  
If a
.Fa upcid
of -1 is specified, locate a pending upcall for the current process and return
it's
.Fa upcid ,
or 0 if no upcalls for the current process are pending.
.Ed
.Pp
.Bd -literal -offset indent -compact
struct upcall {
	int	magic;		/* must be UPCALL_MAGIC */
	int	crit_count;	/* critical section count */
	int	pending;	/* additional upcalls are pending */
};
.Ed
.Pp
This is a user space structure a pointer to which is registered with the 
kernel via
.Fn upc_register
\.
The
.Fa crit_count
field prevents new upcalls from being dispatched.  When an upcall is 
dispatched the kernel automatically adds
.Va UPC_CRITADD
to 
.Fa crit_count
and sets
.Fa pending
to indicate whether any additional upcalls are pending.  A non-zero
.Fa pending
OR
.Fa crit_count
will prevent new upcalls from the being dispatched.  The context function
code is expected to make appropriate checks to dispatch any remaining upcalls
when the current upcall has completed.  In particular, the context function
must subtract
.Va UPC_CRITADD
from 
.Fa crit_count
before restoring the original context or calling
.Fn upc_control "UPC_CONTROL_NEXT" "..."
\.
Note that
.Fa pending
may be set as a side effect to various
.Fn upc_control
system calls as well as as a side effect to upcall dispatches.
.Pp
Userland threading code typically uses
.Fa crit_count
to control critical sections within a virtual cpu (aka cloned process).
Entering a critical section is as simply as add UPC_CRITADD to
.Fa crit_count .
No atomic or locked instructions are required as this field is accessed
only by the current process and any upcalls or interrupts will restore it
to the condition they found it before returning.  Exiting a critical section
is almost as simple as subtracting UPC_CRITADD from
.Fa crit_count .
The routine which performs this function must also check the
.Fa pending
field once the critical section count has reached 0.  If the pending field
is non-zero, the routine will generally call
.Fn upc_control "UPC_CONTROL_DISPATCH" "-1" "NULL"
to dispatch upcalls which were made pending while you were in the critical
section.
.Sh CONTEXT FUNCTION - IA32
The context function is called with the stack pointer pointing at a 
kernel-constructed stack frame.  Only a minimal number of registers are
saved by the kernel. 
.Pp
.Bd -literal -offset indent -compact
frame {
    int32_t	eax;
    int32_t	ecx;
    int32_t	eflags;
    int32_t	origip;
}
.Ed
.Pp
One entry,  %eax will hold the C function pointer and %ecx will hold the
C data pointer.  The context code does not need to push %eax and %ecx
because these are saved for it, but it must generally push at least
the remaining call-used registers (%edx).  The operating system has already
adjusted the
.Fa crit_count
and
.Fa pending
fields in the user-supplied
.Fa upcall
structure, so the context code will generally next push the data pointer
(%ecx) and call the C function through %eax.  Upon return the context code
is responsible for interlocking the upcall return which it does by first
setting
.Fa pending
to 1, then subtracting
.Va UPC_CRITADD
from
.Fa crit_count ,
then restoring its part of the context but leaving the OS context intact,
then calling
.Fn upc_control "UPC_CONTROL_NEXT" "-1" "stack_pointer_to_OS_context"
\.
The control function will not return.  It will either restart the context
at the next upcall, if more are pending, or it will restore the original
context.
.Pp
The context code does not have to follow this regimn.  There is nothing
preventing the context code from restoring the original frame itself and
returning directly to the originally interrupted user code without having
to make another kernel transition.  It is possible to optimize this by
having the context code subtract down
.Va UPC_CRITADD
as per normal but not pre-set the
.Fa pending
field.  If it does this and
.Fa pending 
is 0, it is possible for the kernel to initiate another upcall before
the context code has had a chance to pop its stack and restore the original
user context.  This is ok under controlled circumstances.  On the otherhand,
if
.Fa pending 
is 1
the context code knows there is another upcall pending and can call
.Fn upc_control
as appropriate.
.Pp
.Bd -literal -offset indent -compact
	/*
	 * upc is a global pointing to this process's upcall structure
	 * (just as an example).  The Os-supplied stack frame is:
	 *
	 *	[%eax %ecx %eflags %original_ip]
	 */
callused_wrapper:
	pushl   %edx            /* save %edx */
	pushl   %ecx            /* func=%eax(data=%ecx) */
	call    *%eax		/* call the C function */
	addl    $4,%esp
	incl    upc+PENDING	/* setting pending stops upcalls */
	subl    $32,upc+CRIT_COUNT /* cleanup crit section count */
	popl    %edx		/* restore previously saved %edx */
	pushl   %esp            /* sp pointing to os user frame */
	pushl   $-1             /* upcid */
	pushl   $2              /* FETCH next */
	call    upc_control
	/* not reached */
	/* just for show, restore Os supplied user context */
	popl    %eax		/* code just for show */
	popl    %ecx		/* code just for show */
	popfl			/* code just for show */
	ret			/* code just for show */
.Ed

.Sh ERRORS
.Fn Upc_register
returns
.It Bq Er EFBIG
if the kernel has reached its upcall registration limit.  The limit is on a
per-shared-vmspace basis and is no less then 32.  Otherwise this function
returns a non-zero, positive number indicating the upcall identifier that
was registered.
.Pp
.Fh Upc_control
returns
.It Bq Er ENOENT
if a particular requested
.Fa upcid
cannot be found.
.Sh SEE ALSO
.Xr clone 3
.Sh HISTORY
The
.Fn upc_register
and 
.Fn upc_control
function calls
appeared in
DragonFly 1.0 .