mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add task work mechanism.
Like task_work in Linux, this allows us to register callbacks to be executed before returning to userspace. This is needed for kcov support, which requires coverage information to be up-to-date whenever we are in user mode. We will provide coverage data through the kcov interface to enable coverage-directed fuzzing in syzkaller. One difference from Linux is that task work cannot queue work before the transition to userspace that it precedes; queued work will be picked up before the next transition. PiperOrigin-RevId: 322889984
This commit is contained in:
@@ -132,6 +132,7 @@ go_library(
|
||||
"task_stop.go",
|
||||
"task_syscall.go",
|
||||
"task_usermem.go",
|
||||
"task_work.go",
|
||||
"thread_group.go",
|
||||
"threads.go",
|
||||
"timekeeper.go",
|
||||
|
||||
@@ -68,6 +68,21 @@ type Task struct {
|
||||
// runState is exclusive to the task goroutine.
|
||||
runState taskRunState
|
||||
|
||||
// taskWorkCount represents the current size of the task work queue. It is
|
||||
// used to avoid acquiring taskWorkMu when the queue is empty.
|
||||
//
|
||||
// Must accessed with atomic memory operations.
|
||||
taskWorkCount int32
|
||||
|
||||
// taskWorkMu protects taskWork.
|
||||
taskWorkMu sync.Mutex `state:"nosave"`
|
||||
|
||||
// taskWork is a queue of work to be executed before resuming user execution.
|
||||
// It is similar to the task_work mechanism in Linux.
|
||||
//
|
||||
// taskWork is exclusive to the task goroutine.
|
||||
taskWork []TaskWorker
|
||||
|
||||
// haveSyscallReturn is true if tc.Arch().Return() represents a value
|
||||
// returned by a syscall (or set by ptrace after a syscall).
|
||||
//
|
||||
|
||||
@@ -167,7 +167,22 @@ func (app *runApp) execute(t *Task) taskRunState {
|
||||
return (*runInterrupt)(nil)
|
||||
}
|
||||
|
||||
// We're about to switch to the application again. If there's still a
|
||||
// Execute any task work callbacks before returning to user space.
|
||||
if atomic.LoadInt32(&t.taskWorkCount) > 0 {
|
||||
t.taskWorkMu.Lock()
|
||||
queue := t.taskWork
|
||||
t.taskWork = nil
|
||||
atomic.StoreInt32(&t.taskWorkCount, 0)
|
||||
t.taskWorkMu.Unlock()
|
||||
|
||||
// Do not hold taskWorkMu while executing task work, which may register
|
||||
// more work.
|
||||
for _, work := range queue {
|
||||
work.TaskWork(t)
|
||||
}
|
||||
}
|
||||
|
||||
// We're about to switch to the application again. If there's still an
|
||||
// unhandled SyscallRestartErrno that wasn't translated to an EINTR,
|
||||
// restart the syscall that was interrupted. If there's a saved signal
|
||||
// mask, restore it. (Note that restoring the saved signal mask may unblock
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 kernel
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// TaskWorker is a deferred task.
|
||||
//
|
||||
// This must be savable.
|
||||
type TaskWorker interface {
|
||||
// TaskWork will be executed prior to returning to user space. Note that
|
||||
// TaskWork may call RegisterWork again, but this will not be executed until
|
||||
// the next return to user space, unlike in Linux. This effectively allows
|
||||
// registration of indefinite user return hooks, but not by default.
|
||||
TaskWork(t *Task)
|
||||
}
|
||||
|
||||
// RegisterWork can be used to register additional task work that will be
|
||||
// performed prior to returning to user space. See TaskWorker.TaskWork for
|
||||
// semantics regarding registration.
|
||||
func (t *Task) RegisterWork(work TaskWorker) {
|
||||
t.taskWorkMu.Lock()
|
||||
defer t.taskWorkMu.Unlock()
|
||||
atomic.AddInt32(&t.taskWorkCount, 1)
|
||||
t.taskWork = append(t.taskWork, work)
|
||||
}
|
||||
Reference in New Issue
Block a user