mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Make read and write respect SO_RCVTIMEO and SO_SNDTIMEO
PiperOrigin-RevId: 226387521 Change-Id: I0579ab262320fde6c72d2994dd38437f01a99ea5
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user