Make read and write respect SO_RCVTIMEO and SO_SNDTIMEO

PiperOrigin-RevId: 226387521
Change-Id: I0579ab262320fde6c72d2994dd38437f01a99ea5
This commit is contained in:
Ian Gudger
2018-12-20 13:48:52 -08:00
committed by Shentubot
parent 194ef586fc
commit f6274804e1
3 changed files with 96 additions and 9 deletions
+22 -1
View File
@@ -15,11 +15,15 @@
package linux
import (
"time"
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserror"
"gvisor.googlesource.com/gvisor/pkg/waiter"
@@ -259,6 +263,20 @@ func readv(t *kernel.Task, f *fs.File, dst usermem.IOSequence) (int64, error) {
return n, err
}
// Sockets support read timeouts.
var haveDeadline bool
var deadline ktime.Time
if s, ok := f.FileOperations.(socket.Socket); ok {
dl := s.RecvTimeout()
if dl < 0 && err == syserror.ErrWouldBlock {
return n, err
}
if dl > 0 {
deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond)
haveDeadline = true
}
}
// Register for notifications.
w, ch := waiter.NewChannelEntry(nil)
f.EventRegister(&w, EventMaskRead)
@@ -277,7 +295,10 @@ func readv(t *kernel.Task, f *fs.File, dst usermem.IOSequence) (int64, error) {
}
// Wait for a notification that we should retry.
if err = t.Block(ch); err != nil {
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
if err == syserror.ETIMEDOUT {
err = syserror.ErrWouldBlock
}
break
}
}
+22 -1
View File
@@ -15,11 +15,15 @@
package linux
import (
"time"
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserror"
"gvisor.googlesource.com/gvisor/pkg/waiter"
@@ -263,6 +267,20 @@ func writev(t *kernel.Task, f *fs.File, src usermem.IOSequence) (int64, error) {
return n, err
}
// Sockets support write timeouts.
var haveDeadline bool
var deadline ktime.Time
if s, ok := f.FileOperations.(socket.Socket); ok {
dl := s.SendTimeout()
if dl < 0 && err == syserror.ErrWouldBlock {
return n, err
}
if dl > 0 {
deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond)
haveDeadline = true
}
}
// Register for notifications.
w, ch := waiter.NewChannelEntry(nil)
f.EventRegister(&w, EventMaskWrite)
@@ -281,7 +299,10 @@ func writev(t *kernel.Task, f *fs.File, src usermem.IOSequence) (int64, error) {
}
// Wait for a notification that we should retry.
if err = t.Block(ch); err != nil {
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
if err == syserror.ETIMEDOUT {
err = syserror.ErrWouldBlock
}
break
}
}
+52 -7
View File
@@ -280,7 +280,22 @@ TEST_P(AllSocketPairTest, SndBufSucceeds) {
EXPECT_GT(size, 0);
}
TEST_P(AllSocketPairTest, RecvTimeoutSucceeds) {
TEST_P(AllSocketPairTest, RecvTimeoutReadSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = 10
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
char buf[20] = {};
EXPECT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, RecvTimeoutRecvSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
@@ -295,7 +310,7 @@ TEST_P(AllSocketPairTest, RecvTimeoutSucceeds) {
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, RecvTimeoutOneSecondSucceeds) {
TEST_P(AllSocketPairTest, RecvTimeoutRecvOneSecondSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
@@ -310,7 +325,7 @@ TEST_P(AllSocketPairTest, RecvTimeoutOneSecondSucceeds) {
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, RecvmsgTimeoutSucceeds) {
TEST_P(AllSocketPairTest, RecvTimeoutRecvmsgSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
@@ -332,6 +347,21 @@ TEST_P(AllSocketPairTest, RecvmsgTimeoutSucceeds) {
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, SendTimeoutAllowsWrite) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = 10
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
char buf[20] = {};
ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
SyscallSucceedsWithValue(sizeof(buf)));
}
TEST_P(AllSocketPairTest, SendTimeoutAllowsSend) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
@@ -347,7 +377,7 @@ TEST_P(AllSocketPairTest, SendTimeoutAllowsSend) {
SyscallSucceedsWithValue(sizeof(buf)));
}
TEST_P(AllSocketPairTest, SendmsgTimeoutAllowsSend) {
TEST_P(AllSocketPairTest, SendTimeoutAllowsSendmsg) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
@@ -389,7 +419,7 @@ TEST_P(AllSocketPairTest, SoRcvTimeoIsSetLargerArg) {
SyscallSucceeds());
}
TEST_P(AllSocketPairTest, RecvmsgTimeoutOneSecondSucceeds) {
TEST_P(AllSocketPairTest, RecvTimeoutRecvmsgOneSecondSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
@@ -455,7 +485,22 @@ TEST_P(AllSocketPairTest, SendTimeoutUsecNeg) {
SyscallFailsWithErrno(EDOM));
}
TEST_P(AllSocketPairTest, RecvTimeoutNegSec) {
TEST_P(AllSocketPairTest, RecvTimeoutNegSecRead) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = -1, .tv_usec = 0
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
char buf[20] = {};
EXPECT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, RecvTimeoutNegSecRecv) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
@@ -470,7 +515,7 @@ TEST_P(AllSocketPairTest, RecvTimeoutNegSec) {
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, RecvmsgTimeoutNegSec) {
TEST_P(AllSocketPairTest, RecvTimeoutNegSecRecvmsg) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {