Skip to content

Introduce MountedFast function to determine mount points faster.#97

Closed
manugupt1 wants to merge 2 commits intomoby:mainfrom
manugupt1:fast-mount
Closed

Introduce MountedFast function to determine mount points faster.#97
manugupt1 wants to merge 2 commits intomoby:mainfrom
manugupt1:fast-mount

Conversation

@manugupt1
Copy link
Copy Markdown
Contributor

@manugupt1 manugupt1 commented Dec 8, 2021

MountedFast is a method of detecting mount points really fast.
It works for all kinds of mounts (incl. bind mounts) but requires
a recent (v5.6+) Linux kernel. A mount-point check is guaranteed to
be a mount-point or not only when sure is true. When sure is false,
the caller needs to check for other methods (eg: parse /proc/mounts)
to successfully determine if it is a mount-point.

This has been motivated by
kubernetes/kubernetes#105833 (comment)
and
#96 (comment)

Context

The context is that; k8s relies on reading /proc/mounts to determine mount points. It also enforces a consistency check (ie ensure that two reads of proc/mounts are exactly the same) before determining a mount-point which leads to resource contention. This is different than docker where a lock is acquired before reading the entire /proc/mounts.

Now, k8s wont remove the consistency check as it could lead to all sorts of error that Kir demonstrated here (https://github.com/kolyshkin/procfs-test) and the consistency check mitigates that.

To solve for that k8s took inspiration from moby/sys to do its own openat2 call (kubernetes/kubernetes#105833) and when openat2 is not available, fallback on reading /proc/mounts with the consistency check I mentioned above.

During the course of the discussion, Kir suggested it might be better to upstream the change to moby/sys and k8s can take it as a dependency which is what this PR does. It introduces a MountedFast which uses openat2 (for kernel 5.6+) or stat calls to determine a mount-point, it sends back a sure bool to the caller to guarantee the caller if it is a mount-point or not.
k8s will now first call MountedFast when this change is merged and then if sure is false or an error is thrown, it will fall back to reading /proc/mounts. This is beneficial for k8s on newer kernels as they will never read /proc/mounts because of openat2 path and for older kernels only bind mounts will be validated using /proc/mounts.

@manugupt1
Copy link
Copy Markdown
Contributor Author

cc @kolyshkin please have a look

Comment thread mountinfo/mounted_linux.go Outdated
// sure is true. When sure is false, the caller needs to check for other '
// methods (eg: parse /proc/mounts) to successfully determine if it is a
// mount-point.
func mountedByOpenat2(path string) (mounted, sure bool, err error) {
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.

How about returning an exported error instead of the sure flag?

Apparently in all error cases, sure=false. So returning an error wouldn't override the original errors.

Generally speaking Go code checks err first and doesn't consume other returning values if err != nil. I want to follow the convention here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@kolyshkin Do you have thoughts as you suggested this? I think it boils down to preference.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I agree with @kzys here -- no need to change mountedByOpenat2 signature.

Basically, you need to take mounted from mountinfo/mounted_linux.go, add sure bool to it, and remove the fallback to mountinfo parsing. That's all what's needed.

Comment thread mountinfo/mounted_linux.go Outdated
Comment thread mountinfo/mounted_linux.go
Comment thread mountinfo/mounted_linux.go Outdated
@kolyshkin
Copy link
Copy Markdown
Collaborator

Also, please cherry-pick 03cca99 since you're adding more implementations to test, and this becomes ugly -- and this commit is a nice solution to that.

Instead of copy/pasting the code to check individual mountedBy*
implementations, add a list and a loop to check them.

The only special thing is, we have to check if the name of the function
being tested is "mountedByStat", to skip erroring out on bind mounts.
Otherwise the code is the same.

Signed-off-by: Kir Kolyshkin <[email protected]>
Signed-off-by: Manu Gupta <[email protected]>
@manugupt1 manugupt1 force-pushed the fast-mount branch 2 times, most recently from e3c9d7c to 62d2d10 Compare December 9, 2021 18:20
@manugupt1 manugupt1 requested a review from kolyshkin December 9, 2021 18:25
Comment thread mountinfo/mounted_linux.go
// mount-point or not only when sure is true. When sure is false, the
// caller needs to check for other methods (eg: parse /proc/mounts)
// to successfully determine if it is a mount-point.
func MountedFast(path string) (mounted, sure bool, err error) {
Copy link
Copy Markdown

@jingxu97 jingxu97 Dec 11, 2021

Choose a reason for hiding this comment

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

I think in case of "not sure", we can return error indicating cannot figure out whether it is mounted or not.
in this way, we only need return two parameters "bool, error" as before.

@jingxu97
Copy link
Copy Markdown

jingxu97 commented Dec 14, 2021

/assign @jsafrane
/cc @gnufied

overall looks good to me. @jsafrane could you also take a look?

@manugupt1
Copy link
Copy Markdown
Contributor Author

Hi @kolyshkin, would you be able to have a look at it? I would like to get it reviewed and address comments if you have any. I have added things that I have found out; but a final review would be great.

Comment thread mountinfo/mounted_linux_test.go Outdated
@jsafrane
Copy link
Copy Markdown

jsafrane commented Dec 16, 2021

The code looks good to me.

On personal note, running unit tests as root and depending on kernel version is IMHO bad, as different people (and CI) will test different things. It seems moby is fine with that.

@manugupt1 manugupt1 force-pushed the fast-mount branch 5 times, most recently from 74edffe to 5e7a91b Compare December 17, 2021 19:15
Comment thread mountinfo/mounted_linux_test.go Outdated
Comment thread mountinfo/mounted_linux_test.go Outdated
Comment on lines +323 to +326
if sure == true {
t.Errorf("MountFast: expected sure to be false, got %v", sure)
}
if mounted != false {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The code would be easier to read if you use the same comparison (either == false or != true) in both cases.

Comment thread mountinfo/mounted_linux_test.go Outdated
if sure == true {
t.Errorf("MountFast: expected sure to be false, got %v", sure)
}
if mounted != false {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ditto

Comment thread mountinfo/mounted_linux.go Outdated
Comment thread mountinfo/mounted_linux.go Outdated
Comment on lines +11 to +12
// mountinfo from procfs. A mount-point check is guaranteed to be a
// mount-point or not only when sure is true. When sure is false, the
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: s/mount-point/mount point/. Here "mount point" is a common term, see e.g. man 8 mount.

Comment thread mountinfo/mounted_linux.go Outdated
// mountinfo from procfs. A mount-point check is guaranteed to be a
// mount-point or not only when sure is true. When sure is false, the
// caller needs to check for other methods (eg: parse /proc/mounts)
// to successfully determine if it is a mount-point.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ditto

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also, probably makes sense to say this function is only available on Linux.

@kolyshkin
Copy link
Copy Markdown
Collaborator

I think we're almost good to go.

Please

  • merge the last two commits
  • change "mount-point" to "mount point" everywhere.

@manugupt1 manugupt1 changed the title Introduce MountedFast function to determine mount-points faster. Introduce MountedFast function to determine mount points faster. Dec 22, 2021
MountedFast is a method of detecting mount points ie without reading
/proc/mounts. It works for all kinds of mounts (incl. bind mounts)
but requires a recent (v5.6+) Linux kernel. A mount-point check is
guaranteed to be a mount-point or not only when sure is true. When
sure is false, the caller needs to check for other methods
(eg: parse /proc/mounts) to successfully determine if it is a
mount-point.

Signed-off-by: Manu Gupta <[email protected]>
@manugupt1
Copy link
Copy Markdown
Contributor Author

@kolyshkin Would you be able to have a final go and check it please?

@jingxu97
Copy link
Copy Markdown

@kolyshkin could you help approve this? Thanks!

@kolyshkin
Copy link
Copy Markdown
Collaborator

I started to re-review this, and ended up fixing various nits myself. Please see #100 🙏🏻

@thaJeztah
Copy link
Copy Markdown
Member

This was carried in #100 and now merged, so closing this one

thanks for contributing and reviewing, @manugupt1 !

@thaJeztah thaJeztah closed this Feb 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants