mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #23497 from shishir-a412ed/dm_task_run_failed
Fixes Issue # 23418: Race condition between device deferred removal and resume device.
This commit is contained in:
commit
87e48ecd04
2 changed files with 113 additions and 56 deletions
|
@ -528,7 +528,7 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bo
|
||||||
|
|
||||||
// Make sure deferred removal on device is canceled, if one was
|
// Make sure deferred removal on device is canceled, if one was
|
||||||
// scheduled.
|
// scheduled.
|
||||||
if err := devices.cancelDeferredRemoval(info); err != nil {
|
if err := devices.cancelDeferredRemovalIfNeeded(info); err != nil {
|
||||||
return fmt.Errorf("devmapper: Device Deferred Removal Cancellation Failed: %s", err)
|
return fmt.Errorf("devmapper: Device Deferred Removal Cancellation Failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,11 +841,56 @@ func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) {
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInfo, size uint64) error {
|
func (devices *DeviceSet) takeSnapshot(hash string, baseInfo *devInfo, size uint64) error {
|
||||||
if err := devices.poolHasFreeSpace(); err != nil {
|
var (
|
||||||
|
devinfo *devicemapper.Info
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = devices.poolHasFreeSpace(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if devices.deferredRemove {
|
||||||
|
devinfo, err = devicemapper.GetInfoWithDeferred(baseInfo.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if devinfo != nil && devinfo.DeferredRemove != 0 {
|
||||||
|
err = devices.cancelDeferredRemoval(baseInfo)
|
||||||
|
if err != nil {
|
||||||
|
// If Error is ErrEnxio. Device is probably already gone. Continue.
|
||||||
|
if err != devicemapper.ErrEnxio {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defer devices.deactivateDevice(baseInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
devinfo, err = devicemapper.GetInfo(baseInfo.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doSuspend := devinfo != nil && devinfo.Exists != 0
|
||||||
|
|
||||||
|
if doSuspend {
|
||||||
|
if err = devicemapper.SuspendDevice(baseInfo.Name()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer devicemapper.ResumeDevice(baseInfo.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = devices.createRegisterSnapDevice(hash, baseInfo, size); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInfo, size uint64) error {
|
||||||
deviceID, err := devices.getNextFreeDeviceID()
|
deviceID, err := devices.getNextFreeDeviceID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -858,7 +903,7 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := devicemapper.CreateSnapDevice(devices.getPoolDevName(), deviceID, baseInfo.Name(), baseInfo.DeviceID); err != nil {
|
if err := devicemapper.CreateSnapDeviceRaw(devices.getPoolDevName(), deviceID, baseInfo.DeviceID); err != nil {
|
||||||
if devicemapper.DeviceIDExists(err) {
|
if devicemapper.DeviceIDExists(err) {
|
||||||
// Device ID already exists. This should not
|
// Device ID already exists. This should not
|
||||||
// happen. Now we have a mechanism to find
|
// happen. Now we have a mechanism to find
|
||||||
|
@ -1886,7 +1931,7 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string, storageOpt map[string
|
||||||
return fmt.Errorf("devmapper: Container size cannot be smaller than %s", units.HumanSize(float64(baseInfo.Size)))
|
return fmt.Errorf("devmapper: Container size cannot be smaller than %s", units.HumanSize(float64(baseInfo.Size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := devices.createRegisterSnapDevice(hash, baseInfo, size); err != nil {
|
if err := devices.takeSnapshot(hash, baseInfo, size); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2128,41 +2173,53 @@ func (devices *DeviceSet) removeDevice(devname string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error {
|
func (devices *DeviceSet) cancelDeferredRemovalIfNeeded(info *devInfo) error {
|
||||||
if !devices.deferredRemove {
|
if !devices.deferredRemove {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("devmapper: cancelDeferredRemoval START(%s)", info.Name())
|
logrus.Debugf("devmapper: cancelDeferredRemovalIfNeeded START(%s)", info.Name())
|
||||||
defer logrus.Debugf("devmapper: cancelDeferredRemoval END(%s)", info.Name())
|
defer logrus.Debugf("devmapper: cancelDeferredRemovalIfNeeded END(%s)", info.Name())
|
||||||
|
|
||||||
devinfo, err := devicemapper.GetInfoWithDeferred(info.Name())
|
devinfo, err := devicemapper.GetInfoWithDeferred(info.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if devinfo != nil && devinfo.DeferredRemove == 0 {
|
if devinfo != nil && devinfo.DeferredRemove == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel deferred remove
|
// Cancel deferred remove
|
||||||
for i := 0; i < 100; i++ {
|
if err := devices.cancelDeferredRemoval(info); err != nil {
|
||||||
err = devicemapper.CancelDeferredRemove(info.Name())
|
// If Error is ErrEnxio. Device is probably already gone. Continue.
|
||||||
if err == nil {
|
if err != devicemapper.ErrEnxio {
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == devicemapper.ErrEnxio {
|
|
||||||
// Device is probably already gone. Return success.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != devicemapper.ErrBusy {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// If we see EBUSY it may be a transient error,
|
func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error {
|
||||||
// sleep a bit a retry a few times.
|
logrus.Debugf("devmapper: cancelDeferredRemoval START(%s)", info.Name())
|
||||||
devices.Unlock()
|
defer logrus.Debugf("devmapper: cancelDeferredRemoval END(%s)", info.Name())
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
devices.Lock()
|
var err error
|
||||||
|
|
||||||
|
// Cancel deferred remove
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
err = devicemapper.CancelDeferredRemove(info.Name())
|
||||||
|
if err != nil {
|
||||||
|
if err == devicemapper.ErrBusy {
|
||||||
|
// If we see EBUSY it may be a transient error,
|
||||||
|
// sleep a bit a retry a few times.
|
||||||
|
devices.Unlock()
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
devices.Lock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -750,6 +750,33 @@ func activateDevice(poolName string, name string, deviceID int, size uint64, ext
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateSnapDeviceRaw creates a snapshot device. Caller needs to suspend and resume the origin device if it is active.
|
||||||
|
func CreateSnapDeviceRaw(poolName string, deviceID int, baseDeviceID int) error {
|
||||||
|
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.setSector(0); err != nil {
|
||||||
|
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.setMessage(fmt.Sprintf("create_snap %d %d", deviceID, baseDeviceID)); err != nil {
|
||||||
|
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dmSawExist = false // reset before the task is run
|
||||||
|
if err := task.run(); err != nil {
|
||||||
|
// Caller wants to know about ErrDeviceIDExists so that it can try with a different device id.
|
||||||
|
if dmSawExist {
|
||||||
|
return ErrDeviceIDExists
|
||||||
|
}
|
||||||
|
return fmt.Errorf("devicemapper: Error running deviceCreate (createSnapDevice) %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateSnapDevice creates a snapshot based on the device identified by the baseName and baseDeviceId,
|
// CreateSnapDevice creates a snapshot based on the device identified by the baseName and baseDeviceId,
|
||||||
func CreateSnapDevice(poolName string, deviceID int, baseName string, baseDeviceID int) error {
|
func CreateSnapDevice(poolName string, deviceID int, baseName string, baseDeviceID int) error {
|
||||||
devinfo, _ := GetInfo(baseName)
|
devinfo, _ := GetInfo(baseName)
|
||||||
|
@ -761,42 +788,15 @@ func CreateSnapDevice(poolName string, deviceID int, baseName string, baseDevice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
if err := CreateSnapDeviceRaw(poolName, deviceID, baseDeviceID); err != nil {
|
||||||
if task == nil {
|
|
||||||
if doSuspend {
|
if doSuspend {
|
||||||
ResumeDevice(baseName)
|
if err2 := ResumeDevice(baseName); err2 != nil {
|
||||||
|
return fmt.Errorf("CreateSnapDeviceRaw Error: (%v): ResumeDevice Error: (%v)", err, err2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := task.setSector(0); err != nil {
|
|
||||||
if doSuspend {
|
|
||||||
ResumeDevice(baseName)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := task.setMessage(fmt.Sprintf("create_snap %d %d", deviceID, baseDeviceID)); err != nil {
|
|
||||||
if doSuspend {
|
|
||||||
ResumeDevice(baseName)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dmSawExist = false // reset before the task is run
|
|
||||||
if err := task.run(); err != nil {
|
|
||||||
if doSuspend {
|
|
||||||
ResumeDevice(baseName)
|
|
||||||
}
|
|
||||||
// Caller wants to know about ErrDeviceIDExists so that it can try with a different device id.
|
|
||||||
if dmSawExist {
|
|
||||||
return ErrDeviceIDExists
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("devicemapper: Error running deviceCreate (createSnapDevice) %s", err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if doSuspend {
|
if doSuspend {
|
||||||
if err := ResumeDevice(baseName); err != nil {
|
if err := ResumeDevice(baseName); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Add table
Reference in a new issue