Browse code

Revert "Move layer mount refcounts to mountedLayer"

This reverts commit 563d0711f83952e561a0d7d5c48fef9810b4f010.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2016/03/23 16:33:02
Showing 14 changed files
... ...
@@ -222,7 +222,6 @@ func (daemon *Daemon) exportContainerRw(container *container.Container) (archive
222 222
 
223 223
 	archive, err := container.RWLayer.TarStream()
224 224
 	if err != nil {
225
-		daemon.Unmount(container) // logging is already handled in the `Unmount` function
226 225
 		return nil, err
227 226
 	}
228 227
 	return ioutils.NewReadCloserWrapper(archive, func() error {
... ...
@@ -29,7 +29,6 @@ import (
29 29
 	"os"
30 30
 	"os/exec"
31 31
 	"path"
32
-	"path/filepath"
33 32
 	"strings"
34 33
 	"sync"
35 34
 	"syscall"
... ...
@@ -65,13 +64,21 @@ func init() {
65 65
 	graphdriver.Register("aufs", Init)
66 66
 }
67 67
 
68
+type data struct {
69
+	referenceCount int
70
+	path           string
71
+}
72
+
68 73
 // Driver contains information about the filesystem mounted.
74
+// root of the filesystem
75
+// sync.Mutex to protect against concurrent modifications
76
+// active maps mount id to the count
69 77
 type Driver struct {
70
-	root          string
71
-	uidMaps       []idtools.IDMap
72
-	gidMaps       []idtools.IDMap
73
-	pathCacheLock sync.Mutex
74
-	pathCache     map[string]string
78
+	root       string
79
+	uidMaps    []idtools.IDMap
80
+	gidMaps    []idtools.IDMap
81
+	sync.Mutex // Protects concurrent modification to active
82
+	active     map[string]*data
75 83
 }
76 84
 
77 85
 // Init returns a new AUFS driver.
... ...
@@ -104,10 +111,10 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
104 104
 	}
105 105
 
106 106
 	a := &Driver{
107
-		root:      root,
108
-		uidMaps:   uidMaps,
109
-		gidMaps:   gidMaps,
110
-		pathCache: make(map[string]string),
107
+		root:    root,
108
+		active:  make(map[string]*data),
109
+		uidMaps: uidMaps,
110
+		gidMaps: gidMaps,
111 111
 	}
112 112
 
113 113
 	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
... ...
@@ -221,7 +228,9 @@ func (a *Driver) Create(id, parent, mountLabel string) error {
221 221
 			}
222 222
 		}
223 223
 	}
224
-
224
+	a.Lock()
225
+	a.active[id] = &data{}
226
+	a.Unlock()
225 227
 	return nil
226 228
 }
227 229
 
... ...
@@ -250,91 +259,108 @@ func (a *Driver) createDirsFor(id string) error {
250 250
 
251 251
 // Remove will unmount and remove the given id.
252 252
 func (a *Driver) Remove(id string) error {
253
-	a.pathCacheLock.Lock()
254
-	mountpoint, exists := a.pathCache[id]
255
-	a.pathCacheLock.Unlock()
256
-	if !exists {
257
-		mountpoint = a.getMountpoint(id)
253
+	// Protect the a.active from concurrent access
254
+	a.Lock()
255
+	defer a.Unlock()
256
+
257
+	m := a.active[id]
258
+	if m != nil {
259
+		if m.referenceCount > 0 {
260
+			return nil
261
+		}
262
+		// Make sure the dir is umounted first
263
+		if err := a.unmount(m); err != nil {
264
+			return err
265
+		}
258 266
 	}
259
-	if err := a.unmount(mountpoint); err != nil {
260
-		// no need to return here, we can still try to remove since the `Rename` will fail below if still mounted
261
-		logrus.Debugf("aufs: error while unmounting %s: %v", mountpoint, err)
267
+	tmpDirs := []string{
268
+		"mnt",
269
+		"diff",
262 270
 	}
263 271
 
264 272
 	// Atomically remove each directory in turn by first moving it out of the
265 273
 	// way (so that docker doesn't find it anymore) before doing removal of
266 274
 	// the whole tree.
267
-	tmpMntPath := path.Join(a.mntPath(), fmt.Sprintf("%s-removing", id))
268
-	if err := os.Rename(mountpoint, tmpMntPath); err != nil && !os.IsNotExist(err) {
269
-		return err
270
-	}
271
-	defer os.RemoveAll(tmpMntPath)
272
-
273
-	tmpDiffpath := path.Join(a.diffPath(), fmt.Sprintf("%s-removing", id))
274
-	if err := os.Rename(a.getDiffPath(id), tmpDiffpath); err != nil && !os.IsNotExist(err) {
275
-		return err
275
+	for _, p := range tmpDirs {
276
+		realPath := path.Join(a.rootPath(), p, id)
277
+		tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id))
278
+		if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) {
279
+			return err
280
+		}
281
+		defer os.RemoveAll(tmpPath)
276 282
 	}
277
-	defer os.RemoveAll(tmpDiffpath)
278
-
279 283
 	// Remove the layers file for the id
280 284
 	if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
281 285
 		return err
282 286
 	}
283
-
284
-	a.pathCacheLock.Lock()
285
-	delete(a.pathCache, id)
286
-	a.pathCacheLock.Unlock()
287
+	if m != nil {
288
+		delete(a.active, id)
289
+	}
287 290
 	return nil
288 291
 }
289 292
 
290 293
 // Get returns the rootfs path for the id.
291 294
 // This will mount the dir at it's given path
