Commit Graph
165 Commits
Author SHA1 Message Date
Jing ChenandgVisor bot 090d92a726 Have lgetxattr return ENODATA.
It is a workaround to lgetxattr/lsetxattr working with security.capacity when running docker in gvisor.

PiperOrigin-RevId: 563194162
2023-09-06 13:13:19 -07:00
Nicolas LacasseandgVisor bot 69e6fcb515 tmpfs: Allow non-standard xattr prefixes.
PiperOrigin-RevId: 561105602
2023-08-29 12:31:01 -07:00
Andrei VaginandgVisor bot f556917137 vfs: MountNamespace.Root() has to return a top mount of /
A few mounts can be mounted on top of `/`.

PiperOrigin-RevId: 558264274
2023-08-18 15:35:47 -07:00
Andrei VaginandgVisor bot 4af420d420 Implement setns for mount namespaces
PiperOrigin-RevId: 552859231
2023-08-01 11:12:29 -07:00
Ayush RanjanandgVisor bot 6f55461bf6 Impose default tmpfs size limits correctly.
Syzkaller came up with workloads that fallocate(2) 1 TB in /tmp. The host
mlock(2) or madvise(2) syscalls on memfd(2) files end up hanging for multiple
minutes in such situations causing the watchdog to mark the calling goroutine
as stuck. memfd(2) files have not size limits.

Linux fails such fallocate(2) attempts in /tmp with ENOSPC.
In Linux tmpfs (shmem), when size= mount option is not specified, the default
size limit for the mount is set to 50% of physical RAM size. But in gVisor, it
is set to MaxInt64. Which is why Linux fails with ENOSPC and gVisor doesn't.

In runsc, the physcial RAM size is already exposed to the containerized
application via `/proc/meminfo` which uses `usage.*TotalMemoryBytes`. These
fields are configured using the `MemTotal:` field from host `/proc/meminfo`.
So use that information to set the default size limit correctly.

Reported-by: [email protected]
PiperOrigin-RevId: 548252095
2023-07-14 16:44:25 -07:00
Ayush RanjanandgVisor bot c091d58651 Use write(2) host syscall to perform writes on disk-backed MemoryFiles.
Prepopulating pages for disk-backed MemoryFiles has proved to be futile.
The mf.MapInternal()+safemem.CopySeq() approach used right now incurs a lot of
page faults without page population. Page-by-page faults incurs a lot of
context switching.

On the other hand, the write syscall makes one context switch to kernel, and
faults all the pages that are touched during write. Note that safemem.CopySeq()
avoids a syscall and hence can be faster sometimes when the underlying page
is populated. But with disk writebacks, it is hard to predict/account what is
populated. Writebacks can happen asynchronously based on system load.

Benchmark results show that FIO write performance improves a lot on rootfs:
```
goos: linux
goarch: amd64
cpu: Intel(R) Xeon(R) CPU @ 2.80GHz
                                                       │ benchout.runsc-before │       benchout.runsc-after       │
                                                       │        sec/op         │   sec/op    vs base              │
BuildABSL/page_cache.clean/filesystem.bindfs-4                      90.20 ± 2%   89.44 ± 1%       ~ (p=0.382 n=8)
BuildGRPC/page_cache.clean/filesystem.bindfs-4                      626.0 ± 1%   626.8 ± 0%       ~ (p=0.505 n=8)
RubySpecTest/page_cache.clean/filesystem.bindfs-4                   52.11 ± 1%   52.37 ± 1%       ~ (p=0.105 n=8)
Fio/operation.write/blockSize.4K/filesystem.rootfs-4                2.509m ± 0%   2.509m ± 0%        ~ (p=0.878 n=8)
Fio/operation.write/blockSize.64K/filesystem.rootfs-4               2.009m ± 0%   1.507m ± 0%  -24.98% (p=0.000 n=8)
Fio/operation.write/blockSize.1024K/filesystem.rootfs-4             2.008m ± 0%   1.508m ± 0%  -24.90% (p=0.000 n=8)

                                                        │   benchout.runsc-before    │               benchout.runsc-after                │
                                                        │ bandwidth.bytes_per_second │ bandwidth.bytes_per_second  vs base               │
Fio/operation.write/blockSize.4K/filesystem.rootfs-4                     649.1M ± 2%                  705.2M ± 2%   +8.64% (p=0.000 n=8)
Fio/operation.write/blockSize.64K/filesystem.rootfs-4                    991.1M ± 1%                 1499.1M ± 3%  +51.25% (p=0.000 n=8)
Fio/operation.write/blockSize.1024K/filesystem.rootfs-4                  1.198G ± 2%                  1.945G ± 2%  +62.34% (p=0.000 n=8)

                                                        │ benchout.runsc-before │             benchout.runsc-after             │
                                                        │ io_ops.ops_per_second │ io_ops.ops_per_second  vs base               │
Fio/operation.write/blockSize.4K/filesystem.rootfs-4                158.5k ± 2%             172.2k ± 2%   +8.64% (p=0.000 n=8)
Fio/operation.write/blockSize.64K/filesystem.rootfs-4               15.12k ± 1%             22.87k ± 3%  +51.25% (p=0.000 n=8)
Fio/operation.write/blockSize.1024K/filesystem.rootfs-4             1.143k ± 2%             1.855k ± 2%  +62.34% (p=0.000 n=8)

                                                  │ benchout.runsc-before │    benchout.runsc-after     │
                                                  │       load.sec        │  load.sec   vs base         │
RubySpecTest/page_cache.clean/filesystem.bindfs-4              7.555 ± 1%   7.585 ± 1%  ~ (p=0.457 n=8)
```

