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 { ...@@ -96,7 +96,7 @@ type Inode struct {
// Children of this Inode. // Children of this Inode.
// When you change this, you MUST increment changeCounter. // 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. // Parents of this Inode. Can be more than one due to hard links.
// When you change this, you MUST increment changeCounter. // When you change this, you MUST increment changeCounter.
...@@ -122,7 +122,7 @@ func initInode(n *Inode, ops InodeEmbedder, attr StableAttr, bridge *rawBridge, ...@@ -122,7 +122,7 @@ func initInode(n *Inode, ops InodeEmbedder, attr StableAttr, bridge *rawBridge,
n.persistent = persistent n.persistent = persistent
n.nodeId = nodeId n.nodeId = nodeId
if attr.Mode == fuse.S_IFDIR { if attr.Mode == fuse.S_IFDIR {
n.children = make(map[string]*Inode) n.children.init()
} }
} }
...@@ -169,12 +169,8 @@ func modeStr(m uint32) string { ...@@ -169,12 +169,8 @@ func modeStr(m uint32) string {
func (n *Inode) String() string { func (n *Inode) String() string {
n.mu.Lock() n.mu.Lock()
defer n.mu.Unlock() 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. // sortNodes rearranges inode group in consistent order.
...@@ -319,17 +315,13 @@ func (n *Inode) Path(root *Inode) string { ...@@ -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 // but it could be also valid if only iparent is locked and ichild was just
// created and only one goroutine keeps referencing it. // created and only one goroutine keeps referencing it.
func (iparent *Inode) setEntry(name string, ichild *Inode) { func (iparent *Inode) setEntry(name string, ichild *Inode) {
newParent := parentData{name, iparent}
if ichild.stableAttr.Mode == syscall.S_IFDIR { if ichild.stableAttr.Mode == syscall.S_IFDIR {
// Directories cannot have more than one parent. Clear the map. // Directories cannot have more than one parent. Clear the map.
// This special-case is neccessary because ichild may still have a // This special-case is neccessary because ichild may still have a
// parent that was forgotten (i.e. removed from bridge.inoMap). // parent that was forgotten (i.e. removed from bridge.inoMap).
ichild.parents.clear() ichild.parents.clear()
} }
ichild.parents.add(newParent) iparent.children.set(iparent, name, ichild)
iparent.children[name] = ichild
ichild.changeCounter++
iparent.changeCounter++
} }
// NewPersistentInode returns an Inode whose lifetime is not in // NewPersistentInode returns an Inode whose lifetime is not in
...@@ -402,7 +394,7 @@ retry: ...@@ -402,7 +394,7 @@ retry:
lockme = append(lockme[:0], n) lockme = append(lockme[:0], n)
parents = parents[:0] parents = parents[:0]
nChange := n.changeCounter 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() { for _, p := range n.parents.all() {
parents = append(parents, p) parents = append(parents, p)
lockme = append(lockme, p.parent) lockme = append(lockme, p.parent)
...@@ -422,15 +414,12 @@ retry: ...@@ -422,15 +414,12 @@ retry:
} }
for _, p := range parents { 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 // another node has replaced us already
continue continue
} }
delete(p.parent.children, p.name) p.parent.children.del(p.parent, p.name)
p.parent.changeCounter++
} }
n.parents.clear()
n.changeCounter++
if n.lookupCount != 0 { if n.lookupCount != 0 {
log.Panicf("n%d %p lookupCount changed: %d", n.nodeId, n, n.lookupCount) log.Panicf("n%d %p lookupCount changed: %d", n.nodeId, n, n.lookupCount)
...@@ -453,7 +442,7 @@ retry: ...@@ -453,7 +442,7 @@ retry:
func (n *Inode) GetChild(name string) *Inode { func (n *Inode) GetChild(name string) *Inode {
n.mu.Lock() n.mu.Lock()
defer n.mu.Unlock() 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 // 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) ...@@ -466,13 +455,10 @@ func (n *Inode) AddChild(name string, ch *Inode, overwrite bool) (success bool)
retry: retry:
for { for {
lockNode2(n, ch) lockNode2(n, ch)
prev, ok := n.children[name] prev := n.children.get(name)
parentCounter := n.changeCounter parentCounter := n.changeCounter
if !ok { if prev == nil {
n.children[name] = ch n.children.set(n, name, ch)
ch.parents.add(parentData{name, n})
n.changeCounter++
ch.changeCounter++
unlockNode2(n, ch) unlockNode2(n, ch)
return true return true
} }
...@@ -489,10 +475,7 @@ retry: ...@@ -489,10 +475,7 @@ retry:
} }
prev.parents.delete(parentData{name, n}) prev.parents.delete(parentData{name, n})
n.children[name] = ch n.children.set(n, name, ch)
ch.parents.add(parentData{name, n})
n.changeCounter++
ch.changeCounter++
prev.changeCounter++ prev.changeCounter++
unlockNodes(lockme[:]...) unlockNodes(lockme[:]...)
...@@ -504,16 +487,7 @@ retry: ...@@ -504,16 +487,7 @@ retry:
func (n *Inode) Children() map[string]*Inode { func (n *Inode) Children() map[string]*Inode {
n.mu.Lock() n.mu.Lock()
defer n.mu.Unlock() defer n.mu.Unlock()
r := make(map[string]*Inode, len(n.children)) return n.children.toMap()
for k, v := range n.children {
r[k] = v
}
return r
}
type childEntry struct {
Name string
Inode *Inode
} }
// childrenList returns the list of children of this directory Inode. // childrenList returns the list of children of this directory Inode.
...@@ -522,28 +496,7 @@ type childEntry struct { ...@@ -522,28 +496,7 @@ type childEntry struct {
func (n *Inode) childrenList() []childEntry { func (n *Inode) childrenList() []childEntry {
n.mu.Lock() n.mu.Lock()
defer n.mu.Unlock() defer n.mu.Unlock()
r := make([]childEntry, 0, 2*len(n.children)) return n.children.list()
// 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
} }
// Parents returns a parent of this Inode, or nil if this Inode is // Parents returns a parent of this Inode, or nil if this Inode is
...@@ -589,7 +542,7 @@ retry: ...@@ -589,7 +542,7 @@ retry:
lockme = append(lockme[:0], n) lockme = append(lockme[:0], n)
nChange := n.changeCounter nChange := n.changeCounter
for _, nm := range names { for _, nm := range names {
ch := n.children[nm] ch := n.children.get(nm)
if ch == nil { if ch == nil {
n.mu.Unlock() n.mu.Unlock()
return false, true return false, true
...@@ -599,21 +552,17 @@ retry: ...@@ -599,21 +552,17 @@ retry:
n.mu.Unlock() n.mu.Unlock()
lockNodes(lockme...) lockNodes(lockme...)
if n.changeCounter != nChange { if n.changeCounter != nChange {
unlockNodes(lockme...) unlockNodes(lockme...)
continue retry continue retry
} }
for _, nm := range names { for _, nm := range names {
ch := n.children[nm] n.children.del(n, nm)
delete(n.children, nm)
ch.parents.delete(parentData{nm, n})
ch.changeCounter++
} }
n.changeCounter++
live = n.lookupCount > 0 || len(n.children) > 0 || n.persistent live = n.lookupCount > 0 || n.children.len() > 0 || n.persistent
unlockNodes(lockme...) unlockNodes(lockme...)
// removal successful // removal successful
...@@ -642,8 +591,8 @@ retry: ...@@ -642,8 +591,8 @@ retry:
counter1 := n.changeCounter counter1 := n.changeCounter
counter2 := newParent.changeCounter counter2 := newParent.changeCounter
oldChild := n.children[old] oldChild := n.children.get(old)
destChild := newParent.children[newName] destChild := newParent.children.get(newName)
unlockNode2(n, newParent) unlockNode2(n, newParent)
if destChild != nil && !overwrite { if destChild != nil && !overwrite {
...@@ -657,27 +606,17 @@ retry: ...@@ -657,27 +606,17 @@ retry:
} }
if oldChild != nil { if oldChild != nil {
delete(n.children, old) n.children.del(n, old)
oldChild.parents.delete(parentData{old, n})
n.changeCounter++
oldChild.changeCounter++
} }
if destChild != nil { if destChild != nil {
// This can cause the child to be slated for // This can cause the child to be slated for
// removal; see below // removal; see below
delete(newParent.children, newName) newParent.children.del(newParent, newName)
destChild.parents.delete(parentData{newName, newParent})
destChild.changeCounter++
newParent.changeCounter++
} }
if oldChild != nil { if oldChild != nil {
newParent.children[newName] = oldChild newParent.children.set(newParent, newName, oldChild)
newParent.changeCounter++
oldChild.parents.add(parentData{newName, newParent})
oldChild.changeCounter++
} }
unlockNodes(n, newParent, oldChild, destChild) unlockNodes(n, newParent, oldChild, destChild)
...@@ -699,8 +638,8 @@ retry: ...@@ -699,8 +638,8 @@ retry:
counter1 := oldParent.changeCounter counter1 := oldParent.changeCounter
counter2 := newParent.changeCounter counter2 := newParent.changeCounter
oldChild := oldParent.children[oldName] oldChild := oldParent.children.get(oldName)
destChild := newParent.children[newName] destChild := newParent.children.get(newName)
unlockNode2(oldParent, newParent) unlockNode2(oldParent, newParent)
if destChild == oldChild { if destChild == oldChild {
...@@ -715,34 +654,20 @@ retry: ...@@ -715,34 +654,20 @@ retry:
// Detach // Detach
if oldChild != nil { if oldChild != nil {
delete(oldParent.children, oldName) oldParent.children.del(oldParent, oldName)
oldChild.parents.delete(parentData{oldName, oldParent})
oldParent.changeCounter++
oldChild.changeCounter++
} }
if destChild != nil { if destChild != nil {
delete(newParent.children, newName) newParent.children.del(newParent, newName)
destChild.parents.delete(parentData{newName, newParent})
destChild.changeCounter++
newParent.changeCounter++
} }
// Attach // Attach
if oldChild != nil { if oldChild != nil {
newParent.children[newName] = oldChild newParent.children.set(newParent, newName, oldChild)
newParent.changeCounter++
oldChild.parents.add(parentData{newName, newParent})
oldChild.changeCounter++
} }
if destChild != nil { if destChild != nil {
oldParent.children[oldName] = destChild oldParent.children.set(oldParent, oldName, destChild)
oldParent.changeCounter++
destChild.parents.add(parentData{oldName, oldParent})
destChild.changeCounter++
} }
unlockNodes(oldParent, newParent, oldChild, destChild) unlockNodes(oldParent, newParent, oldChild, destChild)
return 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