devicemapper: Better/faster shutdown

Right now shutdown is looping over *all* devicemapper
devices and actively deactivating them, this is pretty
slow if you have a lot of non-active containers. We
instead only deactivate the devices that are mounted.

We also do the shutdown unmount using MNT_DETACH which
forces the unmount in the global namespace, even if it
is busy because of some container having it mounted.
This means the device will be freed when that container
exits.

Also, we move the call to waitClose to deactivateDevice
because all callers of any of them call both anyway.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
This commit is contained in:
Alexander Larsson 2014-03-20 17:32:59 +01:00
parent 56c156190a
commit a9fa1a13c3
1 changed files with 17 additions and 19 deletions

View File

@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"sync"
"syscall"
"time"
)
@ -677,6 +678,12 @@ func (devices *DeviceSet) deactivateDevice(hash string) error {
utils.Debugf("[devmapper] deactivateDevice(%s)", hash)
defer utils.Debugf("[devmapper] deactivateDevice END")
// Wait for the unmount to be effective,
// by watching the value of Info.OpenCount for the device
if err := devices.waitClose(hash); err != nil {
utils.Errorf("Warning: error waiting for device %s to close: %s\n", hash, err)
}
info := devices.Devices[hash]
if info == nil {
return fmt.Errorf("Unknown device %s", hash)
@ -799,26 +806,20 @@ func (devices *DeviceSet) Shutdown() error {
for _, info := range devices.Devices {
info.lock.Lock()
if info.mountCount > 0 {
if err := sysUnmount(info.mountPath, 0); err != nil {
// We use MNT_DETACH here in case it is still busy in some running
// container. This means it'll go away from the global scope directly,
// and the device will be released when that container dies.
if err := sysUnmount(info.mountPath, syscall.MNT_DETACH); err != nil {
utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err)
}
if err := devices.deactivateDevice(info.Hash); err != nil {
utils.Debugf("Shutdown deactivate %s , error: %s\n", info.Hash, err)
}
}
info.lock.Unlock()
}
for _, d := range devices.Devices {
d.lock.Lock()
if err := devices.waitClose(d.Hash); err != nil {
utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
}
if err := devices.deactivateDevice(d.Hash); err != nil {
utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
}
d.lock.Unlock()
}
if err := devices.deactivatePool(); err != nil {
utils.Debugf("Shutdown deactivate pool , error: %s\n", err)
}
@ -920,14 +921,11 @@ func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error {
return err
}
utils.Debugf("[devmapper] Unmount done")
// Wait for the unmount to be effective,
// by watching the value of Info.OpenCount for the device
if err := devices.waitClose(hash); err != nil {
if err := devices.deactivateDevice(hash); err != nil {
return err
}
devices.deactivateDevice(hash)
info.mountPath = ""
return nil