PiperOrigin-RevId: 548021076
2023-07-13 22:32:34 -07:00
Ayush RanjanandgVisor bot 80e0537d87 Simply pgalloc.MemoryFile.Allocate() API.
Earlier we had another method for AllocateAndPopulate. This change moves the
additional (optional) parameters to pgalloc.AllocOpts.

* Makes `Reader` parameter optional. Not all callers have data to fill into
  the allocated pages. Avoids having to define no-op readers.
* Avoid calling MapInternal() when `Reader == nil` and
  `Mode != AllocateAndWritePopulate`.
* Avoid calling safemem.ReadFullToBlocks() when `Reader == nil`.
* Move AllocationMode upgrade/downgrade logic to callsites. This makes
  Allocate() simpler.

PiperOrigin-RevId: 547688619
2023-07-12 22:12:08 -07:00
Nayana BidariandgVisor bot 14bd67edab Pass memory cgroup id in pgalloc.Allocate()
For memory accounting per task, the memory cgroup id of the task is required.
This CL retrieves the cgroup id from the task context and stores it in the
AllocOpts struct which will later be used to account for memory usage.

PiperOrigin-RevId: 546996195
2023-07-10 15:16:47 -07:00
Ayush RanjanandgVisor bot 6000657987 Prepopulate pages for large writes in tmpfs.
When an application makes a huge write, it is better to pre-fault all the pages
that will later be touched by runtime.memmove() via safemem.CopySeq(). Earlier
we were calling MemoryFile.Allocate(), which does not populate the pages.

Otherwise, these pages fault one at a time as they are touched, which is really
slow due to context switching.

Note that this optimization only works for shmem-backed MemoryFiles. This means
that writes to unallocated regions of tmpfs mounts (like /tmp) will be faster.
rootfs (overlay) does not benefit because even though it has an upper tmpfs
layer, the MemoryFile used is disk-backed.

We only prepopulate if more than 1 page is being written to. This is because
the syscall path is susceptible to certain overheads that the page fault path
does not have. For example seccomp-bpf and syscall auditing.

The improved large writes in tmpfs is evidenced by (on Linux >=5.14):
- Before
```
$ docker run --runtime=runsc --rm ubuntu bash -c "dd if=/dev/zero of=/tmp/file.txt bs=40960 count=100000"
100000+0 records in
100000+0 records out
4096000000 bytes (4.1 GB, 3.8 GiB) copied, 3.10212 s, 1.3 GB/s
```
- After
```
docker run --runtime=runsc --rm ubuntu bash -c "dd if=/dev/zero of=/tmp/file.txt bs=40960 count=100000"
100000+0 records in
100000+0 records out
4096000000 bytes (4.1 GB, 3.8 GiB) copied, 2.38347 s, 1.7 GB/s
```

In this workload, this change reduces latency by 23% (3.10212s -> 2.38347s).

PiperOrigin-RevId: 544056038
2023-06-28 07:53:49 -07:00
Ayush RanjanandgVisor bot 2fce8bfba0 Only populate pages for tmpfs-backed MemoryFile in tmpfs Allocate().
Pre-populating pages on fallocate(2) when the MemoryFile is backed by a disk
is wasteful.

It is currently wasteful because the mlock+munlock mechanism used in
pgalloc.tryPopulate() to populate pages does not work with some disk-backed
filesystems like ext4. We end up faulting read-only pages, which need to
be re-faulted on write anyways.

Later we will update pgalloc.tryPopulate() to use madvice(MADV_POPULATE_WRITE).
It wouldn't work in that case too because even though it does fault writable
pages, they are written back to disk before the application can write to them.
And so they will be re-faulted on write. This would actually worsen the
performance because now we have the extra cost of writeback.

Hence, avoid populating pages for disk-backed MemoryFile on fallocate(2).
Instead we just commit the pages to satisfy fallocate(2) semantics.
When the application writes to these file offsets, the pages will fault
normally.

To allow this distinction between "committing" and "populating" pages in
addition to allocating them, the pgalloc API has been extended by introducing
`pgalloc.AllocationMode` enum. It allows the caller to select if pages should
be only-allocated, allocated+committed or allocated+committed+populated.

