Browse code

devmapper: Provide option to enabled deferred device deletion

Provide a command line option dm.use_deferred_deletion to enable deferred
device deletion feature. By default feature will be turned off.

Not sure if there is much value in deferred deletion being turned on
without deferred removal being turned on. So for now, this feature can
be enabled only if deferred removal is on.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>

Vivek Goyal authored on 2015/10/07 06:37:21
Showing 4 changed files
... ...
@@ -40,6 +40,7 @@ var (
40 40
 	logLevel                     = devicemapper.LogLevelFatal
41 41
 	driverDeferredRemovalSupport = false
42 42
 	enableDeferredRemoval        = false
43
+	enableDeferredDeletion       = false
43 44
 )
44 45
 
45 46
 const deviceSetMetaFile string = "deviceset-metadata"
... ...
@@ -106,6 +107,7 @@ type DeviceSet struct {
106 106
 	transaction           `json:"-"`
107 107
 	overrideUdevSyncCheck bool
108 108
 	deferredRemove        bool   // use deferred removal
109
+	deferredDelete        bool   // use deferred deletion
109 110
 	BaseDeviceUUID        string //save UUID of base device
110 111
 }
111 112
 
... ...
@@ -141,6 +143,12 @@ type Status struct {
141 141
 	UdevSyncSupported bool
142 142
 	// DeferredRemoveEnabled is true then the device is not unmounted.
143 143
 	DeferredRemoveEnabled bool
144
+	// True if deferred deletion is enabled. This is different from
145
+	// deferred removal. "removal" means that device mapper device is
146
+	// deactivated. Thin device is still in thin pool and can be activated
147
+	// again. But "deletion" means that thin device will be deleted from
148
+	// thin pool and it can't be activated again.
149
+	DeferredDeleteEnabled bool
144 150
 }
145 151
 
146 152
 // Structure used to export image/container metadata in docker inspect.
... ...
@@ -1314,13 +1322,27 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
1314 1314
 		return graphdriver.ErrNotSupported
1315 1315
 	}
1316 1316
 
