[XXX: Much is out of date and inaccurate in the following.] The nfs client is the most important user of the rpc client code, so we'll use it as the canonical example of an rpc user below. All of the relevant files are contained in the directories net/sunrpc/ and include/linux/sunrpc/. The major data structures of the rpc client code include: * rpc_client: all the data required to maintain a single stream of rpc requests with a single rpc server. An NFS server, for example, creates a client for each new mountpoint. * rpc_program: describes a single rpc program, including an array of rpc_versions, which include arrays of rpc_procinfo structures describing the rpc procedures available: * rpc_procinfo: the most important fields of this structure are the function pointers p_encode and p_decode; p_encode takes the C structure provided as an argument to an rpc procedure (below) into an xdr-encoded stream of data to be sent on the network, and p_decode translates the xdr-encoded data returned from the server into a C structure containing the results. * rpc_message: all the information that users of the rpc code need to pass to the rpc layer to make the call, including structures to hold the arguments, results, an rpc credential (defaulting to a cred for the current process if null), and a pointer to the relevant rpc_procinfo struct. The simplest way for a user of the rpc code to make an rpc call is to use rpc_call_sync, which is passed an rpc_client and an rpc_message, and returns on success with the argument field of the rpc_message filled in. On failure the return value is an error. Calls can also be made asynchronously, by rpc_call_async, which returns without waiting for the call to complete, leaving the rest of the processing to workqueues. The rpc_call_ops argument provides callbacks for handling results: - rpc_call_prepare(task, calldata): - called iff rpc_call_async() succeeds; so when it is called, initial creation of rpc task has succeeded, but nothing else has been done. Always called before rpc_call_async returns (though unclear if this is by design). - rpc_call_done(task, calldata): - called as the last action on exiting. Not necessarily called if the task is killed. May prevent the task from exiting by setting a new action (see e.g. rpc_restart_call() calls within rpc_call_done() callbacks). - rpc_release(calldata): - called on task destruction to free anything necessary in calldata. Will always be called, whether rpc_call_async() succeeds or not. where 'calldata' is a void * (opaque to the rpc subsystem) provided as an argument to rpc_call_async(). Note actually rpc_call-async() is a small wrapper around rpc_run_task(). [ XXX Examples of use of rpc_call_async here that demonstrate the need for such. ] To allow unified handling of both synchronous and asynchronous calls, the rpc code has its own notion of a task, described the by "struct rpc_task", which generalizes "real" kernel tasks, which can be put to sleep and made to wait on events using standard kernel mechanisms, and asynchronous rpc tasks, which are handled by the rpc code's own scheduler. Functions provided by the scheduler include rpc_sleep_on(rpc_waitq, rpc_task, callback, timer): note that the (optional) callback will be executed on waking, whatever the reason for the wake-up. rpc_wake_up(rpc_waitq) rpc_delay(rpc_task, delay): delay for "delay" jiffies There are some restrictions on any code which might be called by rpciod (e.g., callbacks passed to rpc_call_async). Such code should not sleep for too long (since doing so will delay other work by the rpc client), and should be wary of any resource allocation which may need to deadlocks between itself and the caller waiting on rpc call. (In particular, calling kmalloc with GFP_KERNEL should be avoided). The process of making an rpc call and waiting for the response is described by a simple state machine implemented in clnt.c. An rpc_task is used to keep track of the current state, and each state of the state machine is described by a single function. The functions rpc_call_sync and rpc_call_async both call rpc_call_setup, which sets the task->tk_action pointer to call_start(), the first state in the state machine. Each call_ function which implements a state takes the rpc_task as a parameter, and sets task->tk_action to a pointer to the next state. A diagram of the clnt.c state machine follows: call_start: just does some statistics | V +-> call_reserve: | ^ if cred not uptodate ---> call_refresh: | | otherwise get request ^ | | | slot (xprt_reserve()) | V | | | ^----------- call_refreshresult: | | V | call_reserveresult: | | | V +- call_allocate: allocate task->tk_buffer | V call_bind: ---> call_connect: may call rpc_getport() ^ xprt_connect() which uses child task to | | do pmap request ^ | V | +------- call_connect_status: V | call_transmit: <------------------------+ call_encode() xprt_transmit() | V call_status: [ XXX Unfinished. ] There are three other callback pointers in the task struct besides the tk_action pointer which clnt.c uses to implement the state machine above. They are tk_callback, tk_exit, and tk_release [ XXX called when? May want a separate diagram just for sched.c's little state machine in __rpc_execute. ] The clnt.c file also contains the functions which manage rpc clients, defined by the struct rpc_clnt. A client encapsulates all the data needed to keep track of one client relationship with a server. So, for example, every nfs mount corresponds to a single rpc client. XXX: Random note: It is often useful to note that rpc_call_async calls the provided callback iff it returns 0. Pf: The callback is stored in tk_exit. The only exit from the rpc state machine is via the end of __rpc_execute() (proof?), which calls task->tk_exit unconditionally. It is easy to verify that rpc_call_async calls rpc_exit iff it returns 0. QED