mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
syscall: process_vm_* copies data by chunks
First, it avoids allocating a large buffer that can be costly. Second, it allows to interrupt a system call in case of any signals. PiperOrigin-RevId: 580721720
This commit is contained in:
@@ -141,6 +141,10 @@ type processVMOpArgs struct {
|
||||
writeIovecCount int
|
||||
}
|
||||
|
||||
// maxScratchBufferSize is the maximum size of a scratch buffer. It should be
|
||||
// sufficiently large to minimizing the number of trips through MM.
|
||||
const maxScratchBufferSize = 1 << 20
|
||||
|
||||
func doProcessVMOpMaybeLocked(t *kernel.Task, args processVMOpArgs) (int, error) {
|
||||
// Copy IOVecs in to kernel.
|
||||
readIovecs, err := t.CopyInIovecsAsSlice(args.readAddr, args.readIovecCount)
|
||||
@@ -160,6 +164,9 @@ func doProcessVMOpMaybeLocked(t *kernel.Task, args processVMOpArgs) (int, error)
|
||||
bufSize = int(readIovec.Length())
|
||||
}
|
||||
}
|
||||
if bufSize > maxScratchBufferSize {
|
||||
bufSize = maxScratchBufferSize
|
||||
}
|
||||
buf := t.CopyScratchBuffer(bufSize)
|
||||
|
||||
// Number of bytes written.
|
||||
@@ -169,42 +176,52 @@ func doProcessVMOpMaybeLocked(t *kernel.Task, args processVMOpArgs) (int, error)
|
||||
break
|
||||
}
|
||||
|
||||
buf = buf[0:int(readIovec.Length())]
|
||||
bytes, err := args.readCtx.CopyInBytes(readIovec.Start, buf)
|
||||
if linuxerr.Equals(linuxerr.EFAULT, err) {
|
||||
return n, nil
|
||||
}
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if bytes != int(readIovec.Length()) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
start := 0
|
||||
for bytes > start && 0 < len(writeIovecs) {
|
||||
writeLength := int(writeIovecs[0].Length())
|
||||
if writeLength > (bytes - start) {
|
||||
writeLength = bytes - start
|
||||
for readIovec.Length() != 0 {
|
||||
length := readIovec.Length()
|
||||
if length > maxScratchBufferSize {
|
||||
length = maxScratchBufferSize
|
||||
}
|
||||
out, err := args.writeCtx.CopyOutBytes(writeIovecs[0].Start, buf[start:writeLength+start])
|
||||
n += out
|
||||
start += out
|
||||
buf = buf[0:int(length)]
|
||||
bytes, err := args.readCtx.CopyInBytes(readIovec.Start, buf)
|
||||
if linuxerr.Equals(linuxerr.EFAULT, err) {
|
||||
return n, nil
|
||||
}
|
||||
if err != nil {
|
||||
if bytes == 0 && err != nil {
|
||||
return n, err
|
||||
}
|
||||
if out != writeLength {
|
||||
return n, nil
|
||||
}
|
||||
writeIovecs[0].Start += hostarch.Addr(out)
|
||||
if !writeIovecs[0].WellFormed() {
|
||||
return n, err
|
||||
}
|
||||
if writeIovecs[0].Length() == 0 {
|
||||
writeIovecs = writeIovecs[1:]
|
||||
readIovec.Start += hostarch.Addr(bytes)
|
||||
|
||||
start := 0
|
||||
for bytes > start && 0 < len(writeIovecs) {
|
||||
if t.Interrupted() {
|
||||
if n == 0 {
|
||||
return 0, linuxerr.EINTR
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
writeLength := int(writeIovecs[0].Length())
|
||||
if writeLength > (bytes - start) {
|
||||
writeLength = bytes - start
|
||||
}
|
||||
out, err := args.writeCtx.CopyOutBytes(writeIovecs[0].Start, buf[start:writeLength+start])
|
||||
n += out
|
||||
start += out
|
||||
if linuxerr.Equals(linuxerr.EFAULT, err) {
|
||||
return n, nil
|
||||
}
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if out != writeLength {
|
||||
return n, nil
|
||||
}
|
||||
writeIovecs[0].Start += hostarch.Addr(out)
|
||||
if !writeIovecs[0].WellFormed() {
|
||||
return n, err
|
||||
}
|
||||
if writeIovecs[0].Length() == 0 {
|
||||
writeIovecs = writeIovecs[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,18 @@ struct ProcessVMTestCase {
|
||||
|
||||
using ProcessVMTest = ::testing::TestWithParam<ProcessVMTestCase>;
|
||||
|
||||
std::string getTestBuffer(std::string pattern, size_t size) {
|
||||
std::string s;
|
||||
|
||||
auto pattern_length = pattern.length();
|
||||
s.reserve(size);
|
||||
while (s.length() + pattern_length < size) {
|
||||
s += pattern;
|
||||
}
|
||||
s += pattern.substr(0, size - s.length());
|
||||
return s;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ProcessVMTests, ProcessVMTest,
|
||||
::testing::ValuesIn<ProcessVMTestCase>(
|
||||
@@ -132,7 +144,20 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
{"Obi-wan never told you what happened to your father.",
|
||||
"He told me enough...he told me you killed him."},
|
||||
{"No...I am your father.", "No. No.", "That's not true.",
|
||||
"That's impossible!"}}}),
|
||||
"That's impossible!"}},
|
||||
{
|
||||
"LargeBuffer",
|
||||
{
|
||||
getTestBuffer(
|
||||
"Train yourself to let go of everything you fear to lose.",
|
||||
32 << 20),
|
||||
"Hello there!",
|
||||
},
|
||||
{
|
||||
"Do. Or do not. There is no try.",
|
||||
getTestBuffer("The greatest teacher, failure is.", 32 << 20),
|
||||
},
|
||||
}}),
|
||||
[](const ::testing::TestParamInfo<ProcessVMTest::ParamType>& info) {
|
||||
return info.param.test_name;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user