.\" Copyright (c) 2003 Matthew Dillon .\" 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/libcr/sys/Attic/upc_register.2,v 1.1 2003/12/08 13:56:36 eirikn 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" "(int)priority" .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 The priority will be compared against the current crit_count to determine whether the upcall dispatches or is made pending. .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" "(int)priority" .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. .Pp The priority will be compared against the upcall's pending priority. Only upcalls with greater or equal pending priorities are returned. You must specify a minimum priority of 1 or this call will simply return a random registered upcall that may or may not be 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 edx; int32_t eflags; int32_t origip; } .Ed .Pp On entry, %eax will hold the C function pointer, %ecx will hold the C data pointer, and %edx will hold a pointer to the user-supplied upcall structure. The context code does not need to push %eax, %ecx, or %edx because these registers have already been pushed on the stack for it, but it must generally push any remaining registers that it might use and be careful in regards to others, such as floating point registers, which the OS has not saved. 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 %edx,%eflags %original_ip] */ callused_wrapper: pushl %edx /* save %edx (upcall pointer) */ pushl %ecx /* func=%eax(data=%ecx) */ call *%eax /* call the C function */ addl $4,%esp popl %edx /* restore the upcall pointer */ incl PENDING(%edx) /* setting pending stops upcalls */ subl $32,CRIT_COUNT(%edx) /* cleanup crit section count */ 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 */ popl %edx /* 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 .