1317
-	// If user asked for deferred removal and both library and driver
1318
-	// supports deferred removal use it.
1319
-	if enableDeferredRemoval && driverDeferredRemovalSupport && devicemapper.LibraryDeferredRemovalSupport == true {
1317
+	// If user asked for deferred removal then check both libdm library
1318
+	// and kernel driver support deferred removal otherwise error out.
1319
+	if enableDeferredRemoval {
1320
+		if !driverDeferredRemovalSupport {
1321
+			return fmt.Errorf("devmapper: Deferred removal can not be enabled as kernel does not support it")
1322
+		}
1323
+		if !devicemapper.LibraryDeferredRemovalSupport {
1324
+			return fmt.Errorf("devmapper: Deferred removal can not be enabled as libdm does not support it")
1325
+		}
1320 1326
 		logrus.Debugf("devmapper: Deferred removal support enabled.")
1321 1327
 		devices.deferredRemove = true
1322 1328
 	}
1323 1329
 
1330
+	if enableDeferredDeletion {
1331
+		if !devices.deferredRemove {
1332
+			return fmt.Errorf("devmapper: Deferred deletion can not be enabled as deferred removal is not enabled. Enable deferred removal using --storage-opt dm.use_deferred_removal=true parameter")
1333
+		}
1334
+		logrus.Debugf("devmapper: Deferred deletion support enabled.")
1335
+		devices.deferredDelete = true
1336
+	}
1337
+
1324 1338
 	// https://github.com/docker/docker/issues/4036
1325 1339
 	if supported := devicemapper.UdevSetSyncSupport(true); !supported {
1326 1340
 		logrus.Warn("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/daemon/#daemon-storage-driver-option")
... ...
@@ -1996,6 +2018,7 @@ func (devices *DeviceSet) Status() *Status {
1996 1996
 	status.MetadataLoopback = devices.metadataLoopFile
1997 1997
 	status.UdevSyncSupported = devicemapper.UdevSyncSupported()
1998 1998
 	status.DeferredRemoveEnabled = devices.deferredRemove
1999
+	status.DeferredDeleteEnabled = devices.deferredDelete
1999 2000
 
2000 2001
 	totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus()
2001 2002
 	if err == nil {
... ...
@@ -2128,6 +2151,12 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error
2128 2128
 				return nil, err
2129 2129
 			}
2130 2130
 
2131
+		case "dm.use_deferred_deletion":
2132
+			enableDeferredDeletion, err = strconv.ParseBool(val)
2133
+			if err != nil {
2134
+				return nil, err
2135
+			}
2136
+
2131 2137
 		default:
2132 2138
 			return nil, fmt.Errorf("Unknown option %s\n", key)
2133 2139
 		}
... ...
@@ -84,6 +84,7 @@ func (d *Driver) Status() [][2]string {
84 84
 		{"Metadata Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Available)))},
85 85
 		{"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)},
86 86
 		{"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)},
87
+		{"Deferred Deletion Enabled", fmt.Sprintf("%v", s.DeferredDeleteEnabled)},
87 88
 	}
88 89
 	if len(s.DataLoopback) > 0 {
89 90
 		status = append(status, [2]string{"Data loop file", s.DataLoopback})
... ...
@@ -368,6 +368,46 @@ options for `zfs` start with `zfs`.
368 368
     > Otherwise, set this flag for migrating existing Docker daemons to
369 369
     > a daemon with a supported environment.
370 370
 
371
+ *  `dm.use_deferred_removal`
372
+
373
+    Enables use of deferred device removal if `libdm` and the kernel driver
374
+    support the mechanism.
375
+
376
+    Deferred device removal means that if device is busy when devices are
377
+    being removed/deactivated, then a deferred removal is scheduled on
378
+    device. And devices automatically go away when last user of the device
379
+    exits.
380
+
381
+    For example, when a container exits, its associated thin device is removed.
382
+    If that device has leaked into some other mount namespace and can't be
383
+    removed, the container exit still succeeds and this option causes the
384
+    system to schedule the device for deferred removal. It does not wait in a
385
+    loop trying to remove a busy device.
386
+
387
+    Example use: `docker daemon --storage-opt dm.use_deferred_removal=true`
388
+
389
+ *  `dm.use_deferred_deletion`
390
+
391
+    Enables use of deferred device deletion for thin pool devices. By default,
392
+    thin pool device deletion is synchronous. Before a container is deleted,
393
+    the Docker daemon removes any associated devices. If the storage driver
394
+    can not remove a device, the container deletion fails and daemon returns.
395
+
396
+    `Error deleting container: Error response from daemon: Cannot destroy container`
397
+
398
+    To avoid this failure, enable both deferred device deletion and deferred
399
+    device removal on the daemon.
400
+
401
+    `docker daemon --storage-opt dm.use_deferred_deletion=true --storage-opt dm.use_deferred_removal=true`
402
+
403
+    With these two options enabled, if a device is busy when the driver is
404
+    deleting a container, the driver marks the device as deleted. Later, when
405
+    the device isn't in use, the driver deletes it.
406
+
407
+    In general it should be safe to enable this option by default. It will help
408
+    when unintentional leaking of mount point happens across multiple mount
409
+    namespaces.
410
+
371 411
 Currently supported options of `zfs`:
372 412
 
373 413
  * `zfs.fsname`
... ...
@@ -302,6 +302,28 @@ device.
302 302
 
303 303
 Example use: `docker daemon --storage-opt dm.use_deferred_removal=true`
304 304
 
305
+#### dm.use_deferred_deletion
306
+
307
+Enables use of deferred device deletion for thin pool devices. By default,
308
+thin pool device deletion is synchronous. Before a container is deleted, the
309
+Docker daemon removes any associated devices. If the storage driver can not
310
+remove a device, the container deletion fails and daemon returns.
311
+
312
+`Error deleting container: Error response from daemon: Cannot destroy container`
313
+
314
+To avoid this failure, enable both deferred device deletion and deferred
315
+device removal on the daemon.
316
+
317
+`docker daemon --storage-opt dm.use_deferred_deletion=true --storage-opt dm.use_deferred_removal=true`
318
+
319
+With these two options enabled, if a device is busy when the driver is
320
+deleting a container, the driver marks the device as deleted. Later, when the
321
+device isn't in use, the driver deletes it.
322
+
323
+In general it should be safe to enable this option by default. It will help
324
+when unintentional leaking of mount point happens across multiple mount
325
+namespaces.
326
+
305 327
 #### dm.loopdatasize
306 328
 
307 329
 **Note**: This option configures devicemapper loopback, which should not be used in production.