Skip to content

Conversation

@artqzn
Copy link

@artqzn artqzn commented Jul 7, 2022

Hi, this is implementation of metadata copy only functionality for overlayfs snapshotter,
issue #6310 (comment).

To enable/disable metacopy functionality the new configuration option for containerd
overlayfs snapshotter was introduced into /etc/containerd/config.toml, (metacopy=true/false).

      [plugins."io.containerd.snapshotter.v1.overlayfs"]
        root_path = ""
        upperdir_label = false
        metacopy = true/false

The "metacopy" option of overlayfs snapshotter controls overlayfs behavior according to the
next table:

containerd metacopy option CONFIG_OVERLAY_FS_METACOPY Effect on overlayfs mount points
metacopy=true Y Metacopy enabled
metacopy=true N Metacopy enabled
metacopy=false Y Metacopy disabled
metacopy=false N Metacopy disabled
no option provided Y Metacopy enabled
no option provided N Metacopy disabled

Thanks!

@k8s-ci-robot
Copy link

Hi @artqzn. Thanks for your PR.

I'm waiting for a containerd member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@artqzn
Copy link
Author

artqzn commented Jul 7, 2022

Dear reviewer/maintainers @AkihiroSuda @dmcgowan @rata, could you please check if the approach is ok for you?
Thank you in advance!

Original issue #7141.

metacopyState = MetacopyDefault
} else {
metacopyState = MetacopySupported
}
Copy link
Member

Choose a reason for hiding this comment

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

If the parameter is read successfully, could it just return here?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, reasonable, will fix. Thank you!

Copy link
Contributor

@rata rata left a comment

Choose a reason for hiding this comment

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

@artqzn Thanks! I left some comments, overall looks fine to me :D

}

const (
MetacopyDefault = 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe use iota here? Or do we need it to be these specific numbers?

EDIT: as I mentioned in the other comment, I think SupportsMetacopy can just return a bool instead of these. If that happens to be true, ignore this comment :)

metacopyState: metacopyState,
}

logMetacopyState(s)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to log inside overlayutils.SupportsMetacopy() instead of here?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, reasonable, will fix.

Comment on lines 156 to 157
useMetacopy: config.metacopy,
metacopyState: metacopyState,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need both here? Can't we just save a bool here that is: config.metacopy && metacopySupported and change metacopyState to return true/false to signal if metacopy is supported or not?

Maybe I'm being overly simplistic and missing something? I think if metacopy is supported, then we can always append the metacopy=on/off to the mount point, and if it is not supported then we don't append anything.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, we can do like this. In case of CONFIG_OVERLAY_FS_METACOPY=y the metacopy=on option is just ignored and not shown in mount options, the same for CONFIG_OVERLAY_FS_METACOPY=n the metacopy=off. I'll update this place.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, sounds great then! :)

Copy link
Author

Choose a reason for hiding this comment

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

One note here, I think that we need to keep three states for metacopy field (Enabled, Disabled, Not supported). We need this because of in case of old kernels the mount operation can fail due to metacopy=on/off option is unsupported.

Copy link
Contributor

Choose a reason for hiding this comment

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

But if metacopy is unsupported, we won't add the option, right? What is the issue, then? I don't follow

Copy link
Contributor

@rata rata Jul 8, 2022

Choose a reason for hiding this comment

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

I'm saying we don't need both fields in this struct. We need just one bool that is: config.Metacopy && metacopySupported. IOW, this bool is true only if metacopy is requested in the config and supported in the kernel. Othweise is false

We need to know if the metacopy was enabled in the config and we need to know if the kernel supports metacopy. But we don't need to store both things as different bools here, we just need to store one bool with the AND of both.

Is it clear what I say now? Am I missing something?

Copy link
Author

Choose a reason for hiding this comment

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

First case:
Metacopy supported by kernel and CONFIG_OVERLAY_FS_METACOPY=Y.
In this case overlayfs mounts will be created with metacopy support even if we don't pass metacopy=on option to mount() system call.
config.Metacopy = false.
As result:
config.Metacopy = false && metacopySupported = true == false -> mount with metacopy=off-> ok, metacopy disabled for this mount point

Second case:
Metacopy is not supported by kernel.
config.Metacopy = false.
As result:
config.Metacopy = false && metacopySupported = false == false -> mount with metacopy=off -> mount failed

We need third state to distinguish between these situations.
In your approach if CONFIG_OVERLAY_FS_METACOPY=Y and config.Metacopy = false we just don't add metacopy=on/off option to mount system call and metacopy will be silently enabled for containers without containerd request.

In case of assumption that nobody set CONFIG_OVERLAY_FS_METACOPY=Y, yes, we no need third state.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, right, brain fart here (it was implied in the "f metacopy is supported" that I was wrong, as we lost that data combining the bools).

I do think, though, that we don't need the enum state. We need to know if it is supported and if it was enabled, then we can just add the on/off unconditionally. IMHO it seems simpler (less things to know and reason about) and avoids races like the sysfs value changed just between when we read it and when the overlayfs was mounted.

Does it make sense?

Copy link
Author

Choose a reason for hiding this comment

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

In fact the first iteration use two booleans, one for on/off and one for supported/unsupported. From my point of view it doesn't matter enum or booleans, for me enum is more clear just maybe because of I am mostly C programmer ). I'll just compare what option looks better and publish better version.
Regarding sysfs, we can just use stat for /sys/modules/overlay/parameters/metacopy to check supported or not and yes, add on/off unconditionally. This should be race-free.

Copy link
Contributor

Choose a reason for hiding this comment

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

SGTM

Comment on lines 196 to 198
testData := "Let me use metacopy!"

testDir, err := os.MkdirTemp(root, "metacopy_test")
Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious, in which case can we fail to check via the sysfs files and we want to do a test mount to see if it is available? When sysfs is not mounted? Is there some other case?

I'm ignorant here, but do we care about sysfs not being mounted? Maybe it is common in some scenarios that I don't know about?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, this is for the cases when there is no sysfs mounted or there are some specific LSM policies. But I also not sure that this is the case. So, maybe I will simplify this check to use sysfs only and move this to integration test.

Copy link
Contributor

@rata rata Jul 8, 2022

Choose a reason for hiding this comment

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

I don't have a strong opinion here, not sure if in other parts of the code we already rely on sysfs maybe? I'd let others weight in. Maybe @dmcgowan that also reviewed this?

Note, however, if you are adding an early return here as @dmcgowan suggested, it would not check the LSM and those things you mentioned. Right?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, in this case it will work like this:
check via sysfs is ok? -> return, otherwise -> slowpath, check via xattrs

Copy link
Author

Choose a reason for hiding this comment

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

I don't have a strong opinion here, not sure if in other parts of the code we already rely on sysfs maybe? I'd let others weight in. Maybe @dmcgowan that also reviewed this?
Another argument for simplification is the next:
overlay snapshotter already use sysfs to check overlay "index=off" option.