292 295
 func (a *Driver) Get(id, mountLabel string) (string, error) {
296
+	// Protect the a.active from concurrent access
297
+	a.Lock()
298
+	defer a.Unlock()
299
+
300
+	m := a.active[id]
301
+	if m == nil {
302
+		m = &data{}
303
+		a.active[id] = m
304
+	}
305
+
293 306
 	parents, err := a.getParentLayerPaths(id)
294 307
 	if err != nil && !os.IsNotExist(err) {
295 308
 		return "", err
296 309
 	}
297 310
 
298
-	a.pathCacheLock.Lock()
299
-	m, exists := a.pathCache[id]
300
-	a.pathCacheLock.Unlock()
301
-
302
-	if !exists {
303
-		m = a.getDiffPath(id)
304
-		if len(parents) > 0 {
305
-			m = a.getMountpoint(id)
306
-		}
307
-	}
308
-
309 311
 	// If a dir does not have a parent ( no layers )do not try to mount
310 312
 	// just return the diff path to the data
313
+	m.path = path.Join(a.rootPath(), "diff", id)
311 314
 	if len(parents) > 0 {
312
-		if err := a.mount(id, m, mountLabel, parents); err != nil {
313
-			return "", err
315
+		m.path = path.Join(a.rootPath(), "mnt", id)
316
+		if m.referenceCount == 0 {
317
+			if err := a.mount(id, m, mountLabel, parents); err != nil {
318
+				return "", err
319
+			}
314 320
 		}
315 321
 	}
316
-
317
-	a.pathCacheLock.Lock()
318
-	a.pathCache[id] = m
319
-	a.pathCacheLock.Unlock()
320
-	return m, nil
322
+	m.referenceCount++
323
+	return m.path, nil
321 324
 }
322 325
 
323 326
 // Put unmounts and updates list of active mounts.
324 327
 func (a *Driver) Put(id string) error {
325
-	a.pathCacheLock.Lock()
326
-	m, exists := a.pathCache[id]
327
-	if !exists {
328
-		m = a.getMountpoint(id)
329
-		a.pathCache[id] = m
328
+	// Protect the a.active from concurrent access
329
+	a.Lock()
330
+	defer a.Unlock()
331
+
332
+	m := a.active[id]
333
+	if m == nil {
334
+		// but it might be still here
335
+		if a.Exists(id) {
336
+			path := path.Join(a.rootPath(), "mnt", id)
337
+			err := Unmount(path)
338
+			if err != nil {
339
+				logrus.Debugf("Failed to unmount %s aufs: %v", id, err)
340
+			}
341
+		}
342
+		return nil
330 343
 	}
331
-	a.pathCacheLock.Unlock()
332
-
333
-	err := a.unmount(m)
334
-	if err != nil {
335
-		logrus.Debugf("Failed to unmount %s aufs: %v", id, err)
344
+	if count := m.referenceCount; count > 1 {
345
+		m.referenceCount = count - 1
346
+	} else {
347
+		ids, _ := getParentIds(a.rootPath(), id)
348
+		// We only mounted if there are any parents
349
+		if ids != nil && len(ids) > 0 {
350
+			a.unmount(m)
351
+		}
352
+		delete(a.active, id)
336 353
 	}
337
-	return err
354
+	return nil
338 355
 }
339 356
 
340 357
 // Diff produces an archive of the changes between the specified
... ...
@@ -417,13 +443,16 @@ func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
417 417
 	return layers, nil
418 418
 }
419 419
 
420
-func (a *Driver) mount(id string, target string, mountLabel string, layers []string) error {
420
+func (a *Driver) mount(id string, m *data, mountLabel string, layers []string) error {
421 421
 	// If the id is mounted or we get an error return
422
-	if mounted, err := a.mounted(target); err != nil || mounted {
422
+	if mounted, err := a.mounted(m); err != nil || mounted {
423 423
 		return err
424 424
 	}
425 425
 
426
-	rw := a.getDiffPath(id)
426
+	var (
427
+		target = m.path
428
+		rw     = path.Join(a.rootPath(), "diff", id)
429
+	)
427 430
 
428 431
 	if err := a.aufsMount(layers, rw, target, mountLabel); err != nil {
429 432
 		return fmt.Errorf("error creating aufs mount to %s: %v", target, err)
... ...
@@ -431,39 +460,26 @@ func (a *Driver) mount(id string, target string, mountLabel string, layers []str
431 431
 	return nil
432 432
 }
433 433
 
434
-func (a *Driver) unmount(mountPath string) error {
435
-	if mounted, err := a.mounted(mountPath); err != nil || !mounted {
436
-		return err
437
-	}
438
-	if err := Unmount(mountPath); err != nil {
434
+func (a *Driver) unmount(m *data) error {
435
+	if mounted, err := a.mounted(m); err != nil || !mounted {
439 436
 		return err
440 437
 	}
441
-	return nil
438
+	return Unmount(m.path)
442 439
 }
443 440
 
444
-func (a *Driver) mounted(mountpoint string) (bool, error) {
445
-	return graphdriver.Mounted(graphdriver.FsMagicAufs, mountpoint)
441
+func (a *Driver) mounted(m *data) (bool, error) {
442
+	var buf syscall.Statfs_t
443
+	if err := syscall.Statfs(m.path, &buf); err != nil {
444
+		return false, nil
445
+	}
446
+	return graphdriver.FsMagic(buf.Type) == graphdriver.FsMagicAufs, nil
446 447
 }
447 448
 
448 449
 // Cleanup aufs and unmount all mountpoints
449 450
 func (a *Driver) Cleanup() error {
450
-	var dirs []string
451
-	if err := filepath.Walk(a.mntPath(), func(path string, info os.FileInfo, err error) error {
452
-		if err != nil {
453
-			return err
454
-		}
455
-		if !info.IsDir() {
456
-			return nil
457
-		}
458
-		dirs = append(dirs, path)
459
-		return nil
460
-	}); err != nil {
461
-		return err
462
-	}
463
-
464
-	for _, m := range dirs {
451
+	for id, m := range a.active {
465 452
 		if err := a.unmount(m); err != nil {
466
-			logrus.Debugf("aufs error unmounting %s: %s", stringid.TruncateID(m), err)
453
+			logrus.Errorf("Unmounting %s: %s", stringid.TruncateID(id), err)
467 454
 		}
468 455
 	}
469 456
 	return mountpk.Unmount(a.root)
... ...
@@ -200,7 +200,7 @@ func TestMountedFalseResponse(t *testing.T) {
200 200
 		t.Fatal(err)
201 201
 	}
202 202
 
203
-	response, err := d.mounted(d.getDiffPath("1"))
203
+	response, err := d.mounted(d.active["1"])
204 204
 	if err != nil {
205 205
 		t.Fatal(err)
206 206
 	}
... ...
@@ -227,7 +227,7 @@ func TestMountedTrueReponse(t *testing.T) {
227 227
 		t.Fatal(err)
228 228
 	}
229 229
 
230
-	response, err := d.mounted(d.pathCache["2"])
230
+	response, err := d.mounted(d.active["2"])
231 231
 	if err != nil {
232 232
 		t.Fatal(err)
233 233
 	}
... ...
@@ -293,7 +293,7 @@ func TestRemoveMountedDir(t *testing.T) {
293 293
 		t.Fatal("mntPath should not be empty string")
294 294
 	}
295 295
 
296
-	mounted, err := d.mounted(d.pathCache["2"])
296
+	mounted, err := d.mounted(d.active["2"])
297 297
 	if err != nil {
298 298
 		t.Fatal(err)
299 299
 	}
... ...
@@ -46,19 +46,3 @@ func getParentIds(root, id string) ([]string, error) {
46 46
 	}
47 47
 	return out, s.Err()
48 48
 }
49
-
50
-func (a *Driver) getMountpoint(id string) string {
51
-	return path.Join(a.mntPath(), id)
52
-}
53
-
54
-func (a *Driver) mntPath() string {
55
-	return path.Join(a.rootPath(), "mnt")
56
-}
57
-
58
-func (a *Driver) getDiffPath(id string) string {
59
-	return path.Join(a.diffPath(), id)
60
-}
61
-
62
-func (a *Driver) diffPath() string {
63
-	return path.Join(a.rootPath(), "diff")
64
-}
... ...
@@ -69,6 +69,9 @@ type devInfo struct {
69 69
 	Deleted       bool   `json:"deleted"`
70 70
 	devices       *DeviceSet
71 71
 
72
+	mountCount int
73
+	mountPath  string
74
+
72 75
 	// The global DeviceSet lock guarantees that we serialize all
73 76
 	// the calls to libdevmapper (which is not threadsafe), but we
74 77
 	// sometimes release that lock while sleeping. In that case
... ...
@@ -1988,6 +1991,13 @@ func (devices *DeviceSet) DeleteDevice(hash string, syncDelete bool) error {
1988 1988
 	devices.Lock()
1989 1989
 	defer devices.Unlock()
1990 1990
 
1991
+	// If mountcount is not zero, that means devices is still in use
1992
+	// or has not been Put() properly. Fail device deletion.
1993
+
1994
+	if info.mountCount != 0 {
1995
+		return fmt.Errorf("devmapper: Can't delete device %v as it is still mounted. mntCount=%v", info.Hash, info.mountCount)
1996
+	}
1997
+
1991 1998
 	return devices.deleteDevice(info, syncDelete)
1992 1999
 }
1993 2000
 
... ...
@@ -2106,11 +2116,13 @@ func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error {
2106 2106
 }
2107 2107
 
2108 2108
 // Shutdown shuts down the device by unmounting the root.
2109
-func (devices *DeviceSet) Shutdown(home string) error {
2109
+func (devices *DeviceSet) Shutdown() error {
2110 2110
 	logrus.Debugf("devmapper: [deviceset %s] Shutdown()", devices.devicePrefix)
2111 2111
 	logrus.Debugf("devmapper: Shutting down DeviceSet: %s", devices.root)
2112 2112
 	defer logrus.Debugf("devmapper: [deviceset %s] Shutdown() END", devices.devicePrefix)
2113 2113
 
2114
+	var devs []*devInfo
2115
+
2114 2116
 	// Stop deletion worker. This should start delivering new events to
2115 2117
 	// ticker channel. That means no new instance of cleanupDeletedDevice()
2116 2118
 	// will run after this call. If one instance is already running at
... ...
@@ -2127,46 +2139,30 @@ func (devices *DeviceSet) Shutdown(home string) error {
2127 2127
 	// metadata. Hence save this early before trying to deactivate devices.
2128 2128
 	devices.saveDeviceSetMetaData()
2129 2129
 
2130
-	// ignore the error since it's just a best effort to not try to unmount something that's mounted
2131
-	mounts, _ := mount.GetMounts()
2132
-	mounted := make(map[string]bool, len(mounts))
2133
-	for _, mnt := range mounts {
2134
-		mounted[mnt.Mountpoint] = true
2130
+	for _, info := range devices.Devices {
2131
+		devs = append(devs, info)
2135 2132
 	}
2133
+	devices.Unlock()
2136 2134
 
2137
-	if err := filepath.Walk(path.Join(home, "mnt"), func(p string, info os.FileInfo, err error) error {
2138
-		if err != nil {
2139
-			return err
2140
-		}
2141
-		if !info.IsDir() {
2142
-			return nil
2143
-		}
2144
-
2145
-		if mounted[p] {
2135
+	for _, info := range devs {
2136
+		info.lock.Lock()
2137
+		if info.mountCount > 0 {
2146 2138
 			// We use MNT_DETACH here in case it is still busy in some running
2147 2139
 			// container. This means it'll go away from the global scope directly,
2148 2140
 			// and the device will be released when that container dies.
2149
-			if err := syscall.Unmount(p, syscall.MNT_DETACH); err != nil {
2150
-				logrus.Debugf("devmapper: Shutdown unmounting %s, error: %s", p, err)
2141
+			if err := syscall.Unmount(info.mountPath, syscall.MNT_DETACH); err != nil {
2142
+				logrus.Debugf("devmapper: Shutdown unmounting %s, error: %s", info.mountPath, err)
2151 2143
 			}
2152
-		}
2153 2144
 
2154
-		if devInfo, err := devices.lookupDevice(path.Base(p)); err != nil {
2155
-			logrus.Debugf("devmapper: Shutdown lookup device %s, error: %s", path.Base(p), err)
2156
-		} else {
2157
-			if err := devices.deactivateDevice(devInfo); err != nil {
2158
-				logrus.Debugf("devmapper: Shutdown deactivate %s , error: %s", devInfo.Hash, err)
2145
+			devices.Lock()
2146
+			if err := devices.deactivateDevice(info); err != nil {
2147
+				logrus.Debugf("devmapper: Shutdown deactivate %s , error: %s", info.Hash, err)
2159 2148
 			}
2149
+			devices.Unlock()
2160 2150
 		}
2161
-
2162
-		return nil
2163
-	}); err != nil && !os.IsNotExist(err) {
2164
-		devices.Unlock()
2165
-		return err
2151
+		info.lock.Unlock()
2166 2152
 	}
2167 2153
 
2168
-	devices.Unlock()
2169
-
2170 2154
 	info, _ := devices.lookupDeviceWithLock("")
2171 2155
 	if info != nil {
2172 2156
 		info.lock.Lock()
... ...
@@ -2206,6 +2202,15 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
2206 2206
 	devices.Lock()
2207 2207
 	defer devices.Unlock()
2208 2208
 
2209
+	if info.mountCount > 0 {
2210
+		if path != info.mountPath {
2211
+			return fmt.Errorf("devmapper: Trying to mount devmapper device in multiple places (%s, %s)", info.mountPath, path)
2212
+		}
2213
+
2214
+		info.mountCount++
2215
+		return nil
2216
+	}
2217
+
2209 2218
 	if err := devices.activateDeviceIfNeeded(info, false); err != nil {
2210 2219
 		return fmt.Errorf("devmapper: Error activating devmapper device for '%s': %s", hash, err)
2211 2220
 	}
... ...
@@ -2229,6 +2234,9 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
2229 2229
 		return fmt.Errorf("devmapper: Error mounting '%s' on '%s': %s", info.DevName(), path, err)
2230 2230
 	}
2231 2231
 
2232
+	info.mountCount = 1
2233
+	info.mountPath = path
2234
+
2232 2235
 	return nil
2233 2236
 }
2234 2237
 
... ...
@@ -2248,6 +2256,20 @@ func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error {
2248 2248
 	devices.Lock()
2249 2249
 	defer devices.Unlock()
2250 2250
 
2251
+	// If there are running containers when daemon crashes, during daemon
2252
+	// restarting, it will kill running containers and will finally call
2253
+	// Put() without calling Get(). So info.MountCount may become negative.
2254
+	// if info.mountCount goes negative, we do the unmount and assign
2255
+	// it to 0.
2256
+
2257
+	info.mountCount--
2258
+	if info.mountCount > 0 {
2259
+		return nil
2260
+	} else if info.mountCount < 0 {
2261
+		logrus.Warnf("devmapper: Mount count of device went negative. Put() called without matching Get(). Resetting count to 0")
2262
+		info.mountCount = 0
2263
+	}
2264
+
2251 2265
 	logrus.Debugf("devmapper: Unmount(%s)", mountPath)
2252 2266
 	if err := syscall.Unmount(mountPath, syscall.MNT_DETACH); err != nil {
2253 2267
 		return err
... ...
@@ -2258,6 +2280,8 @@ func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error {
2258 2258
 		return err
2259 2259
 	}
2260 2260
 
2261
+	info.mountPath = ""
2262
+
2261 2263
 	return nil
2262 2264
 }
2263 2265
 
... ...
@@ -108,7 +108,7 @@ func (d *Driver) GetMetadata(id string) (map[string]string, error) {
108 108
 
109 109
 // Cleanup unmounts a device.
110 110
 func (d *Driver) Cleanup() error {
111
-	err := d.DeviceSet.Shutdown(d.home)
111
+	err := d.DeviceSet.Shutdown()
112 112
 
113 113
 	if err2 := mount.Unmount(d.home); err == nil {
114 114
 		err = err2
... ...
@@ -1,19 +1,8 @@
1 1
 package graphdriver
2 2
 
3
-import "syscall"
4
-
5 3
 var (
6 4
 	// Slice of drivers that should be used in an order
7 5
 	priority = []string{
8 6
 		"zfs",
9 7
 	}
10 8
 )
11
-
12
-// Mounted checks if the given path is mounted as the fs type
13
-func Mounted(fsType FsMagic, mountPath string) (bool, error) {
14
-	var buf syscall.Statfs_t
15
-	if err := syscall.Statfs(mountPath, &buf); err != nil {
16
-		return false, err
17
-	}
18
-	return FsMagic(buf.Type) == fsType, nil
19
-}
... ...
@@ -42,8 +42,6 @@ const (
42 42
 	FsMagicXfs = FsMagic(0x58465342)
43 43
 	// FsMagicZfs filesystem id for Zfs
44 44
 	FsMagicZfs = FsMagic(0x2fc12fc1)
45
-	// FsMagicOverlay filesystem id for overlay
46
-	FsMagicOverlay = FsMagic(0x794C7630)
47 45
 )
48 46
 
49 47
 var (
... ...
@@ -88,12 +86,3 @@ func GetFSMagic(rootpath string) (FsMagic, error) {
88 88
 	}
89 89
 	return FsMagic(buf.Type), nil
90 90
 }
91
-
92
-// Mounted checks if the given path is mounted as the fs type
93
-func Mounted(fsType FsMagic, mountPath string) (bool, error) {
94
-	var buf syscall.Statfs_t
95
-	if err := syscall.Statfs(mountPath, &buf); err != nil {
96
-		return false, err
97
-	}
98
-	return FsMagic(buf.Type) == fsType, nil
99
-}
... ...
@@ -88,13 +88,21 @@ func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff archive.Rea
88 88
 // of that. This means all child images share file (but not directory)
89 89
 // data with the parent.
90 90
 
91
+// ActiveMount contains information about the count, path and whether is mounted or not.
92
+// This information is part of the Driver, that contains list of active mounts that are part of this overlay.
93
+type ActiveMount struct {
94
+	count   int
95
+	path    string
96
+	mounted bool
97
+}
98
+
91 99
 // Driver contains information about the home directory and the list of active mounts that are created using this driver.
92 100
 type Driver struct {
93
-	home          string
94
-	pathCacheLock sync.Mutex
95
-	pathCache     map[string]string
96
-	uidMaps       []idtools.IDMap
97
-	gidMaps       []idtools.IDMap
101
+	home       string
102
+	sync.Mutex // Protects concurrent modification to active
103
+	active     map[string]*ActiveMount
104
+	uidMaps    []idtools.IDMap
105
+	gidMaps    []idtools.IDMap
98 106
 }
99 107
 
100 108
 var backingFs = "<unknown>"
... ...
@@ -143,10 +151,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
143 143
 	}
144 144
 
145 145
 	d := &Driver{
146
-		home:      home,
147
-		pathCache: make(map[string]string),
148
-		uidMaps:   uidMaps,
149
-		gidMaps:   gidMaps,
146
+		home:    home,
147
+		active:  make(map[string]*ActiveMount),
148
+		uidMaps: uidMaps,
149
+		gidMaps: gidMaps,
150 150
 	}
151 151
 
152 152
 	return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
... ...
@@ -317,14 +325,23 @@ func (d *Driver) Remove(id string) error {
317 317
 	if err := os.RemoveAll(d.dir(id)); err != nil && !os.IsNotExist(err) {
318 318
 		return err
319 319
 	}
320
-	d.pathCacheLock.Lock()
321
-	delete(d.pathCache, id)
322
-	d.pathCacheLock.Unlock()
323 320
 	return nil
324 321
 }
325 322
 
326 323
 // Get creates and mounts the required file system for the given id and returns the mount path.
327 324
 func (d *Driver) Get(id string, mountLabel string) (string, error) {
325
+	// Protect the d.active from concurrent access
326
+	d.Lock()
327
+	defer d.Unlock()
328
+
329
+	mount := d.active[id]
330
+	if mount != nil {
331
+		mount.count++
332
+		return mount.path, nil
333
+	}
334
+
335
+	mount = &ActiveMount{count: 1}
336
+
328 337
 	dir := d.dir(id)
329 338
 	if _, err := os.Stat(dir); err != nil {
330 339
 		return "", err
... ...
@@ -333,10 +350,9 @@ func (d *Driver) Get(id string, mountLabel string) (string, error) {
333 333
 	// If id has a root, just return it
334 334
 	rootDir := path.Join(dir, "root")
335 335
 	if _, err := os.Stat(rootDir); err == nil {
336
-		d.pathCacheLock.Lock()
337
-		d.pathCache[id] = rootDir
338
-		d.pathCacheLock.Unlock()
339
-		return rootDir, nil
336
+		mount.path = rootDir
337
+		d.active[id] = mount
338
+		return mount.path, nil
340 339
 	}
341 340
 
342 341
 	lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
... ...
@@ -349,12 +365,6 @@ func (d *Driver) Get(id string, mountLabel string) (string, error) {
349 349
 	mergedDir := path.Join(dir, "merged")
350 350
 
351 351
 	opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
352
-
353
-	// if it's mounted already, just return
354
-	if mounted, err := d.mounted(mergedDir); err != nil || mounted {
355
-		return "", err
356
-	}
357
-
358 352
 	if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil {
359 353
 		return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
360 354
 	}
... ...
@@ -368,38 +378,42 @@ func (d *Driver) Get(id string, mountLabel string) (string, error) {
368 368
 	if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
369 369
 		return "", err
370 370
 	}
371
+	mount.path = mergedDir
372
+	mount.mounted = true
373
+	d.active[id] = mount
371 374
 
372
-	d.pathCacheLock.Lock()
373
-	d.pathCache[id] = mergedDir
374
-	d.pathCacheLock.Unlock()
375
-
376
-	return mergedDir, nil
377
-}
378
-
379
-func (d *Driver) mounted(dir string) (bool, error) {
380
-	return graphdriver.Mounted(graphdriver.FsMagicOverlay, dir)
375
+	return mount.path, nil
381 376
 }
382 377
 
383 378
 // Put unmounts the mount path created for the give id.
384 379
 func (d *Driver) Put(id string) error {
385
-	d.pathCacheLock.Lock()
386
-	mountpoint, exists := d.pathCache[id]
387
-	d.pathCacheLock.Unlock()
380
+	// Protect the d.active from concurrent access
381
+	d.Lock()
382
+	defer d.Unlock()
388 383
 
389
-	if !exists {
384
+	mount := d.active[id]
385
+	if mount == nil {
390 386
 		logrus.Debugf("Put on a non-mounted device %s", id)
391 387
 		// but it might be still here
392 388
 		if d.Exists(id) {
393
-			mountpoint = path.Join(d.dir(id), "merged")
389
+			mergedDir := path.Join(d.dir(id), "merged")
390
+			err := syscall.Unmount(mergedDir, 0)
391
+			if err != nil {
392
+				logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
393
+			}
394 394
 		}
395
+		return nil
396
+	}
395 397
 
396
-		d.pathCacheLock.Lock()
397
-		d.pathCache[id] = mountpoint
398
-		d.pathCacheLock.Unlock()
398
+	mount.count--
399
+	if mount.count > 0 {
400
+		return nil
399 401
 	}
400 402
 
401
-	if mounted, err := d.mounted(mountpoint); mounted || err != nil {
402
-		if err = syscall.Unmount(mountpoint, 0); err != nil {
403
+	defer delete(d.active, id)
404
+	if mount.mounted {
405
+		err := syscall.Unmount(mount.path, 0)
406
+		if err != nil {
403 407
 			logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
404 408
 		}
405 409
 		return err
... ...
@@ -13,6 +13,7 @@ import (
13 13
 	"path"
14 14
 	"path/filepath"
15 15
 	"strings"
16
+	"sync"
16 17
 	"syscall"
17 18
 	"time"
18 19
 
... ...
@@ -46,6 +47,10 @@ const (
46 46
 type Driver struct {
47 47
 	// info stores the shim driver information
48 48
 	info hcsshim.DriverInfo
49
+	// Mutex protects concurrent modification to active
50
+	sync.Mutex
51
+	// active stores references to the activated layers
52
+	active map[string]int
49 53
 }
50 54
 
51 55
 var _ graphdriver.DiffGetterDriver = &Driver{}
... ...
@@ -58,6 +63,7 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap)
58 58
 			HomeDir: home,
59 59
 			Flavour: filterDriver,
60 60
 		},
61
+		active: make(map[string]int),
61 62
 	}
62 63
 	return d, nil
63 64
 }
... ...
@@ -70,6 +76,7 @@ func InitDiff(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (
70 70
 			HomeDir: home,
71 71
 			Flavour: diffDriver,
72 72
 		},
73
+		active: make(map[string]int),
73 74
 	}
74 75
 	return d, nil
75 76
 }
... ...
@@ -182,6 +189,9 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
182 182
 	logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel)
183 183
 	var dir string
184 184
 
185
+	d.Lock()
186
+	defer d.Unlock()
187
+
185 188
 	rID, err := d.resolveID(id)
186 189
 	if err != nil {
187 190
 		return "", err
... ...
@@ -193,14 +203,16 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
193 193
 		return "", err
194 194
 	}
195 195
 
196
-	if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
197
-		return "", err
198
-	}
199
-	if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
200
-		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
201
-			logrus.Warnf("Failed to Deactivate %s: %s", id, err)
196
+	if d.active[rID] == 0 {
197
+		if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
198
+			return "", err
199
+		}
200
+		if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
201
+			if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
202
+				logrus.Warnf("Failed to Deactivate %s: %s", id, err)
203
+			}
204
+			return "", err
202 205
 		}
203
-		return "", err
204 206
 	}
205 207
 
206 208
 	mountPath, err := hcsshim.GetLayerMountPath(d.info, rID)
... ...
@@ -211,6 +223,8 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
211 211
 		return "", err
212 212
 	}
213 213
 
214
+	d.active[rID]++
215
+
214 216
 	// If the layer has a mount path, use that. Otherwise, use the
215 217
 	// folder path.
216 218
 	if mountPath != "" {
... ...
@@ -231,10 +245,22 @@ func (d *Driver) Put(id string) error {
231 231
 		return err
232 232
 	}
233 233
 
234
-	if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
235
-		return err
234
+	d.Lock()
235
+	defer d.Unlock()
236
+
237
+	if d.active[rID] > 1 {
238
+		d.active[rID]--
239
+	} else if d.active[rID] == 1 {
240
+		if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
241
+			return err
242
+		}
243
+		if err := hcsshim.DeactivateLayer(d.info, rID); err != nil {
244
+			return err
245
+		}
246
+		delete(d.active, rID)
236 247
 	}
237
-	return hcsshim.DeactivateLayer(d.info, rID)
248
+
249
+	return nil
238 250
 }
239 251
 
240 252
 // Cleanup ensures the information the driver stores is properly removed.
... ...
@@ -244,40 +270,62 @@ func (d *Driver) Cleanup() error {
244 244
 
245 245
 // Diff produces an archive of the changes between the specified
246 246
 // layer and its parent layer which may be "".
247
-// The layer should be mounted when calling this function
248 247
 func (d *Driver) Diff(id, parent string) (_ archive.Archive, err error) {
249 248
 	rID, err := d.resolveID(id)
250 249
 	if err != nil {
251 250
 		return
252 251
 	}
253 252
 
253
+	// Getting the layer paths must be done outside of the lock.
254 254
 	layerChain, err := d.getLayerChain(rID)
255 255
 	if err != nil {
256 256
 		return
257 257
 	}
258 258
 
259
-	// this is assuming that the layer is unmounted
260
-	if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
261
-		return nil, err
262
-	}
263
-	defer func() {
264
-		if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
265
-			logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
259
+	var undo func()
260
+
261
+	d.Lock()
262
+
263
+	// To support export, a layer must be activated but not prepared.
264
+	if d.info.Flavour == filterDriver {
265
+		if d.active[rID] == 0 {
266
+			if err = hcsshim.ActivateLayer(d.info, rID); err != nil {
267
+				d.Unlock()
268
+				return
269
+			}
270
+			undo = func() {
271
+				if err := hcsshim.DeactivateLayer(d.info, rID); err != nil {
272
+					logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
273
+				}
274
+			}
275
+		} else {
276
+			if err = hcsshim.UnprepareLayer(d.info, rID); err != nil {
277
+				d.Unlock()
278
+				return
279
+			}
280
+			undo = func() {
281
+				if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
282
+					logrus.Warnf("Failed to re-PrepareLayer %s: %s", rID, err)
283
+				}
284
+			}
266 285
 		}
267
-	}()
286
+	}
287
+
288
+	d.Unlock()
268 289
 
269 290
 	arch, err := d.exportLayer(rID, layerChain)
270 291
 	if err != nil {
292
+		undo()
271 293
 		return
272 294
 	}
273 295
 	return ioutils.NewReadCloserWrapper(arch, func() error {
296
+		defer undo()
274 297
 		return arch.Close()
275 298
 	}), nil
276 299
 }
277 300
 
278 301
 // Changes produces a list of changes between the specified layer
279 302
 // and its parent layer. If parent is "", then all changes will be ADD changes.
280
-// The layer should be mounted when calling this function
281 303
 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
282 304
 	rID, err := d.resolveID(id)
283 305
 	if err != nil {
... ...
@@ -288,15 +336,31 @@ func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
288 288
 		return nil, err
289 289
 	}
290 290
 
291
-	// this is assuming that the layer is unmounted
292
-	if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
293
-		return nil, err
294
-	}
295
-	defer func() {
296
-		if err := hcsshim.PrepareLayer(d.info, rID, parentChain); err != nil {
297
-			logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
291
+	d.Lock()
292
+	if d.info.Flavour == filterDriver {
293
+		if d.active[rID] == 0 {
294
+			if err = hcsshim.ActivateLayer(d.info, rID); err != nil {
295
+				d.Unlock()
296
+				return nil, err
297
+			}
298
+			defer func() {
299
+				if err := hcsshim.DeactivateLayer(d.info, rID); err != nil {
300
+					logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
301
+				}
302
+			}()
303
+		} else {
304
+			if err = hcsshim.UnprepareLayer(d.info, rID); err != nil {
305
+				d.Unlock()
306
+				return nil, err
307
+			}
308
+			defer func() {
309
+				if err := hcsshim.PrepareLayer(d.info, rID, parentChain); err != nil {
310
+					logrus.Warnf("Failed to re-PrepareLayer %s: %s", rID, err)
311
+				}
312
+			}()
298 313
 		}
299
-	}()
314
+	}
315
+	d.Unlock()
300 316
 
301 317
 	r, err := hcsshim.NewLayerReader(d.info, id, parentChain)
302 318
 	if err != nil {
... ...
@@ -327,7 +391,6 @@ func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
327 327
 // ApplyDiff extracts the changeset from the given diff into the
328 328
 // layer with the specified id and parent, returning the size of the
329 329
 // new layer in bytes.
330
-// The layer should not be mounted when calling this function
331 330
 func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) {
332 331
 	rPId, err := d.resolveID(parent)
333 332
 	if err != nil {
... ...
@@ -22,6 +22,12 @@ import (
22 22
 	"github.com/opencontainers/runc/libcontainer/label"
23 23
 )
24 24
 
25
+type activeMount struct {
26
+	count   int
27
+	path    string
28
+	mounted bool
29
+}
30
+
25 31
 type zfsOptions struct {
26 32
 	fsName    string
27 33
 	mountPath string
... ...
@@ -103,6 +109,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
103 103
 		dataset:          rootDataset,
104 104
 		options:          options,
105 105
 		filesystemsCache: filesystemsCache,
106
+		active:           make(map[string]*activeMount),
106 107
 		uidMaps:          uidMaps,
107 108
 		gidMaps:          gidMaps,
108 109
 	}
... ...
@@ -159,6 +166,7 @@ type Driver struct {
159 159
 	options          zfsOptions
160 160
 	sync.Mutex       // protects filesystem cache against concurrent access
161 161
 	filesystemsCache map[string]bool
162
+	active           map[string]*activeMount
162 163
 	uidMaps          []idtools.IDMap
163 164
 	gidMaps          []idtools.IDMap
164 165
 }
... ...
@@ -294,6 +302,17 @@ func (d *Driver) Remove(id string) error {
294 294
 
295 295
 // Get returns the mountpoint for the given id after creating the target directories if necessary.
296 296
 func (d *Driver) Get(id, mountLabel string) (string, error) {
297
+	d.Lock()
298
+	defer d.Unlock()
299
+
300
+	mnt := d.active[id]
301
+	if mnt != nil {
302
+		mnt.count++
303
+		return mnt.path, nil
304
+	}
305
+
306
+	mnt = &activeMount{count: 1}
307
+
297 308
 	mountpoint := d.mountPath(id)
298 309
 	filesystem := d.zfsPath(id)
299 310
 	options := label.FormatMountLabel("", mountLabel)
... ...
@@ -316,29 +335,48 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
316 316
 	if err := os.Chown(mountpoint, rootUID, rootGID); err != nil {
317 317
 		return "", fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err)
318 318
 	}
319
+	mnt.path = mountpoint
320
+	mnt.mounted = true
321
+	d.active[id] = mnt
319 322
 
320 323
 	return mountpoint, nil
321 324
 }
322 325
 
323 326
 // Put removes the existing mountpoint for the given id if it exists.
324 327
 func (d *Driver) Put(id string) error {
325
-	mountpoint := d.mountPath(id)
326
-	mounted, err := graphdriver.Mounted(graphdriver.FsMagicZfs, mountpoint)
327
-	if err != nil || !mounted {
328
-		return err
328
+	d.Lock()
329
+	defer d.Unlock()
330
+
331
+	mnt := d.active[id]
332
+	if mnt == nil {
333
+		logrus.Debugf("[zfs] Put on a non-mounted device %s", id)
334
+		// but it might be still here
335
+		if d.Exists(id) {
336
+			err := mount.Unmount(d.mountPath(id))
337
+			if err != nil {
338
+				logrus.Debugf("[zfs] Failed to unmount %s zfs fs: %v", id, err)
339
+			}
340
+		}
341
+		return nil
342
+	}
343
+
344
+	mnt.count--
345
+	if mnt.count > 0 {
346
+		return nil
329 347
 	}
330 348
 
331
-	logrus.Debugf(`[zfs] unmount("%s")`, mountpoint)
349
+	defer delete(d.active, id)
350
+	if mnt.mounted {
351
+		logrus.Debugf(`[zfs] unmount("%s")`, mnt.path)
332 352
 
333
-	if err := mount.Unmount(mountpoint); err != nil {
334
-		return fmt.Errorf("error unmounting to %s: %v", mountpoint, err)
353
+		if err := mount.Unmount(mnt.path); err != nil {
354
+			return fmt.Errorf("error unmounting to %s: %v", mnt.path, err)
355
+		}
335 356
 	}
336 357
 	return nil
337 358
 }
338 359
 
339 360
 // Exists checks to see if the cache entry exists for the given id.
340 361
 func (d *Driver) Exists(id string) bool {
341
-	d.Lock()
342
-	defer d.Unlock()
343 362
 	return d.filesystemsCache[d.zfsPath(id)] == true
344 363
 }
345 364
deleted file mode 100644
... ...
@@ -1,95 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"fmt"
5
-	"io/ioutil"
6
-	"os"
7
-	"runtime"
8
-	"strings"
9
-	"sync"
10
-
11
-	"github.com/docker/docker/pkg/integration/checker"
12
-	"github.com/go-check/check"
13
-)
14
-
15
-func (s *DockerSuite) BenchmarkConcurrentContainerActions(c *check.C) {
16
-	maxConcurrency := runtime.GOMAXPROCS(0)
17
-	numIterations := c.N
18
-	outerGroup := &sync.WaitGroup{}
19
-	outerGroup.Add(maxConcurrency)
20
-	chErr := make(chan error, numIterations*2*maxConcurrency)
21
-
22
-	for i := 0; i < maxConcurrency; i++ {
23
-		go func() {
24
-			defer outerGroup.Done()
25
-			innerGroup := &sync.WaitGroup{}
26
-			innerGroup.Add(2)
27
-
28
-			go func() {
29
-				defer innerGroup.Done()
30
-				for i := 0; i < numIterations; i++ {
31
-					args := []string{"run", "-d", defaultSleepImage}
32
-					args = append(args, defaultSleepCommand...)
33
-					out, _, err := dockerCmdWithError(args...)
34
-					if err != nil {
35
-						chErr <- fmt.Errorf(out)
36
-						return
37
-					}
38
-
39
-					id := strings.TrimSpace(out)
40
-					tmpDir, err := ioutil.TempDir("", "docker-concurrent-test-"+id)
41
-					if err != nil {
42
-						chErr <- err
43
-						return
44
-					}
45
-					defer os.RemoveAll(tmpDir)
46
-					out, _, err = dockerCmdWithError("cp", id+":/tmp", tmpDir)
47
-					if err != nil {
48
-						chErr <- fmt.Errorf(out)
49
-						return
50
-					}
51
-
52
-					out, _, err = dockerCmdWithError("kill", id)
53
-					if err != nil {
54
-						chErr <- fmt.Errorf(out)
55
-					}
56
-
57
-					out, _, err = dockerCmdWithError("start", id)
58
-					if err != nil {
59
-						chErr <- fmt.Errorf(out)
60
-					}
61
-
62
-					out, _, err = dockerCmdWithError("kill", id)
63
-					if err != nil {
64
-						chErr <- fmt.Errorf(out)
65
-					}
66
-
67
-					// don't do an rm -f here since it can potentially ignore errors from the graphdriver
68
-					out, _, err = dockerCmdWithError("rm", id)
69
-					if err != nil {
70
-						chErr <- fmt.Errorf(out)
71
-					}
72
-				}
73
-			}()
74
-
75
-			go func() {
76
-				defer innerGroup.Done()
77
-				for i := 0; i < numIterations; i++ {
78
-					out, _, err := dockerCmdWithError("ps")
79
-					if err != nil {
80
-						chErr <- fmt.Errorf(out)
81
-					}
82
-				}
83
-			}()
84
-
85
-			innerGroup.Wait()
86
-		}()
87
-	}
88
-
89
-	outerGroup.Wait()
90
-	close(chErr)
91
-
92
-	for err := range chErr {
93
-		c.Assert(err, checker.IsNil)
94
-	}
95
-}
... ...
@@ -49,10 +49,6 @@ var (
49 49
 	// to be created which would result in a layer depth
50 50
 	// greater than the 125 max.
51 51
 	ErrMaxDepthExceeded = errors.New("max depth exceeded")
52
-
53
-	// ErrNotSupported is used when the action is not supppoted
54
-	// on the current platform
55
-	ErrNotSupported = errors.New("not support on this platform")
56 52
 )
57 53
 
58 54
 // ChainID is the content-addressable ID of a layer.
... ...
@@ -12,7 +12,6 @@ type mountedLayer struct {
12 12
 	mountID    string
13 13
 	initID     string
14 14
 	parent     *roLayer
15
-	path       string
16 15
 	layerStore *layerStore
17 16
 
18 17
 	references map[RWLayer]*referencedRWLayer
... ...
@@ -132,21 +131,10 @@ func (rl *referencedRWLayer) Mount(mountLabel string) (string, error) {
132 132
 		return "", ErrLayerNotRetained
133 133
 	}
134 134
 
135
-	if rl.activityCount > 0 {
136
-		rl.activityCount++
137
-		return rl.path, nil
138
-	}
139
-
140
-	m, err := rl.mountedLayer.Mount(mountLabel)
141
-	if err == nil {
142
-		rl.activityCount++
143
-		rl.path = m
144
-	}
145
-	return m, err
135
+	rl.activityCount++
136
+	return rl.mountedLayer.Mount(mountLabel)
146 137
 }
147 138
 
148
-// Unmount decrements the activity count and unmounts the underlying layer
149
-// Callers should only call `Unmount` once per call to `Mount`, even on error.
150 139
 func (rl *referencedRWLayer) Unmount() error {
151 140
 	rl.activityL.Lock()
152 141
 	defer rl.activityL.Unlock()
... ...
@@ -157,11 +145,7 @@ func (rl *referencedRWLayer) Unmount() error {
157 157
 	if rl.activityCount == -1 {
158 158
 		return ErrLayerNotRetained
159 159
 	}
160
-
161 160
 	rl.activityCount--
162
-	if rl.activityCount > 0 {
163
-		return nil
164
-	}
165 161
 
166 162
 	return rl.mountedLayer.Unmount()
167 163
 }