/*
* 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Transport Interface Library cooperating module - issue 2
*/
#include <sys/sysmacros.h>
/*
* This is the loadable module wrapper.
*/
"timod",
&timinfo,
};
/*
* Module linkage information for the kernel.
*/
};
};
/*
* This module keeps track of capabilities of underlying transport. Information
* whether underlying transport supports TI_GET{MY,PEER}NAME ioctls and
* transport or emulates it when transport doesn't understand these
*
* It is assumed that transport supports T_CAPABILITY_REQ when timod receives
* T_CAPABILITY_ACK from the transport. There is no current standard describing
* transport behaviour when it receives unknown message type, so following
* reactions are expected and handled:
*
* 1) Transport drops unknown T_CAPABILITY_REQ message type. In this case timod
* will wait for tcap_wait time and assume that transport doesn't provide
* this message type. T_CAPABILITY_REQ should never travel over the wire, so
* timeout value should only take into consideration internal processing time
* for the message. From user standpoint it may mean that an application will
* hang for TCAP_WAIT time in the kernel the first time this message is used
*
* 2) Transport responds with T_ERROR_ACK specifying T_CAPABILITY_REQ as
* original message type. In this case it is assumed that transport doesn't
* support it (which may not always be true - some transports return
* T_ERROR_ACK in other cases like lack of system memory).
*
* 3) Transport responds with M_ERROR, effectively shutting down the
* stream. Unfortunately there is no standard way to pass the reason of
* M_ERROR message back to the caller, so it is assumed that if M_ERROR was
* sent in response to T_CAPABILITY_REQ message, transport doesn't support
* it.
*
* It is possible under certain circumstances that timod will incorrectly assume
* that underlying transport doesn't provide T_CAPABILITY_REQ message type. In
* this "worst-case" scenario timod will emulate its functionality by itself and
* will provide only TC1_INFO capability. All other bits in CAP_bits1 field are
* cleaned. TC1_INFO is emulated by sending T_INFO_REQ down to transport
* provider.
*/
/*
* Notes about locking:
*
* tim_list_rwlock protects the list of tim_tim structures itself. When this
* lock is held, the list itself is stable, but the contents of the entries
* themselves might not be.
*
* The rest of the members are generally protected by D_MTQPAIR, which
* specifies a default exclusive inner perimeter. If you're looking at
* q->q_ptr, then it's stable.
*
* There's one exception to this rule: tim_peer{maxlen,len,name}. These members
* are touched without entering the associated STREAMS perimeter because we
* get the pointer via tim_findlink() rather than q_ptr. These are protected
* by tim_mutex instead. If you don't hold that lock, don't look at them.
*
* (It would be possible to separate out the 'set by T_CONN_RES' cases from the
* others, but there appears to be no reason to do so.)
*/
struct tim_tim {
/* Protected by the global tim_list_rwlock for all instances */
/* part of ioctl. */
};
/*
* Local flags used with tim_flags field in instance structure of
* type 'struct _ti_user' declared above.
* Historical note:
* This namespace constants were previously declared in a
* a very messed up namespace in timod.h
*
* There may be 3 states for transport:
*
* 1) It provides T_CAPABILITY_REQ
* 2) It does not provide T_CAPABILITY_REQ
* 3) It is not known yet whether transport provides T_CAPABILITY_REQ or not.
*
* It is assumed that the underlying transport either provides
* T_CAPABILITY_REQ or not and this does not changes during the
* system lifetime.
*
*/
/* Debugging facilities */
/*
* Logging needed for debugging timod should only appear in DEBUG kernel.
*/
#ifdef DEBUG
#else
#endif
/*
* Sleep timeout for T_CAPABILITY_REQ. This message never travels across
* network, so timeout value should be enough to cover all internal processing
* time.
*/
/* Sleep timeout in tim_recover() */
/* Sleep timeout in tim_ioctl_retry() 0.2 seconds */
/*
* Return values for ti_doname().
*/
/*
* Function prototypes
*/
static int ti_expind_on_rdqueues(queue_t *);
static void tim_tcap_timer(void *);
struct iocblk *);
static void tim_clear_peer(struct tim_tim *);
int
_init(void)
{
int error;
if (error != 0) {
return (error);
}
return (0);
}
int
_fini(void)
{
int error;
if (error != 0)
return (error);
return (0);
}
int
{
}
/*
* Hash list for all instances. Used to find tim_tim structure based on
* ACCEPTOR_id in T_CONN_RES. Protected by tim_list_rwlock.
*/
#ifdef _ILP32
#else
#endif /* _ILP32 */
int tim_cnt = 0;
static void tilog(char *, t_scalar_t);
static void tim_addlink(struct tim_tim *);
static void tim_dellink(struct tim_tim *);
static void tim_ioctl_retry(queue_t *);
int dotilog = 0;
/* stream data structure definitions */
(int (*)())timodrput,
(int (*)())timodrsrv,
};
(int (*)())timodwput,
(int (*)())timodwsrv,
};
/*
* timodopen - open routine gets called when the module gets pushed
* onto the stream.
*/
/*ARGSUSED*/
static int
queue_t *q,
int flag,
int sflag,
{
if (q->q_ptr) {
return (0);
}
return (ENOMEM);
/* Must be done before tpi_findprov and _ILP32 q_next walk below */
qprocson(q);
/*
* Defer allocation of the buffers for the local address and
* the peer's address until we need them.
* Assume that timod has to handle getname until we here
* an iocack from the transport provider or we know that
* transport provider doesn't understand it.
*/
TILOG("timodopen: setting DO_MYNAME\n", 0);
}
TILOG("timodopen: setting DO_PEERNAME\n", 0);
}
#ifdef _ILP32
{
/*
* Find my driver's read queue (for T_CONN_RES handling)
*/
}
#else
#endif /* _ILP32 */
/*
* Add this one to the list.
*/
/*
* Send M_SETOPTS to stream head to make sure M_PCPROTO messages
* are not flushed. This prevents application deadlocks.
*/
return (0);
}
static void
{
tp->tim_rtimoutid = 0;
} else {
tp->tim_wtimoutid = 0;
}
enableok(q);
qenable(q);
}
static void
{
tp->tim_rbufcid = 0;
} else {
tp->tim_wbufcid = 0;
}
enableok(q);
qenable(q);
}
/*
* timodclose - This routine gets called when the module gets popped
* off of the stream.
*/
/*ARGSUSED*/
static int
queue_t *q,
int flag,
{
qprocsoff(q);
/*
* Cancel any outstanding bufcall
* or timeout requests.
*/
if (tp->tim_wbufcid) {
tp->tim_wbufcid = 0;
}
if (tp->tim_rbufcid) {
tp->tim_rbufcid = 0;
}
if (tp->tim_wtimoutid) {
tp->tim_wtimoutid = 0;
}
if (tp->tim_rtimoutid) {
tp->tim_rtimoutid = 0;
}
if (tp->tim_tcap_timoutid != 0) {
tp->tim_tcap_timoutid = 0;
}
while (mp) {
}
if (tp->tim_mymaxlen != 0)
if (tp->tim_peermaxlen != 0)
return (0);
}
/*
* timodrput - Module read put procedure. This is called from
* the module, driver, or stream head upstream/downstream.
* Handles M_FLUSH, M_DATA and some M_PROTO (T_DATA_IND,
* and T_UNITDATA_IND) messages. All others are queued to
* be handled by the service procedures.
*/
static void
{
/*
* During flow control and other instances when messages
* are on queue, queue up a non high priority message
*/
return;
}
/*
* Inline processing of data (to avoid additional procedure call).
* Rest is handled in timodrproc.
*/
case M_DATA:
else
break;
case M_PROTO:
case M_PCPROTO:
} else {
}
break;
}
case T_EXDATA_IND:
case T_DATA_IND:
case T_UNITDATA_IND:
else
break;
default:
(void) timodrproc(q, mp);
break;
}
break;
default:
(void) timodrproc(q, mp);
break;
}
}
/*
* timodrsrv - Module read queue service procedure. This is called when
* messages are placed on an empty queue, when high priority
* messages are placed on the queue, and when flow control
* restrictions subside. This code used to be included in a
* put procedure, but it was moved to a service procedure
* because several points were added where memory allocation
* could fail, and there is no reasonable recovery mechanism
* from the put procedure.
*/
/*ARGSUSED*/
static void
{
if (!tp)
return;
if (timodrproc(q, mp)) {
/*
* timodrproc did a putbq - stop processing
* messages.
*/
return;
}
}
}
/*
* Perform common processing when a T_CAPABILITY_ACK or T_INFO_ACK
* arrive. Set the queue properties and adjust the tim_flags according
* to the service type.
*/
static void
{
}
static int
{
default:
break;
case M_ERROR:
/*
* There is no specified standard response for driver when it
* receives unknown message type and M_ERROR is one
* possibility. If we send T_CAPABILITY_REQ down and transport
* provider responds with M_ERROR we assume that it doesn't
* understand this message type. This assumption may be
* sometimes incorrect (transport may reply with M_ERROR for
* some other reason) but there is no way for us to distinguish
* between different cases. In the worst case timod and everyone
* else sharing global transport description with it may end up
* emulating T_CAPABILITY_REQ.
*/
/*
* Check that we are waiting for T_CAPABILITY_ACK and
* T_CAPABILITY_REQ is not implemented by transport or emulated
* by timod.
*/
/*
* Good chances that this transport doesn't provide
* T_CAPABILITY_REQ. Mark this information permanently
* for the module + transport combination.
*/
if (tp->tim_tcap_timoutid != 0) {
tp->tim_tcap_timoutid = 0;
}
}
break;
case M_DATA:
return (1);
}
break;
case M_PROTO:
case M_PCPROTO:
if (blen < sizeof (t_scalar_t)) {
/*
* Note: it's not actually possible to get
* here with db_type M_PCPROTO, because
* timodrput has already checked MBLKL, and
* thus the assertion below. If the length
* was too short, then the message would have
* already been putnext'd, and would thus
* never appear here. Just the same, the code
* below handles the impossible case since
* it's easy to do and saves future
* maintainers from unfortunate accidents.
*/
return (1);
}
break;
}
default:
if (auditing)
break;
case T_ERROR_ACK:
/* Restore db_type - recover() might have changed it */
if (blen < sizeof (struct T_error_ack)) {
break;
}
tilog("timodrproc: Got T_ERROR_ACK, flags = %x\n",
~(WAIT_CONNRESACK | WAITIOCACK);
} else {
}
break;
case T_OK_ACK:
break;
}
tilog("timodrproc: Got T_OK_ACK\n", 0);
resp = (struct T_conn_res *)
goto cresackout;
ntp->tim_peercred =
goto cresackout;
}
indp = (struct T_conn_ind *)
/* true as message is put on list */
tilog("timodwproc: kmem_alloc "
"failed, attempting "
"recovery\n", 0);
tim_recover(q, mp,
indp->SRC_length);
return (1);
}
if (ntp->tim_peermaxlen > 0)
}
~(WAIT_CONNRESACK | WAITIOCACK);
}
break;
case T_BIND_ACK: {
/* Restore db_type - recover() might have changed it */
break;
}
/* save negotiated backlog */
break;
}
caddr_t p;
if (ackp->ADDR_length < 0 ||
break;
}
if (p == NULL) {
tilog("timodrproc: kmem_alloc "
"failed attempt recovery",
0);
tim_recover(q, mp,
ackp->ADDR_length);
return (1);
}
tp->tim_mymaxlen);
}
tp->tim_myname = p;
}
}
break;
}
case T_OPTMGMT_ACK:
tilog("timodrproc: Got T_OPTMGMT_ACK\n", 0);
/* Restore db_type - recover() might have change it */
} else {
}
break;
case T_INFO_ACK: {
/* Restore db_type - recover() might have changed it */
break;
}
tilog("timodrproc: Got T_INFO_ACK, flags = %x\n",
int ioc_cmd;
/*
* The only case when T_INFO_ACK may be received back
* when we are waiting for ioctl to complete is when
* this ioctl sent T_INFO_REQ down.
*/
break;
}
/*
* Was it sent from TI_CAPABILITY emulation?
*/
if (ioc_cmd == TI_CAPABILITY) {
/*
* Perform sanity checks. The only case when we
* send T_INFO_REQ from TI_CAPABILITY is when
* timod emulates T_CAPABILITY_REQ and CAP_bits1
* has TC1_INFO set.
*/
(TI_CAP_RECVD | CAP_WANTS_INFO)) !=
(TI_CAP_RECVD | CAP_WANTS_INFO)) {
break;
}
TILOG("timodrproc: emulating TI_CAPABILITY/"
"info\n", 0);
/* Save info & reuse mp for T_CAPABILITY_ACK */
saved_info = *tia;
sizeof (struct T_capability_ack),
tilog("timodrproc: realloc failed, "
"no recovery attempted\n", 0);
return (1);
}
/*
* Copy T_INFO information into T_CAPABILITY_ACK
*/
break;
}
/*
* The code for TI_SYNC/TI_GETINFO is left here only for
* backward compatibility with staticaly linked old
* TI_CAPABILITY for getting transport info and should
* not use TI_GETINFO/TI_SYNC for this purpose.
*/
/*
* make sure the message sent back is the size of
* the "expected ack"
* For TI_GETINFO, expected ack size is
* sizeof (T_info_ack)
* For TI_SYNC, expected ack size is
* sizeof (struct ti_sync_ack);
*/
break;
}
sizeof (struct T_info_ack); /* TI_GETINFO */
sizeof (struct ti_sync_ack);
}
if (deficit != 0) {
deficit) {
BPRI_HI);
sizeof (struct T_error_ack));
tilog("timodrproc: allocb failed no "
"recovery attempt\n", 0);
pptr = (union T_primitives *)
break;
} else {
pptr = (union T_primitives *)
}
}
}
/*
* We now have "mp" which has enough space for an
* appropriate ack and contains struct T_info_ack
* that the transport provider returned. We now
* stuff it with more stuff to fullfill
* TI_SYNC ioctl needs, as necessary
*/
/*
* Assumes struct T_info_ack is first embedded
* type in struct ti_sync_ack so it is
* automatically there.
*/
/*
* tsap->tsa_qlen needs to be set only if
* TSRF_QLEN_REQ flag is set, but for
* compatibility with statically linked
* applications it is set here regardless of the
* flag since old XTI library expected it to be
* set.
*/
/*
* Request to peek for EXPIND in
* rcvbuf.
*/
if (ti_expind_on_rdqueues(q)) {
/*
* Expedited data is
* queued on the stream
* read side
*/
}
}
}
break;
}
}
break;
case T_ADDR_ACK:
tilog("timodrproc: Got T_ADDR_ACK\n", 0);
break;
case T_CONN_IND: {
tilog("timodrproc: Got T_CONN_IND\n", 0);
} else {
tim_recover(q, mp,
(t_scalar_t)sizeof (mblk_t));
return (1);
}
}
if (auditing)
break;
}
case T_CONN_CON:
tilog("timodrproc: Got T_CONN_CON\n", 0);
break;
case T_DISCON_IND: {
if (q->q_first != 0)
tilog("timodrput: T_DISCON_IND - flow control\n", 0);
break;
}
tilog("timodrproc: Got T_DISCON_IND Reason: %d\n",
break;
}
if (nbp) {
if (pbp)
else
}
break;
}
case T_ORDREL_IND:
tilog("timodrproc: Got T_ORDREL_IND\n", 0);
} else {
}
break;
case T_EXDATA_IND:
case T_DATA_IND:
case T_UNITDATA_IND:
tilog("timodrproc: Got T_EXDATA_IND\n", 0);
return (1);
}
break;
case T_CAPABILITY_ACK: {
break;
}
/* This transport supports T_CAPABILITY_REQ */
tilog("timodrproc: Got T_CAPABILITY_ACK\n", 0);
/* Reset possible pending timeout */
if (tp->tim_tcap_timoutid != 0) {
tp->tim_tcap_timoutid = 0;
}
}
break;
}
break;
case M_FLUSH:
tilog("timodrproc: Got M_FLUSH\n", 0);
else
}
break;
case M_IOCACK:
tilog("timodrproc: Got M_IOCACK\n", 0);
/*
* Transport provider supports this ioctl,
* so I don't have to.
*/
}
if (tp->tim_mymaxlen != 0) {
tp->tim_mymaxlen = 0;
}
/* tim_iocsave may already be overwritten. */
}
/*
* Transport provider supports this ioctl,
* so I don't have to.
*/
}
if (clearit) {
tp->tim_peermaxlen = 0;
tp->tim_peerlen = 0;
}
if (clearit) {
}
}
/* tim_iocsave may already be overwritten. */
}
}
break;
case M_IOCNAK:
tilog("timodrproc: Got M_IOCNAK\n", 0);
}
/* tim_iocsave may already be overwritten. */
}
break;
}
}
break;
}
return (0);
}
/*
* timodwput - Module write put procedure. This is called from
* the module, driver, or stream head upstream/downstream.
* Handles M_FLUSH, M_DATA and some M_PROTO (T_DATA_REQ,
* and T_UNITDATA_REQ) messages. All others are queued to
* be handled by the service procedures.
*/
static void
{
/*
* Enqueue normal-priority messages if our queue already
* holds some messages for deferred processing but don't
* enqueue those M_IOCTLs which will result in an
* M_PCPROTO (ie, high priority) message being created.
*/
default:
return;
case TI_GETINFO:
case TI_SYNC:
case TI_CAPABILITY:
break;
}
} else {
return;
}
}
/*
* Inline processing of data (to avoid additional procedure call).
* Rest is handled in timodwproc.
*/
case M_DATA:
break;
} else {
}
}
else
break;
case M_PROTO:
case M_PCPROTO:
case T_UNITDATA_REQ:
break;
} else {
}
}
else
break;
case T_DATA_REQ:
case T_EXDATA_REQ:
else
break;
default:
(void) timodwproc(q, mp);
break;
}
break;
default:
(void) timodwproc(q, mp);
break;
}
}
/*
* timodwsrv - Module write queue service procedure.
* This is called when messages are placed on an empty queue,
* when high priority messages are placed on the queue, and
* when flow control restrictions subside. This code used to
* be included in a put procedure, but it was moved to a
* service procedure because several points were added where
* memory allocation could fail, and there is no reasonable
* recovery mechanism from the put procedure.
*/
static void
{
return;
if (timodwproc(q, mp)) {
/*
* timodwproc did a putbq - stop processing
* messages.
*/
return;
}
}
}
/*
* Common routine to process write side messages
*/
static int
{
int error;
default:
break;
case M_DATA:
return (1);
} else {
}
}
return (1);
}
break;
case M_IOCTL:
/*
* TPI requires we await response to a previously sent message
* before handling another, put it back on the head of queue.
* Since putbq() may see QWANTR unset when called from the
* service procedure, the queue must be explicitly scheduled
* for service, as no backenable will occur for this case.
* tim_ioctl_retry() sets a timer to handle the qenable.
*/
TILOG("timodwproc: putbq M_IOCTL(%d)\n",
/* Called from timodwsrv() and messages on queue */
tim_ioctl_retry(q);
return (1);
}
default:
break;
case _I_GETPEERCRED:
} else {
}
else
}
break;
case TI_BIND:
case TI_UNBIND:
case TI_OPTMGMT:
case TI_GETADDRS:
TILOG("timodwproc: TI_{BIND|UNBIND|OPTMGMT|GETADDRS}"
"\n", 0);
/*
* We know that tim_send_ioctl_tpi_msg() is only
* going to examine the `type' field, so we only
* check that we can access that much data.
*/
if (error != 0) {
break;
}
break;
case TI_GETINFO:
TILOG("timodwproc: TI_GETINFO\n", 0);
if (error != 0) {
break;
}
break;
case TI_SYNC: {
if (error != 0) {
break;
}
/*
* Save out the value of tsr_flags, in case we
* reallocb() tsr_mp (below).
*/
if ((tsr_flags & TSRF_INFO_REQ) == 0) {
sizeof (struct ti_sync_ack), 0);
/* Can reply immediately. */
tilog("timodwproc: allocb failed no "
"recovery attempt\n", 0);
} else {
}
break;
}
/*
* This code is retained for compatibility with
* old statically linked applications. New code
* should use TI_CAPABILITY for all TPI
* information and should not use TSRF_INFO_REQ
* flag.
*
* defer processsing necessary to rput procedure
* as we need to get information from transport
* driver. Set flags that will tell the read
* side the work needed on this request.
*/
if (tsr_flags & TSRF_IS_EXP_IN_RCVBUF)
/*
* Convert message to a T_INFO_REQ message; relies
* on sizeof (struct ti_sync_req) >= sizeof (struct
* T_info_req)).
*/
sizeof (struct T_info_req);
}
break;
case TI_CAPABILITY: {
if (error != 0) {
break;
}
TILOG("timodwproc: TI_CAPABILITY(CAP_bits1 = %x)\n",
TILOG("timodwproc: invalid msg type %d\n",
break;
}
case PI_YES:
/* Just send T_CAPABILITY_REQ down */
break;
case PI_DONTKNOW:
/*
* It is unknown yet whether transport provides
* T_CAPABILITY_REQ or not. Send message down
* and wait for reply.
*/
} else {
}
break;
case PI_NO:
/*
* Transport doesn't support T_CAPABILITY_REQ.
* Either reply immediately or send T_INFO_REQ
* if needed.
*/
TILOG("timodwproc: sending down "
"T_INFO_REQ, flags = %x\n",
/*
* Generate T_INFO_REQ message and send
* it down
*/
sizeof (struct T_info_req);
iocbp);
break;
}
/*
* Can reply immediately. Just send back
* T_CAPABILITY_ACK with CAP_bits1 set to 0.
*/
sizeof (struct T_capability_ack), M_PCPROTO,
tilog("timodwproc: allocb failed no "
"recovery attempt\n", 0);
break;
}
((struct T_capability_ack *)
/*
* It could happen when timod is awaiting ack
* for TI_GETPEERNAME/TI_GETMYNAME.
*/
}
break;
default:
"timodwproc: unknown tpi_capability value "
break;
}
}
break;
case TI_GETMYNAME:
tilog("timodwproc: Got TI_GETMYNAME\n", 0);
break;
}
goto getname;
case TI_GETPEERNAME:
tilog("timodwproc: Got TI_GETPEERNAME\n", 0);
break;
}
return (1);
}
/*
* tim_iocsave may be non-NULL when timod is awaiting
* ack for another TI_GETPEERNAME/TI_GETMYNAME.
*/
break;
}
break;
case M_IOCDATA:
}
} else
break;
case M_PROTO:
case M_PCPROTO:
return (1);
}
default:
break;
case T_EXDATA_REQ:
case T_DATA_REQ:
tilog("timodwproc: Got T_EXDATA_REQ\n", 0);
return (1);
}
break;
case T_UNITDATA_REQ:
return (1);
} else {
}
}
if (auditing)
return (1);
}
break;
case T_CONN_REQ: {
void *p;
tilog("timodwproc: Got T_CONN_REQ\n", 0);
return (1);
}
reqp->DEST_length)) {
return (1);
}
if (p == NULL) {
tilog("timodwproc: kmem_alloc "
"failed, attempting "
"recovery\n", 0);
tim_recover(q, mp,
reqp->DEST_length);
return (1);
}
if (tp->tim_peermaxlen)
tp->tim_peermaxlen);
tp->tim_peername = p;
}
}
if (auditing)
break;
}
case O_T_CONN_RES:
case T_CONN_RES: {
return (1);
}
break;
}
goto cresout;
return (1);
}
else
/*
* Construct a list with:
* nbp - copy of user's original request
* tmp - the extracted T_conn_ind
*/
/*
* tim_iocsave may be non-NULL when timod is awaiting
* ack for TI_GETPEERNAME/TI_GETMYNAME.
*/
break;
}
case T_DISCON_REQ: {
return (1);
}
/*
* If we are already connected, there won't
* be any messages on tim_consave.
*/
break;
}
if (tmp) {
if (pmp)
else
}
break;
}
case T_ORDREL_REQ:
} else {
}
break;
case T_CAPABILITY_REQ:
tilog("timodwproc: Got T_CAPABILITY_REQ\n", 0);
/*
* XXX: We may know at this point whether transport
* provides T_CAPABILITY_REQ or not and we may utilise
* this knowledge here.
*/
break;
}
break;
case M_FLUSH:
tilog("timodwproc: Got M_FLUSH\n", 0);
else
}
break;
}
return (0);
}
static void
{
if (dotilog) {
if (dotilog & 2)
if (dotilog & 4)
else
}
}
static void
{
if (dotilog) {
if (dotilog & 2)
if (dotilog & 4)
else
}
}
/*
* Process the TI_GETNAME ioctl. If no name exists, return len = 0
* in strbuf structures. The state transitions are determined by what
* is hung of cq_private (cp_private) in the copyresp (copyreq) structure.
* The high-level steps in the ioctl processing are as follows:
*
* 1) we recieve an transparent M_IOCTL with the arg in the second message
* block of the message.
* 2) we send up an M_COPYIN request for the strbuf structure pointed to
* by arg. The block containing arg is hung off cq_private.
* 3) we receive an M_IOCDATA response with cp->cp_private->b_cont == NULL.
* This means that the strbuf structure is found in the message block
* mp->b_cont.
* 4) we send up an M_COPYOUT request with the strbuf message hung off
* cq_private->b_cont. The address we are copying to is strbuf.buf.
* we set strbuf.len to 0 to indicate that we should copy the strbuf
* structure the next time. The message mp->b_cont contains the
* address info.
* 5) we receive an M_IOCDATA with cp_private->b_cont != NULL and
* strbuf.len == 0. Restore strbuf.len to either tp->tim_mylen or
* tp->tim_peerlen.
* 6) we send up an M_COPYOUT request with a copy of the strbuf message
* hung off mp->b_cont. In the strbuf structure in the message hung
* off cq_private->b_cont, we set strbuf.len to 0 and strbuf.maxlen
* to 0. This means that the next step is to ACK the ioctl.
* 7) we receive an M_IOCDATA message with cp_private->b_cont != NULL and
* strbuf.len == 0 and strbuf.maxlen == 0. Free up cp->private and
* send an M_IOCACK upstream, and we are done.
*
*/
static int
queue_t *q, /* queue message arrived at */
{
int ret;
case M_IOCTL:
tilog("ti_doname: bad M_IOCTL command\n", 0);
ret = DONAME_FAIL;
break;
}
ret = DONAME_FAIL;
break;
}
ret = DONAME_CONT;
break;
case M_IOCDATA:
ret = DONAME_FAIL;
break;
}
ret = DONAME_FAIL;
break;
}
if (getpeer)
if (getpeer) {
if (tp->tim_peerlen == 0) {
/* copy just strbuf */
} else if (tp->tim_peerlen >
ret = DONAME_FAIL;
break;
} else {
/* copy buffer */
}
} else {
/* copy just strbuf */
ret = DONAME_FAIL;
break;
} else {
/* copy buffer */
}
}
}
/*
* restore strbuf.len
*/
if (getpeer)
else
if (getpeer)
/*
* ack the ioctl
*/
ret = DONAME_DONE;
break;
}
"ti_doname: allocb failed no recovery attempt\n", 0);
ret = DONAME_FAIL;
break;
}
ret = DONAME_CONT;
break;
}
/*
* copy the address to the user
*/
== NULL) {
if (getpeer)
tilog("ti_doname: allocb failed no recovery attempt\n",
0);
ret = DONAME_FAIL;
break;
}
if (getpeer) {
} else {
}
ret = DONAME_CONT;
break;
default:
tilog("ti_doname: freeing bad message type = %d\n",
ret = DONAME_FAIL;
break;
}
return (ret);
}
/*
* Fill in the address of a connectionless data packet if a connect
* had been done on this endpoint.
*/
static mblk_t *
{
BPRI_MED);
up->OPT_length = 0;
up->OPT_offset = 0;
if (tp->tim_peerlen > 0) {
tp->tim_peerlen);
}
}
} else {
if (up->DEST_length != 0)
return (mp);
if (plen > 0) {
}
if (up->OPT_length == 0) {
nup->OPT_length = 0;
nup->OPT_offset = 0;
} else {
nup->OPT_offset =
sizeof (struct T_unitdata_req) + plen;
up->OPT_length);
}
return (bp);
}
}
tim_recover(q, mp,
}
return (bp);
}
static void
{
tim_cnt++;
}
static void
{
tim_cnt--;
}
static struct tim_tim *
{
break;
}
}
return (tp);
}
static void
{
/*
* Avoid re-enabling the queue.
*/
noenable(q);
/*
* Make sure there is at most one outstanding request per queue.
*/
return;
} else {
return;
}
else
} else {
else
}
}
/*
* Timod is waiting on a downstream ioctl reply, come back soon
* to reschedule the write side service routine, which will check
* if the ioctl is done and another can proceed.
*/
static void
{
/*
* Make sure there is at most one outstanding request per wqueue.
*/
return;
}
/*
* Inspect the data on read queues starting from read queues passed as
* paramter (timod read queue) and traverse until
* q_next is NULL (stream head). Look for a TPI T_EXDATA_IND message
* reutrn 1 if found, 0 if not found.
*/
static int
{
queue_t *q;
q = rq;
/*
* We are going to walk q_next, so protect stream from plumbing
* changes.
*/
claimstr(q);
do {
/*
* Hold QLOCK while referencing data on queues
*/
/*
* Walk the messages on the queue looking
* for a possible T_EXDATA_IND
*/
sizeof (struct T_exdata_ind)) &&
== T_EXDATA_IND)) {
/* bp is T_EXDATA_IND */
releasestr(q); /* decrement sd_refcnt */
return (1); /* expdata is on a read queue */
}
}
releasestr(q);
return (0); /* no expdata on read queues */
}
static void
{
tp->tim_tcap_timoutid = 0;
TILOG("tim_tcap_timer: fired\n", 0);
tim_tcap_genreply(q, tp);
}
/*
* tim_tcap_genreply() is called either from timeout routine or when
* T_ERROR_ACK is received. In both cases it means that underlying
* transport doesn't provide T_CAPABILITY_REQ.
*/
static void
{
TILOG("timodrproc: tim_tcap_genreply\n", 0);
/* Save this information permanently in the module */
if (tp->tim_tcap_timoutid != 0) {
tp->tim_tcap_timoutid = 0;
}
/* Send T_INFO_REQ down */
/* Emulate TC1_INFO */
TILOG("emulate_tcap_ioc_req: sending T_INFO_REQ\n", 0);
} else {
tilog("emulate_tcap_req: allocb fail, "
"no recovery attmpt\n", 0);
}
} else {
/* Reply immediately */
sizeof (struct T_capability_ack), M_PCPROTO,
((struct T_capability_ack *)
} else {
tilog("timodwproc:allocb failed no "
"recovery attempt\n", 0);
}
}
}
static void
{
/* It is safe to call freemsg for NULL pointers */
}
/*
* All ioctl's may return more data than was specified by
* count arg. For TI_CAPABILITY count is treated as maximum data size.
*/
else {
/* Truncate message if too large */
}
}
/*
* Send M_IOCACK for errors.
*/
static void
{
/* Always send this to the read side of the queue */
q = RD(q);
} else if (error_prim == T_CAPABILITY_REQ) {
TILOG("timodrproc: T_ERROR_ACK/T_CAPABILITY_REQ\n", 0);
tim_tcap_genreply(q, tp);
} else {
TILOG("tim_send_ioc_error_ack: T_ERROR_ACK: prim %d\n",
switch (error_prim) {
default:
TILOG("timodrproc: Unknown T_ERROR_ACK: tlierror %d\n",
break;
case T_INFO_REQ:
case T_SVR4_OPTMGMT_REQ:
case T_OPTMGMT_REQ:
case O_T_BIND_REQ:
case T_BIND_REQ:
case T_UNBIND_REQ:
case T_ADDR_REQ:
case T_CAPABILITY_REQ:
TILOG("ioc_err_ack: T_ERROR_ACK: tlierror %x\n",
/* get saved ioctl msg and set values */
break;
}
}
}
/*
* Send reply to a usual message or ioctl message upstream.
* Should be called from the read side only.
*/
static void
{
/* Restore db_type - recover() might have changed it */
else {
}
}
/*
* Reply to TI_SYNC reequest without sending anything downstream.
*/
static void
{
if (tsr_flags == 0 ||
/*
* unsupported/bad flag setting
* or no flag set.
*/
TILOG("timodwproc: unsupported/bad flag setting %x\n",
return;
}
if ((tsr_flags & TSRF_QLEN_REQ) != 0)
if ((tsr_flags & TSRF_IS_EXP_IN_RCVBUF) != 0 &&
ti_expind_on_rdqueues(RD(q))) {
/*
* Expedited data is queued on
* the stream read side
*/
}
}
/*
* Send TPI message from IOCTL message, ssave original ioctl header and TPI
* message type. Should be called from write side only.
*/
static void
{
/*
* For TI_GETINFO, the attached message is a T_INFO_REQ
* For TI_SYNC, we generate the T_INFO_REQ message above
* For TI_CAPABILITY the attached message is either
* T_CAPABILITY_REQ or T_INFO_REQ.
* Among TPI request messages possible,
* T_INFO_REQ/T_CAPABILITY_ACK messages are a M_PCPROTO, rest
* are M_PROTO
*/
ioc_cmd == TI_CAPABILITY) {
} else {
}
/* Verify credentials in STREAM */
}
static void
{
}
tp->tim_peerlen = 0;
}