summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2017-09-08 22:21:47 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2017-09-08 22:24:24 +0000
commit1ee89f3ab0fcdfbd337595dc3b86031c68704474 (patch)
tree4609a12aafbebfbc199fe7894738b69aa07b627b
parenta7395cadf27a69889b3c5acb6d39491a2e76a1f6 (diff)
downloadillumos-joyent-1ee89f3ab0fcdfbd337595dc3b86031c68704474.tar.gz
OS-6276 lx set TCP_KEEPINTVL fails
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Approved by: Patrick Mooney <patrick.mooney@joyent.com>
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_socket.c82
1 files changed, 78 insertions, 4 deletions
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_socket.c b/usr/src/uts/common/brand/lx/syscall/lx_socket.c
index 81e39199cf..251f9c2d8c 100644
--- a/usr/src/uts/common/brand/lx/syscall/lx_socket.c
+++ b/usr/src/uts/common/brand/lx/syscall/lx_socket.c
@@ -2866,6 +2866,9 @@ lx_setsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t optlen)
{
int error;
lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_tcp_sockopts);
+ cred_t *cr = CRED();
+ uint32_t rto_max, abrt_thresh;
+ boolean_t abrt_changed = B_FALSE, rto_max_changed = B_FALSE;
if (optname == LX_TCP_DEFER_ACCEPT) {
int *intval;
@@ -2890,13 +2893,13 @@ lx_setsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t optlen)
if (*intval > 0) {
error = socket_setsockopt(so, SOL_FILTER, FIL_ATTACH,
- dfp, 9, CRED());
+ dfp, 9, cr);
if (error == EEXIST) {
error = 0;
}
} else {
error = socket_setsockopt(so, SOL_FILTER, FIL_DETACH,
- dfp, 9, CRED());
+ dfp, 9, cr);
if (error == ENXIO) {
error = 0;
}
@@ -2909,8 +2912,79 @@ lx_setsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t optlen)
return (ENOPROTOOPT);
}
- error = socket_setsockopt(so, IPPROTO_TCP, optname, optval, optlen,
- CRED());
+ if (optname == TCP_KEEPINTVL) {
+ /*
+ * When setting TCP_KEEPINTVL there is an unfortunate set of
+ * dependencies. TCP_KEEPINTVL must be <= TCP_RTO_MAX and
+ * TCP_RTO_MAX must be <= TCP_ABORT_THRESHOLD. Thus, we may
+ * have to increase one or both of these in order to increase
+ * TCP_KEEPINTVL. Note that TCP_KEEPINTVL is passed in seconds
+ * but TCP_RTO_MAX and TCP_ABORT_THRESHOLD are in milliseconds.
+ * Also note that we currently make no attempt to handle
+ * concurrent application threads simultaneously changing
+ * TCP_KEEPINTVL, since that is unlikely. We could revisit
+ * locking if it ever becomes an issue.
+ */
+ uint32_t new_val = *(uint_t *)optval * 1000;
+ uint32_t len;
+
+ /*
+ * Linux limits this to 32k, so we do too. However, anything
+ * over 2 hours (7200000 ms) will fail anyway due to the
+ * system-wide default (see "_rexmit_interval_max" in
+ * tcp_tunables.c). Our 2 hour default seems reasonable as a
+ * practical limit for now.
+ */
+ if (*(uint_t *)optval > SHRT_MAX)
+ return (EINVAL);
+
+ len = sizeof (rto_max);
+ if ((error = socket_getsockopt(so, IPPROTO_TCP, TCP_RTO_MAX,
+ &rto_max, &len, 0, cr)) != 0)
+ return (error);
+ len = sizeof (abrt_thresh);
+ if ((error = socket_getsockopt(so, IPPROTO_TCP,
+ TCP_ABORT_THRESHOLD, &abrt_thresh, &len, 0, cr)) != 0)
+ return (error);
+
+ if (new_val > abrt_thresh) {
+ error = socket_setsockopt(so, IPPROTO_TCP,
+ TCP_ABORT_THRESHOLD, &new_val, sizeof (new_val),
+ cr);
+ if (error != 0)
+ goto fail;
+ abrt_changed = B_TRUE;
+ }
+ if (new_val > rto_max) {
+ error = socket_setsockopt(so, IPPROTO_TCP,
+ TCP_RTO_MAX, &new_val, sizeof (new_val), cr);
+ if (error != 0)
+ goto fail;
+ rto_max_changed = B_TRUE;
+ }
+ }
+
+ error = socket_setsockopt(so, IPPROTO_TCP, optname, optval, optlen, cr);
+
+fail:
+ if (error != 0 && optname == TCP_KEEPINTVL) {
+ /*
+ * If changing TCP_KEEPINTVL failed then we may need to
+ * restore the previous values for TCP_ABORT_THRESHOLD and
+ * TCP_RTO_MAX.
+ */
+ if (rto_max_changed) {
+ (void) socket_setsockopt(so, IPPROTO_TCP,
+ TCP_RTO_MAX, &rto_max,
+ sizeof (rto_max), cr);
+ }
+ if (abrt_changed) {
+ (void) socket_setsockopt(so, IPPROTO_TCP,
+ TCP_ABORT_THRESHOLD, &abrt_thresh,
+ sizeof (abrt_thresh), cr);
+ }
+ }
+
return (error);
}