Re: [PATCH v14 2/7] mm: add VM_DROPPABLE for designating always lazily freeable mappings

Andy Lutomirski
2023-01-03T18:36:43Z

On Tue, Jan 3, 2023 at 2:50 AM Ingo Molnar <mingo@kernel.org> wrote:
>
>
> * Jason A. Donenfeld <Jason@zx2c4.com> wrote:
>
> > The vDSO getrandom() implementation works with a buffer allocated with a
> > new system call that has certain requirements:
> >
> > - It shouldn't be written to core dumps.
> > * Easy: VM_DONTDUMP.
> > - It should be zeroed on fork.
> > * Easy: VM_WIPEONFORK.

I have a rather different suggestion: make a special mapping. Jason,
you're trying to shoehorn all kinds of bizarre behavior into the core
mm, and none of that seems to me to belong to the core mm. Instead,
have an actual special mapping with callbacks that does the right
thing. No fancy VM flags.

Memory pressure: have it free and unmap it self. Gets accessed again?
->fault can handle it.

Want to mlock it? No, don't do that -- that's absurd. Just arrange
so that, if it gets evicted, it's not written out anywhere. And when
it gets faulted back in it does the right thing -- see above.

Zero on fork? I'm sure that's manageable with a special mapping. If
not, you can add a new vm operation or similar to make it work. (Kind
of like how we extended special mappings to get mremap right a couple
years go.) But maybe you don't want to *zero* it on fork and you want
to do something more intelligent. Fine -- you control ->fault!

> >
> > - It shouldn't be written to swap.
> > * Uh-oh: mlock is rlimited.
> > * Uh-oh: mlock isn't inherited by forks.

No mlock, no problems.

> >
> > - It shouldn't reserve actual memory, but it also shouldn't crash when
> > page faulting in memory if none is available
> > * Uh-oh: MAP_NORESERVE respects vm.overcommit_memory=2.
> > * Uh-oh: VM_NORESERVE means segfaults.

->fault can do whatever you want.

And there is no shortage of user memory that *must* be made available
on fault in order to resume the faulting process. ->fault can handle
this.

> >
> > It turns out that the vDSO getrandom() function has three really nice
> > characteristics that we can exploit to solve this problem:
> >
> > 1) Due to being wiped during fork(), the vDSO code is already robust to
> > having the contents of the pages it reads zeroed out midway through
> > the function's execution.
> >
> > 2) In the absolute worst case of whatever contingency we're coding for,
> > we have the option to fallback to the getrandom() syscall, and
> > everything is fine.
> >
> > 3) The buffers the function uses are only ever useful for a maximum of
> > 60 seconds -- a sort of cache, rather than a long term allocation.
> >
> > These characteristics mean that we can introduce VM_DROPPABLE, which
> > has the following semantics:

No need for another vm flag.

> >
> > a) It never is written out to swap.

No need to complicate the swap logic for this.

> > b) Under memory pressure, mm can just drop the pages (so that they're
> > zero when read back again).

Or ->fault could even repopulate it without needing to ever read zeros.

> > c) If there's not enough memory to service a page fault, it's not fatal,
> > and no signal is sent. Instead, writes are simply lost.

This just seems massively overcomplicated to me. If there isn't
enough memory to fault in a page of code, we don't have some magic
instruction emulator in the kernel. We either OOM or we wait for
memory to show up.

> > d) It is inherited by fork.

If you have a special mapping and you fork, it doesn't magically turn
into normal memory.

> > e) It doesn't count against the mlock budget, since nothing is locked.

Special mapping -> no mlock.

> >
> > This is fairly simple to implement, with the one snag that we have to
> > use 64-bit VM_* flags, but this shouldn't be a problem, since the only
> > consumers will probably be 64-bit anyway.
> >
> > This way, allocations used by vDSO getrandom() can use:
> >
> > VM_DROPPABLE | VM_DONTDUMP | VM_WIPEONFORK | VM_NORESERVE
> >
> > And there will be no problem with OOMing, crashing on overcommitment,
> > using memory when not in use, not wiping on fork(), coredumps, or
> > writing out to swap.
> >
> > At the moment, rather than skipping writes on OOM, the fault handler
> > just returns to userspace, and the instruction is retried. This isn't
> > terrible, but it's not quite what is intended. The actual instruction
> > skipping has to be implemented arch-by-arch, but so does this whole
> > vDSO series, so that's fine. The following commit addresses it for x86.

I really dislike this. I'm with Ingo.