func supportsIndex() bool {

Copy link
Contributor

Choose a reason for hiding this comment

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

@artqzn right, I think that is what I was thinking about that we already use it. My vote is to remove all of the rest, then.

Copy link
Author

Choose a reason for hiding this comment

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

Ok, great, will do like this.

Comment on lines 183 to 184
if _, err := os.Stat("/sys/module/overlay/parameters/metacopy"); err == nil {
metacopy, err := os.ReadFile("/sys/module/overlay/parameters/metacopy")
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems the stat is not used. Can't we just remove the first if to stat, and try to read the file? If it doesn't exist, then it should return an error

Copy link
Contributor

@rata rata Jul 8, 2022

Choose a reason for hiding this comment

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

Hmm, or maybe we don't need to read the file and just stat it with the other simplifications. If the file exists, we can clearly use metacopy in the mounts and that is all we need to know, we don't need to know the content (after the simplifications are done). Right?

Copy link
Author

Choose a reason for hiding this comment

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

If we don't want to support the case when metacopy enabled by default in Linux kernel (CONFIG_OVERLAY_FS_METACOPY=Y) and we would like to disable it by passing metacopy=off to overlay mount point we can do like this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this will become more clear once you push the next iteration. We can discuss it then, if needed :)

@artqzn artqzn force-pushed the overlayfs-metacopy branch from 362826e to 091b59f Compare July 8, 2022 15:40
}

// metacopyState returns the "metacopy" option according to kernel support and containerd config.
func metacopyState(state bool) int {
Copy link
Contributor

@rata rata Jul 8, 2022

Choose a reason for hiding this comment

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

If we are keeping the enum, I think this should not be an int but it's own type? I don't know, int seems weird, but no strong opinion.

In any case, I think changing this to return a bool instead seems simpler. Like func metacopySupported() bool (or whatever is a good name, haven't thought about it).

Then, if it is supported, we add the on/off as it was requested on the params. Otherwise, we don't add those params and just log.

Does it make sense?

Copy link
Author

Choose a reason for hiding this comment

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

I've updated the PR. I think that enum is better, especially after comment from @AkihiroSuda #7141 (comment), due to it would be good to hide all checks in one small function that return 3-state metacopy configuration and then use simple switch to provide right mount option. I think it is just more readable and clear.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point, SGTM! Thanks!

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for review Rodrigo!

Copy link
Contributor

@rata rata left a comment

Choose a reason for hiding this comment

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

Just one more idea, let me know what you think

case metacopyDisabled:
options = append(options, "metacopy=off")
}

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Author

Choose a reason for hiding this comment

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

Yes, thanks, I'll add this check

@artqzn artqzn force-pushed the overlayfs-metacopy branch from 091b59f to de43a9d Compare July 11, 2022 11:36
@artqzn artqzn marked this pull request as ready for review July 11, 2022 11:42
Copy link
Contributor

@rata rata left a comment

Choose a reason for hiding this comment

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

LGTM :)

Left one small doubt, but on a cursory look, seems fine for me :)

