mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
`runsc` creates some FDs that are slow to release (`AF_PACKET` sockets for
networking, KVM FDs for the KVM platform).
By releasing them asynchronously, we free up the sandbox process memory
earlier during teardown, allowing better density of sandboxes to exist.
Also allows faster sandbox recycling for systems like Substrate which spawn
a new sandbox as soon as one exits.
This uses an `io_uring` hack to move the release work to a kworker thread.
The ring is held by the gofer (not the Sentry), is disabled
(`IORING_SETUP_R_DISABLED`), and is unusable anyway due to seccomp filters.
Includes a test to verify this kernel implementation detail.
Benchmarks:
```
$ sudo runsc do --platform=kvm --network=sandbox
│ before │ after │
│ sec/op │ sec/op vs base │
RunscDo 459.5m ± 6% 432.5m ± 4% -5.88% (p=0.001 n=30)
│ before │ after │
│ sec/op │ sec/op vs base │
OCICreate 155.8m ± 5% 155.0m ± 7% ~ (p=0.623 n=30)
OCIDelete 8.838m ± 4% 7.713m ± 9% -12.73% (p=0.000 n=30)
OCIExit 80.511m ± 6% 2.015m ± 7% -97.50% (p=0.000 n=30)
OCIReady 170.6m ± 6% 171.0m ± 7% ~ (p=0.708 n=30)
OCIStart 15.51m ± 3% 15.97m ± 5% ~ (p=0.223 n=30)
OCITotal 261.0m ± 5% 180.8m ± 6% -30.73% (p=0.000 n=30)
```
255 lines
7.9 KiB
Go
255 lines
7.9 KiB
Go
// Copyright 2022 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 linux
|
|
|
|
import (
|
|
"structs"
|
|
)
|
|
|
|
// Constants for io_uring_setup(2). See include/uapi/linux/io_uring.h.
|
|
const (
|
|
IORING_SETUP_IOPOLL = (1 << 0)
|
|
IORING_SETUP_SQPOLL = (1 << 1)
|
|
IORING_SETUP_SQ_AFF = (1 << 2)
|
|
IORING_SETUP_CQSIZE = (1 << 3)
|
|
IORING_SETUP_CLAMP = (1 << 4)
|
|
IORING_SETUP_ATTACH_WQ = (1 << 5)
|
|
IORING_SETUP_R_DISABLED = (1 << 6)
|
|
IORING_SETUP_SUBMIT_ALL = (1 << 7)
|
|
)
|
|
|
|
// Constants for io_uring_enter(2). See include/uapi/linux/io_uring.h.
|
|
const (
|
|
IORING_ENTER_GETEVENTS = (1 << 0)
|
|
)
|
|
|
|
// Constants for IoUringParams.Features. See include/uapi/linux/io_uring.h.
|
|
const (
|
|
IORING_FEAT_SINGLE_MMAP = (1 << 0)
|
|
)
|
|
|
|
// Constants for IO_URING. See include/uapi/linux/io_uring.h.
|
|
const (
|
|
IORING_SETUP_COOP_TASKRUN = (1 << 8)
|
|
IORING_SETUP_TASKRUN_FLAG = (1 << 9)
|
|
IORING_SETUP_SQE128 = (1 << 10)
|
|
IORING_SETUP_CQE32 = (1 << 11)
|
|
)
|
|
|
|
// Constants for IO_URING. See io_uring/io_uring.c.
|
|
const (
|
|
IORING_MAX_ENTRIES = (1 << 15) // 32768
|
|
IORING_MAX_CQ_ENTRIES = (2 * IORING_MAX_ENTRIES)
|
|
)
|
|
|
|
// Constants for the offsets for the application to mmap the data it needs.
|
|
// See include/uapi/linux/io_uring.h.
|
|
const (
|
|
IORING_OFF_SQ_RING = 0
|
|
IORING_OFF_CQ_RING = 0x8000000
|
|
IORING_OFF_SQES = 0x10000000
|
|
)
|
|
|
|
// Constants for the IO_URING opcodes. See include/uapi/linux/io_uring.h.
|
|
const (
|
|
IORING_OP_NOP = 0
|
|
IORING_OP_READV = 1
|
|
)
|
|
|
|
// Constants for io_uring_register(2). See include/uapi/linux/io_uring.h.
|
|
const (
|
|
IORING_REGISTER_FILES = 2
|
|
)
|
|
|
|
// IORingIndex represents SQE array indexes.
|
|
//
|
|
// +marshal
|
|
type IORingIndex uint32
|
|
|
|
// IOSqRingOffsets implements io_sqring_offsets struct.
|
|
// IOSqRingOffsets represents offsets into IORings.
|
|
// See struct io_sqring_offsets in include/uapi/linux/io_uring.h.
|
|
//
|
|
// +marshal
|
|
type IOSqRingOffsets struct {
|
|
_ structs.HostLayout
|
|
Head uint32 // Offset to io_rings.sq.head
|
|
Tail uint32 // Offset to io_rings.sq.tail
|
|
RingMask uint32 // Offset to io_rings.sq_ring_mask
|
|
RingEntries uint32 // Offset to io_rings.sq_ring_entries
|
|
Flags uint32 // Offset to io_rings.sq_flags
|
|
Dropped uint32 // Offset to io_rings.sq_dropped
|
|
Array uint32 // Offset to an array of SQE indices
|
|
Resv1 uint32 // Currently reserved and expected to be zero
|
|
Resv2 uint64 // Currently reserved and expected to be zero
|
|
}
|
|
|
|
// IOCqRingOffsets implements io_cqring_offsets struct.
|
|
// IOCqRingOffsets represents offsets into IORings.
|
|
// See struct io_cqring_offsets in include/uapi/linux/io_uring.h.
|
|
//
|
|
// +marshal
|
|
type IOCqRingOffsets struct {
|
|
_ structs.HostLayout
|
|
Head uint32 // Offset to io_rings.cq.head
|
|
Tail uint32 // Offset to io_rings.cq.tail
|
|
RingMask uint32 // Offset to io_rings.cq_ring_mask
|
|
RingEntries uint32 // Offset to io_rings.cq_ring_entries
|
|
Overflow uint32 // Offset to io_rings.cq_overflow
|
|
Cqes uint32 // Offset to io_rings.cqes
|
|
Flags uint32 // Offset to io_rings.cq_flags
|
|
Resv1 uint32 // Currently reserved and expected to be zero
|
|
Resv2 uint64 // Currently reserved and expected to be zero
|
|
}
|
|
|
|
// IOUringParams implements io_uring_params struct.
|
|
// See struct io_uring_params in include/uapi/linux/io_uring.h.
|
|
//
|
|
// +marshal
|
|
type IOUringParams struct {
|
|
_ structs.HostLayout
|
|
SqEntries uint32
|
|
CqEntries uint32
|
|
Flags uint32
|
|
SqThreadCPU uint32
|
|
SqThreadIdle uint32
|
|
Features uint32
|
|
WqFd uint32
|
|
Resv [3]uint32
|
|
SqOff IOSqRingOffsets
|
|
CqOff IOCqRingOffsets
|
|
}
|
|
|
|
// IOUringCqe implements IO completion data structure (Completion Queue Entry)
|
|
// io_uring_cqe struct. As we don't currently support IORING_SETUP_CQE32 flag
|
|
// its size is 16 bytes.
|
|
// See struct io_uring_cqe in include/uapi/linux/io_uring.h.
|
|
//
|
|
// +marshal
|
|
// +stateify savable
|
|
type IOUringCqe struct {
|
|
_ structs.HostLayout
|
|
UserData uint64
|
|
Res int32
|
|
Flags uint32
|
|
}
|
|
|
|
// IOUring implements io_uring struct.
|
|
// See struct io_uring in io_uring/io_uring.c.
|
|
//
|
|
// +marshal
|
|
// +stateify savable
|
|
type IOUring struct {
|
|
_ structs.HostLayout
|
|
// Both head and tail should be cacheline aligned. And we assume that
|
|
// cacheline size is 64 bytes.
|
|
Head uint32
|
|
_ [60]byte
|
|
Tail uint32
|
|
_ [60]byte
|
|
}
|
|
|
|
// IORings implements io_rings struct.
|
|
// This struct describes layout of the mapped region backed by the ringBuffersFile.
|
|
// See struct io_rings in io_uring/io_uring.c.
|
|
//
|
|
// +marshal
|
|
// +stateify savable
|
|
type IORings struct {
|
|
_ structs.HostLayout
|
|
Sq IOUring
|
|
Cq IOUring
|
|
SqRingMask uint32
|
|
CqRingMask uint32
|
|
SqRingEntries uint32
|
|
CqRingEntries uint32
|
|
sqDropped uint32
|
|
sqFlags int32
|
|
cqFlags uint32
|
|
CqOverflow uint32
|
|
_ [32]byte // Padding so cqes is cacheline aligned
|
|
// Linux has an additional field struct io_uring_cqe cqes[], which represents
|
|
// a dynamic array. We don't include it here in order to enable marshalling.
|
|
}
|
|
|
|
// IOUringSqe implements io_uring_sqe struct.
|
|
// This struct represents IO submission data structure (Submission Queue Entry). As we don't yet
|
|
// support IORING_SETUP_SQE128 flag, its size is 64 bytes with no extra padding at the end.
|
|
// See include/uapi/linux/io_uring.h.
|
|
//
|
|
// +marshal
|
|
// +stateify savable
|
|
type IOUringSqe struct {
|
|
_ structs.HostLayout
|
|
Opcode uint8
|
|
Flags uint8
|
|
IoPrio uint16
|
|
Fd int32
|
|
OffOrAddrOrCmdOp uint64
|
|
AddrOrSpliceOff uint64
|
|
Len uint32
|
|
specialFlags uint32
|
|
UserData uint64
|
|
BufIndexOrGroup uint16
|
|
personality uint16
|
|
spliceFDOrFileIndex int32
|
|
addr3 uint64
|
|
_ uint64
|
|
}
|
|
|
|
const (
|
|
_IOSqRingOffset = 0 // +checkoffset . IORings.Sq
|
|
_IOSqRingOffsetHead = 0 // +checkoffset . IOUring.Head
|
|
_IOSqRingOffsetTail = 64 // +checkoffset . IOUring.Tail
|
|
_IOSqRingOffsetMask = 256 // +checkoffset . IORings.SqRingMask
|
|
_IOSqRingOffsetEntries = 264 // +checkoffset . IORings.SqRingEntries
|
|
_IOSqRingOffsetFlags = 276 // +checkoffset . IORings.sqFlags
|
|
_IOSqRingOffsetDropped = 272 // +checkoffset . IORings.sqDropped
|
|
)
|
|
|
|
// PreComputedIOSqRingOffsets returns precomputed values for IOSqRingOffsets.
|
|
func PreComputedIOSqRingOffsets() IOSqRingOffsets {
|
|
return IOSqRingOffsets{
|
|
Head: _IOSqRingOffset + _IOSqRingOffsetHead,
|
|
Tail: _IOSqRingOffset + _IOSqRingOffsetTail,
|
|
RingMask: _IOSqRingOffsetMask,
|
|
RingEntries: _IOSqRingOffsetEntries,
|
|
Flags: _IOSqRingOffsetFlags,
|
|
Dropped: _IOSqRingOffsetDropped,
|
|
}
|
|
}
|
|
|
|
const (
|
|
_IOCqRingOffset = 128 // +checkoffset . IORings.Cq
|
|
_IOCqRingOffsetHead = 0 // +checkoffset . IOUring.Head
|
|
_IOCqRingOffsetTail = 64 // +checkoffset . IOUring.Tail
|
|
_IOCqRingOffsetMask = 260 // +checkoffset . IORings.CqRingMask
|
|
_IOCqRingOffsetEntries = 268 // +checkoffset . IORings.CqRingEntries
|
|
_IOCqRingOffsetFlags = 280 // +checkoffset . IORings.cqFlags
|
|
_IOCqRingOffsetOverflow = 284 // +checkoffset . IORings.CqOverflow
|
|
)
|
|
|
|
// PreComputedIOCqRingOffsets returns precomputed values for IOCqRingOffsets.
|
|
func PreComputedIOCqRingOffsets() IOCqRingOffsets {
|
|
return IOCqRingOffsets{
|
|
Head: _IOCqRingOffset + _IOCqRingOffsetHead,
|
|
Tail: _IOCqRingOffset + _IOCqRingOffsetTail,
|
|
RingMask: _IOCqRingOffsetMask,
|
|
RingEntries: _IOCqRingOffsetEntries,
|
|
Overflow: _IOCqRingOffsetOverflow,
|
|
Flags: _IOCqRingOffsetFlags,
|
|
}
|
|
}
|