1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 30 #include "mt.h" 31 #include <rpc/trace.h> 32 #include <unistd.h> 33 #include <stdlib.h> 34 #include <errno.h> 35 #include <stropts.h> 36 #include <sys/stream.h> 37 #define _SUN_TPI_VERSION 2 38 #include <sys/tihdr.h> 39 #include <sys/timod.h> 40 #include <xti.h> 41 #include <assert.h> 42 #include <syslog.h> 43 #include "tx.h" 44 45 46 /* 47 * t_snd.c and t_sndv.c are very similar and contain common code. 48 * Any changes to either of them should be reviewed to see whether they 49 * are applicable to the other file. 50 */ 51 int 52 _tx_sndv(int fd, const struct t_iovec *tiov, unsigned int tiovcount, 53 int flags, int api_semantics) 54 { 55 struct T_data_req datareq; 56 struct strbuf ctlbuf, databuf; 57 unsigned int bytes_sent, bytes_remaining, bytes_to_send, nbytes; 58 char *curptr; 59 struct iovec iov[T_IOV_MAX]; 60 int iovcount; 61 char *dataptr; 62 int first_time; 63 struct _ti_user *tiptr; 64 int band; 65 int retval, lookevent; 66 int sv_errno; 67 int doputmsg = 0; 68 int32_t tsdu_limit; 69 70 trace5(TR_t_sndv, 0, fd, tiov, tiovcount, flags); 71 assert(api_semantics == TX_XTI_XNS5_API); 72 if ((tiptr = _t_checkfd(fd, 0, api_semantics)) == NULL) { 73 sv_errno = errno; 74 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 75 errno = sv_errno; 76 return (-1); 77 } 78 sig_mutex_lock(&tiptr->ti_lock); 79 80 if (tiptr->ti_servtype == T_CLTS) { 81 t_errno = TNOTSUPPORT; 82 sig_mutex_unlock(&tiptr->ti_lock); 83 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 84 return (-1); 85 } 86 87 if (tiovcount == 0 || tiovcount > T_IOV_MAX) { 88 t_errno = TBADDATA; 89 sig_mutex_unlock(&tiptr->ti_lock); 90 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 91 return (-1); 92 } 93 94 if (! (tiptr->ti_state == T_DATAXFER || 95 tiptr->ti_state == T_INREL)) { 96 t_errno = TOUTSTATE; 97 sig_mutex_unlock(&tiptr->ti_lock); 98 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 99 return (-1); 100 } 101 /* 102 * XXX 103 * Is it OK to do this TBADFLAG check when XTI spec 104 * is being extended with new and interesting flags 105 * everyday ? 106 */ 107 if ((flags & ~(TX_ALL_VALID_FLAGS)) != 0) { 108 t_errno = TBADFLAG; 109 sig_mutex_unlock(&tiptr->ti_lock); 110 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 111 return (-1); 112 } 113 if (flags & T_EXPEDITED) 114 tsdu_limit = tiptr->ti_etsdusize; 115 else { 116 /* normal data */ 117 tsdu_limit = tiptr->ti_tsdusize; 118 } 119 120 /* 121 * nbytes is the sum of the bytecounts in the tiov vector 122 * A value larger than INT_MAX is truncated to INT_MAX by 123 * _t_bytecount_upto_intmax() 124 */ 125 nbytes = _t_bytecount_upto_intmax(tiov, tiovcount); 126 127 if ((tsdu_limit > 0) && /* limit meaningful and ... */ 128 (nbytes > (uint32_t)tsdu_limit)) { 129 t_errno = TBADDATA; 130 sig_mutex_unlock(&tiptr->ti_lock); 131 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 132 return (-1); 133 } 134 135 /* 136 * Check for incoming disconnect only. XNS Issue 5 makes it optional 137 * to check for incoming orderly release 138 */ 139 lookevent = _t_look_locked(fd, tiptr, 0, api_semantics); 140 if (lookevent < 0) { 141 sv_errno = errno; 142 sig_mutex_unlock(&tiptr->ti_lock); 143 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 144 errno = sv_errno; 145 return (-1); 146 } 147 if (lookevent == T_DISCONNECT) { 148 t_errno = TLOOK; 149 sig_mutex_unlock(&tiptr->ti_lock); 150 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 151 return (-1); 152 } 153 154 /* sending zero length data when not allowed */ 155 if (nbytes == 0 && !(tiptr->ti_prov_flag & (SENDZERO|OLD_SENDZERO))) { 156 t_errno = TBADDATA; 157 sig_mutex_unlock(&tiptr->ti_lock); 158 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, flags); 159 return (-1); 160 } 161 162 doputmsg = (tiptr->ti_tsdusize != 0) || (flags & T_EXPEDITED); 163 164 if (doputmsg) { 165 /* 166 * Initialize ctlbuf for use in sending/receiving control part 167 * of the message. 168 */ 169 ctlbuf.maxlen = (int)sizeof (struct T_data_req); 170 ctlbuf.len = (int)sizeof (struct T_data_req); 171 ctlbuf.buf = (char *)&datareq; 172 173 band = TI_NORMAL; /* band 0 */ 174 if (flags & T_EXPEDITED) { 175 datareq.PRIM_type = T_EXDATA_REQ; 176 if (! (tiptr->ti_prov_flag & EXPINLINE)) 177 band = TI_EXPEDITED; /* band > 0 */ 178 } else 179 datareq.PRIM_type = T_DATA_REQ; 180 /* 181 * Allocate a databuffer into which we will gather the 182 * input vector data, and make a call to putmsg(). We 183 * do this since we don't have the equivalent of a putmsgv() 184 */ 185 if (nbytes != 0) { 186 if ((dataptr = malloc((size_t)nbytes)) == NULL) { 187 sv_errno = errno; 188 sig_mutex_unlock(&tiptr->ti_lock); 189 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, 190 flags); 191 errno = sv_errno; 192 t_errno = TSYSERR; 193 return (-1); /* error */ 194 } 195 /* 196 * Gather the input buffers, into the single linear 197 * buffer allocated above, while taking care to see 198 * that no more than INT_MAX bytes will be copied. 199 */ 200 _t_gather(dataptr, tiov, tiovcount); 201 curptr = dataptr; /* Initialize for subsequent use */ 202 } else { 203 dataptr = NULL; 204 curptr = NULL; 205 } 206 } 207 208 bytes_remaining = nbytes; 209 /* 210 * Calls to send data (write or putmsg) can potentially 211 * block, for MT case, we drop the lock and enable signals here 212 * and acquire it back 213 */ 214 sig_mutex_unlock(&tiptr->ti_lock); 215 first_time = 1; 216 do { 217 bytes_to_send = bytes_remaining; 218 if (doputmsg) { 219 /* 220 * transport provider supports TSDU concept 221 * (unlike TCP) or it is expedited data. 222 * In this case do the fragmentation 223 */ 224 if (bytes_to_send > (unsigned int)tiptr->ti_maxpsz) { 225 datareq.MORE_flag = 1; 226 bytes_to_send = (unsigned int)tiptr->ti_maxpsz; 227 } else { 228 if (flags&T_MORE) 229 datareq.MORE_flag = 1; 230 else 231 datareq.MORE_flag = 0; 232 } 233 databuf.maxlen = bytes_to_send; 234 databuf.len = bytes_to_send; 235 databuf.buf = curptr; 236 retval = putpmsg(fd, &ctlbuf, &databuf, band, MSG_BAND); 237 if (retval == 0) { 238 bytes_sent = bytes_to_send; 239 curptr = curptr + bytes_sent; 240 } 241 } else { 242 /* 243 * transport provider does *not* support TSDU concept 244 * (e.g. TCP) and it is not expedited data. A 245 * perf. optimization is used. Note: the T_MORE 246 * flag is ignored here even if set by the user. 247 */ 248 /* 249 * The first time, setup the tiovec for doing a writev 250 * call. We assume that T_IOV_MAX <= IOV_MAX. 251 * Since writev may return a partial count, we need 252 * the loop. After the first time, we just adjust 253 * the iov vector to not include the already 254 * written bytes. 255 */ 256 if (first_time) { 257 first_time = 0; 258 _t_copy_tiov_to_iov(tiov, tiovcount, iov, 259 &iovcount); 260 } else { 261 /* 262 * bytes_sent - value set below in the previous 263 * iteration of the loop is used now. 264 */ 265 _t_adjust_iov(bytes_sent, iov, &iovcount); 266 } 267 retval = (int)writev(fd, iov, iovcount); 268 if (retval >= 0) { 269 /* Amount that was actually sent */ 270 bytes_sent = retval; 271 } 272 } 273 274 if (retval < 0) { 275 if (nbytes == bytes_remaining) { 276 /* 277 * Error on *first* putmsg/write attempt. 278 * Return appropriate error 279 */ 280 if (errno == EAGAIN) 281 t_errno = TFLOW; 282 else 283 t_errno = TSYSERR; 284 if (dataptr) 285 free(dataptr); 286 sv_errno = errno; 287 trace5(TR_t_sndv, 1, fd, tiov, tiovcount, 288 flags); 289 errno = sv_errno; 290 return (-1); /* return error */ 291 } else { 292 /* 293 * Not the first putmsg/write 294 * [ partial completion of t_snd() case. 295 * 296 * Error on putmsg/write attempt but 297 * some data was transmitted so don't 298 * return error. Don't attempt to 299 * send more (break from loop) but 300 * return OK. 301 */ 302 break; 303 } 304 } 305 bytes_remaining = bytes_remaining - bytes_sent; 306 } while (bytes_remaining != 0); 307 308 if (dataptr != NULL) 309 free(dataptr); 310 _T_TX_NEXTSTATE(T_SND, tiptr, "t_snd: invalid state event T_SND"); 311 sv_errno = errno; 312 trace5(TR_t_sndv, 0, fd, tiov, tiovcount, flags); 313 errno = sv_errno; 314 return (nbytes - bytes_remaining); 315 } 316