This reverts commit 563d0711f83952e561a0d7d5c48fef9810b4f010.
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -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 |
} |