Here are the benchmarking results:
```
goos: linux
goarch: amd64
cpu: Intel(R) Xeon(R) CPU @ 2.80GHz
                                                        │ /tmp/benchout.runsc │      /tmp/benchout.runsc-cl1       │
                                                        │       sec/op        │    sec/op     vs base              │
BuildABSL/page_cache.clean/filesystem.bindfs-4                   100.73 ± 12%    99.72 ± 58%       ~ (p=0.805 n=7)
BuildGRPC/page_cache.clean/filesystem.bindfs-4                    703.5 ±  1%    690.9 ±  1%  -1.78% (p=0.004 n=7)
Fio/operation.write/blockSize.4K/filesystem.bindfs-4             2.008m ±  0%   2.009m ± 25%       ~ (p=0.805 n=7)
Fio/operation.write/blockSize.64K/filesystem.bindfs-4            1.507m ±  0%   1.507m ±  0%       ~ (p=0.318 n=7)
Fio/operation.write/blockSize.1024K/filesystem.bindfs-4          1.507m ±  0%   1.507m ±  0%       ~ (p=1.000 n=7)
Fio/operation.write/blockSize.4K/filesystem.tmpfs-4              2.510m ±  0%   2.509m ±  0%       ~ (p=0.805 n=7)
Fio/operation.write/blockSize.64K/filesystem.tmpfs-4             2.009m ±  0%   2.009m ±  0%       ~ (p=0.805 n=7)
Fio/operation.write/blockSize.1024K/filesystem.tmpfs-4           2.009m ±  0%   2.008m ±  0%       ~ (p=0.097 n=7)
Fio/operation.write/blockSize.4K/filesystem.rootfs-4             3.010m ± 17%   2.510m ±  0%       ~ (p=0.053 n=7)
Fio/operation.write/blockSize.64K/filesystem.rootfs-4            2.009m ± 25%   2.008m ±  0%       ~ (p=1.000 n=7)
Fio/operation.write/blockSize.1024K/filesystem.rootfs-4          2.009m ±  0%   2.008m ±  0%       ~ (p=0.259 n=7)
RubySpecTest/page_cache.clean/filesystem.bindfs-4                 63.15 ±  2%    61.65 ±  2%  -2.38% (p=0.011 n=7)
geomean                                                          34.15m         33.48m        -1.94%

                                                        │    /tmp/benchout.runsc     │              /tmp/benchout.runsc-cl1              │
                                                        │ bandwidth.bytes_per_second │ bandwidth.bytes_per_second  vs base               │
Fio/operation.write/blockSize.4K/filesystem.bindfs-4                    939.6M ±  2%                 944.7M ±  4%        ~ (p=0.737 n=7)
Fio/operation.write/blockSize.64K/filesystem.bindfs-4                   2.937G ±  5%                 2.873G ±  2%        ~ (p=0.216 n=7)
Fio/operation.write/blockSize.1024K/filesystem.bindfs-4                 3.139G ±  7%                 3.112G ±  6%        ~ (p=0.879 n=7)
Fio/operation.write/blockSize.4K/filesystem.tmpfs-4                     1.355G ±  3%                 1.382G ±  5%        ~ (p=0.090 n=7)
Fio/operation.write/blockSize.64K/filesystem.tmpfs-4                    3.913G ± 10%                 3.785G ± 10%        ~ (p=0.779 n=7)
Fio/operation.write/blockSize.1024K/filesystem.tmpfs-4                  3.692G ± 11%                 3.855G ±  6%        ~ (p=1.000 n=7)
Fio/operation.write/blockSize.4K/filesystem.rootfs-4                    788.4M ±  2%                 628.6M ±  1%  -20.26% (p=0.001 n=7)
Fio/operation.write/blockSize.64K/filesystem.rootfs-4                  1346.1M ±  5%                 940.4M ±  6%  -30.13% (p=0.001 n=7)
Fio/operation.write/blockSize.1024K/filesystem.rootfs-4                 1.646G ±  5%                 1.173G ±  3%  -28.75% (p=0.001 n=7)
geomean                                                                 1.882G                       1.699G         -9.72%

                                                        │  /tmp/benchout.runsc  │           /tmp/benchout.runsc-cl1            │
                                                        │ io_ops.ops_per_second │ io_ops.ops_per_second  vs base               │
Fio/operation.write/blockSize.4K/filesystem.bindfs-4               229.4k ±  2%            230.6k ±  4%        ~ (p=0.737 n=7)
Fio/operation.write/blockSize.64K/filesystem.bindfs-4              44.82k ±  5%            43.84k ±  2%        ~ (p=0.216 n=7)
Fio/operation.write/blockSize.1024K/filesystem.bindfs-4            2.994k ±  7%            2.967k ±  6%        ~ (p=0.879 n=7)
Fio/operation.write/blockSize.4K/filesystem.tmpfs-4                330.7k ±  3%            337.3k ±  5%        ~ (p=0.090 n=7)
Fio/operation.write/blockSize.64K/filesystem.tmpfs-4               59.70k ± 10%            57.76k ± 10%        ~ (p=0.779 n=7)
Fio/operation.write/blockSize.1024K/filesystem.tmpfs-4             3.521k ± 11%            3.676k ±  6%        ~ (p=1.000 n=7)
Fio/operation.write/blockSize.4K/filesystem.rootfs-4               192.5k ±  2%            153.5k ±  1%  -20.26% (p=0.001 n=7)
Fio/operation.write/blockSize.64K/filesystem.rootfs-4              20.54k ±  5%            14.35k ±  6%  -30.13% (p=0.001 n=7)
Fio/operation.write/blockSize.1024K/filesystem.rootfs-4            1.570k ±  5%            1.119k ±  3%  -28.73% (p=0.001 n=7)
geomean                                                            28.72k                  25.93k         -9.72%

                                                  │ /tmp/benchout.runsc │     /tmp/benchout.runsc-cl1      │
                                                  │      load.sec       │  load.sec   vs base              │
RubySpecTest/page_cache.clean/filesystem.bindfs-4            9.430 ± 7%   9.150 ± 7%  -2.97% (p=0.026 n=7)
```

