aufs: Unmount inactive devices

This implements the new Put() operation such that
Get()/Put() maintains a refcount for each ID, mounting
only on first Get() and unmounting on the last Get().

This means we avoid littering the system with lots of mounts
and free resources related to them.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
This commit is contained in:
Alexander Larsson 2014-01-14 12:23:20 +01:00
parent 886f650d9b
commit 5fe26ee426
1 changed files with 47 additions and 5 deletions

View File

@ -31,6 +31,7 @@ import (
"os/exec"
"path"
"strings"
"sync"
)
func init() {
@ -38,7 +39,9 @@ func init() {
}
type Driver struct {
root string
root string
sync.Mutex // Protects concurrent modification to active
active map[string]int
}
// New returns a new AUFS driver.
@ -54,12 +57,17 @@ func Init(root string) (graphdriver.Driver, error) {
"layers",
}
a := &Driver{
root: root,
active: make(map[string]int),
}
// Create the root aufs driver dir and return
// if it already exists
// If not populate the dir structure
if err := os.MkdirAll(root, 0755); err != nil {
if os.IsExist(err) {
return &Driver{root}, nil
return a, nil
}
return nil, err
}
@ -69,7 +77,7 @@ func Init(root string) (graphdriver.Driver, error) {
return nil, err
}
}
return &Driver{root}, nil
return a, nil
}
// Return a nil error if the kernel supports aufs
@ -167,6 +175,14 @@ func (a *Driver) createDirsFor(id string) error {
// Unmount and remove the dir information
func (a *Driver) Remove(id string) error {
// Protect the a.active from concurrent access
a.Lock()
defer a.Unlock()
if a.active[id] != 0 {
utils.Errorf("Warning: removing active id %s\n", id)
}
// Make sure the dir is umounted first
if err := a.unmount(id); err != nil {
return err
@ -210,19 +226,45 @@ func (a *Driver) Get(id string) (string, error) {
ids = []string{}
}
// Protect the a.active from concurrent access
a.Lock()
defer a.Unlock()
count := a.active[id]
// If a dir does not have a parent ( no layers )do not try to mount
// just return the diff path to the data
out := path.Join(a.rootPath(), "diff", id)
if len(ids) > 0 {
out = path.Join(a.rootPath(), "mnt", id)
if err := a.mount(id); err != nil {
return "", err
if count == 0 {
if err := a.mount(id); err != nil {
return "", err
}
}
}
a.active[id] = count + 1
return out, nil
}
func (a *Driver) Put(id string) {
// Protect the a.active from concurrent access
a.Lock()
defer a.Unlock()
if count := a.active[id]; count > 1 {
a.active[id] = count - 1
} else {
ids, _ := getParentIds(a.rootPath(), id)
// We only mounted if there are any parents
if ids != nil && len(ids) > 0 {
a.unmount(id)
}
delete(a.active, id)
}
}
// Returns an archive of the contents for the id