return metacopyDisabled
}
if userxattr {
logrus.Warnf("userxattr, and metacopy=on can't be used together. Metacopy disabled.")
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm unsure which is is the right behavior: (a) logging and continue or (b) returning an error. But no strong opinion, I lack the context about how userxattr is set

Copy link
Author

Choose a reason for hiding this comment

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

The userxattr option is set in case of we try to create unprivileged mount point of overlayfs, for example in case of containerd is running inside user namespace under user that is not a real root. @AkihiroSuda, please correct me if I am wrong. In this case we will be unable to create overlayfs mount point with metacopy=on.

Regarding error handling I've just following the userxattr option handling approach.
If mounting of rootfs with metacopy=on is impossible, this is not a critical failure. So, we can log warning here.
Dear reviewers @AkihiroSuda @dmcgowan , could you please share your opinion here?

Thanks.

Copy link
Author

Choose a reason for hiding this comment

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

Also, maybe it is necessary to add a sanity check for redirect_dir, due to metacopy=on will not work if redirect_dir={off|nofollow|follow(*)}. This is from https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt.

But currently containerd doesn't touch redirect_dir, due to this fact I drop this check.

Copy link
Member

Choose a reason for hiding this comment

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

Warn looks good

Copy link
Author

Choose a reason for hiding this comment

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

Thanks @AkihiroSuda!

@artqzn artqzn requested review from AkihiroSuda and dmcgowan July 14, 2022 10:18

if !metacopy {
logrus.Infof("Metacopy disabled.")
return metacopyDisabled
Copy link
Member

Choose a reason for hiding this comment

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

Our recommendation has been to use the system parameters here. If you had previously enabled then upgraded with this change, would it become disabled by default with no configuration change?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, with no configuration change metacopy will be disabled, even it was enabled by CONFIG_OVERLAY_FS_METACOPY=Y in Linux kernel. The CONFIG_OVERLAY_FS_METACOPY option is usually disabled by default, but yes, we can have customized installation or customized overlay.ko that contains CONFIG_OVERLAY_FS_METACOPY = Y.

Possibly we can do like this:
In case of metacopy=true in /etc/containerd/config.toml we add metacopy=on option to overlayfs mount point if metacopy is supported. Otherwise, don't add any option to overlayfs mount point and use system defaults. In this case it will be safe to use default configuration if CONFIG_OVERLAY_FS_METACOPY=Y

With such approach we will be unable to switch metacopy support off for overlayfs snapshotter if metacopy is enabled system-wide. But looks like this is not our goal.

I'll update the logic If such approach is ok.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can just have the config field be a *bool instead? We know when it is not set (it is nil) and we don't change anything, or when it is set to true/false and do the right thing.

Copy link
Author

Choose a reason for hiding this comment

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

@rata Good idea, it would be bad to introduce string field in config or something like this to enable 3-state configuration. Will test and update PR. Thanks!

Copy link
Author

Choose a reason for hiding this comment

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

Hi, I've updated the implementation. Now, if there is no metacopy option in /etc/containerd/config.toml the overlayfs module configuration is used, and containerd doesn't override system default.
Also, in case of *bool config option for metacopy support it is better to use booleans in snapshotter config instead of enum, due to it simplify options conversion.

@rata, @AkihiroSuda, @dmcgowan, could you have a look please.

Thank you in advance!

@artqzn artqzn requested a review from dmcgowan July 20, 2022 08:05
@artqzn artqzn force-pushed the overlayfs-metacopy branch from de43a9d to 82b3bf9 Compare July 26, 2022 08:42
Copy link
Contributor

@rata rata left a comment

Choose a reason for hiding this comment

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

@artqzn thanks! This looks okay to me, left some nit comments. But maybe they are too much about personal taste :)

return false
}

// supportsIndex checks whether the "metacopy" option is supported by the kernel.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// supportsIndex checks whether the "metacopy" option is supported by the kernel.
// supportsMetacopy checks whether the "metacopy" option is supported by the kernel.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks fixed

Comment on lines 60 to 68
if config.Metacopy != nil {
oOpts = append(oOpts, overlay.WithMetacopyCtrl)
if *config.Metacopy {
oOpts = append(oOpts, overlay.WithMetacopy)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not convinced still that making the field in SnapshotterConfig a *bool isn't simpler. We just assign it here (we won't need two "overlay.WithMetacopy..." functions), and then we test for not nil in other parts of the code (instead of metacopyCtr) and that is it.

Just my personal taste (and I'm no maintainer here), definitely not a must.

Copy link
Author

Choose a reason for hiding this comment

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

Here I agree, but I just don't want to change the original behavior. Currently the parameters for func NewSnapshotter(root string, opts ...Opt) (snapshots.Snapshotter, error) function are passed using "ops", like

// WithUpperdirLabel adds as an optional label
// "containerd.io/snapshot/overlay.upperdir". This stores the location
// of the upperdir that contains the changeset between the labelled
// snapshot and its parent.
func WithUpperdirLabel(config *SnapshotterConfig) error {
    config.upperdirLabel = true
    return nil
}

We can pass SnapshotterConfig structure directly to NewSnapshotter, but in this case it should be prepared in plugin.go.
I can do this, not a problem, if there no objections from other reviewers, due to I don't know the story, why current approach was used.

Comment on lines 138 to 139
if config.metacopyCtrl {
if metacopyOn {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we are keeping the two bools, then this is redundant. metacopyOn is true if config.metacopyCtrl.

Of course, you can't just change that for if metacopyOn as the else case would be broken (we can add when it is not supported). But it seems like this could be simplified.

Maybe the if should be: if config.metacopyCtrl && supportsMetacopy() and inside that you test for metacopyOn and userxattr?

Copy link
Author

Choose a reason for hiding this comment

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

This is just debug leftover, I'll remove it, thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you introduced the bug I mentioned it is easy to introduce here. But let's move the conversation to: https://github.com/containerd/containerd/pull/7141/files#r929864915

@thaJeztah
Copy link
Member

thaJeztah commented Jul 26, 2022

Question; what's the behavior if this option is enabled (or disabled) while existing state (snapshots) exist? Must the snapshotter be aware of how the snapshot was created (with this option enabled or disabled) in order to properly handle it?

@artqzn artqzn force-pushed the overlayfs-metacopy branch from 82b3bf9 to a545b1d Compare July 26, 2022 11:14
if o.metacopyOn {
options = append(options, "metacopy=on")
} else {
options = append(options, "metacopy=off")
Copy link
Contributor

Choose a reason for hiding this comment

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

But with this change, then, it has the bug that I mentioned here: #7141 (comment)

If metacopy is set to false in the config but the kernel doesn't support it, we will append the metacopy=off option. I'm not sure that is incorrect (the user asked for it), but the true value is only used when it is also supported.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, sorry, fixed.

Copy link
Author

Choose a reason for hiding this comment

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

Current version is ok.
metacopyCtrl := config.metacopyCtrl && supportsMetacopy() - true only if metacopy is supported, so we add metacopy=on/off only in case we have support from overlayfs.

@artqzn
Copy link
Author

artqzn commented Jul 26, 2022

Question; what's the behavior if this option is enabled (or disabled) while existing state (snapshots) exist? Must the snapshotted be aware of how the snapshot was created (with this option enabled or disabled) in order to properly handle it?

Hi, there is one possible problem, but I am not sure if this can be the real case.
If snapshot was created with metacopy=on enabled and we can have inodes in upperdir that have just xattrs and permissions and don't have file content. For example after chown operation. If such snapshots used with disabled metacopy the content of files that have inodes in "upperdir" becomes not accessible in "merged" directory.
Also, it is possible to enable metacopy for snapshots that were created with metacopy=off.

Solutions:

  • I've checked previously, podman use special files in snapshotter directory, like a flags that keep snapshotter configuration.
    Like ../metacopyOn, or ../metacopyOff.
  • Maybe it is possible to add information about metacopy option to snapshot Info structure.

Thanks!

@artqzn artqzn force-pushed the overlayfs-metacopy branch from a545b1d to c17babf Compare July 26, 2022 12:18
@thaJeztah
Copy link
Member

Thanks! Right, so from that information, it seems indeed that some state / metadata should be preserved somewhere. I must admit I'm not familiar enough with options where to store additional metadata like this. A simple file could perhaps work (and the advantage that it's easy to read without much overhead), but perhaps it should be something more generic, and generic enough for possible uses by other snapshotters? Maybe others have an opinion / idea on that.

For completeness; IIUC, there will be three possible states;

  • A snapshot that was created with a containerd version before this change must have an implicit metacopy=false, because versions before this patch didn't support the feature, and should not (I think) obtain the new behavior where "no option set" should automatically follow CONFIG_OVERLAY_FS_METACOPY (?)
  • A snapshot that was created with this feature, should preserve the metacopy=true or metacopy=false state either as detected (no explicit config set, so implicitly following CONFIG_OVERLAY_FS_METACOPY), or as configured explicitly.

Which, basically means; if no metadata about metacopy is present, metacopy=false for the snapshot.

FWIW (and not sure if needed for this feature) a PR from @neersighted was merged recently in Moby to perform detection of metacopy support; see moby/moby#43557

@artqzn
Copy link
Author

artqzn commented Jul 26, 2022

Thanks! Right, so from that information, it seems indeed that some state / metadata should be preserved somewhere. I must admit I'm not familiar enough with options where to store additional metadata like this. A simple file could perhaps work (and the advantage that it's easy to read without much overhead), but perhaps it should be something more generic, and generic enough for possible uses by other snapshotters? Maybe others have an opinion / idea on that.

Thanks! The file definitely should work, but yes, maybe somebody will be able to suggest the right place for storing metacopy state.

For completeness; IIUC, there will be three possible states;

  • A snapshot that was created with a containerd version before this change must have an implicit metacopy=false, because versions before this patch didn't support the feature, and should not (I think) obtain the new behavior where "no option set" should automatically follow CONFIG_OVERLAY_FS_METACOPY (?)

Unfortunately I don't have a lot of information about use cases here, the question is if it is possible that CONFIG_OVERLAY_FS_METACOPY is changed? In general yes, it is possible to write to /sys/module/overlay/parameters/metacopy, or at least it can be done by kernel module update. If so, we should just follow snapshot metadata here. If metacopy is not enabled for snapshot we should just explicitly disable it, even in case of CONFIG_OVERLAY_FS_METACOPY=Y.

  • A snapshot that was created with this feature, should preserve the metacopy=true or metacopy=false state either as detected (no explicit config set, so implicitly following CONFIG_OVERLAY_FS_METACOPY), or as configured explicitly.

Agreed, but here again looks like we just should follow snapshot metadata. If we preserve metacopy state in snapshot metadata we should deploy snapshot according it and fail if metacopy is not supported. Please correct me if I am wrong here.

Which, basically means; if no metadata about metacopy is present, metacopy=false for the snapshot.

FWIW (and not sure if needed for this feature) a PR from @neersighted was merged recently in Moby to perform detection of metacopy support; see moby/moby#43557

Here I am not sure. Looks like it is enough to check if /sys/module/overlay/parameters/metacopy is present in sysfs. We discussed it with @rata previously and looks like there is no reason to test metacopy support by creating real mount and xattrs checking. But maybe here @neersighted can provide more details. The very first version of patch had such kind of metacopy check.

Thanks!

@neersighted
Copy link
Contributor

Here I am not sure. Looks like it is enough to check if /sys/module/overlay/parameters/metacopy is present in sysfs. We discussed it with @rata previously and looks like there is no reason to test metacopy support by creating real mount and xattrs checking. But maybe here @neersighted can provide more details. The very first version of patch had such king of metacopy check.

Thanks!

To test if metacopy is supported, it should be sufficient to check if the parameter exists on the kernel module. The purpose of the Moby PR is to check if metacopy is on at runtime, as Moby never explicitly enables or disables it. Metacopy being on or off can be useful for debugging disk usage or permissions issues (on old kernels that had a bug in metacopy), but if you're explicitly managing it I don't see a reason to create a mount and test.

@artqzn
Copy link
Author

artqzn commented Jul 26, 2022

Here I am not sure. Looks like it is enough to check if /sys/module/overlay/parameters/metacopy is present in sysfs. We discussed it with @rata previously and looks like there is no reason to test metacopy support by creating real mount and xattrs checking. But maybe here @neersighted can provide more details. The very first version of patch had such king of metacopy check.
Thanks!

To test if metacopy is supported, it should be sufficient to check if the parameter exists on the kernel module. The purpose of the Moby PR is to check if metacopy is on at runtime, as Moby never explicitly enables or disables it. Metacopy being on or off can be useful for debugging disk usage or permissions issues (on old kernels that had a bug in metacopy), but if you're explicitly managing it I don't see a reason to create a mount and test.

@neersighted Thanks for clarification! In this case it should be enough to just use stat /sys/module/overlay/parameters/metacopy to check if metacopy is supported and read /sys/module/overlay/parameters/metacopy to check if metacopy is enabled by default.

@IlyaHanov
Copy link
Contributor

Cc @dmcgowan @AkihiroSuda

metacopyOn *bool
)
if config.metacopyOn != nil {
if metacopyKernel, err = overlayutils.SupportsMetacopy(); err != nil && !os.IsExist(err) {
Copy link
Member

Choose a reason for hiding this comment

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

why !os.IsExist here? (Should it be IsNotExist?) And shouldn't we treat it as "failed to check for kernel option" as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

And shouldn't we treat it as "failed to check for kernel option" as well?

It's reasonable, if a user specified metacopy option then we expect that there's support from the kernel, otherwise just error out. I'll fix this in the next iteration. Thanks!

upperdirLabel bool
indexOff bool
userxattr bool // whether to enable "userxattr" mount option
metacopyOn *bool
Copy link
Member

Choose a reason for hiding this comment

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

why can't this be a bool? In other words, is there any difference between metacopyOn == nil and *metacopyOn == false?

Copy link
Contributor

Choose a reason for hiding this comment

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

There are three options:

  1. metacopy=on by config.
  2. metacopy=off by config.
  3. metacopy option wasn't specified.

In previous version it was:

type snapshotter struct {
	root          string
	ms            *storage.MetaStore
	asyncRemove   bool
	upperdirLabel bool
	indexOff      bool
	userxattr     bool // whether to enable "userxattr" mount option
	metacopyOn    bool
        metacopyCtrl  bool
}

if metacopy option specified -- metacopyCtrl = true if it's not -- metacopyCtrl = false. We decided to make metacopyOn a simple *bool only for the sake of simplicity, so now if metacopyOn = nil that means a user didn't specify metacopy option.

@dmcgowan dmcgowan added the status/needs-discussion Needs discussion and decision from maintainers label Jun 11, 2023
@IlyaHanov IlyaHanov force-pushed the overlayfs-metacopy branch from 041b130 to 07f1d29 Compare June 19, 2023 11:25
if err != nil {
t.Fatal(err)
} else if userxattr {
expected = append(expected, "userxattr")
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks wrong to me, actually testOverlayOverlayMount does the same, but according to the sequence in snapshots/overlay/overlay.go if userxattr and index is supported then we should have m.Options = ["userxattr", "index=off"], but expected = ["index=off", "userxattr"]?

@IlyaHanov
Copy link
Contributor

@dmcgowan , @AkihiroSuda , @rata PTAL

@IlyaHanov IlyaHanov force-pushed the overlayfs-metacopy branch from 07f1d29 to 9a08295 Compare June 19, 2023 14:42
artqzn added 2 commits June 19, 2023 17:45
This patch introduces support of metacopy optimization for overlayfs mount points.
(https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt).
Metacopy support for containerd can be enabled by new option "metacopy=on/off"
in configuration file (/etc/containerd/config.toml). The metacopy option is
located in overlayfs snapshotter configuration section:

  [plugins."io.containerd.snapshotter.v1.overlayfs"]
    root_path = ""
    upperdir_label = false
    mount_options = ["metacopy=on/off"]

If /etc/containerd/config.toml doesn't provide metacopy option,
containerd doesn't override system configuration and overlayfs
module defaults are used.

Signed-off-by: Artem Kuzin <[email protected]>
Signed-off-by: Ilya Hanov <[email protected]>
@IlyaHanov IlyaHanov force-pushed the overlayfs-metacopy branch from 9a08295 to 151b86d Compare June 19, 2023 15:27
// check one more time because user may not specify the option but above we did it.
if hasOption(config.mountOptions, "metacopy=on", false) {
if hasOption(config.mountOptions, "userxattr", false) {
logrus.Warnf("cannot apply \"metacopy=on\" when \"userxattr\" is set")
Copy link
Member

Choose a reason for hiding this comment

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

What is the conflict with userxattr?

The docs only say

Note: redirect_dir={off|nofollow|follow[*]} and nfs_export=on mount options conflict with metacopy=on, and will result in an error.

Copy link
Contributor

@IlyaHanov IlyaHanov Jun 21, 2023

Choose a reason for hiding this comment

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

Maybe docs aren't complete, check this. I need to re-read the docs.

[480303.568757] overlayfs: conflicting options: userxattr,metacopy=on

@fuweid fuweid self-requested a review July 11, 2023 15:05
@samuelkarp
Copy link
Member

/ok-to-test

@IlyaHanov
Copy link
Contributor

/retest

@k8s-ci-robot
Copy link

@artqzn: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
pull-containerd-node-e2e 151b86d link true /test pull-containerd-node-e2e

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@tonistiigi
Copy link
Member

This potentially has quite a lot of implications for buildkit.

One case is the overlay-specific differ that walks the upper dir. This would need to understand the origin xattr or it would create bogus file content in the layer blobs.

Another case is the DiffOp (and MergeOp?), where layers can be accessed without parents. If some of such layers will be metadata-only stubs then that will not produce correct content.

Not against the concept as a whole, but we need to think through all such possible cases and patch where needed.

@AkihiroSuda @ktock @sipsma

@sipsma
Copy link
Contributor

sipsma commented Sep 1, 2023

This potentially has quite a lot of implications for buildkit.

Agreed, I think the baseline for all the impacted buildkit features would be to just disable the optimizations when metacopy is being used in a given image layer. Then we can re-incorporate support for metacopy into the features over time.

However, one immediate concern remains which is that MergeOp/DiffOp specifically work with both local layers and remote layers (lazy references to them). To disable optimizations when metacopy is being used, we'd need to know whether a layer is using it, which is easy enough for local layers but unclear for remote layers.

Is it possible (or already implemented) to include OCI annotations in layers that indicate they use metacopy? That would let us grab the remote metadata without having to pull the entire layer locally, unpack it and scan it for any metacopy usage.

Apologies if this is already addressed previously in this PR/discussion or otherwise missing something obvious, I'm just dropping in with little context after being pinged 😅

@IlyaHanov
Copy link
Contributor

This potentially has quite a lot of implications for buildkit.

I don't agree. This PR tries to prevent few of dead cases and has nothing to do with breaking any existent behavior. We only enable metacopy if it already is or we have no containers (which means you're free to disable it on system-wide level before creating snapshots or so). The dead case that is not controlled at all is that a user may disable metacopy if there already are some containers with metacopy=on which means a possibility of losing content in upperdir, but if a user still want to do it we just rely on system's choice (which means we do almost nothing).

So, this PR tries to enable metacopy only if it is safe, otherwise user can change it as s(he) wishes. @tonistiigi , the cases you mentioned make sense and I agree that they must be controlled, but this PR doesn't touch them. It should be different story.

Is it possible (or already implemented) to include OCI annotations in layers that indicate they use metacopy?

@sipsma , I'm not very familiar with OCI annotations and its implementation, but looks like it is possible because those layers have metacopy xattr, so you're free to check it and include an annotation.

Copy link
Contributor

@neersighted neersighted left a comment

Choose a reason for hiding this comment

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

Circling back to register a -1 for now; after extensive discussion on how to integrate metacopy with higher-level tools e.g. BuildKit with @tonistiigi, I have concerns about adding integration points for other tools (e.g. custom differs).

I also find the code in overlay.go to be too imperative; I think we need to very carefully define each and every circumstance this code handles and make it more clear up front.

I'm going to try to find some time to review this in more detail and provide more actionable feedback/a PR against the PR.

@k8s-ci-robot
Copy link

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@github-actions
Copy link

This PR is stale because it has been open 90 days with no activity. This PR will be closed in 7 days unless new comments are made or the stale label is removed.

@github-actions github-actions bot added the Stale label Apr 10, 2024
@github-actions
Copy link

This PR was closed because it has been stalled for 7 days with no activity.

@github-actions github-actions bot closed this Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-rebase ok-to-test Stale status/needs-discussion Needs discussion and decision from maintainers

Projects

Status: Done
Archived in project

Development

Successfully merging this pull request may close these issues.