UPC_REGISTER(2) BSD System Calls Manual UPC_REGISTER(2)NAME
upc_register, upc_control — configure and control upcalls
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include <sys/upcall.h>
int
upc_register(struct upcall *upc, upcall_func_t ctxfunc,
upcall_func_t cfunc, void *data);
int
upc_control(int command, int upcall_id, void *data);
DESCRIPTION
The upc_register() function 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. 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 libc. cfunc is a pointer to a C style func‐
tion and data is the data passed to it as an argument. A positive upcall
identifier will be returned or -1 if an error occurred.
When an upcall is executed the kernel will add TDPRI_CRIT 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 ctxfunc in reg‐
isters. ctxfunc will then proceed to save appropriate state, call the C
function, and deal with cleanup. Cleanup typically involves an inter‐
locking operation and a call to upc_control() in order to undo the criti‐
cal section count and process any additional pending upcalls atomically.
ctxfunc will typically pop most of its state, set upcall->pending to 1,
subtract TDPRI_CRIT from upcall->crit_count, and call
upc_control(UPC_CONTROL_NEXT, -1, stack_pointer) which atomically handles
any further pending upcalls and/or pops the original stack context sup‐
plied to upc_control() and resumes the originally interrupted code. This
is all very complex which is why libc typically supplies ctxfunc.
Note that upcalls can only occur if the target process is not in a criti‐
cal 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 upc_control() function to handle
any pending upcalls.
The upcall identifier space is shared amongst all processes sharing the
same VM space. That is, all the processes created through clone() or
rfork(RFMEM). Through appropriate 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.
The upc_control() function, is a multi-function system call capable of
dispatching upcalls, handling the context assembly function's interlocks,
deleting upcalls, and polling for upcalls.
int upc_control(UPC_CONTROL_DISPATCH, upcid, (int)priority)
Dispatch a particular upcall.
The value
0
is returned on success,
ENOENT
if
upcid
does not exist.
You can dispatch upcalls belonging to your process or
to another process.
You may specify a
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.
The priority will be compared against the current crit_count to determine
whether the upcall dispatches or is made pending.
This command is most often used to alert a target process to a change in
a shared structure, queue, etc.
int upc_control(UPC_CONTROL_NEXT, -1, stack_pointer)
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.
int upc_control(UPC_CONTROL_DELETE, upcid, NULL)
Delete the specified
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
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.
int upc_control(UPC_CONTROL_POLL, upcid, NULL)
int upc_control(UPC_CONTROL_POLLANDCLEAR, upcid, (int)priority)
Poll or poll-and-clear the pending status for a particular upcall.
The value
0
or
1
is returned, or
-1
if an error occurred (e.g.
ENOENT ).
If a
upcid
of -1 is specified, locate a pending upcall for the current process and return
it's
upcid,
or 0 if no upcalls for the current process are pending.
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.
struct upcall {
int magic; /* must be UPCALL_MAGIC */
int crit_count; /* critical section count */
int pending; /* additional upcalls are pending */
};
This is a user space structure a pointer to which is registered with the
kernel via upc_register() The crit_count field prevents new upcalls from
being dispatched. When an upcall is dispatched the kernel automatically
adds UPC_CRITADD to crit_count and sets pending to indicate whether any
additional upcalls are pending. A non-zero pending OR 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 func‐
tion must subtract UPC_CRITADD from crit_count before restoring the orig‐
inal context or calling upc_control(UPC_CONTROL_NEXT, ...) Note that
pending may be set as a side effect to various upc_control() system calls
as well as as a side effect to upcall dispatches.
Userland threading code typically uses crit_count to control critical
sections within a virtual CPU (i.e., cloned process). Entering a criti‐
cal section is as simply as add UPC_CRITADD to 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 con‐
dition they found it before returning. Exiting a critical section is
almost as simple as subtracting UPC_CRITADD from crit_count. The routine
which performs this function must also check the pending field once the
critical section count has reached 0. If the pending field is non-zero,
the routine will generally call upc_control(UPC_CONTROL_DISPATCH, -1,
NULL) to dispatch upcalls which were made pending while you were in the
critical section.
CONTEXT FUNCTION - IA32
The context function is called with the stack pointer pointing at a ker‐
nel-constructed stack frame. Only a minimal number of registers are
saved by the kernel.
frame {
int32_t eax;
int32_t ecx;
int32_t edx;
int32_t eflags;
int32_t origip;
}
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
crit_count and pending fields in the user-supplied 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 pending to 1, then subtracting UPC_CRITADD from crit_count, then
restoring its part of the context but leaving the OS context intact, then
calling 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.
The context code does not have to follow this regime. 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 UPC_CRITADD as per normal but not
pre-set the pending field. If it does this and pending is 0, it is pos‐
sible 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 other hand, if pending
is 1 the context code knows there is another upcall pending and can call
upc_control() as appropriate.
/*
* 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 */
ERRORS
The upc_register() function returns:
[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 iden‐
tifier that was registered.
The upc_control() function returns
[ENOENT] if a particular requested upcid cannot be found.
SEE ALSOrfork(2), clone(3)HISTORY
The upc_register() and upc_control() function calls appeared in
DragonFly 1.0.
BSD November 20, 2003 BSD