From a9fa1a13c3b0a654a96be01ff7ec19e8009b2094 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Mar 2014 17:32:59 +0100 Subject: [PATCH] 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 (github: alexlarsson) --- runtime/graphdriver/devmapper/deviceset.go | 36 ++++++++++------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index 4d33e243e0..dfdb180bb2 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -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