From e5394e35c7a8f730ac76d24dee74d769049a0428 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Apr 2014 10:21:53 +0200 Subject: [PATCH 1/7] devmapper: Pass info rather than hash to activateDeviceIfNeeded There is no need to look this up again, we have it already in all callers. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- runtime/graphdriver/devmapper/deviceset.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index 731e9dab8b..b323627ac2 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -235,12 +235,8 @@ func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*Dev return info, nil } -func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error { - utils.Debugf("activateDeviceIfNeeded(%v)", hash) - info := devices.Devices[hash] - if info == nil { - return fmt.Errorf("Unknown device %s", hash) - } +func (devices *DeviceSet) activateDeviceIfNeeded(info *DevInfo) error { + utils.Debugf("activateDeviceIfNeeded(%v)", info.Hash) if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 { return nil @@ -343,7 +339,7 @@ func (devices *DeviceSet) setupBaseImage() error { utils.Debugf("Creating filesystem on base device-manager snapshot") - if err = devices.activateDeviceIfNeeded(""); err != nil { + if err = devices.activateDeviceIfNeeded(info); err != nil { utils.Debugf("\n--->Err: %s\n", err) return err } @@ -605,7 +601,7 @@ func (devices *DeviceSet) deleteDevice(hash string) error { // This is a workaround for the kernel not discarding block so // on the thin pool when we remove a thinp device, so we do it // manually - if err := devices.activateDeviceIfNeeded(hash); err == nil { + if err := devices.activateDeviceIfNeeded(info); err == nil { if err := BlockDeviceDiscard(info.DevName()); err != nil { utils.Debugf("Error discarding block on device: %s (ignoring)\n", err) } @@ -858,7 +854,7 @@ func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) erro return nil } - if err := devices.activateDeviceIfNeeded(hash); err != nil { + if err := devices.activateDeviceIfNeeded(info); err != nil { return fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err) } @@ -1028,7 +1024,7 @@ func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) { TransactionId: info.TransactionId, } - if err := devices.activateDeviceIfNeeded(hash); err != nil { + if err := devices.activateDeviceIfNeeded(info); err != nil { return nil, fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err) } From 8e39b35c7cd02bbb644b7faf2a434de0098e6dea Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Apr 2014 10:24:26 +0200 Subject: [PATCH 2/7] devmapper: Pass info rather than hash to deleteDevice All the callers already have the info, no need for an extra lookup. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- runtime/graphdriver/devmapper/deviceset.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index b323627ac2..c74db036d2 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -313,7 +313,7 @@ func (devices *DeviceSet) setupBaseImage() error { if oldInfo != nil && !oldInfo.Initialized { utils.Debugf("Removing uninitialized base image") - if err := devices.deleteDevice(""); err != nil { + if err := devices.deleteDevice(oldInfo); err != nil { utils.Debugf("\n--->Err: %s\n", err) return err } @@ -592,12 +592,7 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error { return nil } -func (devices *DeviceSet) deleteDevice(hash string) error { - info := devices.Devices[hash] - if info == nil { - return fmt.Errorf("hash %s doesn't exists", hash) - } - +func (devices *DeviceSet) deleteDevice(info *DevInfo) error { // This is a workaround for the kernel not discarding block so // on the thin pool when we remove a thinp device, so we do it // manually @@ -652,7 +647,7 @@ func (devices *DeviceSet) DeleteDevice(hash string) error { info.lock.Lock() defer info.lock.Unlock() - return devices.deleteDevice(hash) + return devices.deleteDevice(info) } func (devices *DeviceSet) deactivatePool() error { From 5955846774c9b43291d6de0584fa8c3f62414c43 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Apr 2014 10:31:34 +0200 Subject: [PATCH 3/7] devmapper: Pass info rather than hash to deactivateDevice() We already have the info in most cases, no need to look this up multiple times. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- runtime/graphdriver/devmapper/deviceset.go | 33 +++++++++------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index c74db036d2..fa6b259b63 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -666,20 +666,16 @@ func (devices *DeviceSet) deactivatePool() error { return nil } -func (devices *DeviceSet) deactivateDevice(hash string) error { - utils.Debugf("[devmapper] deactivateDevice(%s)", hash) +func (devices *DeviceSet) deactivateDevice(info *DevInfo) error { + utils.Debugf("[devmapper] deactivateDevice(%s)", info.Hash) defer utils.Debugf("[devmapper] deactivateDevice END") // Wait for the unmount to be effective, // by watching the value of Info.OpenCount for the device - if err := devices.waitClose(hash); err != nil { - utils.Errorf("Warning: error waiting for device %s to close: %s\n", hash, err) + if err := devices.waitClose(info); err != nil { + utils.Errorf("Warning: error waiting for device %s to close: %s\n", info.Hash, err) } - info := devices.Devices[hash] - if info == nil { - return fmt.Errorf("Unknown device %s", hash) - } devinfo, err := getInfo(info.Name()) if err != nil { utils.Debugf("\n--->Err: %s\n", err) @@ -760,11 +756,7 @@ func (devices *DeviceSet) waitRemove(devname string) error { // waitClose blocks until either: // a) the device registered at - is closed, // or b) the 10 second timeout expires. -func (devices *DeviceSet) waitClose(hash string) error { - info := devices.Devices[hash] - if info == nil { - return fmt.Errorf("Unknown device %s", hash) - } +func (devices *DeviceSet) waitClose(info *DevInfo) error { i := 0 for ; i < 1000; i += 1 { devinfo, err := getInfo(info.Name()) @@ -772,7 +764,7 @@ func (devices *DeviceSet) waitClose(hash string) error { return err } if i%100 == 0 { - utils.Debugf("Waiting for unmount of %s: opencount=%d", hash, devinfo.OpenCount) + utils.Debugf("Waiting for unmount of %s: opencount=%d", info.Hash, devinfo.OpenCount) } if devinfo.OpenCount == 0 { break @@ -782,7 +774,7 @@ func (devices *DeviceSet) waitClose(hash string) error { devices.Lock() } if i == 1000 { - return fmt.Errorf("Timeout while waiting for device %s to close", hash) + return fmt.Errorf("Timeout while waiting for device %s to close", info.Hash) } return nil } @@ -805,15 +797,18 @@ func (devices *DeviceSet) Shutdown() error { utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err) } - if err := devices.deactivateDevice(info.Hash); err != nil { + if err := devices.deactivateDevice(info); err != nil { utils.Debugf("Shutdown deactivate %s , error: %s\n", info.Hash, err) } } info.lock.Unlock() } - if err := devices.deactivateDevice(""); err != nil { - utils.Debugf("Shutdown deactivate base , error: %s\n", err) + info := devices.Devices[""] + if info != nil { + if err := devices.deactivateDevice(info); err != nil { + utils.Debugf("Shutdown deactivate base , error: %s\n", err) + } } if err := devices.deactivatePool(); err != nil { @@ -920,7 +915,7 @@ func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error { } utils.Debugf("[devmapper] Unmount done") - if err := devices.deactivateDevice(hash); err != nil { + if err := devices.deactivateDevice(info); err != nil { return err } From 74edcaf1e84aa8bf35e496b2bead833172a79fca Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Apr 2014 10:34:44 +0200 Subject: [PATCH 4/7] devmapper: Pass info rather than hash to setInitialized We already have this at the caller, no need to look up again. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- runtime/graphdriver/devmapper/deviceset.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index fa6b259b63..12407af1b2 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -864,7 +864,7 @@ func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) erro info.mountPath = path info.floating = false - return devices.setInitialized(hash) + return devices.setInitialized(info) } func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error { @@ -955,12 +955,7 @@ func (devices *DeviceSet) HasActivatedDevice(hash string) bool { return devinfo != nil && devinfo.Exists != 0 } -func (devices *DeviceSet) setInitialized(hash string) error { - info := devices.Devices[hash] - if info == nil { - return fmt.Errorf("Unknown device %s", hash) - } - +func (devices *DeviceSet) setInitialized(info *DevInfo) error { info.Initialized = true if err := devices.saveMetadata(); err != nil { info.Initialized = false From e01b71cebeb96755641a18762dea5b843f107bee Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Apr 2014 10:45:40 +0200 Subject: [PATCH 5/7] devmapper: Add lookupDevice() helper This centralizes the lookup of devices so it is only done in one place. This will be needed later when we change the locking for it. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- runtime/graphdriver/devmapper/deviceset.go | 55 +++++++++++++--------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index 12407af1b2..4faf997002 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -214,6 +214,14 @@ func (devices *DeviceSet) saveMetadata() error { return nil } +func (devices *DeviceSet) lookupDevice(hash string) (*DevInfo, error) { + info := devices.Devices[hash] + if info == nil { + return nil, fmt.Errorf("Unknown device %s", hash) + } + return info, nil +} + func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*DevInfo, error) { utils.Debugf("registerDevice(%v, %v)", id, hash) info := &DevInfo{ @@ -306,7 +314,7 @@ func (devices *DeviceSet) loadMetaData() error { } func (devices *DeviceSet) setupBaseImage() error { - oldInfo := devices.Devices[""] + oldInfo, _ := devices.lookupDevice("") if oldInfo != nil && oldInfo.Initialized { return nil } @@ -565,13 +573,13 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error { devices.Lock() defer devices.Unlock() - if devices.Devices[hash] != nil { - return fmt.Errorf("hash %s already exists", hash) + if info, _ := devices.lookupDevice(hash); info != nil { + return fmt.Errorf("device %s already exists", hash) } - baseInfo := devices.Devices[baseHash] - if baseInfo == nil { - return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash) + baseInfo, err := devices.lookupDevice(baseHash) + if err != nil { + return err } baseInfo.lock.Lock() @@ -639,9 +647,9 @@ 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, err := devices.lookupDevice(hash) + if err != nil { + return err } info.lock.Lock() @@ -804,7 +812,7 @@ func (devices *DeviceSet) Shutdown() error { info.lock.Unlock() } - info := devices.Devices[""] + info, _ := devices.lookupDevice("") if info != nil { if err := devices.deactivateDevice(info); err != nil { utils.Debugf("Shutdown deactivate base , error: %s\n", err) @@ -822,9 +830,9 @@ func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) erro devices.Lock() defer devices.Unlock() - info := devices.Devices[hash] - if info == nil { - return fmt.Errorf("Unknown device %s", hash) + info, err := devices.lookupDevice(hash) + if err != nil { + return err } info.lock.Lock() @@ -851,7 +859,7 @@ func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) erro var flags uintptr = sysMsMgcVal mountOptions := label.FormatMountLabel("discard", mountLabel) - err := sysMount(info.DevName(), path, "ext4", flags, mountOptions) + err = sysMount(info.DevName(), path, "ext4", flags, mountOptions) if err != nil && err == sysEInval { mountOptions = label.FormatMountLabel(mountLabel, "") err = sysMount(info.DevName(), path, "ext4", flags, mountOptions) @@ -873,9 +881,9 @@ func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error { devices.Lock() defer devices.Unlock() - info := devices.Devices[hash] - if info == nil { - return fmt.Errorf("UnmountDevice: no such device %s\n", hash) + info, err := devices.lookupDevice(hash) + if err != nil { + return err } info.lock.Lock() @@ -928,14 +936,15 @@ func (devices *DeviceSet) HasDevice(hash string) bool { devices.Lock() defer devices.Unlock() - return devices.Devices[hash] != nil + info, _ := devices.lookupDevice(hash) + return info != nil } func (devices *DeviceSet) HasInitializedDevice(hash string) bool { devices.Lock() defer devices.Unlock() - info := devices.Devices[hash] + info, _ := devices.lookupDevice(hash) return info != nil && info.Initialized } @@ -943,7 +952,7 @@ func (devices *DeviceSet) HasActivatedDevice(hash string) bool { devices.Lock() defer devices.Unlock() - info := devices.Devices[hash] + info, _ := devices.lookupDevice(hash) if info == nil { return false } @@ -995,9 +1004,9 @@ func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) { devices.Lock() defer devices.Unlock() - info := devices.Devices[hash] - if info == nil { - return nil, fmt.Errorf("No device %s", hash) + info, err := devices.lookupDevice(hash) + if err != nil { + return nil, err } info.lock.Lock() From 70826e8b3fee27b971852aad89053507c6866d3e Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Apr 2014 11:05:30 +0200 Subject: [PATCH 6/7] devmapper: Add lock to protext Devices map Currently access to the Devices map is serialized by the main DeviceSet lock, but we need to access it outside that lock, so we add a separate lock for this and grab that everywhere we modify or read the map. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- runtime/graphdriver/devmapper/deviceset.go | 27 +++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index 4faf997002..f972c34bb1 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -51,7 +51,8 @@ type DevInfo struct { } type MetaData struct { - Devices map[string]*DevInfo `json:devices` + Devices map[string]*DevInfo `json:devices` + devicesLock sync.Mutex `json:"-"` // Protects all read/writes to Devices map } type DeviceSet struct { @@ -179,7 +180,9 @@ func (devices *DeviceSet) allocateTransactionId() uint64 { } func (devices *DeviceSet) saveMetadata() error { + devices.devicesLock.Lock() jsonData, err := json.Marshal(devices.MetaData) + devices.devicesLock.Unlock() if err != nil { return fmt.Errorf("Error encoding metadata to json: %s", err) } @@ -215,6 +218,8 @@ func (devices *DeviceSet) saveMetadata() error { } func (devices *DeviceSet) lookupDevice(hash string) (*DevInfo, error) { + devices.devicesLock.Lock() + defer devices.devicesLock.Unlock() info := devices.Devices[hash] if info == nil { return nil, fmt.Errorf("Unknown device %s", hash) @@ -233,10 +238,15 @@ func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*Dev devices: devices, } + devices.devicesLock.Lock() devices.Devices[hash] = info + devices.devicesLock.Unlock() + if err := devices.saveMetadata(); err != nil { // Try to remove unused device + devices.devicesLock.Lock() delete(devices.Devices, hash) + devices.devicesLock.Unlock() return nil, err } @@ -632,10 +642,14 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error { } devices.allocateTransactionId() + devices.devicesLock.Lock() delete(devices.Devices, info.Hash) + devices.devicesLock.Unlock() if err := devices.saveMetadata(); err != nil { + devices.devicesLock.Lock() devices.Devices[info.Hash] = info + devices.devicesLock.Unlock() utils.Debugf("Error saving meta data: %s\n", err) return err } @@ -795,7 +809,15 @@ func (devices *DeviceSet) Shutdown() error { utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root) defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix) + var devs []*DevInfo + + devices.devicesLock.Lock() for _, info := range devices.Devices { + devs = append(devs, info) + } + devices.devicesLock.Unlock() + + for _, info := range devs { info.lock.Lock() if info.mountCount > 0 { // We use MNT_DETACH here in case it is still busy in some running @@ -979,12 +1001,15 @@ func (devices *DeviceSet) List() []string { devices.Lock() defer devices.Unlock() + devices.devicesLock.Lock() ids := make([]string, len(devices.Devices)) i := 0 for k := range devices.Devices { ids[i] = k i++ } + devices.devicesLock.Unlock() + return ids } From 2ffef1b7eb618162673c6ffabccb9ca57c7dfce3 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Apr 2014 11:28:21 +0200 Subject: [PATCH 7/7] devmapper: Avoid AB-BA deadlock We currently drop the global lock while holding a per-device lock when waiting for device removal, and then we re-aquire it when the sleep is done. This is causing a AB-BA deadlock if anyone at the same time tries to do any operation on that device like this: thread A: thread B grabs global lock grabs device lock releases global lock sleeps grabs global lock blocks on device lock wakes up blocks on global lock To trigger this you can for instance do: ID=`docker run -d fedora sleep 5` cd /var/lib/docker/devicemapper/mnt/$ID docker wait $ID docker rm $ID & docker rm $ID The unmount will fail due to the mount being busy thus causing the timeout and the second rm will then trigger the deadlock. We fix this by adding a lock ordering such that the device locks are always grabbed before the global lock. This is safe since the device lookups now have a separate lock. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- runtime/graphdriver/devmapper/deviceset.go | 58 +++++++++++++--------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index f972c34bb1..97d670a3d9 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -47,6 +47,11 @@ type DevInfo struct { // 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. + // + // WARNING: In order to avoid AB-BA deadlocks when releasing + // the global lock while holding the per-device locks all + // device locks must be aquired *before* the device lock, and + // multiple device locks should be aquired parent before child. lock sync.Mutex `json:"-"` } @@ -580,13 +585,6 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { } func (devices *DeviceSet) AddDevice(hash, baseHash string) error { - devices.Lock() - defer devices.Unlock() - - if info, _ := devices.lookupDevice(hash); info != nil { - return fmt.Errorf("device %s already exists", hash) - } - baseInfo, err := devices.lookupDevice(baseHash) if err != nil { return err @@ -595,6 +593,13 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error { baseInfo.lock.Lock() defer baseInfo.lock.Unlock() + devices.Lock() + defer devices.Unlock() + + if info, _ := devices.lookupDevice(hash); info != nil { + return fmt.Errorf("device %s already exists", hash) + } + deviceId := devices.allocateDeviceId() if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil { @@ -658,9 +663,6 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error { } func (devices *DeviceSet) DeleteDevice(hash string) error { - devices.Lock() - defer devices.Unlock() - info, err := devices.lookupDevice(hash) if err != nil { return err @@ -669,6 +671,9 @@ func (devices *DeviceSet) DeleteDevice(hash string) error { info.lock.Lock() defer info.lock.Unlock() + devices.Lock() + defer devices.Unlock() + return devices.deleteDevice(info) } @@ -802,8 +807,6 @@ func (devices *DeviceSet) waitClose(info *DevInfo) error { } func (devices *DeviceSet) Shutdown() error { - devices.Lock() - defer devices.Unlock() utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix) utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root) @@ -827,31 +830,36 @@ func (devices *DeviceSet) Shutdown() error { utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err) } + devices.Lock() if err := devices.deactivateDevice(info); err != nil { utils.Debugf("Shutdown deactivate %s , error: %s\n", info.Hash, err) } + devices.Unlock() } info.lock.Unlock() } info, _ := devices.lookupDevice("") if info != nil { + info.lock.Lock() + devices.Lock() if err := devices.deactivateDevice(info); err != nil { utils.Debugf("Shutdown deactivate base , error: %s\n", err) } + devices.Unlock() + info.lock.Unlock() } + devices.Lock() if err := devices.deactivatePool(); err != nil { utils.Debugf("Shutdown deactivate pool , error: %s\n", err) } + devices.Unlock() return nil } func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) error { - devices.Lock() - defer devices.Unlock() - info, err := devices.lookupDevice(hash) if err != nil { return err @@ -860,6 +868,9 @@ func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) erro info.lock.Lock() defer info.lock.Unlock() + devices.Lock() + defer devices.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) @@ -900,8 +911,6 @@ func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) erro func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error { utils.Debugf("[devmapper] UnmountDevice(hash=%s, mode=%d)", hash, mode) defer utils.Debugf("[devmapper] UnmountDevice END") - devices.Lock() - defer devices.Unlock() info, err := devices.lookupDevice(hash) if err != nil { @@ -911,6 +920,9 @@ func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error { info.lock.Lock() defer info.lock.Unlock() + devices.Lock() + defer devices.Unlock() + if mode == UnmountFloat { if info.floating { return fmt.Errorf("UnmountDevice: can't float floating reference %s\n", hash) @@ -971,9 +983,6 @@ func (devices *DeviceSet) HasInitializedDevice(hash string) bool { } func (devices *DeviceSet) HasActivatedDevice(hash string) bool { - devices.Lock() - defer devices.Unlock() - info, _ := devices.lookupDevice(hash) if info == nil { return false @@ -982,6 +991,9 @@ func (devices *DeviceSet) HasActivatedDevice(hash string) bool { info.lock.Lock() defer info.lock.Unlock() + devices.Lock() + defer devices.Unlock() + devinfo, _ := getInfo(info.Name()) return devinfo != nil && devinfo.Exists != 0 } @@ -1026,9 +1038,6 @@ func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSec } func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) { - devices.Lock() - defer devices.Unlock() - info, err := devices.lookupDevice(hash) if err != nil { return nil, err @@ -1037,6 +1046,9 @@ func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) { info.lock.Lock() defer info.lock.Unlock() + devices.Lock() + defer devices.Unlock() + status := &DevStatus{ DeviceId: info.DeviceId, Size: info.Size,