From 15c158b20725fd62e2ee0a72ffaf1617852cd0d9 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH] devmapper: Provide a new parameter dm.deferred_device_removal Provide a new command line knob dm.deferred_device_removal which will enable deferred device deactivation if driver and library support it. This patch also checks for library support and driver version. Signed-off-by: Vivek Goyal --- daemon/graphdriver/devmapper/README.md | 20 +++++++ daemon/graphdriver/devmapper/deviceset.go | 65 ++++++++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/daemon/graphdriver/devmapper/README.md b/daemon/graphdriver/devmapper/README.md index a090b731fa..bd5b67c49c 100644 --- a/daemon/graphdriver/devmapper/README.md +++ b/daemon/graphdriver/devmapper/README.md @@ -252,3 +252,23 @@ Here is the list of supported options: > Otherwise, set this flag for migrating existing Docker daemons to a > daemon with a supported environment. + * `dm.use_deferred_removal` + + Enables use of deferred device removal if libdm and kernel driver + support the mechanism. + + Deferred device removal means that if device is busy when devices is + being removed/deactivated, then a deferred removal is scheduled on + device. And devices automatically goes away when last user of device + exits. + + For example, when contianer exits, its associated thin device is + removed. If that devices has leaked into some other mount namespace + can can't be removed now, container exit will still be successful + and this option will just schedule device for deferred removal and + will not wait in a loop trying to remove a busy device. + + Example use: + + ``docker -d --storage-opt dm.use_deferred_device_removal=true`` + diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index b5d67fa119..0856758cb7 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -37,7 +37,9 @@ var ( // We retry device removal so many a times that even error messages // will fill up console during normal operation. So only log Fatal // messages by default. - DMLogLevel int = devicemapper.LogLevelFatal + DMLogLevel int = devicemapper.LogLevelFatal + DriverDeferredRemovalSupport bool = false + EnableDeferredRemoval bool = false ) const deviceSetMetaFile string = "deviceset-metadata" @@ -103,6 +105,7 @@ type DeviceSet struct { thinPoolDevice string Transaction `json:"-"` overrideUdevSyncCheck bool + deferredRemove bool // use deferred removal } type DiskUsage struct { @@ -960,16 +963,67 @@ func (devices *DeviceSet) closeTransaction() error { return nil } +func determineDriverCapabilities(version string) error { + /* + * Driver version 4.27.0 and greater support deferred activation + * feature. + */ + + logrus.Debugf("devicemapper: driver version is %s", version) + + versionSplit := strings.Split(version, ".") + major, err := strconv.Atoi(versionSplit[0]) + if err != nil { + return graphdriver.ErrNotSupported + } + + if major > 4 { + DriverDeferredRemovalSupport = true + return nil + } + + if major < 4 { + return nil + } + + minor, err := strconv.Atoi(versionSplit[1]) + if err != nil { + return graphdriver.ErrNotSupported + } + + /* + * If major is 4 and minor is 27, then there is no need to + * check for patch level as it can not be less than 0. + */ + if minor >= 27 { + DriverDeferredRemovalSupport = true + return nil + } + + return nil +} + func (devices *DeviceSet) initDevmapper(doInit bool) error { // give ourselves to libdm as a log handler devicemapper.LogInit(devices) - _, err := devicemapper.GetDriverVersion() + version, err := devicemapper.GetDriverVersion() if err != nil { // Can't even get driver version, assume not supported return graphdriver.ErrNotSupported } + if err := determineDriverCapabilities(version); err != nil { + return graphdriver.ErrNotSupported + } + + // If user asked for deferred removal and both library and driver + // supports deferred removal use it. + if EnableDeferredRemoval && DriverDeferredRemovalSupport && devicemapper.LibraryDeferredRemovalSupport == true { + logrus.Debugf("devmapper: Deferred removal support enabled.") + devices.deferredRemove = true + } + // https://github.com/docker/docker/issues/4036 if supported := devicemapper.UdevSetSyncSupport(true); !supported { logrus.Errorf("Udev sync is not supported. This will lead to unexpected behavior, data loss and errors. For more information, see https://docs.docker.com/reference/commandline/cli/#daemon-storage-driver-option") @@ -1671,6 +1725,13 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error if err != nil { return nil, err } + + case "dm.use_deferred_removal": + EnableDeferredRemoval, err = strconv.ParseBool(val) + if err != nil { + return nil, err + } + default: return nil, fmt.Errorf("Unknown option %s\n", key) }