1 /* $NetBSD: svc_vc.c,v 1.30 2013/03/11 20:19:29 tron Exp $ */
4 * Copyright (c) 2010, Oracle America, Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 * * Neither the name of the "Oracle America, Inc." nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/cdefs.h>
35 #if defined(LIBC_SCCS) && !defined(lint)
37 static char *sccsid
= "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
38 static char *sccsid
= "@(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC";
40 __RCSID("$NetBSD: svc_vc.c,v 1.30 2013/03/11 20:19:29 tron Exp $");
45 * svc_vc.c, Server side for Connection Oriented based RPC.
47 * Actually implements two flavors of transporter -
48 * a tcp rendezvouser (a listner and connection establisher)
49 * and a record/tcp stream.
52 #include "namespace.h"
53 #include "reentrant.h"
54 #include <sys/types.h>
55 #include <sys/param.h>
57 #include <sys/socket.h>
60 #include <netinet/in.h>
73 #include "svc_fdset.h"
74 #include "rpc_internal.h"
77 __weak_alias(svc_fd_create
,_svc_fd_create
)
78 __weak_alias(svc_vc_create
,_svc_vc_create
)
82 extern rwlock_t svc_fd_lock
;
85 static SVCXPRT
*makefd_xprt(int, u_int
, u_int
);
86 static bool_t
rendezvous_request(SVCXPRT
*, struct rpc_msg
*);
87 static enum xprt_stat
rendezvous_stat(SVCXPRT
*);
88 static void svc_vc_destroy(SVCXPRT
*);
89 static void __svc_vc_dodestroy(SVCXPRT
*);
90 static int read_vc(caddr_t
, caddr_t
, int);
91 static int write_vc(caddr_t
, caddr_t
, int);
92 static enum xprt_stat
svc_vc_stat(SVCXPRT
*);
93 static bool_t
svc_vc_recv(SVCXPRT
*, struct rpc_msg
*);
94 static bool_t
svc_vc_getargs(SVCXPRT
*, xdrproc_t
, caddr_t
);
95 static bool_t
svc_vc_freeargs(SVCXPRT
*, xdrproc_t
, caddr_t
);
96 static bool_t
svc_vc_reply(SVCXPRT
*, struct rpc_msg
*);
97 static void svc_vc_rendezvous_ops(SVCXPRT
*);
98 static void svc_vc_ops(SVCXPRT
*);
99 static bool_t
svc_vc_control(SVCXPRT
*, const u_int
, void *);
100 static bool_t
svc_vc_rendezvous_control(SVCXPRT
*, const u_int
, void *);
102 struct cf_rendezvous
{ /* kept in xprt->xp_p1 for rendezvouser */
108 struct cf_conn
{ /* kept in xprt->xp_p1 for actual connection */
109 enum xprt_stat strm_stat
;
112 char verf_body
[MAX_AUTH_BYTES
];
117 struct timeval last_recv_time
;
122 * xprt = svc_vc_create(sock, send_buf_size, recv_buf_size);
124 * Creates, registers, and returns a (rpc) tcp based transporter.
125 * Once *xprt is initialized, it is registered as a transporter
126 * see (svc.h, xprt_register). This routine returns
127 * a NULL if a problem occurred.
129 * The filedescriptor passed in is expected to refer to a bound, but
130 * not yet connected socket.
132 * Since streams do buffered io similar to stdio, the caller can specify
133 * how big the send and receive buffers are via the second and third parms;
134 * 0 => use the system default.
137 svc_vc_create(int fd
, u_int sendsize
, u_int recvsize
)
140 struct cf_rendezvous
*r
= NULL
;
141 struct __rpc_sockinfo si
;
142 struct sockaddr_storage sslocal
;
146 if (!__rpc_fd2sockinfo(fd
, &si
))
149 r
= mem_alloc(sizeof(*r
));
151 warn("%s: out of memory", __func__
);
154 r
->sendsize
= __rpc_get_t_size(si
.si_af
, si
.si_proto
, (int)sendsize
);
155 r
->recvsize
= __rpc_get_t_size(si
.si_af
, si
.si_proto
, (int)recvsize
);
156 r
->maxrec
= __svc_maxrec
;
157 xprt
= mem_alloc(sizeof(SVCXPRT
));
159 warn("%s: out of memory", __func__
);
160 goto cleanup_svc_vc_create
;
163 xprt
->xp_p1
= (caddr_t
)(void *)r
;
166 xprt
->xp_verf
= _null_auth
;
167 svc_vc_rendezvous_ops(xprt
);
168 xprt
->xp_port
= (u_short
)-1; /* It is the rendezvouser */
171 slen
= sizeof (struct sockaddr_storage
);
172 if (getsockname(fd
, (struct sockaddr
*)(void *)&sslocal
, &slen
) < 0) {
173 warn("%s: could not retrieve local addr", __func__
);
174 goto cleanup_svc_vc_create
;
178 * We want to be able to check credentials on local sockets.
180 if (sslocal
.ss_family
== AF_LOCAL
)
181 if (setsockopt(fd
, 0, LOCAL_CREDS
, &one
, (socklen_t
)sizeof one
)
183 goto cleanup_svc_vc_create
;
185 xprt
->xp_ltaddr
.maxlen
= xprt
->xp_ltaddr
.len
= sslocal
.ss_len
;
186 xprt
->xp_ltaddr
.buf
= mem_alloc((size_t)sslocal
.ss_len
);
187 if (xprt
->xp_ltaddr
.buf
== NULL
) {
188 warn("%s: out of memory", __func__
);
189 goto cleanup_svc_vc_create
;
191 memcpy(xprt
->xp_ltaddr
.buf
, &sslocal
, (size_t)sslocal
.ss_len
);
193 xprt
->xp_rtaddr
.maxlen
= sizeof (struct sockaddr_storage
);
194 if (!xprt_register(xprt
))
195 goto cleanup_svc_vc_create
;
197 cleanup_svc_vc_create
:
199 mem_free(xprt
, sizeof(*xprt
));
201 mem_free(r
, sizeof(*r
));
206 * Like svtcp_create(), except the routine takes any *open* UNIX file
207 * descriptor as its first input.
210 svc_fd_create(int fd
, u_int sendsize
, u_int recvsize
)
212 struct sockaddr_storage ss
;
216 _DIAGASSERT(fd
!= -1);
218 ret
= makefd_xprt(fd
, sendsize
, recvsize
);
222 slen
= sizeof (struct sockaddr_storage
);
223 if (getsockname(fd
, (struct sockaddr
*)(void *)&ss
, &slen
) < 0) {
224 warn("%s: could not retrieve local addr", __func__
);
227 ret
->xp_ltaddr
.maxlen
= ret
->xp_ltaddr
.len
= ss
.ss_len
;
228 ret
->xp_ltaddr
.buf
= mem_alloc((size_t)ss
.ss_len
);
229 if (ret
->xp_ltaddr
.buf
== NULL
) {
230 warn("%s: out of memory", __func__
);
233 memcpy(ret
->xp_ltaddr
.buf
, &ss
, (size_t)ss
.ss_len
);
235 slen
= sizeof (struct sockaddr_storage
);
236 if (getpeername(fd
, (struct sockaddr
*)(void *)&ss
, &slen
) < 0) {
237 warn("%s: could not retrieve remote addr", __func__
);
240 ret
->xp_rtaddr
.maxlen
= ret
->xp_rtaddr
.len
= ss
.ss_len
;
241 ret
->xp_rtaddr
.buf
= mem_alloc((size_t)ss
.ss_len
);
242 if (ret
->xp_rtaddr
.buf
== NULL
) {
243 warn("%s: out of memory", __func__
);
246 memcpy(ret
->xp_rtaddr
.buf
, &ss
, (size_t)ss
.ss_len
);
248 if (ss
.ss_family
== AF_INET
) {
249 ret
->xp_raddr
= *(struct sockaddr_in
*)ret
->xp_rtaddr
.buf
;
250 ret
->xp_addrlen
= sizeof (struct sockaddr_in
);
257 if (ret
->xp_ltaddr
.buf
!= NULL
)
258 mem_free(ret
->xp_ltaddr
.buf
, rep
->xp_ltaddr
.maxlen
);
264 makefd_xprt(int fd
, u_int sendsize
, u_int recvsize
)
269 struct __rpc_sockinfo si
;
271 _DIAGASSERT(fd
!= -1);
273 xprt
= mem_alloc(sizeof(SVCXPRT
));
276 memset(xprt
, 0, sizeof *xprt
);
277 cd
= mem_alloc(sizeof(struct cf_conn
));
280 cd
->strm_stat
= XPRT_IDLE
;
281 xdrrec_create(&(cd
->xdrs
), sendsize
, recvsize
,
282 (caddr_t
)(void *)xprt
, read_vc
, write_vc
);
283 xprt
->xp_p1
= (caddr_t
)(void *)cd
;
284 xprt
->xp_verf
.oa_base
= cd
->verf_body
;
285 svc_vc_ops(xprt
); /* truely deals with calls */
286 xprt
->xp_port
= 0; /* this is a connection, not a rendezvouser */
288 if (__rpc_fd2sockinfo(fd
, &si
) && __rpc_sockinfo2netid(&si
, &netid
))
289 if ((xprt
->xp_netid
= strdup(netid
)) == NULL
)
292 if (!xprt_register(xprt
))
297 warn("svc_tcp: makefd_xprt");
300 mem_free(xprt
, sizeof(SVCXPRT
));
306 rendezvous_request(SVCXPRT
*xprt
, struct rpc_msg
*msg
)
309 struct cf_rendezvous
*r
;
311 struct sockaddr_storage addr
;
313 struct __rpc_sockinfo si
;
317 _DIAGASSERT(xprt
!= NULL
);
318 _DIAGASSERT(msg
!= NULL
);
320 r
= (struct cf_rendezvous
*)xprt
->xp_p1
;
323 if ((sock
= accept(xprt
->xp_fd
, (struct sockaddr
*)(void *)&addr
,
328 * Clean out the most idle file descriptor when we're
331 if (errno
== EMFILE
|| errno
== ENFILE
) {
332 cleanfds
= *get_fdset();
333 if (__svc_clean_idle(&cleanfds
, 0, FALSE
))
339 * make a new transporter (re-uses xprt)
341 newxprt
= makefd_xprt(sock
, r
->sendsize
, r
->recvsize
);
344 newxprt
->xp_rtaddr
.buf
= mem_alloc(len
);
345 if (newxprt
->xp_rtaddr
.buf
== NULL
)
347 memcpy(newxprt
->xp_rtaddr
.buf
, &addr
, len
);
348 newxprt
->xp_rtaddr
.len
= len
;
350 if (addr
.ss_family
== AF_INET
) {
351 newxprt
->xp_raddr
= *(struct sockaddr_in
*)newxprt
->xp_rtaddr
.buf
;
352 newxprt
->xp_addrlen
= sizeof (struct sockaddr_in
);
355 if (__rpc_fd2sockinfo(sock
, &si
))
356 __rpc_setnodelay(sock
, &si
);
358 cd
= (struct cf_conn
*)newxprt
->xp_p1
;
360 cd
->recvsize
= r
->recvsize
;
361 cd
->sendsize
= r
->sendsize
;
362 cd
->maxrec
= r
->maxrec
;
364 if (cd
->maxrec
!= 0) {
365 flags
= fcntl(sock
, F_GETFL
, 0);
368 if (fcntl(sock
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
370 if (cd
->recvsize
> (u_int
)cd
->maxrec
)
371 cd
->recvsize
= cd
->maxrec
;
373 __xdrrec_setnonblock(&cd
->xdrs
, cd
->maxrec
);
375 cd
->nonblock
= FALSE
;
377 (void)gettimeofday(&cd
->last_recv_time
, NULL
);
379 return FALSE
; /* there is never an rpc msg to be processed */
382 return FALSE
; /* there was an error */
386 static enum xprt_stat
387 rendezvous_stat(SVCXPRT
*xprt
)
394 svc_vc_destroy(SVCXPRT
*xprt
)
396 _DIAGASSERT(xprt
!= NULL
);
398 xprt_unregister(xprt
);
399 __svc_vc_dodestroy(xprt
);
403 __svc_vc_dodestroy(SVCXPRT
*xprt
)
406 struct cf_rendezvous
*r
;
408 cd
= (struct cf_conn
*)xprt
->xp_p1
;
410 if (xprt
->xp_fd
!= RPC_ANYFD
)
411 (void)close(xprt
->xp_fd
);
412 if (xprt
->xp_port
!= 0) {
413 /* a rendezvouser socket */
414 r
= (struct cf_rendezvous
*)xprt
->xp_p1
;
415 mem_free(r
, sizeof (struct cf_rendezvous
));
418 /* an actual connection socket */
419 XDR_DESTROY(&(cd
->xdrs
));
420 mem_free(cd
, sizeof(struct cf_conn
));
422 if (xprt
->xp_rtaddr
.buf
)
423 mem_free(xprt
->xp_rtaddr
.buf
, xprt
->xp_rtaddr
.maxlen
);
424 if (xprt
->xp_ltaddr
.buf
)
425 mem_free(xprt
->xp_ltaddr
.buf
, xprt
->xp_ltaddr
.maxlen
);
429 free(xprt
->xp_netid
);
430 mem_free(xprt
, sizeof(SVCXPRT
));
435 svc_vc_control(SVCXPRT
*xprt
, const u_int rq
, void *in
)
442 svc_vc_rendezvous_control(SVCXPRT
*xprt
, const u_int rq
, void *in
)
444 struct cf_rendezvous
*cfp
;
446 cfp
= (struct cf_rendezvous
*)xprt
->xp_p1
;
450 case SVCGET_CONNMAXREC
:
451 *(int *)in
= cfp
->maxrec
;
453 case SVCSET_CONNMAXREC
:
454 cfp
->maxrec
= *(int *)in
;
463 * reads data from the tcp connection.
464 * any error is fatal and the connection is closed.
465 * (And a read of zero bytes is a half closed stream => error.)
466 * All read operations timeout after 35 seconds. A timeout is
467 * fatal for the connection.
470 read_vc(caddr_t xprtp
, caddr_t buf
, int len
)
474 struct pollfd pollfd
;
482 static const struct timespec ts
= { 35, 0 };
484 xprt
= (SVCXPRT
*)(void *)xprtp
;
485 _DIAGASSERT(xprt
!= NULL
);
489 sa
= (struct sockaddr
*)xprt
->xp_rtaddr
.buf
;
490 if (sa
->sa_family
== AF_LOCAL
&& xprt
->xp_p2
== NULL
) {
491 memset(&msg
, 0, sizeof msg
);
492 crmsgsize
= CMSG_SPACE(SOCKCREDSIZE(NGROUPS
));
493 crmsg
= malloc(crmsgsize
);
496 memset(crmsg
, 0, crmsgsize
);
498 msg
.msg_control
= crmsg
;
499 msg
.msg_controllen
= crmsgsize
;
501 if (recvmsg(sock
, &msg
, 0) < 0)
504 if (msg
.msg_controllen
== 0 ||
505 (msg
.msg_flags
& MSG_CTRUNC
) != 0)
508 cmp
= CMSG_FIRSTHDR(&msg
);
509 if (cmp
->cmsg_level
!= SOL_SOCKET
||
510 cmp
->cmsg_type
!= SCM_CREDS
)
513 sc
= (struct sockcred
*)(void *)CMSG_DATA(cmp
);
515 xprt
->xp_p2
= mem_alloc(SOCKCREDSIZE(sc
->sc_ngroups
));
516 if (xprt
->xp_p2
== NULL
)
519 memcpy(xprt
->xp_p2
, sc
, SOCKCREDSIZE(sc
->sc_ngroups
));
524 cfp
= (struct cf_conn
*)xprt
->xp_p1
;
527 len
= (int)read(sock
, buf
, (size_t)len
);
535 gettimeofday(&cfp
->last_recv_time
, NULL
);
541 pollfd
.events
= POLLIN
;
542 switch (pollts(&pollfd
, 1, &ts
, NULL
)) {
544 if (errno
== EINTR
) {
554 } while ((pollfd
.revents
& POLLIN
) == 0);
556 if ((len
= (int)read(sock
, buf
, (size_t)len
)) > 0) {
557 gettimeofday(&cfp
->last_recv_time
, NULL
);
564 ((struct cf_conn
*)(xprt
->xp_p1
))->strm_stat
= XPRT_DIED
;
569 * writes data to the tcp connection.
570 * Any error is fatal and the connection is closed.
573 write_vc(caddr_t xprtp
, caddr_t buf
, int len
)
578 struct timeval tv0
, tv1
;
580 xprt
= (SVCXPRT
*)(void *)xprtp
;
581 _DIAGASSERT(xprt
!= NULL
);
583 cd
= (struct cf_conn
*)xprt
->xp_p1
;
586 gettimeofday(&tv0
, NULL
);
588 for (cnt
= len
; cnt
> 0; cnt
-= i
, buf
+= i
) {
589 if ((i
= (int)write(xprt
->xp_fd
, buf
, (size_t)cnt
)) < 0) {
590 if (errno
!= EAGAIN
|| !cd
->nonblock
) {
591 cd
->strm_stat
= XPRT_DIED
;
596 * For non-blocking connections, do not
597 * take more than 2 seconds writing the
600 * XXX 2 is an arbitrary amount.
602 gettimeofday(&tv1
, NULL
);
603 if (tv1
.tv_sec
- tv0
.tv_sec
>= 2) {
604 cd
->strm_stat
= XPRT_DIED
;
614 static enum xprt_stat
615 svc_vc_stat(SVCXPRT
*xprt
)
619 _DIAGASSERT(xprt
!= NULL
);
621 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
623 if (cd
->strm_stat
== XPRT_DIED
)
625 if (! xdrrec_eof(&(cd
->xdrs
)))
626 return XPRT_MOREREQS
;
631 svc_vc_recv(SVCXPRT
*xprt
, struct rpc_msg
*msg
)
636 _DIAGASSERT(xprt
!= NULL
);
637 _DIAGASSERT(msg
!= NULL
);
639 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
643 if (!__xdrrec_getrec(xdrs
, &cd
->strm_stat
, TRUE
))
647 xdrs
->x_op
= XDR_DECODE
;
648 (void)xdrrec_skiprecord(xdrs
);
650 if (xdr_callmsg(xdrs
, msg
)) {
651 cd
->x_id
= msg
->rm_xid
;
654 cd
->strm_stat
= XPRT_DIED
;
659 svc_vc_getargs(SVCXPRT
*xprt
, xdrproc_t xdr_args
, caddr_t args_ptr
)
662 _DIAGASSERT(xprt
!= NULL
);
663 /* args_ptr may be NULL */
665 return (*xdr_args
)(&(((struct cf_conn
*)(xprt
->xp_p1
))->xdrs
),
670 svc_vc_freeargs(SVCXPRT
*xprt
, xdrproc_t xdr_args
, caddr_t args_ptr
)
674 _DIAGASSERT(xprt
!= NULL
);
675 /* args_ptr may be NULL */
677 xdrs
= &(((struct cf_conn
*)(xprt
->xp_p1
))->xdrs
);
679 xdrs
->x_op
= XDR_FREE
;
680 return (*xdr_args
)(xdrs
, args_ptr
);
684 svc_vc_reply(SVCXPRT
*xprt
, struct rpc_msg
*msg
)
690 _DIAGASSERT(xprt
!= NULL
);
691 _DIAGASSERT(msg
!= NULL
);
693 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
696 xdrs
->x_op
= XDR_ENCODE
;
697 msg
->rm_xid
= cd
->x_id
;
698 rstat
= xdr_replymsg(xdrs
, msg
);
699 (void)xdrrec_endofrecord(xdrs
, TRUE
);
704 svc_vc_ops(SVCXPRT
*xprt
)
706 static struct xp_ops ops
;
707 static struct xp_ops2 ops2
;
709 extern mutex_t ops_lock
;
712 /* VARIABLES PROTECTED BY ops_lock: ops, ops2 */
714 mutex_lock(&ops_lock
);
715 if (ops
.xp_recv
== NULL
) {
716 ops
.xp_recv
= svc_vc_recv
;
717 ops
.xp_stat
= svc_vc_stat
;
718 ops
.xp_getargs
= svc_vc_getargs
;
719 ops
.xp_reply
= svc_vc_reply
;
720 ops
.xp_freeargs
= svc_vc_freeargs
;
721 ops
.xp_destroy
= svc_vc_destroy
;
722 ops2
.xp_control
= svc_vc_control
;
725 xprt
->xp_ops2
= &ops2
;
726 mutex_unlock(&ops_lock
);
730 svc_vc_rendezvous_ops(SVCXPRT
*xprt
)
732 static struct xp_ops ops
;
733 static struct xp_ops2 ops2
;
735 extern mutex_t ops_lock
;
737 mutex_lock(&ops_lock
);
738 if (ops
.xp_recv
== NULL
) {
739 ops
.xp_recv
= rendezvous_request
;
740 ops
.xp_stat
= rendezvous_stat
;
742 (bool_t (*)(SVCXPRT
*, xdrproc_t
, caddr_t
))abort
;
744 (bool_t (*)(SVCXPRT
*, struct rpc_msg
*))abort
;
746 (bool_t (*)(SVCXPRT
*, xdrproc_t
, caddr_t
))abort
;
747 ops
.xp_destroy
= svc_vc_destroy
;
748 ops2
.xp_control
= svc_vc_rendezvous_control
;
751 xprt
->xp_ops2
= &ops2
;
752 mutex_unlock(&ops_lock
);
756 * Destroy xprts that have not have had any activity in 'timeout' seconds.
757 * If 'cleanblock' is true, blocking connections (the default) are also
758 * cleaned. If timeout is 0, the least active connection is picked.
761 __svc_clean_idle(fd_set
*fds
, int timeout
, bool_t cleanblock
)
764 SVCXPRT
*xprt
, *least_active
;
765 struct timeval tv
, tdiff
, tmax
;
768 gettimeofday(&tv
, NULL
);
769 tmax
.tv_sec
= tmax
.tv_usec
= 0;
771 rwlock_wrlock(&svc_fd_lock
);
772 for (i
= ncleaned
= 0; i
<= svc_maxfd
; i
++) {
773 if (FD_ISSET(i
, fds
)) {
774 xprt
= __svc_xports
[i
];
775 if (xprt
== NULL
|| xprt
->xp_ops
== NULL
||
776 xprt
->xp_ops
->xp_recv
!= svc_vc_recv
)
778 cd
= (struct cf_conn
*)xprt
->xp_p1
;
779 if (!cleanblock
&& !cd
->nonblock
)
782 timersub(&tv
, &cd
->last_recv_time
, &tdiff
);
783 if (timercmp(&tdiff
, &tmax
, >)) {
789 if (tv
.tv_sec
- cd
->last_recv_time
.tv_sec
> timeout
) {
790 __xprt_unregister_unlocked(xprt
);
791 __svc_vc_dodestroy(xprt
);
796 if (timeout
== 0 && least_active
!= NULL
) {
797 __xprt_unregister_unlocked(least_active
);
798 __svc_vc_dodestroy(least_active
);
801 rwlock_unlock(&svc_fd_lock
);
802 return ncleaned
> 0 ? TRUE
: FALSE
;