/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
/* Copyright (c) 1990 Mentat Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Kernel RPC filtering module
*/
#include <sys/sysmacros.h>
#include <rpc/rpc_rdma.h>
/*
* This is the loadable module wrapper.
*/
"rpcmod",
&rpcinfo,
};
/*
* Module linkage information for the kernel.
*/
};
/*
* For the RPC system call.
*/
2,
};
"RPC syscall",
};
#ifdef _SYSCALL32_IMPL
"32-bit RPC syscall",
};
#endif /* _SYSCALL32_IMPL */
{
&modlsys,
#ifdef _SYSCALL32_IMPL
#endif
}
};
int
_init(void)
{
int error = 0;
int status;
svc_init();
clnt_init();
/*
* Could not install module, cleanup previous
* initialization work.
*/
clnt_fini();
(void) callb_delete(cid);
return (error);
}
/*
* Load up the RDMA plugins and initialize the stats. Even if the
* plugins loadup fails, but rpcmod was successfully installed the
* counters still get initialized.
*/
/*
* Get our identification into ldi. This is used for loading
* other modules, e.g. rpcib.
*/
if (status != 0) {
}
return (error);
}
/*
* The unload entry point fails, because we advertise entry points into
* rpcmod from the rest of kRPC: rpcmod_release().
*/
int
_fini(void)
{
return (EBUSY);
}
int
{
}
extern int nulldev();
/*
* To save instructions, since STREAMS ignores the return value
* from these functions, they are defined as void here. Kind of icky, but...
*/
void rpcmodrsrv();
void rpcmodwsrv(queue_t *);
(int (*)())rmm_rput,
(int (*)())rmm_rsrv,
};
/*
* The write put procedure is simply putnext to conserve stack space.
* The write service procedure is not used to queue data, but instead to
* synchronize with flow control.
*/
(int (*)())rmm_wput,
(int (*)())rmm_wsrv,
};
struct xprt_style_ops {
int (*xo_open)();
int (*xo_close)();
void (*xo_wput)();
void (*xo_wsrv)();
void (*xo_rput)();
void (*xo_rsrv)();
};
/*
* Read side has no service procedure.
*/
};
};
/*
* Per rpcmod "slot" data structure. q->q_ptr points to one of these.
*/
struct rpcm {
};
struct temp_slot {
void *cell;
int type;
};
typedef struct mir_s {
/* must be first in the structure. */
/*
* mir_head_mp points the first mblk being collected in
* the current RPC message. Record headers are removed
* before data is linked into mir_head_mp.
*/
/*
* mir_tail_mp points to the last mblk in the message
* chain starting at mir_head_mp. It is only valid
* if mir_head_mp is non-NULL and is used to add new
* data blocks to the end of chain quickly.
*/
/*
* mir_frag_len starts at -4 for beginning of each fragment.
* When this length is negative, it indicates the number of
* bytes that rpcmod needs to complete the record marker
* header. When it is positive or zero, it holds the number
* of bytes that have arrived for the current fragment and
* are held in mir_header_mp.
*/
/*
* Fragment header as collected for the current fragment.
* It holds the last-fragment indicator and the number
* of bytes in the fragment.
*/
unsigned int
/* side until outbound flow control */
/* is relieved. */
/*
* On client streams, mir_clntreq is 0 or 1; it is set
* to 1 whenever a new request is sent out (mir_wput)
* and cleared when the timer fires (mir_timer). If
* the timer fires with this value equal to 0, then the
* stream is considered idle and kRPC is notified.
*/
/*
* On server streams, stop accepting messages
*/
/*
* This value is copied from clnt_idle_timeout or
* svc_idle_timeout during the appropriate ioctl.
* Kept in milliseconds
*/
/*
* This value is set to lbolt
* every time a client stream sends or receives data.
* Even if the timer message arrives, we don't shutdown
* client unless:
* lbolt >= MSEC_TO_TICK(mir_idle_timeout)+mir_use_timestamp.
* This value is kept in HZ.
*/
/*
* This pointer is set to &clnt_max_msg_size or
* &svc_max_msg_size during the appropriate ioctl.
*/
/* Server-side fields. */
/* counts the number of references */
/* that a kernel RPC server thread */
/* (see svc_run()) has on this rpcmod */
/* slot. Effectively, it is the */
/* number of unprocessed messages */
/* that have been passed up to the */
/* kRPC layer */
/* T_DISCON_IND */
/*
* these fields are for both client and server, but for debugging,
* it is easier to have these last in the structure.
*/
} mir_t;
NULL,
NULL,
NULL,
};
void
{
case M_PCPROTO:
case T_INFO_ACK:
mutex_enter(&t->lock);
mutex_exit(&t->lock);
return;
default:
break;
}
default:
break;
}
/*
* Not an info-ack, so free it. This is ok because we should
* not be receiving data until the open finishes: rpcmod
* is pushed well before the end-point is bound to an address.
*/
}
int
{
int error = 0;
/*
* Check for re-opens.
*/
if (q->q_ptr) {
"rpcmodopen_end:(%s)", "q->qptr");
return (0);
}
t = &ts;
bzero(t, sizeof (*t));
q->q_ptr = (void *)t;
/*
* Allocate the required messages upfront.
*/
return (ENOBUFS);
}
qprocson(q);
mutex_enter(&t->lock);
break;
}
}
mutex_exit(&t->lock);
if (error)
goto out;
} else {
}
out:
if (error)
qprocsoff(q);
mutex_destroy(&t->lock);
cv_destroy(&t->wait);
return (error);
}
void
{
}
void
{
}
void
{
}
void
{
}
int
{
}
/*
* rpcmodopen - open routine gets called when the module gets pushed
* onto the stream.
*/
/*ARGSUSED*/
int
{
/*
* Initialize entry points to release a rpcmod slot (and an input
* message if supplied) and to send an output message to the module
* below rpcmod.
*/
/*
* Only sufficiently privileged users can use this module, and it
* is assumed that they will use this module properly, and NOT send
* bulk data from downstream.
*/
if (secpolicy_rpcmod_open(crp) != 0)
return (EPERM);
/*
* Allocate slot data structure.
*/
/*
* slot type will be set by kRPC client and server ioctl's
*/
return (0);
}
/*
* rpcmodclose - This routine gets called when the module gets popped
* off of the stream.
*/
/*ARGSUSED*/
int
{
/*
* Mark our state as closing.
*/
/*
* Check and see if there are any messages on the queue. If so, send
* the messages, regardless whether the downstream module is ready to
* accept data.
*/
/*
* call into SVC to clean the queue
*/
svc_queueclean(q);
/*
* Block while there are kRPC threads with a reference
* to this message.
*/
}
/*
* It is now safe to remove this queue from the stream. No kRPC
* threads have a reference to the stream, and none ever will,
* because RM_CLOSING is set.
*/
qprocsoff(q);
/* Notify kRPC that this stream is going away. */
svc_queueclose(q);
} else {
qprocsoff(q);
}
return (0);
}
/*
* rpcmodrput - Module read put procedure. This is called from
* the module, driver, or stream head downstream.
*/
void
{
int hdrsz;
return;
}
default:
break;
case M_PROTO:
case M_PCPROTO:
/*
* Forward this message to kRPC if it is data.
*/
/*
* Check if the module is being popped.
*/
break;
}
case RPC_CLIENT:
/*
* Make sure the header is sane.
*/
if (hdrsz < TUNITDATAINDSZ ||
return;
}
/*
* Call clnt_clts_dispatch_notify, so that it
* can pass the message to the proper caller.
* Don't discard the header just yet since the
* client may need the sender's address.
*/
return;
case RPC_SERVER:
/*
* rm_krpc_cell is exclusively used by the kRPC
* CLTS server. Try to submit the message to
* kRPC. Since this is an unreliable channel, we
* can just free the message in case the kRPC
* does not accept new messages.
*/
if (rmp->rm_krpc_cell &&
/*
* Raise the reference count on this
* module to prevent it from being
* popped before kRPC generates the
* reply.
*/
} else {
}
return;
default:
return;
} /* end switch(rmp->rm_type) */
/*
* Make sure the header is sane
*/
if (hdrsz < TUDERRORINDSZ ||
return;
}
/*
* In the case where a unit data error has been
* received, all we need to do is clear the message from
* the queue.
*/
"%ld\n", gethrestime_sec());
return;
} /* end else if (pptr->type == T_UDERROR_IND) */
break;
} /* end switch (mp->b_datap->db_type) */
"rpcmodrput_end:");
/*
* Return codes are not looked at by the STREAMS framework.
*/
}
/*
* write put procedure
*/
void
{
case M_PROTO:
case M_PCPROTO:
break;
default:
rpcmodwput_other(q, mp);
return;
}
/*
* Check to see if we can send the message downstream.
*/
if (canputnext(q)) {
return;
}
/*
* The first canputnext failed. Try again except this time with the
* lock held, so that we can check the state of the stream to see if
* it is closing. If either of these conditions evaluate to true
* then send the meesage.
*/
} else {
/*
* canputnext failed again and the stream is not closing.
* Place the message on the queue and let the service
* procedure handle the message.
*/
}
}
static void
{
case M_IOCTL:
case RPC_CLIENT:
case RPC_SERVER:
return;
default:
/*
* pass the ioctl downstream and hope someone
* down there knows how to handle it.
*/
return;
}
default:
break;
}
/*
* This is something we definitely do not know how to handle, just
* pass the message downstream
*/
}
/*
* Module write service procedure. This is called by downstream modules
* for back enabling during flow control.
*/
void
{
/*
* Get messages that may be queued and send them down stream
*/
/*
* Optimize the service procedure for the server-side, by
* avoiding a call to canputnext().
*/
continue;
}
return;
}
}
/* ARGSUSED */
static void
{
/*
* For now, just free the message.
*/
if (bp)
}
}
/*
* This part of rpcmod is pushed on a connection-oriented transport for use
* by RPC. It serves to bypass the Stream head, implements
* the record marking protocol, and dispatches incoming RPC messages.
*/
/* Default idle timer values */
(mir_ptr)->mir_inrservice = 0; \
(mir_ptr)->mir_closing) \
}
/*
* Don't block service procedure (and mir_close) if
* we are in the process of closing.
*/
static void mir_clnt_idle_do_stop(queue_t *);
static void mir_timer(void *);
extern void (*clnt_stop_idle)(queue_t *);
/*
* Timeout for subsequent notifications of idle connection. This is
* typically used to clean up after a wedged orderly release.
*/
extern uint_t *clnt_max_msg_sizep;
extern uint_t *svc_max_msg_sizep;
static void
{
/*
* Since the mir_mutex lock needs to be released to call
* untimeout(), we need to make sure that no other thread
* that time. The mir_timer_call bit and the mir_timer_cv
* condition variable are used to synchronize this. Setting
* mir_timer_call also tells mir_timer() (refer to the comments
* in mir_timer()) that it does not need to do anything.
*/
while (mir->mir_timer_call)
mir->mir_timer_id = 0;
}
}
static void
{
while (mir->mir_timer_call)
}
/* Only start the timer when it is not closing. */
if (!mir->mir_closing) {
}
}
static int
{
/*
* This loop is a bit tacky -- it walks the STREAMS list of
* flow-controlled messages.
*/
do {
return (1);
}
return (0);
}
static int
{
}
/*
* Set mir_closing so we get notified when MIR_SVC_QUIESCED()
* is TRUE. And mir_timer_start() won't start the timer again.
*/
/*
* This will prevent more requests from arriving and
* will force rpcmod to ignore flow control.
*/
(queue_cleaned == FALSE)) {
/*
* call into SVC to clean the queue
*/
svc_queueclean(q);
continue;
}
/*
* Bugid 1253810 - Force the write service
* procedure to send its messages, regardless
* whether the downstream module is ready
* to accept data.
*/
}
qprocsoff(q);
/* Notify kRPC that this stream is going away. */
svc_queueclose(q);
} else {
qprocsoff(q);
}
return (0);
}
/*
* This is server side only (RPC_SERVER).
*
* Exit idle mode.
*/
static void
{
}
/*
* This is server side only (RPC_SERVER).
*
* Start idle processing, which will include setting idle timer if the
* stream is not being closed.
*/
static void
{
/*
* Don't re-start idle timer if we are closing queues.
*/
if (mir->mir_closing) {
(void *)q);
/*
* We will call mir_svc_idle_start() whenever MIR_SVC_QUIESCED()
* is true. When it is true, and we are in the process of
* closing the stream, signal any thread waiting in
* mir_close().
*/
if (mir->mir_inwservice == 0)
} else {
/*
* Normal condition, start the idle timer. If an orderly
* release has been sent, set the timeout to wait for the
* client to close its side of the connection. Otherwise,
* use the normal idle timeout.
*/
}
}
/* ARGSUSED */
static int
{
/* Set variables used directly by kRPC. */
if (!mir_rele)
if (!mir_start)
if (!clnt_stop_idle)
if (!clnt_max_msg_sizep)
if (!svc_max_msg_sizep)
/* Allocate a zero'ed out mir structure for this stream. */
/*
* We set hold inbound here so that incoming messages will
* be held on the read-side queue until the stream is completely
* initialized with a RPC_CLIENT or RPC_SERVER ioctl. During
* the ioctl processing, the flag is cleared and any messages that
* arrived between the open and the ioctl are delivered to kRPC.
*
* Early data should never arrive on a client stream since
* servers only respond to our requests and we do not send any.
* until after the stream is initialized. Early data is
* very common on a server stream where the client will start
* sending data as soon as the connection is made (and this
* is especially true with TCP where the protocol accepts the
* connection before nfsd or kRPC is notified about it).
*/
/*
* Start the record marker looking for a 4-byte header. When
* this length is negative, it indicates that rpcmod is looking
* for bytes to consume for the record marker header. When it
* is positive, it holds the number of bytes that have arrived
* for the current fragment and are being held in mir_header_mp.
*/
/*
* We noenable the read-side queue because we don't want it
* automatically enabled by putq. We enable it explicitly
* in mir_wsrv when appropriate. (See additional comments on
* flow control at the beginning of mir_rsrv.)
*/
noenable(q);
qprocson(q);
return (0);
}
/*
* Read-side put routine for both the client and server side. Does the
* record marking for incoming RPC messages, and when complete, dispatches
* the message to either the client or server.
*/
static void
{
int excess;
/*
* If the stream has not been set up as a RPC_CLIENT or RPC_SERVER
* with the corresponding ioctl, then don't accept
* any inbound data. This should never happen for streams
* created by nfsd or client-side kRPC because they are careful
* to set the mode of the stream before doing anything else.
*/
return;
}
case M_DATA:
break;
case M_PROTO:
case M_PCPROTO:
return;
}
mir_rput_proto(q, mp);
return;
}
/* Throw away the T_DATA_IND block and continue with data. */
break;
case M_SETOPTS:
/*
* If a module on the stream is trying set the Stream head's
* high water mark, then set our hiwater to the requested
* value. We are the "stream head" for all inbound
* data messages since messages are passed directly to kRPC.
*/
}
}
return;
case M_FLUSH:
return;
default:
return;
}
/*
* If this connection is closing, don't accept any new messages.
*/
if (mir->mir_svc_no_more_msgs) {
return;
}
/* Get local copies for quicker access. */
/* Loop, processing each message block in the mp chain separately. */
do {
/*
* Drop zero-length mblks to prevent unbounded kernel memory
* consumption.
*/
continue;
}
/*
* If frag_len is negative, we're still in the process of
* building frag_header -- try to complete it with this mblk.
*/
frag_len++;
frag_header <<= 8;
}
/*
* We consumed this mblk while trying to complete the
* fragment header. Free it and move on.
*/
continue;
}
/*
* Now frag_header has the number of bytes in this fragment
* and we're just waiting to collect them all. Chain our
* latest mblk onto the list and see if we now have enough
* bytes to complete the fragment.
*/
} else {
}
if (excess < 0) {
/*
* We still haven't received enough data to complete
* the fragment, so continue on to the next mblk.
*/
continue;
}
/*
* We've got a complete fragment. If there are excess bytes,
* then they're part of the next fragment's header (of either
* this RPC message or the next RPC message). Split that part
* into its own mblk so that we can safely freeb() it when
* building frag_header above.
*/
if (excess > 0) {
mir->mir_frag_header = 0;
return;
}
/*
* Relink the message chain so that the next mblk is
* the next fragment header, followed by the rest of
* the message chain.
*/
/*
* Data in the new mblk begins at the next fragment,
* and data in the old mblk ends at the next fragment.
*/
}
/*
* Reset frag_len and frag_header for the next fragment.
*/
if (!(frag_header & MIR_LASTFRAG)) {
/*
* The current fragment is complete, but more
* fragments need to be processed before we can
* pass along the RPC message headed at head_mp.
*/
frag_header = 0;
continue;
}
frag_header = 0;
/*
* We've got a complete RPC message; pass it to the
* appropriate consumer.
*/
case RPC_CLIENT:
/*
* Mark this stream as active. This marker
* is used in mir_timer().
*/
} else {
}
break;
case RPC_SERVER:
/*
* Check for flow control before passing the
* message to kRPC.
*/
if (!mir->mir_hold_inbound) {
if (mir->mir_krpc_cell) {
if (mir_check_len(q, head_mp))
return;
/*
* If the reference count is 0
* (not including this
* request), then the stream is
* transitioning from idle to
* non-idle. In this case, we
* cancel the idle timer.
*/
if (mir->mir_ref_cnt++ == 0)
stop_timer = B_TRUE;
} else {
}
} else {
/*
* Count # of times this happens. Should
* be never, but experience shows
* otherwise.
*/
}
} else {
/*
* If the outbound side of the stream is
* flow controlled, then hold this message
* until client catches up. mir_hold_inbound
* is set in mir_wput and cleared in mir_wsrv.
*/
}
break;
default:
break;
}
/*
* Reset the chain since we're starting on a new RPC message.
*/
/*
* Sanity check the message length; if it's too large mir_check_len()
* will shutdown the connection, drop mir_mutex, and return non-zero.
*/
mir_check_len(q, head_mp))
return;
/* Save our local copies back in the mir structure. */
/*
* The timer is stopped after the whole message chain is processed.
* The reason is that stopping the timer releases the mir_mutex
* lock temporarily. This means that the request can be serviced
* while we are still processing the message chain. This is not
* good. So we stop the timer here instead.
*
* Note that if the timer fires before we stop it, it will not
* do any harm as MIR_SVC_QUIESCED() is false and mir_timer()
* will just return.
*/
if (stop_timer) {
"ref cnt going to non zero\n", (void *)WR(q));
}
}
static void
{
case RPC_CLIENT:
switch (type) {
case T_DISCON_IND:
reason = ((struct T_discon_ind *)
/*FALLTHROUGH*/
case T_ORDREL_IND:
if (mir->mir_head_mp) {
}
/*
* We are disconnecting, but not necessarily
* closing. By not closing, we will fail to
* pick up a possibly changed global timeout value,
* unless we store it now.
*/
/*
* Even though we are unconnected, we still
* leave the idle timer going on the client. The
* reason for is that if we've disconnected due
* to a server-side disconnect, reset, or connection
* timeout, there is a possibility the client may
* retry the RPC request. This retry needs to done on
* the same bound address for the server to interpret
* it as such. However, we don't want
* to wait forever for that possibility. If the
* end-point stays unconnected for mir_idle_timeout
* units of time, then that is a signal to the
* connection manager to give up waiting for the
* application (eg. NFS) to send a retry.
*/
return;
case T_ERROR_ACK:
{
(void *)q);
return;
} else {
return;
}
break;
}
case T_OK_ACK:
{
return;
} else {
return;
}
break;
}
case T_CONN_CON:
case T_INFO_ACK:
case T_OPTMGMT_ACK:
return;
break;
case T_BIND_ACK:
break;
default:
"for kRPC client\n",
break;
}
break;
case RPC_SERVER:
switch (type) {
case T_BIND_ACK:
{
/*
* If this is a listening stream, then shut
* off the idle timer.
*/
if (tbind->CONIND_number > 0) {
/*
* mark this as a listen endpoint
* for special handling.
*/
}
break;
}
case T_DISCON_IND:
case T_ORDREL_IND:
: "orderly release");
/*
* For listen endpoint just pass
* on the message.
*/
if (mir->mir_listen_stream)
break;
/*
* If client wants to break off connection, record
* that fact.
*/
/*
* If we are idle, then send the orderly release
* or disconnect indication to nfsd.
*/
if (MIR_SVC_QUIESCED(mir)) {
break;
}
"disconnect/ord rel indication not passed "
"upstream on 0x%p\n", (void *)q);
/*
* Hold the indication until we get idle
* If there already is an indication stored,
* replace it if the new one is a disconnect. The
* reasoning is that disconnection takes less time
* to process, and once a client decides to
* disconnect, we should do that.
*/
if (mir->mir_svc_pend_mp) {
if (type == T_DISCON_IND) {
" held disconnect/ord rel"
" indication with disconnect on"
" 0x%p\n", (void *)q);
} else {
"held a disconnect/ord rel "
"indication. freeing ord rel "
"ind on 0x%p\n", (void *)q);
}
} else
return;
default:
/* nfsd handles server-side non-data messages. */
break;
}
break;
default:
break;
}
}
/*
* The server-side read queues are used to hold inbound messages while
* outbound flow control is exerted. When outbound flow control is
* relieved, mir_wsrv qenables the read-side queue. Read-side queues
* are not enabled by STREAMS and are explicitly noenable'ed in mir_open.
*/
static void
{
case RPC_SERVER:
if (mir->mir_ref_cnt == 0)
mir->mir_hold_inbound = 0;
if (mir->mir_hold_inbound)
break;
if (mir->mir_krpc_cell &&
(mir->mir_svc_no_more_msgs == 0)) {
if (mir_check_len(q, mp))
return;
/*
* If we were idle, turn off idle timer
* since we aren't idle any more.
*/
if (mir->mir_ref_cnt++ == 0)
stop_timer = B_TRUE;
} else {
break;
}
} else {
/*
* Count # of times this happens. Should be
* never, but experience shows otherwise.
*/
}
}
break;
case RPC_CLIENT:
break;
default:
return;
}
/*
* The timer is stopped after all the messages are processed.
* The reason is that stopping the timer releases the mir_mutex
* lock temporarily. This means that the request can be serviced
* while we are still processing the message queue. This is not
* good. So we stop the timer here instead.
*/
if (stop_timer) {
"cnt going to non zero\n", (void *)WR(q));
}
}
"disconnect/ord rel indication upstream\n",
__LINE__);
}
return;
}
}
static int mir_svc_policy_fails;
/*
* connection close.
*/
static int
{
#ifdef DEBUG
#endif
/*
* Create an M_DATA message with the event code and pass it to the
* Stream head (nfsd or whoever created the stream will consume it).
*/
if (!mp) {
"%d\n", event);
return (ENOMEM);
}
return (0);
}
/*
* Server side: start the close phase. We want to get this rpcmod slot in an
* idle state before mir_close() is called.
*/
static void
{
/*
* Do not accept any more messages.
*/
/*
* Next two statements will make the read service procedure
* free everything stuck in the streams read queue.
* It's not necessary because enabling the write queue will
* have the same effect, but why not speed the process along?
*/
mir->mir_hold_inbound = 0;
/*
* Meanwhile force the write service procedure to send the
* responses downstream, regardless of flow control.
*/
}
/*
* This routine is called directly by kRPC after a request is completed,
* whether a reply was sent or the request was dropped.
*/
static void
{
if (mp)
if (enable)
/*
* Start idle processing if this is the last reference.
*/
}
if (cmp) {
"disconnect/ord rel indication upstream on queue 0x%p\n",
}
/*
* Start idle processing if this is the last reference.
*/
"because ref cnt is zero\n", (void *) wq);
}
mir->mir_ref_cnt--;
/*
* Wake up the thread waiting to close.
*/
}
/*
* This routine is called by server-side kRPC when it is ready to
* handle inbound messages on the stream.
*/
static void
{
/*
* no longer need to take the mir_mutex because the
* mir_setup_complete field has been moved out of
* the binary field protected by the mir_mutex.
*/
}
/*
* client side wrapper for stopping timer with normal idle timeout.
*/
static void
{
}
/*
* client side wrapper for stopping timer with normal idle timeout.
*/
static void
{
}
/*
* client side only. Forces rpcmod to stop sending T_ORDREL_REQs on
* end-points that aren't connected.
*/
static void
{
}
/*
* Timer handler. It handles idle timeout and memory shortage problem.
*/
static void
{
/*
* mir_timer_call is set only when either mir_timer_[start|stop]
* is progressing. And mir_timer() can only be run while they
* are progressing if the timer is being stopped. So just
* return.
*/
if (mir->mir_timer_call) {
return;
}
mir->mir_timer_id = 0;
case RPC_CLIENT:
/*
* For clients, the timer fires at clnt_idle_timeout
* intervals. If the activity marker (mir_clntreq) is
* zero, then the stream has been idle since the last
* timer event and we notify kRPC. If mir_clntreq is
* non-zero, then the stream is active and we just
* restart the timer for another interval. mir_clntreq
* is set to 1 in mir_wput for every request passed
* downstream.
*
* If this was a memory shortage timer reset the idle
* timeout regardless; the mir_clntreq will not be a
* valid indicator.
*
* The timer is initially started in mir_wput during
* RPC_CLIENT ioctl processing.
*
* The timer interval can be changed for individual
* streams with the ND variable "mir_idle_timeout".
*/
now = ddi_get_lbolt();
if (tout < 0)
tout = 1000;
#if 0
printf("mir_timer[%d < %d + %d]: reset client timer "
#endif
mir->mir_clntreq = 0;
return;
}
#if 0
#endif
/*
* We are disconnecting, but not necessarily
* closing. By not closing, we will fail to
* pick up a possibly changed global timeout value,
* unless we store it now.
*/
/*
* We pass T_ORDREL_REQ as an integer value
* to kRPC as the indication that the stream
* is idle. This is not a T_ORDREL_REQ message,
* it is just a convenient value since we call
* the same kRPC routine for T_ORDREL_INDs and
* T_DISCON_INDs.
*/
return;
case RPC_SERVER:
/*
* For servers, the timer is only running when the stream
* is really idle or memory is short. The timer is started
* by mir_wput when mir_type is set to RPC_SERVER and
* by mir_svc_idle_start whenever the stream goes idle
* (mir_ref_cnt == 0). The timer is cancelled in
* mir_rput whenever a new inbound request is passed to kRPC
* and the stream was previously idle.
*
* The timer interval can be changed for individual
* streams with the ND variable "mir_idle_timeout".
*
* If the stream is not idle do nothing.
*/
if (!MIR_SVC_QUIESCED(mir)) {
return;
}
/*
* If there is no packet queued up in read queue, the stream
* is really idle so notify nfsd to close it.
*/
if (notify) {
}
return;
default:
return;
}
}
/*
* Called by the RPC package to send either a call or a return, or a
* transport connection request. Adds the record marking header.
*/
static void
{
if (!mir) {
return;
}
mir_wput_other(q, mp);
return;
}
(void *)q);
return;
}
/* Stick in the 4 byte record marking header. */
/*
* Since we know that M_DATA messages are created exclusively
* by kRPC, we expect that kRPC will leave room for our header
* and 4 byte align which is normal for XDR.
* If kRPC (or someone else) does not cooperate, then we
* just throw away the message.
*/
"fragment header (%d bytes left)\n",
return;
}
/*
* For the client, set mir_clntreq to indicate that the
* connection is active.
*/
}
/*
* If we haven't already queued some data and the downstream module
* can accept more data, send it on, otherwise we queue the message
* and take other actions depending on mir_type.
*/
/*
* Now we pass the RPC message downstream.
*/
return;
}
case RPC_CLIENT:
/*
* Check for a previous duplicate request on the
* queue. If there is one, then we throw away
* the current message and let the previous one
* go through. If we can't find a duplicate, then
* send this one. This tap dance is an effort
* to reduce traffic and processing requirements
* under load conditions.
*/
if (mir_clnt_dup_request(q, mp)) {
return;
}
break;
case RPC_SERVER:
/*
* Set mir_hold_inbound so that new inbound RPC
* messages will be held until the client catches
* up on the earlier replies. This flag is cleared
* in mir_wsrv after flow control is relieved;
* the read-side queue is also enabled at that time.
*/
break;
default:
break;
}
}
static void
{
case M_IOCTL:
case RPC_CLIENT:
return;
}
/*
* Clear mir_hold_inbound which was set to 1 by
* mir_open. This flag is not used on client
* streams.
*/
mir->mir_hold_inbound = 0;
/*
* Start the idle timer. See mir_timer() for more
* information on how client timers work.
*/
mir_clnt_idle_start(q, mir);
return;
case RPC_SERVER:
goto ioc_eperm;
/*
* We don't clear mir_hold_inbound here because
* mir_hold_inbound is used in the flow control
* model. If we cleared it here, then we'd commit
* a small violation to the model where the transport
* might immediately block downstream flow.
*/
/*
* Start the idle timer. See mir_timer() for more
* information on how server timers work.
*
* Note that it is important to start the idle timer
* here so that connections time out even if we
* never receive any data on them.
*/
"because we got RPC_SERVER ioctl\n", (void *)q);
mir_svc_idle_start(q, mir);
return;
default:
break;
}
break;
case M_PROTO:
/*
* We are likely being called from the context of a
* service procedure. So we need to enqueue. However
* enqueing may put our message behind data messages.
* So flush the data first.
*/
flush_in_svc = TRUE;
}
break;
case T_DATA_REQ:
/* Don't pass T_DATA_REQ messages downstream. */
return;
case T_ORDREL_REQ:
(void *)q);
/*
* We are likely being called from
* clnt_dispatch_notifyall(). Sending
* a T_ORDREL_REQ will result in
* a some kind of _IND message being sent,
* will be another call to
* clnt_dispatch_notifyall(). To keep the stack
* lean, queue this message.
*/
return;
}
/*
* Mark the structure such that we don't accept any
* more requests from client. We could defer this
* until we actually send the orderly release
* request downstream, but all that does is delay
* the closing of this stream.
*/
" so calling mir_svc_start_close\n", (void *)q);
mir_svc_start_close(q, mir);
/*
* If we have sent down a T_ORDREL_REQ, don't send
* any more.
*/
if (mir->mir_ordrel_pending) {
return;
}
/*
* If the stream is not idle, then we hold the
* orderly release until it becomes idle. This
* ensures that kRPC will be able to reply to
* all requests that we have passed to it.
*
* We also queue the request if there is data already
* queued, because we cannot allow the T_ORDREL_REQ
* to go before data. When we had a separate reply
* count, this was not a problem, because the
* reply count was reconciled when mir_wsrv()
* completed.
*/
if (!MIR_SVC_QUIESCED(mir) ||
"T_ORDREL_REQ on 0x%p\n", (void *)q);
return;
}
/*
* Mark the structure so that we know we sent
* an orderly release request, and reset the idle timer.
*/
" on 0x%p because we got T_ORDREL_REQ\n",
(void *)q);
mir_svc_idle_start(q, mir);
/*
* When we break, we will putnext the T_ORDREL_REQ.
*/
break;
case T_CONN_REQ:
}
/*
* Restart timer in case mir_clnt_idle_do_stop() was
* called.
*/
mir_clnt_idle_stop(q, mir);
mir_clnt_idle_start(q, mir);
break;
default:
/*
* T_DISCON_REQ is one of the interesting default
* cases here. Ideally, an M_FLUSH is done before
* T_DISCON_REQ is done. However, that is somewhat
* cumbersome for clnt_cots.c to do. So we queue
* T_DISCON_REQ, and let the service procedure
* flush all M_DATA.
*/
break;
}
/* fallthru */;
default:
"wq 0x%p\n", (void *)q);
} else {
}
} else {
"M_FLUSH on wq 0x%p\n", (void *)q);
}
}
break;
}
break;
}
qenable(q);
return;
}
}
static void
{
mir->mir_inwflushdata = 0;
/*
* Do not send any more data if we have sent
* a T_ORDREL_REQ.
*/
continue;
}
/*
* Make sure that the stream can really handle more
* data.
*/
if (!MIR_WCANPUTNEXT(mir, q)) {
return;
}
/*
* Now we pass the RPC message downstream.
*/
continue;
}
/*
* This is not an RPC message, pass it downstream
* (ignoring flow control) if the server side is not sending a
* T_ORDREL_REQ downstream.
*/
T_ORDREL_REQ) {
continue;
}
/*
* Don't send two T_ORDRELs
*/
continue;
}
/*
* Mark the structure so that we know we sent an orderly
* release request. We will check to see slot is idle at the
* end of this routine, and if so, reset the idle timer to
* handle orderly release timeouts.
*/
(void *)q);
/*
* Send the orderly release downstream. If there are other
* pending replies we won't be able to send them. However,
* the only reason we should send the orderly release is if
* we were idle, or if an unusual event occurred.
*/
}
/*
* If we call mir_svc_idle_start() below, then
* clearing mir_inwservice here will also result in
* any thread waiting in mir_close() to be signaled.
*/
mir->mir_inwservice = 0;
return;
}
/*
* If idle we call mir_svc_idle_start to start the timer (or wakeup
* a close). Also make sure not to start the idle timer on the
* listener stream. This can cause nfsd to send an orderly release
* command on the listener stream.
*/
"because mir slot is idle\n", (void *)q);
mir_svc_idle_start(q, mir);
}
/*
* If outbound flow control has been relieved, then allow new
* inbound requests to be processed.
*/
if (mir->mir_hold_inbound) {
mir->mir_hold_inbound = 0;
}
}
static void
{
case RPC_CLIENT:
/*
* We are disconnecting, but not necessarily
* closing. By not closing, we will fail to
* pick up a possibly changed global timeout value,
* unless we store it now.
*/
/*
* T_DISCON_REQ is passed to kRPC as an integer value
* (this is not a TPI message). It is used as a
* convenient value to indicate a sanity check
* failure -- the same kRPC routine is also called
* for T_DISCON_INDs and T_ORDREL_INDs.
*/
break;
case RPC_SERVER:
"stream head listener to disconnect stream "
"(0x%p)\n", (void *) q);
(void) mir_svc_policy_notify(q, 2);
break;
default:
break;
}
}
/*
* Sanity check the message length, and if it's too large, shutdown the
* connection. Returns 1 if the connection is shutdown; 0 otherwise.
*/
static int
{
return (0);
mir->mir_frag_header = 0;
"kRPC: record fragment from %s of size(%lu) exceeds "
"maximum (%u). Disconnecting",
}
mir_disconnect(q, mir);
return (1);
}