The broader filesystem benchmarks (ABSL, gRPC, Ruby) all improved with this
change. Improvement is ~2-3% reduction in absolute time to run these
workloads. However, the FIO microbenchmark is a little more complicated.
The latency metric shows improvement (for blockSize=4K & filesystem=rootfs).
It is stable for other blockSize values. But FIO bandwidth seems to have
regressed. We think this is an accounting gimmick. We suspect that sec/op
includes the app fallocate() and bandwidth does not. Since this change moves
ext4_mpage_readpages() from app fallocate() to the page faults that occur
during app write(), bandwidth appears to have regressed.

Given that the broader filesystem benchmarks look good and that this is more
consistent with Linux (app fallocate() now only commits disk space rather than
also materializing memory pages), this change should be an improvement.

PiperOrigin-RevId: 542331863
2023-06-21 12:36:14 -07:00
Ayush RanjanandgVisor bot 8a167a5f72 Prepare to make root overlay the default.
We plan on making --overlay2=root:self the default for runsc. That will be a
risky change which might need rollbacks. This change is in preparation for
that. We manually set --overlay2=none in places where we don't want the
overlay configuration to impact. This change should be a noop. The intention
for this change is to make the risky change very small and limited to just
flipping a flag.

PiperOrigin-RevId: 513930702
2023-03-04 00:46:47 -08:00
Adin ScannellandgVisor bot 2911d8c3b8 Add default_applicable_licenses rules to packages.
PiperOrigin-RevId: 513581243
2023-03-02 10:50:04 -08:00
Jamie LiuandgVisor bot b8ce9356c3 Make hostarch.Page/HugePage/CacheLine* functions generic and canonical.
Also add the missing HugePage equivalents of Page functions.

The fix for golang/go#56280, which first appears in Go 1.20, is needed to
prevent this CL from regressing performance:

Before fix:
```
TEXT gvisor/pkg/sentry/mm/mm.(*MemoryManager).SetNumaPolicy(SB) gvisor/pkg/sentry/mm/syscalls.go
  ...
  addr.go:75            0x7f000054e35e          488d053bc2b100          LEAQ gvisor/pkg/hostarch/hostarch..dict.IsPageAligned[gvisor/pkg/hostarch/hostarch.Addr](SB), AX
  addr.go:75            0x7f000054e365          e8961cc4ff              CALL gvisor/pkg/hostarch/hostarch.IsPageAligned[go.shape.uintptr](SB)
```

After fix:
```
TEXT gvisor/pkg/sentry/mm/mm.(*MemoryManager).SetNumaPolicy(SB) gvisor/pkg/sentry/mm/syscalls.go
  ...
  addr.go:75            0x7f00004da61a          90                      NOPL
  addr.go:75            0x7f00004da61b          0f1f440000              NOPL 0(AX)(AX*1)
  sizes_util.go:57      0x7f00004da620          48f7c3ff0f0000          TESTQ $0xfff, BX
  syscalls.go:1021      0x7f00004da627          0f855f010000            JNE 0x7f00004da78c
```

PiperOrigin-RevId: 507608080
2023-02-06 16:09:19 -08:00
Ayush RanjanandgVisor bot 1e2bc64038 Create separate overlay filestore for subcontainers.
Earlier we were using the same filestore for all containers in the
sandbox. This was based on the assumption that all containers in a
k8s pod have the same runsc configuration. We get rid of our
dependence on that assumption. Subcontainers can independently use
overlay configurations.

This is needed because in k8s, different ephemeral storage limits can
be enforced on containers in the same pod. This behavior is required
to get limit enforcement to work correctly on a per container basis.

This change also moves the ownership of overlay filestore to tmpfs.
Now that each container has its own overlay-backing MemoryFile, it will
be much harder to manage lifecycle of these MemoryFiles as containers
are created and destroyed.

Instead, move ownership to tmpfs mount. When the overlay upper layer tmpfs
is unmounted, it will destroy the MemoryFile.

We also extract out IMA work around logic from NewMemoryFile() and perform
it outside the sandbox. This is done because now tmpfs is creating the
MemoryFile. By then the sandbox is already running with seccomp filters
installed that do not allow certain mmap(2) calls that the said work around
uses. We hit the same issue for subcontainers. When they are started,
the sandbox is already running with seccomp filters. Added a new
MemoryFileOpts field for this which defaults to old behavior.

PiperOrigin-RevId: 505892085
2023-01-31 09:42:00 -08:00
Ayush RanjanandgVisor bot 73667dc455 Handle terminal symlinks correctly in OpenAt() implementations.
All filesystems except tmpfs had the same bug regarding handling symlinks
in the terminal path component. When they tried to open(2) such a symlink
file, these implementations called into stepLocked() with
Then these implementations also called vfs.HandleSymlink() which would
also advance rp (when rp.Done() was already true).

This was tripping the invariants checks in sentry/vfs/debug.go.

PiperOrigin-RevId: 504098263
2023-01-23 15:35:15 -08:00
Ayush RanjanandgVisor bot 34aad4cb57 Make directory open flags checks consistent in tmpfs and kernfs.
Overlayfs and goferfs seem to be up to date.
Updated syscall tests.

PiperOrigin-RevId: 503521750
2023-01-20 14:22:38 -08:00
Ayush RanjanandgVisor bot 700a1c8569 Fix tmpfs accounting bug in Allocate().
FileRangeSet.Fill() can fill in gaps partially and fail. Earlier, we were
over-correcting on failure by unaccounting everything that was reserved for the
operation. But that can lead to accounting errors in case of partial Fill()
failures. In such a case, when the file is later deleted, we would again
unaccount all the pages that were allocated via the partial Fill(), which will
causes accounting to go negative and sentry will panic.

Also did some refactoring for clearer code.

PiperOrigin-RevId: 493935801
2022-12-08 10:41:25 -08:00
Ayush RanjanandgVisor bot d1c4ffbdd8 Set default tmpfs size option to the large value advertised in statfs(2).
We were already returning this large size via statfs(2). But there was no
accounting being done against it. Even after page allocations, there was no
change in values returned by statfs(2) about number of pages free.

PHP runtime test ext/standard/tests/file/disk_free_space_basic.phpt and Python
runtime test test_shutil tests for this. This change is required to get these
tests to pass with overlayfs.

PiperOrigin-RevId: 493903408
2022-12-08 08:35:57 -08:00
Ayush RanjanandgVisor bot 170981d796 Make tmpfs size accounting lock-free by using atomics.
This is likely to be a speed-up in the common case when contention between
threads is low. In such situations, generally atomic operations are faster.

PiperOrigin-RevId: 493731992
2022-12-07 15:58:10 -08:00
Lucas ManningandgVisor bot 253bafa83c Display the proper root path in mountinfo.
PiperOrigin-RevId: 492323370
2022-12-01 16:05:00 -08:00
Ayush RanjanandgVisor bot d937df6218 Replace pkg/refs with pkg/refsvfs2.
All VFS1 only bits have been deleted.

Updates #1624

PiperOrigin-RevId: 492273183
2022-12-01 12:46:11 -08:00
329def4002 Add overlay2 flag in runsc.
--overlay2 flag supersedes --overlay flag. It allows more granular
configuration for overlayfs in runsc. It does so in two ways:

1. Allows to apply overlay on all mounts or only the root mount.
   --overlay applies overlay to all mounts.
2. Allows to specify if overlay's upper layer should be backed by
   container memory or disk. --overlay always used container memory.

Allowing tmpfs to be backed by a file on disk prevents the container
memory from bloating up. Note that the tmpfs filesystem tree will
still be stored in sentry memory.

Using overlay on the root filesystem, helps avoid expensive
communication with the gofer process. The root filesystem of the
container is not preserved across container lifecycle. So we don't
need to keep updating the host filesystem, which will anyways be
destroyed once the container is destroyed. It is wasted effort.
Instead we keep all the changes to the root filesystem in tmpfs which
is directly accessible by the sentry.

The host file is created as an unnamed file using O_TMPFILE. Support
has been added for sub-containers too. Save/restore support is
still lacking.

