diff options
author | gVisor bot <gvisor-bot@google.com> | 2020-04-23 22:53:56 +0000 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2020-04-23 22:53:56 +0000 |
commit | 4b90fa57811fd8ad60e10cfe8fa0fca9edb7522c (patch) | |
tree | 549c8ddc4af0084dd1c937169636c18242a8aeb2 | |
parent | 621ac539dfe6db975450a12ed4cc0140e3e1d2f5 (diff) | |
parent | 93dd47146185ec7004f514e23bad9f225f55efb1 (diff) |
Merge release-20200323.0-226-g93dd471 (automated)
-rwxr-xr-x | pkg/abi/linux/epoll_amd64.go | 4 | ||||
-rwxr-xr-x | pkg/abi/linux/epoll_arm64.go | 4 | ||||
-rwxr-xr-x | pkg/abi/linux/linux_amd64_abi_autogen_unsafe.go | 93 | ||||
-rwxr-xr-x | pkg/abi/linux/linux_amd64_state_autogen.go | 1 | ||||
-rwxr-xr-x | pkg/abi/linux/linux_arm64_abi_autogen_unsafe.go | 95 | ||||
-rwxr-xr-x | pkg/abi/linux/linux_arm64_state_autogen.go | 1 | ||||
-rw-r--r-- | pkg/sentry/kernel/epoll/epoll.go | 20 | ||||
-rw-r--r-- | pkg/sentry/syscalls/epoll.go | 3 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_epoll.go | 27 | ||||
-rwxr-xr-x | pkg/sentry/syscalls/linux/vfs2/epoll.go | 7 | ||||
-rwxr-xr-x | pkg/sentry/syscalls/linux/vfs2/epoll_unsafe.go | 44 |
11 files changed, 202 insertions, 97 deletions
diff --git a/pkg/abi/linux/epoll_amd64.go b/pkg/abi/linux/epoll_amd64.go index 34ff18009..7e74b1143 100755 --- a/pkg/abi/linux/epoll_amd64.go +++ b/pkg/abi/linux/epoll_amd64.go @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +// +build amd64 + package linux // EpollEvent is equivalent to struct epoll_event from epoll(2). // -// +marshal +// +marshal slice:EpollEventSlice type EpollEvent struct { Events uint32 // Linux makes struct epoll_event::data a __u64. We represent it as diff --git a/pkg/abi/linux/epoll_arm64.go b/pkg/abi/linux/epoll_arm64.go index f86c35329..a35939cc9 100755 --- a/pkg/abi/linux/epoll_arm64.go +++ b/pkg/abi/linux/epoll_arm64.go @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +// +build arm64 + package linux // EpollEvent is equivalent to struct epoll_event from epoll(2). // -// +marshal +// +marshal slice:EpollEventSlice type EpollEvent struct { Events uint32 // Linux makes struct epoll_event a __u64, necessitating 4 bytes of padding diff --git a/pkg/abi/linux/linux_amd64_abi_autogen_unsafe.go b/pkg/abi/linux/linux_amd64_abi_autogen_unsafe.go index 75397fd08..3553526b7 100755 --- a/pkg/abi/linux/linux_amd64_abi_autogen_unsafe.go +++ b/pkg/abi/linux/linux_amd64_abi_autogen_unsafe.go @@ -2,6 +2,7 @@ // +build amd64 // +build amd64 +// +build amd64 package linux @@ -119,6 +120,92 @@ func (e *EpollEvent) WriteTo(w io.Writer) (int64, error) { return int64(length), err } +// CopyEpollEventSliceIn copies in a slice of EpollEvent objects from the task's memory. +func CopyEpollEventSliceIn(task marshal.Task, addr usermem.Addr, dst []EpollEvent) (int, error) { + count := len(dst) + if count == 0 { + return 0, nil + } + size := (*EpollEvent)(nil).SizeBytes() + + ptr := unsafe.Pointer(&dst) + val := gohacks.Noescape(unsafe.Pointer((*reflect.SliceHeader)(ptr).Data)) + + // Construct a slice backed by dst's underlying memory. + var buf []byte + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + hdr.Data = uintptr(val) + hdr.Len = size * count + hdr.Cap = size * count + + length, err := task.CopyInBytes(addr, buf) + // Since we bypassed the compiler's escape analysis, indicate that dst + // must live until the use above. + runtime.KeepAlive(dst) + return length, err +} + +// CopyEpollEventSliceOut copies a slice of EpollEvent objects to the task's memory. +func CopyEpollEventSliceOut(task marshal.Task, addr usermem.Addr, src []EpollEvent) (int, error) { + count := len(src) + if count == 0 { + return 0, nil + } + size := (*EpollEvent)(nil).SizeBytes() + + ptr := unsafe.Pointer(&src) + val := gohacks.Noescape(unsafe.Pointer((*reflect.SliceHeader)(ptr).Data)) + + // Construct a slice backed by dst's underlying memory. + var buf []byte + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + hdr.Data = uintptr(val) + hdr.Len = size * count + hdr.Cap = size * count + + length, err := task.CopyOutBytes(addr, buf) + // Since we bypassed the compiler's escape analysis, indicate that src + // must live until the use above. + runtime.KeepAlive(src) + return length, err +} + +// MarshalUnsafeEpollEventSlice is like EpollEvent.MarshalUnsafe, but for a []EpollEvent. +func MarshalUnsafeEpollEventSlice(src []EpollEvent, dst []byte) (int, error) { + count := len(src) + if count == 0 { + return 0, nil + } + size := (*EpollEvent)(nil).SizeBytes() + + ptr := unsafe.Pointer(&src) + val := gohacks.Noescape(unsafe.Pointer((*reflect.SliceHeader)(ptr).Data)) + + length, err := safecopy.CopyIn(dst[:(size*count)], val) + // Since we bypassed the compiler's escape analysis, indicate that src + // must live until the use above. + runtime.KeepAlive(src) + return length, err +} + +// UnmarshalUnsafeEpollEventSlice is like EpollEvent.UnmarshalUnsafe, but for a []EpollEvent. +func UnmarshalUnsafeEpollEventSlice(dst []EpollEvent, src []byte) (int, error) { + count := len(dst) + if count == 0 { + return 0, nil + } + size := (*EpollEvent)(nil).SizeBytes() + + ptr := unsafe.Pointer(&dst) + val := gohacks.Noescape(unsafe.Pointer((*reflect.SliceHeader)(ptr).Data)) + + length, err := safecopy.CopyOut(val, src[:(size*count)]) + // Since we bypassed the compiler's escape analysis, indicate that dst + // must live until the use above. + runtime.KeepAlive(dst) + return length, err +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (s *Stat) SizeBytes() int { return 72 + @@ -204,7 +291,7 @@ func (s *Stat) Packed() bool { // MarshalUnsafe implements marshal.Marshallable.MarshalUnsafe. func (s *Stat) MarshalUnsafe(dst []byte) { - if s.ATime.Packed() && s.MTime.Packed() && s.CTime.Packed() { + if s.CTime.Packed() && s.ATime.Packed() && s.MTime.Packed() { safecopy.CopyIn(dst, unsafe.Pointer(s)) } else { s.MarshalBytes(dst) @@ -223,7 +310,7 @@ func (s *Stat) UnmarshalUnsafe(src []byte) { // CopyOutN implements marshal.Marshallable.CopyOutN. //go:nosplit func (s *Stat) CopyOutN(task marshal.Task, addr usermem.Addr, limit int) (int, error) { - if !s.ATime.Packed() && s.MTime.Packed() && s.CTime.Packed() { + if !s.CTime.Packed() && s.ATime.Packed() && s.MTime.Packed() { // Type Stat doesn't have a packed layout in memory, fall back to MarshalBytes. buf := task.CopyScratchBuffer(s.SizeBytes()) // escapes: okay. s.MarshalBytes(buf) // escapes: fallback. @@ -253,7 +340,7 @@ func (s *Stat) CopyOut(task marshal.Task, addr usermem.Addr) (int, error) { // CopyIn implements marshal.Marshallable.CopyIn. //go:nosplit func (s *Stat) CopyIn(task marshal.Task, addr usermem.Addr) (int, error) { - if !s.CTime.Packed() && s.ATime.Packed() && s.MTime.Packed() { + if !s.ATime.Packed() && s.MTime.Packed() && s.CTime.Packed() { // Type Stat doesn't have a packed layout in memory, fall back to UnmarshalBytes. buf := task.CopyScratchBuffer(s.SizeBytes()) // escapes: okay. length, err := task.CopyInBytes(addr, buf) // escapes: okay. diff --git a/pkg/abi/linux/linux_amd64_state_autogen.go b/pkg/abi/linux/linux_amd64_state_autogen.go index a98193a5b..b3e3be0b6 100755 --- a/pkg/abi/linux/linux_amd64_state_autogen.go +++ b/pkg/abi/linux/linux_amd64_state_autogen.go @@ -2,5 +2,6 @@ // +build amd64 // +build amd64 +// +build amd64 package linux diff --git a/pkg/abi/linux/linux_arm64_abi_autogen_unsafe.go b/pkg/abi/linux/linux_arm64_abi_autogen_unsafe.go index 593289f86..a87ef68be 100755 --- a/pkg/abi/linux/linux_arm64_abi_autogen_unsafe.go +++ b/pkg/abi/linux/linux_arm64_abi_autogen_unsafe.go @@ -1,6 +1,7 @@ // Automatically generated marshal implementation. See tools/go_marshal. // +build arm64 +// +build arm64 package linux @@ -122,6 +123,92 @@ func (e *EpollEvent) WriteTo(w io.Writer) (int64, error) { return int64(length), err } +// CopyEpollEventSliceIn copies in a slice of EpollEvent objects from the task's memory. +func CopyEpollEventSliceIn(task marshal.Task, addr usermem.Addr, dst []EpollEvent) (int, error) { + count := len(dst) + if count == 0 { + return 0, nil + } + size := (*EpollEvent)(nil).SizeBytes() + + ptr := unsafe.Pointer(&dst) + val := gohacks.Noescape(unsafe.Pointer((*reflect.SliceHeader)(ptr).Data)) + + // Construct a slice backed by dst's underlying memory. + var buf []byte + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + hdr.Data = uintptr(val) + hdr.Len = size * count + hdr.Cap = size * count + + length, err := task.CopyInBytes(addr, buf) + // Since we bypassed the compiler's escape analysis, indicate that dst + // must live until the use above. + runtime.KeepAlive(dst) + return length, err +} + +// CopyEpollEventSliceOut copies a slice of EpollEvent objects to the task's memory. +func CopyEpollEventSliceOut(task marshal.Task, addr usermem.Addr, src []EpollEvent) (int, error) { + count := len(src) + if count == 0 { + return 0, nil + } + size := (*EpollEvent)(nil).SizeBytes() + + ptr := unsafe.Pointer(&src) + val := gohacks.Noescape(unsafe.Pointer((*reflect.SliceHeader)(ptr).Data)) + + // Construct a slice backed by dst's underlying memory. + var buf []byte + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + hdr.Data = uintptr(val) + hdr.Len = size * count + hdr.Cap = size * count + + length, err := task.CopyOutBytes(addr, buf) + // Since we bypassed the compiler's escape analysis, indicate that src + // must live until the use above. + runtime.KeepAlive(src) + return length, err +} + +// MarshalUnsafeEpollEventSlice is like EpollEvent.MarshalUnsafe, but for a []EpollEvent. +func MarshalUnsafeEpollEventSlice(src []EpollEvent, dst []byte) (int, error) { + count := len(src) + if count == 0 { + return 0, nil + } + size := (*EpollEvent)(nil).SizeBytes() + + ptr := unsafe.Pointer(&src) + val := gohacks.Noescape(unsafe.Pointer((*reflect.SliceHeader)(ptr).Data)) + + length, err := safecopy.CopyIn(dst[:(size*count)], val) + // Since we bypassed the compiler's escape analysis, indicate that src + // must live until the use above. + runtime.KeepAlive(src) + return length, err +} + +// UnmarshalUnsafeEpollEventSlice is like EpollEvent.UnmarshalUnsafe, but for a []EpollEvent. +func UnmarshalUnsafeEpollEventSlice(dst []EpollEvent, src []byte) (int, error) { + count := len(dst) + if count == 0 { + return 0, nil + } + size := (*EpollEvent)(nil).SizeBytes() + + ptr := unsafe.Pointer(&dst) + val := gohacks.Noescape(unsafe.Pointer((*reflect.SliceHeader)(ptr).Data)) + + length, err := safecopy.CopyOut(val, src[:(size*count)]) + // Since we bypassed the compiler's escape analysis, indicate that dst + // must live until the use above. + runtime.KeepAlive(dst) + return length, err +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (s *Stat) SizeBytes() int { return 72 + @@ -206,7 +293,7 @@ func (s *Stat) UnmarshalBytes(src []byte) { // Packed implements marshal.Marshallable.Packed. //go:nosplit func (s *Stat) Packed() bool { - return s.CTime.Packed() && s.ATime.Packed() && s.MTime.Packed() + return s.ATime.Packed() && s.MTime.Packed() && s.CTime.Packed() } // MarshalUnsafe implements marshal.Marshallable.MarshalUnsafe. @@ -220,7 +307,7 @@ func (s *Stat) MarshalUnsafe(dst []byte) { // UnmarshalUnsafe implements marshal.Marshallable.UnmarshalUnsafe. func (s *Stat) UnmarshalUnsafe(src []byte) { - if s.ATime.Packed() && s.MTime.Packed() && s.CTime.Packed() { + if s.MTime.Packed() && s.CTime.Packed() && s.ATime.Packed() { safecopy.CopyOut(unsafe.Pointer(s), src) } else { s.UnmarshalBytes(src) @@ -230,7 +317,7 @@ func (s *Stat) UnmarshalUnsafe(src []byte) { // CopyOutN implements marshal.Marshallable.CopyOutN. //go:nosplit func (s *Stat) CopyOutN(task marshal.Task, addr usermem.Addr, limit int) (int, error) { - if !s.CTime.Packed() && s.ATime.Packed() && s.MTime.Packed() { + if !s.ATime.Packed() && s.MTime.Packed() && s.CTime.Packed() { // Type Stat doesn't have a packed layout in memory, fall back to MarshalBytes. buf := task.CopyScratchBuffer(s.SizeBytes()) // escapes: okay. s.MarshalBytes(buf) // escapes: fallback. @@ -260,7 +347,7 @@ func (s *Stat) CopyOut(task marshal.Task, addr usermem.Addr) (int, error) { // CopyIn implements marshal.Marshallable.CopyIn. //go:nosplit func (s *Stat) CopyIn(task marshal.Task, addr usermem.Addr) (int, error) { - if !s.CTime.Packed() && s.ATime.Packed() && s.MTime.Packed() { + if !s.ATime.Packed() && s.MTime.Packed() && s.CTime.Packed() { // Type Stat doesn't have a packed layout in memory, fall back to UnmarshalBytes. buf := task.CopyScratchBuffer(s.SizeBytes()) // escapes: okay. length, err := task.CopyInBytes(addr, buf) // escapes: okay. diff --git a/pkg/abi/linux/linux_arm64_state_autogen.go b/pkg/abi/linux/linux_arm64_state_autogen.go index 7b31374fe..b144adbda 100755 --- a/pkg/abi/linux/linux_arm64_state_autogen.go +++ b/pkg/abi/linux/linux_arm64_state_autogen.go @@ -1,5 +1,6 @@ // automatically generated by stateify. // +build arm64 +// +build arm64 package linux diff --git a/pkg/sentry/kernel/epoll/epoll.go b/pkg/sentry/kernel/epoll/epoll.go index 592650923..3d78cd48f 100644 --- a/pkg/sentry/kernel/epoll/epoll.go +++ b/pkg/sentry/kernel/epoll/epoll.go @@ -20,6 +20,7 @@ import ( "fmt" "syscall" + "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/sentry/fs" @@ -30,19 +31,6 @@ import ( "gvisor.dev/gvisor/pkg/waiter" ) -// Event describes the event mask that was observed and the user data to be -// returned when one of the events occurs. It has this format to match the linux -// format to avoid extra copying/allocation when writing events to userspace. -type Event struct { - // Events is the event mask containing the set of events that have been - // observed on an entry. - Events uint32 - - // Data is an opaque 64-bit value provided by the caller when adding the - // entry, and returned to the caller when the entry reports an event. - Data [2]int32 -} - // EntryFlags is a bitmask that holds an entry's flags. type EntryFlags int @@ -227,9 +215,9 @@ func (e *EventPoll) Readiness(mask waiter.EventMask) waiter.EventMask { } // ReadEvents returns up to max available events. -func (e *EventPoll) ReadEvents(max int) []Event { +func (e *EventPoll) ReadEvents(max int) []linux.EpollEvent { var local pollEntryList - var ret []Event + var ret []linux.EpollEvent e.listsMu.Lock() @@ -251,7 +239,7 @@ func (e *EventPoll) ReadEvents(max int) []Event { } // Add event to the array that will be returned to caller. - ret = append(ret, Event{ + ret = append(ret, linux.EpollEvent{ Events: uint32(ready), Data: entry.userData, }) diff --git a/pkg/sentry/syscalls/epoll.go b/pkg/sentry/syscalls/epoll.go index 87dcad18b..d9fb808c0 100644 --- a/pkg/sentry/syscalls/epoll.go +++ b/pkg/sentry/syscalls/epoll.go @@ -17,6 +17,7 @@ package syscalls import ( "time" + "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/epoll" ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" @@ -118,7 +119,7 @@ func RemoveEpoll(t *kernel.Task, epfd int32, fd int32) error { } // WaitEpoll implements the epoll_wait(2) linux syscall. -func WaitEpoll(t *kernel.Task, fd int32, max int, timeout int) ([]epoll.Event, error) { +func WaitEpoll(t *kernel.Task, fd int32, max int, timeout int) ([]linux.EpollEvent, error) { // Get epoll from the file descriptor. epollfile := t.GetFile(fd) if epollfile == nil { diff --git a/pkg/sentry/syscalls/linux/sys_epoll.go b/pkg/sentry/syscalls/linux/sys_epoll.go index 3ab93fbde..51bf205cf 100644 --- a/pkg/sentry/syscalls/linux/sys_epoll.go +++ b/pkg/sentry/syscalls/linux/sys_epoll.go @@ -21,7 +21,6 @@ import ( "gvisor.dev/gvisor/pkg/sentry/kernel/epoll" "gvisor.dev/gvisor/pkg/sentry/syscalls" "gvisor.dev/gvisor/pkg/syserror" - "gvisor.dev/gvisor/pkg/usermem" "gvisor.dev/gvisor/pkg/waiter" ) @@ -72,7 +71,7 @@ func EpollCtl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc var data [2]int32 if op != linux.EPOLL_CTL_DEL { var e linux.EpollEvent - if _, err := t.CopyIn(eventAddr, &e); err != nil { + if _, err := e.CopyIn(t, eventAddr); err != nil { return 0, nil, err } @@ -105,28 +104,6 @@ func EpollCtl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } } -// copyOutEvents copies epoll events from the kernel to user memory. -func copyOutEvents(t *kernel.Task, addr usermem.Addr, e []epoll.Event) error { - const itemLen = 12 - buffLen := len(e) * itemLen - if _, ok := addr.AddLength(uint64(buffLen)); !ok { - return syserror.EFAULT - } - - b := t.CopyScratchBuffer(buffLen) - for i := range e { - usermem.ByteOrder.PutUint32(b[i*itemLen:], e[i].Events) - usermem.ByteOrder.PutUint32(b[i*itemLen+4:], uint32(e[i].Data[0])) - usermem.ByteOrder.PutUint32(b[i*itemLen+8:], uint32(e[i].Data[1])) - } - - if _, err := t.CopyOutBytes(addr, b); err != nil { - return err - } - - return nil -} - // EpollWait implements the epoll_wait(2) linux syscall. func EpollWait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { epfd := args[0].Int() @@ -140,7 +117,7 @@ func EpollWait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } if len(r) != 0 { - if err := copyOutEvents(t, eventsAddr, r); err != nil { + if _, err := linux.CopyEpollEventSliceOut(t, eventsAddr, r); err != nil { return 0, nil, err } } diff --git a/pkg/sentry/syscalls/linux/vfs2/epoll.go b/pkg/sentry/syscalls/linux/vfs2/epoll.go index 5a938cee2..34c90ae3e 100755 --- a/pkg/sentry/syscalls/linux/vfs2/epoll.go +++ b/pkg/sentry/syscalls/linux/vfs2/epoll.go @@ -28,6 +28,8 @@ import ( "gvisor.dev/gvisor/pkg/waiter" ) +var sizeofEpollEvent = (*linux.EpollEvent)(nil).SizeBytes() + // EpollCreate1 implements Linux syscall epoll_create1(2). func EpollCreate1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { flags := args[0].Int() @@ -124,7 +126,7 @@ func EpollWait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys maxEvents := int(args[2].Int()) timeout := int(args[3].Int()) - const _EP_MAX_EVENTS = math.MaxInt32 / sizeofEpollEvent // Linux: fs/eventpoll.c:EP_MAX_EVENTS + var _EP_MAX_EVENTS = math.MaxInt32 / sizeofEpollEvent // Linux: fs/eventpoll.c:EP_MAX_EVENTS if maxEvents <= 0 || maxEvents > _EP_MAX_EVENTS { return 0, nil, syserror.EINVAL } @@ -157,7 +159,8 @@ func EpollWait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys maxEvents -= n if n != 0 { // Copy what we read out. - copiedEvents, err := copyOutEvents(t, eventsAddr, events[:n]) + copiedBytes, err := linux.CopyEpollEventSliceOut(t, eventsAddr, events[:n]) + copiedEvents := copiedBytes / sizeofEpollEvent // rounded down eventsAddr += usermem.Addr(copiedEvents * sizeofEpollEvent) total += copiedEvents if err != nil { diff --git a/pkg/sentry/syscalls/linux/vfs2/epoll_unsafe.go b/pkg/sentry/syscalls/linux/vfs2/epoll_unsafe.go deleted file mode 100755 index 825f325bf..000000000 --- a/pkg/sentry/syscalls/linux/vfs2/epoll_unsafe.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package vfs2 - -import ( - "reflect" - "runtime" - "unsafe" - - "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/gohacks" - "gvisor.dev/gvisor/pkg/sentry/kernel" - "gvisor.dev/gvisor/pkg/usermem" -) - -const sizeofEpollEvent = int(unsafe.Sizeof(linux.EpollEvent{})) - -func copyOutEvents(t *kernel.Task, addr usermem.Addr, events []linux.EpollEvent) (int, error) { - if len(events) == 0 { - return 0, nil - } - // Cast events to a byte slice for copying. - var eventBytes []byte - eventBytesHdr := (*reflect.SliceHeader)(unsafe.Pointer(&eventBytes)) - eventBytesHdr.Data = uintptr(gohacks.Noescape(unsafe.Pointer(&events[0]))) - eventBytesHdr.Len = len(events) * sizeofEpollEvent - eventBytesHdr.Cap = len(events) * sizeofEpollEvent - copiedBytes, err := t.CopyOutBytes(addr, eventBytes) - runtime.KeepAlive(events) - copiedEvents := copiedBytes / sizeofEpollEvent // rounded down - return copiedEvents, err -} |