Skip to content

Optimizations for recursive unmount#34379

Merged
vdemeester merged 1 commit intomoby:masterfrom
cpuguy83:mount_optimizations
Jan 24, 2018
Merged

Optimizations for recursive unmount#34379
vdemeester merged 1 commit intomoby:masterfrom
cpuguy83:mount_optimizations

Conversation

@cpuguy83
Copy link
Copy Markdown
Member

@cpuguy83 cpuguy83 commented Aug 3, 2017

When a recursive unmount fails, don't bother parsing the mount table to check
if what we expected to be a mountpoint is still mounted. EINVAL is
returned when you try to unmount something that is not a mountpoint, the
other cases of EINVAL would not apply here unless everything is just
wrong. Parsing the mount table over and over is relatively expensive,
especially in the code path that it's in.

@cpuguy83 cpuguy83 force-pushed the mount_optimizations branch from adc2d8d to 9bfb553 Compare August 3, 2017 01:45
@thaJeztah thaJeztah changed the title Optimizations for recurrsive unmount Optimizations for recursive unmount Aug 6, 2017
@thaJeztah
Copy link
Copy Markdown
Member

there's a recursive r in your commit message 😂 s/recurrsive/recursive/

import (
"sort"
"strings"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stray empty line

// If the error is EINVAL either this whole package is wrong (invalid flags passed to unmount(2)) or this is
// not a mountpoint (which is ok in this case).
// Meanwhile calling `Mounted()` is very expensive.
if err := Unmount(m.Mountpoint); err != nil && err != syscall.EINVAL && i == len(mounts)-1 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps break this up a bit, e.g.;

if err := Unmount(m.Mountpoint); (err == nil || err == syscall.EINVAL) {
    continue
}
if i == len(mounts)-1 {
    if mounted, err := Mounted(m.Mountpoint); err != nil || mounted {
        return err
    }
}

The i == len(mounts)-1 may need a comment / explanation perhaps

@thaJeztah
Copy link
Copy Markdown
Member

ping @cpuguy83 😇

@cpuguy83 cpuguy83 force-pushed the mount_optimizations branch from 9bfb553 to 08d4dcd Compare October 26, 2017 19:46
@cpuguy83
Copy link
Copy Markdown
Member Author

Updated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're returning the original error here, correct?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright; wasn't sure if we wanted the error returned by Mounted() as well

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really because the Mounted call is just a sanity check and the original error is the real interest. We also can't wrap two errors but the low-level system error is what we really want to propagate.

Copy link
Copy Markdown
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In view of 069fdc8 and similar commits, shouldn't we use sys/unix instead of syscall here and below?

Copy link
Copy Markdown
Member Author

@cpuguy83 cpuguy83 Jan 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason I chose syscall is because this is called from both Windows and Linux, both of which have EINVAL defined in syscall, but unix. is not compiled on Windows (and vice-versa).
We ultimately have the go1 guarantee on this and being it's not functionality which could be buggy but rather an error value I think we are fine here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a check for the top (i.e. final) mount? It seems it's wrong, as we are iterating through all mounts, not just the one provided by a user and its submounts.

In other words, I believe the correct check here has to be m.Mountpoint == target.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the path passed in may not even be a mount. The reason it's the final mount is the list is sorted in reverse-lexicographic order.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it make sense to raise level to warning (as this message will be a good hint about why the final umount failed), as well as saying this is a submount. Something like

logrus.WithError(err).Warnf("Failed ti umount submount %s", m.Mountpoint)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In here, Unmount() calls Mounted(), which is expensive, and making optimization of removing another Mounted() on an error path kinda moot.

Perhaps unmount() should be used here instead, as it does not call Mounted().

@thaJeztah
Copy link
Copy Markdown
Member

ping @cpuguy83

When a recursive unmount fails, don't bother parsing the mount table to check
if what we expected to be a mountpoint is still mounted. `EINVAL` is
returned when you try to unmount something that is not a mountpoint, the
other cases of `EINVAL` would not apply here unless everything is just
wrong. Parsing the mount table over and over is relatively expensive,
especially in the code path that it's in.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
@cpuguy83
Copy link
Copy Markdown
Member Author

Updated

@kolyshkin
Copy link
Copy Markdown
Contributor

Not directly related to this PR, but I wonder why mount.Unmount() checks the mount table? Would be easier just to call umount() and ignore EINVAL. I traced the code back to original commit adding this flow:
commit 45d7dcf ("Handle external mounts outside of lxc").

@crosbymichael being the original author of the above, do you think we can drop the check?

@cpuguy83
Copy link
Copy Markdown
Member Author

Ping

@kolyshkin
Copy link
Copy Markdown
Contributor

LGTM

1 similar comment
@selansen
Copy link
Copy Markdown
Contributor

LGTM

Copy link
Copy Markdown
Member

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🐸

@vdemeester vdemeester merged commit 59e8606 into moby:master Jan 24, 2018
@cpuguy83 cpuguy83 deleted the mount_optimizations branch January 24, 2018 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants