1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

devmapper: Add per-device lock

We currently use a global lock to protect global data (like the
Devices map) as well as device data itself and access to
(non-threadsafe) libdevmapper.

This commit also adds a per-device lock, which will allow per-device
operations to temporarily release the global lock while e.g. waiting.
The per-device lock will make sure that nothing else accesses that
device while we're operating on it.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
This commit is contained in:
Alexander Larsson 2014-03-06 18:14:56 +01:00
parent fb314c266b
commit 3e8a02a939

View file

@ -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)
}
@ -773,20 +791,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 +829,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 +878,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 +950,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 +1008,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,