瀏覽代碼

Wait for device removal if deferredRemoval=true and deferredDeletion=false

There have been some cases where umount, a device can be busy for a very
short duration. Maybe its udev rules, or maybe it is runc related races
or probably it is something else. We don't know yet.

If deferred removal is enabled but deferred deletion is not, then for the
case of "docker run -ti --rm fedora bash", a container will exit, device
will be deferred removed and then immediately a call will come to delete
the device. It is possible that deletion will fail if device was busy
at that time.

A device can't be deleted if it can't be removed/deactivated first. There
is only one exception and that is when deferred deletion is on. In that
case graph driver will keep track of deleted device and try to delete it
later and return success to caller.

Always make sure that device deactivation is synchronous when device is
being deleted (except the case when deferred deletion is enabled).

This should also take care of small races when device is busy for a short
duration and it is being deleted.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
Vivek Goyal 8 年之前
父節點
當前提交
36cb6efebc
共有 2 個文件被更改,包括 33 次插入8 次删除
  1. 25 8
      daemon/graphdriver/devmapper/deviceset.go
  2. 8 0
      pkg/devicemapper/devmapper.go

+ 25 - 8
daemon/graphdriver/devmapper/deviceset.go

@@ -2088,7 +2088,16 @@ func (devices *DeviceSet) deleteDevice(info *devInfo, syncDelete bool) error {
 	}
 	}
 
 
 	// Try to deactivate device in case it is active.
 	// Try to deactivate device in case it is active.
-	if err := devices.deactivateDevice(info); err != nil {
+	// If deferred removal is enabled and deferred deletion is disabled
+	// then make sure device is removed synchronously. There have been
+	// some cases of device being busy for short duration and we would
+	// rather busy wait for device removal to take care of these cases.
+	deferredRemove := devices.deferredRemove
+	if !devices.deferredDelete {
+		deferredRemove = false
+	}
+
+	if err := devices.deactivateDeviceMode(info, deferredRemove); err != nil {
 		logrus.Debugf("devmapper: Error deactivating device: %s", err)
 		logrus.Debugf("devmapper: Error deactivating device: %s", err)
 		return err
 		return err
 	}
 	}
@@ -2145,6 +2154,11 @@ func (devices *DeviceSet) deactivatePool() error {
 }
 }
 
 
 func (devices *DeviceSet) deactivateDevice(info *devInfo) error {
 func (devices *DeviceSet) deactivateDevice(info *devInfo) error {
+	return devices.deactivateDeviceMode(info, devices.deferredRemove)
+}
+
+func (devices *DeviceSet) deactivateDeviceMode(info *devInfo, deferredRemove bool) error {
+	var err error
 	logrus.Debugf("devmapper: deactivateDevice START(%s)", info.Hash)
 	logrus.Debugf("devmapper: deactivateDevice START(%s)", info.Hash)
 	defer logrus.Debugf("devmapper: deactivateDevice END(%s)", info.Hash)
 	defer logrus.Debugf("devmapper: deactivateDevice END(%s)", info.Hash)
 
 
@@ -2157,14 +2171,17 @@ func (devices *DeviceSet) deactivateDevice(info *devInfo) error {
 		return nil
 		return nil
 	}
 	}
 
 
-	if devices.deferredRemove {
-		if err := devicemapper.RemoveDeviceDeferred(info.Name()); err != nil {
-			return err
-		}
+	if deferredRemove {
+		err = devicemapper.RemoveDeviceDeferred(info.Name())
 	} else {
 	} else {
-		if err := devices.removeDevice(info.Name()); err != nil {
-			return err
-		}
+		err = devices.removeDevice(info.Name())
+	}
+
+	// This function's semantics is such that it does not return an
+	// error if device does not exist. So if device went away by
+	// the time we actually tried to remove it, do not return error.
+	if err != devicemapper.ErrEnxio {
+		return err
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 8 - 0
pkg/devicemapper/devmapper.go

@@ -336,10 +336,14 @@ func RemoveDevice(name string) error {
 	defer UdevWait(cookie)
 	defer UdevWait(cookie)
 
 
 	dmSawBusy = false // reset before the task is run
 	dmSawBusy = false // reset before the task is run
+	dmSawEnxio = false
 	if err = task.run(); err != nil {
 	if err = task.run(); err != nil {
 		if dmSawBusy {
 		if dmSawBusy {
 			return ErrBusy
 			return ErrBusy
 		}
 		}
+		if dmSawEnxio {
+			return ErrEnxio
+		}
 		return fmt.Errorf("devicemapper: Error running RemoveDevice %s", err)
 		return fmt.Errorf("devicemapper: Error running RemoveDevice %s", err)
 	}
 	}
 
 
@@ -380,7 +384,11 @@ func RemoveDeviceDeferred(name string) error {
 	// by udev, what UdevWait is just cleaning up the semaphore.
 	// by udev, what UdevWait is just cleaning up the semaphore.
 	defer UdevWait(cookie)
 	defer UdevWait(cookie)
 
 
+	dmSawEnxio = false
 	if err = task.run(); err != nil {
 	if err = task.run(); err != nil {
+		if dmSawEnxio {
+			return ErrEnxio
+		}
 		return fmt.Errorf("devicemapper: Error running RemoveDeviceDeferred %s", err)
 		return fmt.Errorf("devicemapper: Error running RemoveDeviceDeferred %s", err)
 	}
 	}