Co-authored-by: Andrei Vagin <[email protected]>
PiperOrigin-RevId: 491988485
2022-11-30 12:29:14 -08:00
Ayush RanjanandgVisor bot 1003ae8e93 Delete VFS1 completely.
- Delete pkg/sentry/fs/*.
- Move pkg/sentry/fs/fsutil out of VFS1 directory and remove VFS1 components.
- Remove remaining unused references to VFS1 from remaining codebase.
- Rename/refactor code to avoid even referencing VFS2, unless necessary.
- Rewrite VFS1-only tests to VFS2.

Updates #1624

PiperOrigin-RevId: 490064269
2022-11-21 13:57:52 -08:00
Ayush RanjanandgVisor bot ee523cfbc7 Delete VFS1 filesystem implementations.
Updates #1624

PiperOrigin-RevId: 488986080
2022-11-16 11:05:10 -08:00
Andrei VaginandgVisor bot 97c5781cb2 AddSeals has to take the write lock to modify seals
Reported-by: [email protected]
Reported-by: [email protected]
PiperOrigin-RevId: 488764524
2022-11-15 14:49:20 -08:00
Kevin KrakauerandgVisor bot 3a76efe831 convert uses of interface{} to any
Done via:
  find . -name "*.go" | xargs sed -i -E 's/interface\{\}/any/g'

PiperOrigin-RevId: 487033228
2022-11-08 13:14:06 -08:00
Rahat MahmoodandgVisor bot 54778fdd22 tmpfs: Handle symlink resolution at root
Previously we weren't resolving any links when walking a single
component path (i.e. the root of the FS was a symlink).

PiperOrigin-RevId: 485618692
2022-11-02 09:45:48 -07:00
Jamie LiuandgVisor bot e7d7eef71c Add populate parameter to pgalloc.MemoryFile.AllocateAndFill().
This targets the issue raised by #7977, but uses mlock(2) to force
mm_populate() of a precise address range, and also enables the use of the same
feature in other places where it makes sense (when breaking copy-on-write, and
when filling gofer page caches).

On the benchmark given in #7977, using KVM platform:
```
docker exec -ti $id fio --output-format=json --ioengine=sync --name=write --size=10G --blocksize=4K --filename=/data/data.test --iodepth=4 --rw=write
```

Before this CL:
```
      "write" : {
        "io_bytes" : 10737418240,
        "io_kbytes" : 10485760,
        "bw" : 889981,
        "iops" : 222495.331862,
        "runtime" : 11782,
        "total_ios" : 2621440,
        "short_ios" : 0,
        "drop_ios" : 0,
        "slat_ns" : {
          "min" : 0,
          "max" : 0,
          "mean" : 0.000000,
          "stddev" : 0.000000
        },
        "clat_ns" : {
          "min" : 3678,
          "max" : 183826,
          "mean" : 4253.962175,
          "stddev" : 1003.818651,
          "percentile" : {
            "1.000000" : 3792,
            "5.000000" : 3824,
            "10.000000" : 3856,
            "20.000000" : 3888,
            "30.000000" : 3920,
            "40.000000" : 3952,
            "50.000000" : 3984,
            "60.000000" : 4016,
            "70.000000" : 4080,
            "80.000000" : 4576,
            "90.000000" : 5024,
            "95.000000" : 5344,
            "99.000000" : 6752,
            "99.500000" : 7840,
            "99.900000" : 16512,
            "99.950000" : 19840,
            "99.990000" : 28544,
            "0.00" : 0,
            "0.00" : 0,
            "0.00" : 0
          }
        },
        "lat_ns" : {
          "min" : 3714,
          "max" : 183863,
          "mean" : 4294.114721,
          "stddev" : 1008.663442
        },
        "bw_min" : 760015,
        "bw_max" : 922560,
        "bw_agg" : 100.000000,
        "bw_mean" : 892699.913043,
        "bw_dev" : 41761.998527,
        "bw_samples" : 23,
        "iops_min" : 190003,
        "iops_max" : 230640,
        "iops_mean" : 223174.826087,
        "iops_stddev" : 10440.656065,
        "iops_samples" : 23
      },
```

After this CL:
```
      "write" : {
        "io_bytes" : 10737418240,
        "io_kbytes" : 10485760,
        "bw" : 1354049,
        "iops" : 338512.396694,
        "runtime" : 7744,
        "total_ios" : 2621440,
        "short_ios" : 0,
        "drop_ios" : 0,
        "slat_ns" : {
          "min" : 0,
          "max" : 0,
          "mean" : 0.000000,
          "stddev" : 0.000000
        },
        "clat_ns" : {
          "min" : 2471,
          "max" : 125666,
          "mean" : 2732.605543,
          "stddev" : 702.327985,
          "percentile" : {
            "1.000000" : 2512,
            "5.000000" : 2544,
            "10.000000" : 2576,
            "20.000000" : 2576,
            "30.000000" : 2576,
            "40.000000" : 2608,
            "50.000000" : 2608,
            "60.000000" : 2640,
            "70.000000" : 2672,
            "80.000000" : 2768,
            "90.000000" : 3056,
            "95.000000" : 3088,
            "99.000000" : 3888,
            "99.500000" : 4576,
            "99.900000" : 13504,
            "99.950000" : 18048,
            "99.990000" : 25984,
            "0.00" : 0,
            "0.00" : 0,
            "0.00" : 0
          }
        },
        "lat_ns" : {
          "min" : 2514,
          "max" : 125707,
          "mean" : 2778.332321,
          "stddev" : 708.893090
        },
        "bw_min" : 1173397,
        "bw_max" : 1402299,
        "bw_agg" : 99.894475,
        "bw_mean" : 1352620.133333,
        "bw_dev" : 74302.901710,
        "bw_samples" : 15,
        "iops_min" : 293349,
        "iops_max" : 350574,
        "iops_mean" : 338154.866667,
        "iops_stddev" : 18575.487826,
        "iops_samples" : 15
      },
```

Simple process creation benchmark to exercise copy-on-write breaking
`docker run --runtime=runsc debian /bin/bash -c 'time for i in {1..10000}; do /bin/true; done;'`
using KVM platform:

Before this CL:
```
real	1m5.794s
user	0m7.680s
sys	1m37.050s
```

After this CL:
```
real	0m57.837s
user	0m7.310s
sys	1m30.500s
```

PiperOrigin-RevId: 479461293
2022-10-06 18:22:39 -07:00
Shambhavi SrivastavaandgVisor bot 41afc326a7 Accounting against tmpfs size only when page is allocated.
Implementing memory allocation taking into consideration the fact
that the page allocation does not changes whenever the size of a
file changes. For instance, ftruncate may increase the size of a
file without actually allocating that size. So it changes when the
pages are actually allocated.

Also making amends to allow for partial allocations. For instance,
a write, mmap can allocate pages as long as the size option
is not exhausted.

Fixes #7827

PiperOrigin-RevId: 470426017
2022-08-27 02:42:05 -07:00
Ayush RanjanandgVisor bot 69cae607a0 Make tmpfs actually allocate pages on fallocate(2).
This is consistent with Linux. Earlier, the allocate() handler in tmpfs was
simply calling into truncate(). However fallocate(2) must also reserve the disk
space for the range specified by offset and length. This is because, after a
successful fallocate(2) call, writes in that range must not fail due to lack of
disk space.

Updates #7827

PiperOrigin-RevId: 465203457
2022-08-03 19:08:40 -07:00
Ayush RanjanandgVisor bot e40196a811 Add name length checks to renameat(2).
In Linux, the callers of fs/namei.c:do_renameat2() do this check
by calling fs/namei.c:getname().

This is important of consistency with Linux. Also, without this
we can see panics in overlayfs rename if new file's name is too
long. Because upperlayer RenameAt operation would succeed but
SetXattr operation will fail leading to an inconsistent state.

PiperOrigin-RevId: 464173793
2022-07-29 15:48:41 -07:00
Ayush RanjanandgVisor bot a9181cfe67 Move all InotifyWithParent() callers in FDImpl to VFS layer.
`vfs.FileDescription` in general handles all inotify work. Only
SetStat, SetXattr, RemoveXattr and Iterdirents require FDImpls to
handle the Inotify work. This leads to repeated code. And for
kernfs users, there are too many FD implementors. So it becomes
difficult plumbing Inotify consistenly through all FDImpls.

So make it consistent that all Inotify work is handled by
`vfs.FileDescription`, not FDImpls.

PiperOrigin-RevId: 461751122
2022-07-18 17:13:41 -07:00
Shambhavi SrivastavaandgVisor bot 737814166b Adding tests to handle empty size & uint64 overflow in parsing size.
PiperOrigin-RevId: 449876821
2022-05-19 18:26:46 -07:00
Andrei VaginandgVisor bot b2c05f13ec tmpfs: use lockdep mutexes
PiperOrigin-RevId: 449803147
2022-05-19 12:07:37 -07:00
Ayush RanjanandgVisor bot 2026e1f078 Reformat codebase.
PiperOrigin-RevId: 449358041
2022-05-17 17:48:35 -07:00
Shambhavi SrivastavaandgVisor bot bdede951be Tmpfs with size option enabled bug fix.
Adding test case to check empty size parsing in Linux.
If a value is not passed with size the mount(2) should return EINVAL.
Handling empty size parsing in gVisor.

PiperOrigin-RevId: 448132149
2022-05-11 18:36:17 -07:00
Ayush RanjanandgVisor bot 4e74b47047 Update comments about tmpfs size.
- Removed old TODO from VFS1. We will not support this option on VFS1.
- Enhanced comments about how tmpfs pages are accounted for by write(2)s.

PiperOrigin-RevId: 446072121
2022-05-02 17:39:26 -07:00
Shambhavi SrivastavaandgVisor bot 6ab983fb01 Added integration tests for mounting tmpfs with size option enabled.
PiperOrigin-RevId: 444705540
2022-04-26 17:00:56 -07:00
Shambhavi SrivastavaandgVisor bot ed18dd3052 Fixing memory allocations for tmpfs size.
Fixing partial write memory allocation.
Accounting for memory allocation in hard links.
Adding test cases to check for hard link memory allocation for the same.

PiperOrigin-RevId: 443742013
2022-04-22 13:19:14 -07:00
Kevin KrakauerandgVisor bot 3e7fcef1df switch fsimpl/ from sync/atomic to atomicbitops for 32 bit values
PiperOrigin-RevId: 443535714
2022-04-21 18:32:04 -07:00
Kevin KrakauerandgVisor bot acee72c261 fix nogo errors from checker SA4006
PiperOrigin-RevId: 443448448
2022-04-21 12:32:40 -07:00
Shambhavi SrivastavaandgVisor bot 1fce5624fd Enable tmpfs size mount option.
When tmpfs is mounted with `size` option it imposes a limit for
the tmpfs mount. The total size of all files in that mount must not exceed this
limit. See tmpfs(5) for more details. When this option was not supported and
specifying it caused mount(2) to return EINVAL instead of ENOSPC.

In Linux, tmpfs charges symlinks, regular files and directories against this
`size` limit. All accounting is done on the granularity of page size. The `size`
limit and all individual file sizes are rounded up to number of pages while
performing calculations. gVisor has aimed to replicate the same behavior.

One difference from Linux is that if the `size` option is not specified, then
Linux uses 50% of physical RAM as the `size`. We don't replicate this behavior
in gVisor as the actual size of host physical RAM should not be exposed to
application. In gVisor, if `size` option is not specified, then no limit is
imposed. This is consistent with the behavior before this change.

Added syscall tests for regular files and symlink.

PiperOrigin-RevId: 442686814
2022-04-18 18:50:24 -07:00
Kevin KrakauerandgVisor bot b8dd528b97 prohibit direct use of sync/atomic (u)int64 functions
All atomic 64 bit ints are changed to atomicbitops.(Ui|I)nt64. A nogo checker
enforces that sync/atomic 64 bit functions are not called.

For reviewers: the interesting changes are in the atomicbitops and checkaligned
packages.

Why do this?
- It is very easy to accidentally use atomic values without sync/atomic funcs.
- We have checkatomics, but this is optional and is forgotten in several places.
  - Using a type+checker to enforce this seems less error prone and simpler.
- We get NoCopy protection.
- Use of 64 bit atomics can break 32 bit builds. We have types to handle this
  without any runtime cost, so we might as well use them.

PiperOrigin-RevId: 440473398
2022-04-08 16:06:26 -07:00
Kevin KrakauerandgVisor bot 87df090030 use atomicbitops, not sync/atomic, in refsvfs2
Since refsvfs2 are used in netstack, we should use atomicbitops to avoid
breaking 32-bit builds.

On 64-bit builds there is no performance difference.

PiperOrigin-RevId: 439687980
2022-04-05 15:16:01 -07:00
Jamie LiuandgVisor bot 6f83b4957a Check mount writability in VFS2 [f]access[at][2] implementations.
Linux exempts special files (sockets, named pipes, and character/block device
special files) from mount writability checks in both open
(fs/open.c:do_dentry_open()) and access (fs/open.c:do_faccessat()) paths. We
don't currently do so in open (in VFS1 or VFS2) or access (in VFS1), so we
don't do so in VFS2 access either.

PiperOrigin-RevId: 439398002
2022-04-04 13:33:02 -07:00
Adin ScannellandgVisor bot 140b4e80f6 Align Context API with kernel internals.
This change adapts the existing context to use more suitable non-channel-based
methods. This is a requisite for migrating the kernel internals to a
sleeper-based notification mechanism.

The last uses of amutex outside those migrated as part of this change were
dropped in a previous change. Since amutex depends on the channel-based
implementation, this package is also deleted as part of this change.

PiperOrigin-RevId: 415189675
2021-12-08 23:51:37 -08:00
Ayush RanjanandgVisor bot b433c95abc Add support for flexible filename limits for tmpfs.
PiperOrigin-RevId: 413038601
2021-11-29 19:27:46 -08:00
Adin ScannellandgVisor bot 02c150bfd7 Drop final amutex uses.
These amutex lock uses are limited to vfs1 and provide questionable utility.
They protect only offset access, and not blocking operations. In order to
completely remove amutex, drop these uses. The amutex package will be removed
in a subsequent commit, which migrates other (less questionable) uses to a new
Context API.

PiperOrigin-RevId: 409716979
2021-11-13 15:07:19 -08:00
Fabricio VoznikaandgVisor bot 8760c61b0f Reduce host VMA fragmentation
MemoryFile allocated offsets from the top down. This can generate
fragmentation when allocations happen in the opposite direction.

Given that applications control the order of allocations, use simple
heuristics to track the last faulted address to determine whether memory
file should be allocated from the top-down or bottom-up.

Here is the number of PMAs used across all processes in some common
workloads:

Workload | Before  | After   | Diff
---------|---------|---------|------
rustc    | 135,269 |   1,090 | 0.8%
mysql    |  16,466 |   4,576 |  28%
nginx    |   1,738 |   1,609 |  93%
jenkins  |  13,830 |   6,380 |  46%
redis    |     334 |     308 |  92%
absl     | 297,419 | 292,121 |  98%

Here is the direction of PMA allocations:

Workload | BottomUp  | TopDown |
---------|-----------|---------|
mysql    |  13,849   |   1,990 |
nginx    |     783   |     628 |
jenkins  |  13,922   |   2,333 |
redis    |      88   |     109 |
absl     | 212,403   |  44,019 |

Tests were done on Intel x64.

PiperOrigin-RevId: 409014690
2021-11-10 17:12:42 -08:00
Fabricio VoznikaandgVisor bot c25d4f3f85 Report ramdiskfs usage correctly
Updates #1035

PiperOrigin-RevId: 404072231
2021-10-18 15:09:22 -07:00