mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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>
This commit is contained in:
parent
fcaa79b842
commit
36cb6efebc
2 changed files with 33 additions and 8 deletions
|
@ -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 deferredRemove {
|
||||||
if err := devicemapper.RemoveDeviceDeferred(info.Name()); err != nil {
|
err = devicemapper.RemoveDeviceDeferred(info.Name())
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if err := devices.removeDevice(info.Name()); err != nil {
|
err = devices.removeDevice(info.Name())
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue