mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Split the ring implementation into its own package.
The ring data structure's requires it's own implementation of Entry. With the new version you can more easily be part of multiple rings at once. Also, a user who wants to use ilist probably doesn't need ring as well, so it makes sense to separate the two. PiperOrigin-RevId: 563151782
This commit is contained in:
committed by
gVisor bot
parent
c584a1a435
commit
d30c39364d
@@ -57,3 +57,50 @@ go_template(
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_template(
|
||||
name = "generic_ring",
|
||||
srcs = [
|
||||
"ring.go",
|
||||
],
|
||||
opt_types = [
|
||||
"Container",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "test_ring",
|
||||
out = "test_ring.go",
|
||||
package = "ring",
|
||||
prefix = "test",
|
||||
template = "//pkg/ilist:generic_ring",
|
||||
types = {
|
||||
"Container": "*testContainer",
|
||||
},
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "ring",
|
||||
srcs = [
|
||||
"interface_ring.go",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "interface_ring",
|
||||
out = "interface_ring.go",
|
||||
package = "ring",
|
||||
template = ":generic_ring",
|
||||
types = {},
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "ring_test",
|
||||
srcs = [
|
||||
"ring_test.go",
|
||||
"test_ring.go",
|
||||
],
|
||||
library = ":ring",
|
||||
)
|
||||
|
||||
@@ -271,48 +271,3 @@ func (e *Entry) SetNext(elem Element) {
|
||||
func (e *Entry) SetPrev(elem Element) {
|
||||
e.prev = elem
|
||||
}
|
||||
|
||||
// RingInit instantiates an Element to be an item in a ring (circularly-linked
|
||||
// list).
|
||||
//
|
||||
//go:nosplit
|
||||
func RingInit(e Element) {
|
||||
linker := ElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(e)
|
||||
linker.SetPrev(e)
|
||||
}
|
||||
|
||||
// RingAdd adds new to old's ring.
|
||||
//
|
||||
//go:nosplit
|
||||
func RingAdd(old Element, new Element) {
|
||||
oldLinker := ElementMapper{}.linkerFor(old)
|
||||
newLinker := ElementMapper{}.linkerFor(new)
|
||||
next := oldLinker.Next()
|
||||
prev := old
|
||||
|
||||
next.SetPrev(new)
|
||||
newLinker.SetNext(next)
|
||||
newLinker.SetPrev(prev)
|
||||
oldLinker.SetNext(new)
|
||||
}
|
||||
|
||||
// RingRemove removes e from its ring.
|
||||
//
|
||||
//go:nosplit
|
||||
func RingRemove(e Element) {
|
||||
eLinker := ElementMapper{}.linkerFor(e)
|
||||
next := eLinker.Next()
|
||||
prev := eLinker.Prev()
|
||||
next.SetPrev(prev)
|
||||
prev.SetNext(next)
|
||||
RingInit(e)
|
||||
}
|
||||
|
||||
// RingEmpty returns true if there are no other elements in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func RingEmpty(e Element) bool {
|
||||
linker := ElementMapper{}.linkerFor(e)
|
||||
return linker.Next() == e
|
||||
}
|
||||
|
||||
@@ -183,63 +183,6 @@ func TestReset(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingAdd(t *testing.T) {
|
||||
e1 := &testEntry{value: 1}
|
||||
e2 := &testEntry{value: 2}
|
||||
e3 := &testEntry{value: 3}
|
||||
|
||||
RingInit(e1)
|
||||
RingAdd(e1, e2)
|
||||
RingAdd(e1, e3)
|
||||
|
||||
sum := 0
|
||||
for e := e1.Next(); e != e1; e = e.Next() {
|
||||
sum += e.(*testEntry).value
|
||||
}
|
||||
if sum != 5 {
|
||||
t.Errorf("wrong sum: want 5, got %d", sum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingRemove(t *testing.T) {
|
||||
e1 := &testEntry{value: 1}
|
||||
e2 := &testEntry{value: 2}
|
||||
e3 := &testEntry{value: 3}
|
||||
|
||||
RingInit(e1)
|
||||
RingAdd(e1, e2)
|
||||
RingAdd(e2, e3)
|
||||
RingRemove(e2)
|
||||
|
||||
sum := 0
|
||||
for e := e1.Next(); e != e1; e = e.Next() {
|
||||
sum += e.(*testEntry).value
|
||||
}
|
||||
if sum != 3 {
|
||||
t.Errorf("wrong sum: want 3, got %d", sum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingEmpty(t *testing.T) {
|
||||
e1 := &testEntry{value: 1}
|
||||
e2 := &testEntry{value: 2}
|
||||
e3 := &testEntry{value: 3}
|
||||
|
||||
RingInit(e1)
|
||||
RingAdd(e1, e2)
|
||||
RingAdd(e2, e3)
|
||||
RingRemove(e3)
|
||||
RingRemove(e2)
|
||||
|
||||
sum := 0
|
||||
for e := e1.Next(); e != e1; e = e.Next() {
|
||||
sum += e.(*testEntry).value
|
||||
}
|
||||
if sum != 0 {
|
||||
t.Errorf("wrong sum: want 0, got %d", sum)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIterateForward(b *testing.B) {
|
||||
var l List
|
||||
for i := 0; i < 1000000; i++ {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2023 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 ring is an implementation of an intrusive circular linked list.
|
||||
package ring
|
||||
|
||||
// Container is the type that holds the list entries.
|
||||
type Container any
|
||||
|
||||
// Entry is an element in the circular linked list.
|
||||
//
|
||||
// +stateify savable
|
||||
type Entry struct {
|
||||
next *Entry
|
||||
prev *Entry
|
||||
container Container
|
||||
}
|
||||
|
||||
// Init instantiates an Element to be an item in a ring (circularly-linked
|
||||
// list).
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *Entry) Init(container Container) {
|
||||
e.next = e
|
||||
e.prev = e
|
||||
e.container = container
|
||||
}
|
||||
|
||||
// Add adds new to old's ring.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *Entry) Add(new *Entry) {
|
||||
next := e.next
|
||||
prev := e
|
||||
|
||||
next.prev = new
|
||||
new.next = next
|
||||
new.prev = prev
|
||||
e.next = new
|
||||
}
|
||||
|
||||
// Remove removes e from its ring and reinitializes it.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *Entry) Remove() {
|
||||
next := e.next
|
||||
prev := e.prev
|
||||
|
||||
next.prev = prev
|
||||
prev.next = next
|
||||
e.Init(e.container)
|
||||
}
|
||||
|
||||
// Empty returns true if there are no other elements in the ring.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *Entry) Empty() bool {
|
||||
return e.next == e
|
||||
}
|
||||
|
||||
// Next returns the next containing object pointed to by the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *Entry) Next() Container {
|
||||
return e.next.container
|
||||
}
|
||||
|
||||
// Prev returns the previous containing object pointed to by the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *Entry) Prev() Container {
|
||||
return e.prev.container
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2023 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 ring
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testContainer struct {
|
||||
value int
|
||||
entry testEntry
|
||||
}
|
||||
|
||||
func newContainer(value int) *testContainer {
|
||||
c := &testContainer{value: value}
|
||||
c.entry.Init(c)
|
||||
return c
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
e1 := newContainer(1)
|
||||
e2 := newContainer(2)
|
||||
e3 := newContainer(3)
|
||||
|
||||
e1.entry.Add(&e2.entry)
|
||||
e1.entry.Add(&e3.entry)
|
||||
|
||||
sum := 0
|
||||
want := 6
|
||||
e := e1
|
||||
for {
|
||||
sum += e.value
|
||||
e = e.entry.Next()
|
||||
if e == e1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if sum != want {
|
||||
t.Errorf("wrong sum: want %d, got %d", want, sum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
e1 := newContainer(1)
|
||||
e2 := newContainer(2)
|
||||
e3 := newContainer(3)
|
||||
|
||||
e1.entry.Add(&e2.entry)
|
||||
e2.entry.Add(&e3.entry)
|
||||
e2.entry.Remove()
|
||||
|
||||
sum := 0
|
||||
want := 4
|
||||
e := e1
|
||||
for {
|
||||
sum += e.value
|
||||
e = e.entry.Next()
|
||||
if e == e1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if sum != want {
|
||||
t.Errorf("wrong sum: want %d, got %d", want, sum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
head := newContainer(1)
|
||||
e2 := newContainer(2)
|
||||
e3 := newContainer(3)
|
||||
|
||||
head.entry.Add(&e2.entry)
|
||||
e2.entry.Add(&e3.entry)
|
||||
e3.entry.Remove()
|
||||
e2.entry.Remove()
|
||||
|
||||
sum := 0
|
||||
want := 0
|
||||
for e := head.entry.Next(); e != head; e = e.entry.Next() {
|
||||
sum += e.value
|
||||
}
|
||||
if sum != want {
|
||||
t.Errorf("wrong sum: want %d, got %d", want, sum)
|
||||
}
|
||||
}
|
||||
@@ -54,15 +54,13 @@ go_template_instance(
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "shared_list",
|
||||
out = "shared_list.go",
|
||||
name = "mount_ring",
|
||||
out = "mount_ring.go",
|
||||
package = "vfs",
|
||||
prefix = "shared",
|
||||
template = "//pkg/ilist:generic_list",
|
||||
prefix = "mount",
|
||||
template = "//pkg/ilist:generic_ring",
|
||||
types = {
|
||||
"Element": "*Mount",
|
||||
"Linker": "*sharedEntry",
|
||||
"ElementMapper": "sharedMapper",
|
||||
"Container": "*Mount",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -144,6 +142,7 @@ go_library(
|
||||
"lock.go",
|
||||
"mount.go",
|
||||
"mount_namespace_refs.go",
|
||||
"mount_ring.go",
|
||||
"mount_unsafe.go",
|
||||
"namespace.go",
|
||||
"opath.go",
|
||||
@@ -153,7 +152,6 @@ go_library(
|
||||
"propagation.go",
|
||||
"resolving_path.go",
|
||||
"save_restore.go",
|
||||
"shared_list.go",
|
||||
"vfs.go",
|
||||
"virtual_filesystem_mutex.go",
|
||||
],
|
||||
|
||||
@@ -92,7 +92,7 @@ type Mount struct {
|
||||
|
||||
// sharedEntry represents an entry in a circular list (ring) of mounts in a
|
||||
// shared peer group.
|
||||
sharedEntry
|
||||
sharedEntry mountEntry
|
||||
|
||||
// groupID is the ID for this mount's shared peer group. If the mount is not
|
||||
// in a peer group, this is 0.
|
||||
@@ -110,10 +110,6 @@ type Mount struct {
|
||||
writers atomicbitops.Int64
|
||||
}
|
||||
|
||||
type sharedMapper struct{}
|
||||
|
||||
func (sharedMapper) linkerFor(mnt *Mount) *sharedEntry { return &mnt.sharedEntry }
|
||||
|
||||
func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *MountNamespace, opts *MountOptions) *Mount {
|
||||
mnt := &Mount{
|
||||
ID: vfs.lastMountID.Add(1),
|
||||
@@ -128,7 +124,7 @@ func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *Mount
|
||||
if opts.ReadOnly {
|
||||
mnt.setReadOnlyLocked(true)
|
||||
}
|
||||
sharedRingInit(mnt)
|
||||
mnt.sharedEntry.Init(mnt)
|
||||
refs.Register(mnt)
|
||||
return mnt
|
||||
}
|
||||
|
||||
@@ -62,15 +62,15 @@ func (vfs *VirtualFilesystem) setPropagation(mnt *Mount, pflag uint32) error {
|
||||
return err
|
||||
}
|
||||
mnt.groupID = id
|
||||
sharedRingInit(mnt)
|
||||
mnt.sharedEntry.Init(mnt)
|
||||
mnt.isShared = true
|
||||
}
|
||||
case linux.MS_PRIVATE:
|
||||
if mnt.isShared {
|
||||
if sharedRingEmpty(mnt) {
|
||||
if mnt.sharedEntry.Empty() {
|
||||
vfs.freeGroupID(mnt.groupID)
|
||||
}
|
||||
sharedRingRemove(mnt)
|
||||
mnt.sharedEntry.Remove()
|
||||
mnt.groupID = 0
|
||||
mnt.isShared = false
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func (vfs *VirtualFilesystem) setPropagation(mnt *Mount, pflag uint32) error {
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) addPeer(mnt *Mount, new *Mount) {
|
||||
sharedRingAdd(mnt, new)
|
||||
mnt.sharedEntry.Add(&new.sharedEntry)
|
||||
new.isShared = true
|
||||
new.groupID = mnt.groupID
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user