summaryrefslogtreecommitdiff
path: root/graphdriver/devmapper
diff options
context:
space:
mode:
Diffstat (limited to 'graphdriver/devmapper')
-rw-r--r--graphdriver/devmapper/deviceset.go57
-rw-r--r--graphdriver/devmapper/driver.go7
2 files changed, 57 insertions, 7 deletions
diff --git a/graphdriver/devmapper/deviceset.go b/graphdriver/devmapper/deviceset.go
index 303e363e92..4d33e243e0 100644
--- a/graphdriver/devmapper/deviceset.go
+++ b/graphdriver/devmapper/deviceset.go
@@ -39,6 +39,13 @@ type DevInfo struct {
// first get (since we need to mount to set up the device
// a bit first).
floating bool `json:"-"`
+
+ // The global DeviceSet lock guarantees that we serialize all
+ // the calls to libdevmapper (which is not threadsafe), but we
+ // sometimes release that lock while sleeping. In that case
+ // this per-device lock is still held, protecting against
+ // other accesses to the device that we're doing the wait on.
+ lock sync.Mutex `json:"-"`
}
type MetaData struct {
@@ -47,7 +54,7 @@ type MetaData struct {
type DeviceSet struct {
MetaData
- sync.Mutex
+ sync.Mutex // Protects Devices map and serializes calls into libdevmapper
root string
devicePrefix string
TransactionId uint64
@@ -569,6 +576,9 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash)
}
+ baseInfo.lock.Lock()
+ defer baseInfo.lock.Unlock()
+
deviceId := devices.allocateDeviceId()
if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
@@ -636,6 +646,14 @@ func (devices *DeviceSet) DeleteDevice(hash string) error {
devices.Lock()
defer devices.Unlock()
+ info := devices.Devices[hash]
+ if info == nil {
+ return fmt.Errorf("Unknown device %s", hash)
+ }
+
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
return devices.deleteDevice(hash)
}
@@ -683,7 +701,7 @@ func (devices *DeviceSet) deactivateDevice(hash string) error {
func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
var err error
- for i := 0; i < 10; i++ {
+ for i := 0; i < 1000; i++ {
devices.sawBusy = false
err = removeDevice(devname)
if err == nil {
@@ -695,7 +713,9 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
// If we see EBUSY it may be a transient error,
// sleep a bit a retry a few times.
- time.Sleep(5 * time.Millisecond)
+ devices.Unlock()
+ time.Sleep(10 * time.Millisecond)
+ devices.Lock()
}
if err != nil {
return err
@@ -709,7 +729,7 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
// waitRemove blocks until either:
// a) the device registered at <device_set_prefix>-<hash> is removed,
-// or b) the 1 second timeout expires.
+// or b) the 10 second timeout expires.
func (devices *DeviceSet) waitRemove(devname string) error {
utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname)
defer utils.Debugf("[deviceset %s] waitRemove(%s) END", devices.devicePrefix, devname)
@@ -728,7 +748,9 @@ func (devices *DeviceSet) waitRemove(devname string) error {
break
}
- time.Sleep(1 * time.Millisecond)
+ devices.Unlock()
+ time.Sleep(10 * time.Millisecond)
+ devices.Lock()
}
if i == 1000 {
return fmt.Errorf("Timeout while waiting for device %s to be removed", devname)
@@ -738,7 +760,7 @@ func (devices *DeviceSet) waitRemove(devname string) error {
// waitClose blocks until either:
// a) the device registered at <device_set_prefix>-<hash> is closed,
-// or b) the 1 second timeout expires.
+// or b) the 10 second timeout expires.
func (devices *DeviceSet) waitClose(hash string) error {
info := devices.Devices[hash]
if info == nil {
@@ -756,7 +778,9 @@ func (devices *DeviceSet) waitClose(hash string) error {
if devinfo.OpenCount == 0 {
break
}
- time.Sleep(1 * time.Millisecond)
+ devices.Unlock()
+ time.Sleep(10 * time.Millisecond)
+ devices.Lock()
}
if i == 1000 {
return fmt.Errorf("Timeout while waiting for device %s to close", hash)
@@ -773,20 +797,26 @@ func (devices *DeviceSet) Shutdown() error {
defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
for _, info := range devices.Devices {
+ info.lock.Lock()
if info.mountCount > 0 {
if err := sysUnmount(info.mountPath, 0); err != nil {
utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err)
}
}
+ info.lock.Unlock()
}
for _, d := range devices.Devices {
+ d.lock.Lock()
+
if err := devices.waitClose(d.Hash); err != nil {
utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
}
if err := devices.deactivateDevice(d.Hash); err != nil {
utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
}
+
+ d.lock.Unlock()
}
if err := devices.deactivatePool(); err != nil {
@@ -805,6 +835,9 @@ func (devices *DeviceSet) MountDevice(hash, path string) error {
return fmt.Errorf("Unknown device %s", hash)
}
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
if info.mountCount > 0 {
if path != info.mountPath {
return fmt.Errorf("Trying to mount devmapper device in multple places (%s, %s)", info.mountPath, path)
@@ -851,6 +884,9 @@ func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error {
return fmt.Errorf("UnmountDevice: no such device %s\n", hash)
}
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
if mode == UnmountFloat {
if info.floating {
return fmt.Errorf("UnmountDevice: can't float floating reference %s\n", hash)
@@ -920,6 +956,10 @@ func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
if info == nil {
return false
}
+
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
devinfo, _ := getInfo(info.Name())
return devinfo != nil && devinfo.Exists != 0
}
@@ -974,6 +1014,9 @@ func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) {
return nil, fmt.Errorf("No device %s", hash)
}
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
status := &DevStatus{
DeviceId: info.DeviceId,
Size: info.Size,
diff --git a/graphdriver/devmapper/driver.go b/graphdriver/devmapper/driver.go
index 4d414f9a75..8c5a19eea0 100644
--- a/graphdriver/devmapper/driver.go
+++ b/graphdriver/devmapper/driver.go
@@ -90,6 +90,13 @@ func (d *Driver) Create(id, parent string) error {
}
func (d *Driver) Remove(id string) error {
+ if !d.DeviceSet.HasDevice(id) {
+ // Consider removing a non-existing device a no-op
+ // This is useful to be able to progress on container removal
+ // if the underlying device has gone away due to earlier errors
+ return nil
+ }
+
// Sink the float from create in case no Get() call was made
if err := d.DeviceSet.UnmountDevice(id, UnmountSink); err != nil {
return err