Commit 6eaf1ece authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fs: move children map into separate struct

Encapsulates the handling the children map, in preparation of changing
the representation. It also reduces repetition for handling the
parents field and changeCounter

Change-Id: Ic741a376c6261dab606ed0705cdd130e70c8b69e
parent bb90a4c9
......@@ -96,7 +96,7 @@ type Inode struct {
// Children of this Inode.
// When you change this, you MUST increment changeCounter.
children map[string]*Inode
children inodeChildren
// Parents of this Inode. Can be more than one due to hard links.
// When you change this, you MUST increment changeCounter.
......@@ -122,7 +122,7 @@ func initInode(n *Inode, ops InodeEmbedder, attr StableAttr, bridge *rawBridge,
n.persistent = persistent
n.nodeId = nodeId
if attr.Mode == fuse.S_IFDIR {
n.children = make(map[string]*Inode)
n.children.init()
}
}
......@@ -169,12 +169,8 @@ func modeStr(m uint32) string {
func (n *Inode) String() string {
n.mu.Lock()
defer n.mu.Unlock()
var ss []string
for nm, ch := range n.children {
ss = append(ss, fmt.Sprintf("%q=i%d[%s]", nm, ch.stableAttr.Ino, modeStr(ch.stableAttr.Mode)))
}
return fmt.Sprintf("i%d (%s): %s", n.stableAttr.Ino, modeStr(n.stableAttr.Mode), strings.Join(ss, ","))
return fmt.Sprintf("i%d (%s): %s", n.stableAttr.Ino, modeStr(n.stableAttr.Mode), n.children.String())
}
// sortNodes rearranges inode group in consistent order.
......@@ -319,17 +315,13 @@ func (n *Inode) Path(root *Inode) string {
// but it could be also valid if only iparent is locked and ichild was just
// created and only one goroutine keeps referencing it.
func (iparent *Inode) setEntry(name string, ichild *Inode) {
newParent := parentData{name, iparent}
if ichild.stableAttr.Mode == syscall.S_IFDIR {
// Directories cannot have more than one parent. Clear the map.
// This special-case is neccessary because ichild may still have a
// parent that was forgotten (i.e. removed from bridge.inoMap).
ichild.parents.clear()
}
ichild.parents.add(newParent)
iparent.children[name] = ichild
ichild.changeCounter++
iparent.changeCounter++
iparent.children.set(iparent, name, ichild)
}
// NewPersistentInode returns an Inode whose lifetime is not in
......@@ -402,7 +394,7 @@ retry:
lockme = append(lockme[:0], n)
parents = parents[:0]
nChange := n.changeCounter
live = n.lookupCount > 0 || len(n.children) > 0 || n.persistent
live = n.lookupCount > 0 || n.children.len() > 0 || n.persistent
for _, p := range n.parents.all() {
parents = append(parents, p)
lockme = append(lockme, p.parent)
......@@ -422,15 +414,12 @@ retry:
}
for _, p := range parents {
if p.parent.children[p.name] != n {
if p.parent.children.get(p.name) != n {
// another node has replaced us already
continue
}
delete(p.parent.children, p.name)
p.parent.changeCounter++
p.parent.children.del(p.parent, p.name)
}
n.parents.clear()
n.changeCounter++
if n.lookupCount != 0 {
log.Panicf("n%d %p lookupCount changed: %d", n.nodeId, n, n.lookupCount)
......@@ -453,7 +442,7 @@ retry:
func (n *Inode) GetChild(name string) *Inode {
n.mu.Lock()
defer n.mu.Unlock()
return n.children[name]
return n.children.get(name)
}
// AddChild adds a child to this node. If overwrite is false, fail if
......@@ -466,13 +455,10 @@ func (n *Inode) AddChild(name string, ch *Inode, overwrite bool) (success bool)
retry:
for {
lockNode2(n, ch)
prev, ok := n.children[name]
prev := n.children.get(name)
parentCounter := n.changeCounter
if !ok {
n.children[name] = ch
ch.parents.add(parentData{name, n})
n.changeCounter++
ch.changeCounter++
if prev == nil {
n.children.set(n, name, ch)
unlockNode2(n, ch)
return true
}
......@@ -489,10 +475,7 @@ retry:
}
prev.parents.delete(parentData{name, n})
n.children[name] = ch
ch.parents.add(parentData{name, n})
n.changeCounter++
ch.changeCounter++
n.children.set(n, name, ch)
prev.changeCounter++
unlockNodes(lockme[:]...)
......@@ -504,16 +487,7 @@ retry:
func (n *Inode) Children() map[string]*Inode {
n.mu.Lock()
defer n.mu.Unlock()
r := make(map[string]*Inode, len(n.children))
for k, v := range n.children {
r[k] = v
}
return r
}
type childEntry struct {
Name string
Inode *Inode
return n.children.toMap()
}
// childrenList returns the list of children of this directory Inode.
......@@ -522,28 +496,7 @@ type childEntry struct {
func (n *Inode) childrenList() []childEntry {
n.mu.Lock()
defer n.mu.Unlock()
r := make([]childEntry, 0, 2*len(n.children))
// The spec doesn't guarantee this, but as long as maps remain
// backed by hash tables, the simplest mechanism for
// randomization is picking a random start index. We undo this
// here by picking a deterministic start index again. If the
// Go runtime ever implements a memory moving GC, we might
// have to look at the keys instead.
minNode := ^uintptr(0)
minIdx := -1
for k, v := range n.children {
if p := uintptr(unsafe.Pointer(v)); p < minNode {
minIdx = len(r)
minNode = p
}
r = append(r, childEntry{Name: k, Inode: v})
}
if minIdx > 0 {
r = append(r[minIdx:], r[:minIdx]...)
}
return r
return n.children.list()
}
// Parents returns a parent of this Inode, or nil if this Inode is
......@@ -589,7 +542,7 @@ retry:
lockme = append(lockme[:0], n)
nChange := n.changeCounter
for _, nm := range names {
ch := n.children[nm]
ch := n.children.get(nm)
if ch == nil {
n.mu.Unlock()
return false, true
......@@ -599,21 +552,17 @@ retry:
n.mu.Unlock()
lockNodes(lockme...)
if n.changeCounter != nChange {
unlockNodes(lockme...)
continue retry
}
for _, nm := range names {
ch := n.children[nm]
delete(n.children, nm)
ch.parents.delete(parentData{nm, n})
ch.changeCounter++
n.children.del(n, nm)
}
n.changeCounter++
live = n.lookupCount > 0 || len(n.children) > 0 || n.persistent
live = n.lookupCount > 0 || n.children.len() > 0 || n.persistent
unlockNodes(lockme...)
// removal successful
......@@ -642,8 +591,8 @@ retry:
counter1 := n.changeCounter
counter2 := newParent.changeCounter
oldChild := n.children[old]
destChild := newParent.children[newName]
oldChild := n.children.get(old)
destChild := newParent.children.get(newName)
unlockNode2(n, newParent)
if destChild != nil && !overwrite {
......@@ -657,27 +606,17 @@ retry:
}
if oldChild != nil {
delete(n.children, old)
oldChild.parents.delete(parentData{old, n})
n.changeCounter++
oldChild.changeCounter++
n.children.del(n, old)
}
if destChild != nil {
// This can cause the child to be slated for
// removal; see below
delete(newParent.children, newName)
destChild.parents.delete(parentData{newName, newParent})
destChild.changeCounter++
newParent.changeCounter++
newParent.children.del(newParent, newName)
}
if oldChild != nil {
newParent.children[newName] = oldChild
newParent.changeCounter++
oldChild.parents.add(parentData{newName, newParent})
oldChild.changeCounter++
newParent.children.set(newParent, newName, oldChild)
}
unlockNodes(n, newParent, oldChild, destChild)
......@@ -699,8 +638,8 @@ retry:
counter1 := oldParent.changeCounter
counter2 := newParent.changeCounter
oldChild := oldParent.children[oldName]
destChild := newParent.children[newName]
oldChild := oldParent.children.get(oldName)
destChild := newParent.children.get(newName)
unlockNode2(oldParent, newParent)
if destChild == oldChild {
......@@ -715,34 +654,20 @@ retry:
// Detach
if oldChild != nil {
delete(oldParent.children, oldName)
oldChild.parents.delete(parentData{oldName, oldParent})
oldParent.changeCounter++
oldChild.changeCounter++
oldParent.children.del(oldParent, oldName)
}
if destChild != nil {
delete(newParent.children, newName)
destChild.parents.delete(parentData{newName, newParent})
destChild.changeCounter++
newParent.changeCounter++
newParent.children.del(newParent, newName)
}
// Attach
if oldChild != nil {
newParent.children[newName] = oldChild
newParent.changeCounter++
oldChild.parents.add(parentData{newName, newParent})
oldChild.changeCounter++
newParent.children.set(newParent, newName, oldChild)
}
if destChild != nil {
oldParent.children[oldName] = destChild
oldParent.changeCounter++
destChild.parents.add(parentData{oldName, oldParent})
destChild.changeCounter++
oldParent.children.set(oldParent, oldName, destChild)
}
unlockNodes(oldParent, newParent, oldChild, destChild)
return
......
// Copyright 2023 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"fmt"
"strings"
"unsafe"
)
type childEntry struct {
Name string
Inode *Inode
}
type inodeChildren struct {
children map[string]*Inode
}
func (c *inodeChildren) init() {
c.children = make(map[string]*Inode)
}
func (c *inodeChildren) String() string {
var ss []string
for nm, ch := range c.children {
ss = append(ss, fmt.Sprintf("%q=i%d[%s]", nm, ch.stableAttr.Ino, modeStr(ch.stableAttr.Mode)))
}
return strings.Join(ss, ",")
}
func (c *inodeChildren) get(name string) *Inode {
return c.children[name]
}
func (c *inodeChildren) set(parent *Inode, name string, ch *Inode) {
c.children[name] = ch
parent.changeCounter++
ch.parents.add(parentData{name, parent})
ch.changeCounter++
}
func (c *inodeChildren) len() int {
return len(c.children)
}
func (c *inodeChildren) toMap() map[string]*Inode {
r := make(map[string]*Inode, len(c.children))
for k, v := range c.children {
r[k] = v
}
return r
}
func (c *inodeChildren) del(parent *Inode, name string) {
ch := c.children[name]
if ch == nil {
return
}
delete(c.children, name)
ch.parents.delete(parentData{name, parent})
ch.changeCounter++
parent.changeCounter++
}
func (c *inodeChildren) list() []childEntry {
r := make([]childEntry, 0, 2*len(c.children))
// The spec doesn't guarantee this, but as long as maps remain
// backed by hash tables, the simplest mechanism for
// randomization is picking a random start index. We undo this
// here by picking a deterministic start index again. If the
// Go runtime ever implements a memory moving GC, we might
// have to look at the keys instead.
minNode := ^uintptr(0)
minIdx := -1
for k, v := range c.children {
if p := uintptr(unsafe.Pointer(v)); p < minNode {
minIdx = len(r)
minNode = p
}
r = append(r, childEntry{Name: k, Inode: v})
}
if minIdx > 0 {
r = append(r[minIdx:], r[:minIdx]...)
}
return r
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment