Files
Ayush Ranjan ab9d8455d4 Re-use the same device file inode in tmpfs for overlay whiteout files.
When using overlayfs with tmpfs as the upper layer (common case), depending on
the application, a lot of whiteouts can be created. This leads to a lot of
memory allocation because new dentry and inode structs need to be allocated for
each whiteout. With this change, we at least avoid the inode allocations.

This is analogous with what Linux does. See fs/overlayfs/ovl_entry.h:ovl_fs's
field `whiteout` with comment "Shared whiteout cache".

PiperOrigin-RevId: 704897805
2024-12-10 17:11:22 -08:00

69 lines
2.1 KiB
Go

// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tmpfs
import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
)
// +stateify savable
type deviceFile struct {
inode inode
kind vfs.DeviceKind
major uint32
minor uint32
}
func isOvlWhiteoutDev(mode linux.FileMode, major, minor uint32) bool {
return mode.FileType() == linux.S_IFCHR &&
mode.Permissions() == linux.WHITEOUT_MODE &&
linux.MakeDeviceID(uint16(major), minor) == linux.WHITEOUT_DEV
}
// Precondition: fs.mu must be locked for writing.
func (fs *filesystem) newDeviceFileLocked(kuid auth.KUID, kgid auth.KGID, mode linux.FileMode, major, minor uint32, parentDir *directory) *inode {
ovlWhiteout := isOvlWhiteoutDev(mode, major, minor)
if ovlWhiteout && fs.ovlWhiteout != nil {
// If reusing the same inode, acts like a hard link.
fs.ovlWhiteout.inode.incLinksLocked()
return &fs.ovlWhiteout.inode
}
file := &deviceFile{
major: major,
minor: minor,
}
switch mode.FileType() {
case linux.S_IFBLK:
file.kind = vfs.BlockDevice
case linux.S_IFCHR:
file.kind = vfs.CharDevice
default:
panic(fmt.Sprintf("invalid file type for device file: %s", mode))
}
file.inode.init(file, fs, kuid, kgid, mode, parentDir)
file.inode.nlink = atomicbitops.FromUint32(1) // from parent directory
if ovlWhiteout {
fs.ovlWhiteout = file
// An extra link is held by fs, so nlink doesn't fall to 0.
file.inode.incLinksLocked()
}
return &file.inode
}