mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
This change adapts the existing context to use more suitable non-channel-based methods. This is a requisite for migrating the kernel internals to a sleeper-based notification mechanism. The last uses of amutex outside those migrated as part of this change were dropped in a previous change. Since amutex depends on the channel-based implementation, this package is also deleted as part of this change. PiperOrigin-RevId: 415189675
111 lines
3.3 KiB
Go
111 lines
3.3 KiB
Go
// Copyright 2019 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 gofer
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
"gvisor.dev/gvisor/pkg/context"
|
|
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
|
"gvisor.dev/gvisor/pkg/waiter"
|
|
)
|
|
|
|
// Global pipe used by blockUntilNonblockingPipeHasWriter since we can't create
|
|
// pipes after sentry initialization due to syscall filters.
|
|
var (
|
|
tempPipeMu sync.Mutex
|
|
tempPipeReadFD int
|
|
tempPipeWriteFD int
|
|
tempPipeBuf [1]byte
|
|
)
|
|
|
|
func init() {
|
|
var pipeFDs [2]int
|
|
if err := unix.Pipe(pipeFDs[:]); err != nil {
|
|
panic(fmt.Sprintf("failed to create pipe for gofer.blockUntilNonblockingPipeHasWriter: %v", err))
|
|
}
|
|
tempPipeReadFD = pipeFDs[0]
|
|
tempPipeWriteFD = pipeFDs[1]
|
|
}
|
|
|
|
func blockUntilNonblockingPipeHasWriter(ctx context.Context, fd int32) error {
|
|
for {
|
|
ok, err := nonblockingPipeHasWriter(fd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ok {
|
|
return nil
|
|
}
|
|
// Delay before trying again.
|
|
if sleepErr := sleepBetweenNamedPipeOpenChecks(ctx); sleepErr != nil {
|
|
// Another application thread may have opened this pipe for
|
|
// writing, succeeded because we previously opened the pipe for
|
|
// reading, and subsequently interrupted us for checkpointing (e.g.
|
|
// this occurs in mknod tests under cooperative save/restore). In
|
|
// this case, our open has to succeed for the checkpoint to include
|
|
// a readable FD for the pipe, which is in turn necessary to
|
|
// restore the other thread's writable FD for the same pipe
|
|
// (otherwise it will get ENXIO). So we have to check
|
|
// nonblockingPipeHasWriter() once last time.
|
|
ok, err := nonblockingPipeHasWriter(fd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ok {
|
|
return nil
|
|
}
|
|
return sleepErr
|
|
}
|
|
}
|
|
}
|
|
|
|
func nonblockingPipeHasWriter(fd int32) (bool, error) {
|
|
tempPipeMu.Lock()
|
|
defer tempPipeMu.Unlock()
|
|
// Copy 1 byte from fd into the temporary pipe.
|
|
n, err := unix.Tee(int(fd), tempPipeWriteFD, 1, unix.SPLICE_F_NONBLOCK)
|
|
if linuxerr.Equals(linuxerr.EAGAIN, err) {
|
|
// The pipe represented by fd is empty, but has a writer.
|
|
return true, nil
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if n == 0 {
|
|
// The pipe represented by fd is empty and has no writer.
|
|
return false, nil
|
|
}
|
|
// The pipe represented by fd is non-empty, so it either has, or has
|
|
// previously had, a writer. Remove the byte copied to the temporary pipe
|
|
// before returning.
|
|
if n, err := unix.Read(tempPipeReadFD, tempPipeBuf[:]); err != nil || n != 1 {
|
|
panic(fmt.Sprintf("failed to drain pipe for gofer.blockUntilNonblockingPipeHasWriter: got (%d, %v), wanted (1, nil)", n, err))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func sleepBetweenNamedPipeOpenChecks(ctx context.Context) error {
|
|
var q waiter.NeverReady
|
|
left, ok := ctx.BlockWithTimeoutOn(&q, waiter.EventIn, 100*time.Millisecond)
|
|
if !ok && left != 0 {
|
|
return linuxerr.ErrInterrupted
|
|
}
|
|
return nil
|
|
}
|