From: Mike Rapoport <rppt@kernel.org> Date: 2021-01-21 12:46:40
From: Mike Rapoport <redacted>
Hi,
@Andrew, this is based on v5.11-rc4-mmots-2021-01-19-13-54 with secretmem
patches dropped from there, I can rebase whatever way you prefer.
This is an implementation of "secret" mappings backed by a file descriptor.
The file descriptor backing secret memory mappings is created using a
dedicated memfd_secret system call The desired protection mode for the
memory is configured using flags parameter of the system call. The mmap()
of the file descriptor created with memfd_secret() will create a "secret"
memory mapping. The pages in that mapping will be marked as not present in
the direct map and will be present only in the page table of the owning mm.
Although normally Linux userspace mappings are protected from other users,
such secret mappings are useful for environments where a hostile tenant is
trying to trick the kernel into giving them access to other tenants
mappings.
Additionally, in the future the secret mappings may be used as a mean to
protect guest memory in a virtual machine host.
For demonstration of secret memory usage we've created a userspace library
https://git.kernel.org/pub/scm/linux/kernel/git/jejb/secret-memory-preloader.git
that does two things: the first is act as a preloader for openssl to
redirect all the OPENSSL_malloc calls to secret memory meaning any secret
keys get automatically protected this way and the other thing it does is
expose the API to the user who needs it. We anticipate that a lot of the
use cases would be like the openssl one: many toolkits that deal with
secret keys already have special handling for the memory to try to give
them greater protection, so this would simply be pluggable into the
toolkits without any need for user application modification.
Hiding secret memory mappings behind an anonymous file allows (ab)use of
the page cache for tracking pages allocated for the "secret" mappings as
well as using address_space_operations for e.g. page migration callbacks.
The anonymous file may be also used implicitly, like hugetlb files, to
implement mmap(MAP_SECRET) and use the secret memory areas with "native" mm
ABIs in the future.
To limit fragmentation of the direct map to splitting only PUD-size pages,
I've added an amortizing cache of PMD-size pages to each file descriptor
that is used as an allocation pool for the secret memory areas.
As the memory allocated by secretmem becomes unmovable, we use CMA to back
large page caches so that page allocator won't be surprised by failing attempt
to migrate these pages.
v16:
* Fix memory leak intorduced in v15
* Clean the data left from previous page user before handing the page to
the userspace
v15: https://lore.kernel.org/lkml/20210120180612.1058-1-rppt@kernel.org
* Add riscv/Kconfig update to disable set_memory operations for nommu
builds (patch 3)
* Update the code around add_to_page_cache() per Matthew's comments
(patches 6,7)
* Add fixups for build/checkpatch errors discovered by CI systems
v14: https://lore.kernel.org/lkml/20201203062949.5484-1-rppt@kernel.org
* Finally s/mod_node_page_state/mod_lruvec_page_state/
v13: https://lore.kernel.org/lkml/20201201074559.27742-1-rppt@kernel.org
* Added Reviewed-by, thanks Catalin and David
* s/mod_node_page_state/mod_lruvec_page_state/ as Shakeel suggested
v12: https://lore.kernel.org/lkml/20201125092208.12544-1-rppt@kernel.org
* Add detection of whether set_direct_map has actual effect on arm64 and bail
out of CMA allocation for secretmem and the memfd_secret() syscall if pages
would not be removed from the direct map
Older history:
v11: https://lore.kernel.org/lkml/20201124092556.12009-1-rppt@kernel.org
v10: https://lore.kernel.org/lkml/20201123095432.5860-1-rppt@kernel.org
v9: https://lore.kernel.org/lkml/20201117162932.13649-1-rppt@kernel.org
v8: https://lore.kernel.org/lkml/20201110151444.20662-1-rppt@kernel.org
v7: https://lore.kernel.org/lkml/20201026083752.13267-1-rppt@kernel.org
v6: https://lore.kernel.org/lkml/20200924132904.1391-1-rppt@kernel.org
v5: https://lore.kernel.org/lkml/20200916073539.3552-1-rppt@kernel.org
v4: https://lore.kernel.org/lkml/20200818141554.13945-1-rppt@kernel.org
v3: https://lore.kernel.org/lkml/20200804095035.18778-1-rppt@kernel.org
v2: https://lore.kernel.org/lkml/20200727162935.31714-1-rppt@kernel.org
v1: https://lore.kernel.org/lkml/20200720092435.17469-1-rppt@kernel.org
Mike Rapoport (11):
mm: add definition of PMD_PAGE_ORDER
mmap: make mlock_future_check() global
riscv/Kconfig: make direct map manipulation options depend on MMU
set_memory: allow set_direct_map_*_noflush() for multiple pages
set_memory: allow querying whether set_direct_map_*() is actually enabled
mm: introduce memfd_secret system call to create "secret" memory areas
secretmem: use PMD-size pages to amortize direct map fragmentation
secretmem: add memcg accounting
PM: hibernate: disable when there are active secretmem users
arch, mm: wire up memfd_secret system call where relevant
secretmem: test: add basic selftest for memfd_secret(2)
arch/arm64/include/asm/Kbuild | 1 -
arch/arm64/include/asm/cacheflush.h | 6 -
arch/arm64/include/asm/set_memory.h | 17 +
arch/arm64/include/uapi/asm/unistd.h | 1 +
arch/arm64/kernel/machine_kexec.c | 1 +
arch/arm64/mm/mmu.c | 6 +-
arch/arm64/mm/pageattr.c | 23 +-
arch/riscv/Kconfig | 4 +-
arch/riscv/include/asm/set_memory.h | 4 +-
arch/riscv/include/asm/unistd.h | 1 +
arch/riscv/mm/pageattr.c | 8 +-
arch/x86/entry/syscalls/syscall_32.tbl | 1 +
arch/x86/entry/syscalls/syscall_64.tbl | 1 +
arch/x86/include/asm/set_memory.h | 4 +-
arch/x86/mm/pat/set_memory.c | 8 +-
fs/dax.c | 11 +-
include/linux/pgtable.h | 3 +
include/linux/secretmem.h | 30 ++
include/linux/set_memory.h | 16 +-
include/linux/syscalls.h | 1 +
include/uapi/asm-generic/unistd.h | 6 +-
include/uapi/linux/magic.h | 1 +
kernel/power/hibernate.c | 5 +-
kernel/power/snapshot.c | 4 +-
kernel/sys_ni.c | 2 +
mm/Kconfig | 5 +
mm/Makefile | 1 +
mm/filemap.c | 3 +-
mm/gup.c | 10 +
mm/internal.h | 3 +
mm/mmap.c | 5 +-
mm/secretmem.c | 451 ++++++++++++++++++++++
mm/vmalloc.c | 5 +-
scripts/checksyscalls.sh | 4 +
tools/testing/selftests/vm/.gitignore | 1 +
tools/testing/selftests/vm/Makefile | 3 +-
tools/testing/selftests/vm/memfd_secret.c | 296 ++++++++++++++
tools/testing/selftests/vm/run_vmtests | 17 +
38 files changed, 917 insertions(+), 52 deletions(-)
create mode 100644 arch/arm64/include/asm/set_memory.h
create mode 100644 include/linux/secretmem.h
create mode 100644 mm/secretmem.c
create mode 100644 tools/testing/selftests/vm/memfd_secret.c
--
2.28.0
From: Mike Rapoport <rppt@kernel.org> Date: 2021-01-21 12:29:08
From: Mike Rapoport <redacted>
The definition of PMD_PAGE_ORDER denoting the number of base pages in the
second-level leaf page is already used by DAX and maybe handy in other
cases as well.
Several architectures already have definition of PMD_ORDER as the size of
second level page table, so to avoid conflict with these definitions use
PMD_PAGE_ORDER name and update DAX respectively.
Signed-off-by: Mike Rapoport <redacted>
Reviewed-by: David Hildenbrand <redacted>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Christopher Lameter <redacted>
Cc: Dan Williams <redacted>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Elena Reshetova <elena.reshetova@intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Bottomley <redacted>
Cc: "Kirill A. Shutemov" <redacted>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Michael Kerrisk <redacted>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <redacted>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
Cc: Roman Gushchin <redacted>
Cc: Shakeel Butt <redacted>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Thomas Gleixner <redacted>
Cc: Tycho Andersen <redacted>
Cc: Will Deacon <will@kernel.org>
Cc: Hagen Paul Pfeifer <redacted>
Cc: Palmer Dabbelt <redacted>
---
fs/dax.c | 11 ++++-------
include/linux/pgtable.h | 3 +++
2 files changed, 7 insertions(+), 7 deletions(-)
@@ -28,6 +28,9 @@#define USER_PGTABLES_CEILING 0UL#endif+/* Number of base pages in a second level leaf page */+#define PMD_PAGE_ORDER (PMD_SHIFT - PAGE_SHIFT)+/**Apagetablepagecanbethoughtofanarraylikethis:pXd_t[PTRS_PER_PxD]*
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-25 16:56:17
On Thu 21-01-21 14:27:20, Mike Rapoport wrote:
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
What does this mean? What are the lifetime rules?
[...]
+static int secretmem_account_pages(struct page *page, gfp_t gfp, int order)
+{
+ int err;
+
+ err = memcg_kmem_charge_page(page, gfp, order);
+ if (err)
+ return err;
+
+ /*
+ * seceremem caches are unreclaimable kernel allocations, so treat
+ * them as unreclaimable slab memory for VM statistics purposes
+ */
+ mod_lruvec_page_state(page, NR_SLAB_UNRECLAIMABLE_B,
+ PAGE_SIZE << order);
A lot of memcg accounted memory is not reclaimable. Why do you abuse
SLAB counter when this is not a slab owned memory? Why do you use the
kmem accounting API when __GFP_ACCOUNT should give you the same without
this details?
--
Michal Hocko
SUSE Labs
From: Mike Rapoport <rppt@kernel.org> Date: 2021-01-25 21:41:17
On Mon, Jan 25, 2021 at 05:54:51PM +0100, Michal Hocko wrote:
On Thu 21-01-21 14:27:20, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
What does this mean?
That means that the accounting is updated when secretmem does cma_alloc()
and cma_relase().
What are the lifetime rules?
Hmm, what do you mean by lifetime rules?
[...]
quoted
+static int secretmem_account_pages(struct page *page, gfp_t gfp, int order)
+{
+ int err;
+
+ err = memcg_kmem_charge_page(page, gfp, order);
+ if (err)
+ return err;
+
+ /*
+ * seceremem caches are unreclaimable kernel allocations, so treat
+ * them as unreclaimable slab memory for VM statistics purposes
+ */
+ mod_lruvec_page_state(page, NR_SLAB_UNRECLAIMABLE_B,
+ PAGE_SIZE << order);
A lot of memcg accounted memory is not reclaimable. Why do you abuse
SLAB counter when this is not a slab owned memory? Why do you use the
kmem accounting API when __GFP_ACCOUNT should give you the same without
this details?
That's not true -- Mlocked is also unreclaimable. And doesn't this
feel more like mlocked memory than unreclaimable slab? It's also
Unevictable, so could be counted there instead.
That's not true -- Mlocked is also unreclaimable. And doesn't this
feel more like mlocked memory than unreclaimable slab? It's also
Unevictable, so could be counted there instead.
yes, that is indeed true, except the unreclaimable counter is tracking
the unevictable LRUs. These pages are not on any LRU and that can cause
some confusion. Maybe they shouldn't be so special and they should live
on unevistable LRU and get their stats automagically.
I definitely do agree that this would be a better fit than NR_SLAB
abuse. But considering that this is somehow even more special than mlock
then a dedicated counter sounds as even better fit.
--
Michal Hocko
SUSE Labs
That's not true -- Mlocked is also unreclaimable. And doesn't this
feel more like mlocked memory than unreclaimable slab? It's also
Unevictable, so could be counted there instead.
yes, that is indeed true, except the unreclaimable counter is tracking
the unevictable LRUs. These pages are not on any LRU and that can cause
some confusion. Maybe they shouldn't be so special and they should live
on unevistable LRU and get their stats automagically.
I definitely do agree that this would be a better fit than NR_SLAB
abuse. But considering that this is somehow even more special than mlock
then a dedicated counter sounds as even better fit.
I think it depends on how large these areas will be in practice.
If they will be measured in single or double digits MBs, a separate entry
is hardly a good choice: because of the batching the displayed value
will be in the noise range, plus every new vmstat item adds to the
struct mem_cgroup size.
If it will be measured in GBs, of course, a separate counter is preferred.
So I'd suggest to go with NR_SLAB (which should have been named NR_KMEM)
as now and conditionally switch to a separate counter later.
Thanks!
That's not true -- Mlocked is also unreclaimable. And doesn't this
feel more like mlocked memory than unreclaimable slab? It's also
Unevictable, so could be counted there instead.
yes, that is indeed true, except the unreclaimable counter is tracking
the unevictable LRUs. These pages are not on any LRU and that can cause
some confusion. Maybe they shouldn't be so special and they should live
on unevistable LRU and get their stats automagically.
I definitely do agree that this would be a better fit than NR_SLAB
abuse. But considering that this is somehow even more special than mlock
then a dedicated counter sounds as even better fit.
I think it depends on how large these areas will be in practice.
If they will be measured in single or double digits MBs, a separate entry
is hardly a good choice: because of the batching the displayed value
will be in the noise range, plus every new vmstat item adds to the
struct mem_cgroup size.
If it will be measured in GBs, of course, a separate counter is preferred.
So I'd suggest to go with NR_SLAB (which should have been named NR_KMEM)
as now and conditionally switch to a separate counter later.
I really do not think the overall usage matters when it comes to abusing
other counters. Changing this in future will be always tricky and there
always be our favorite "Can this break userspace" question. Yes we dared
to change meaning of some counters but this is not generally possible.
Just have a look how accounting shmem as a page cache has turned out
being much more tricky than many like.
Really if a separate counter is a big deal, for which I do not see any
big reason, then this should be accounted as unevictable (as suggested
by Matthew) and ideally pages of those mappings should be sitting in the
unevictable LRU as well unless there is a strong reason against.
--
Michal Hocko
SUSE Labs
That's not true -- Mlocked is also unreclaimable. And doesn't this
feel more like mlocked memory than unreclaimable slab? It's also
Unevictable, so could be counted there instead.
yes, that is indeed true, except the unreclaimable counter is tracking
the unevictable LRUs. These pages are not on any LRU and that can cause
some confusion. Maybe they shouldn't be so special and they should live
on unevistable LRU and get their stats automagically.
I definitely do agree that this would be a better fit than NR_SLAB
abuse. But considering that this is somehow even more special than mlock
then a dedicated counter sounds as even better fit.
I think it depends on how large these areas will be in practice.
If they will be measured in single or double digits MBs, a separate entry
is hardly a good choice: because of the batching the displayed value
will be in the noise range, plus every new vmstat item adds to the
struct mem_cgroup size.
If it will be measured in GBs, of course, a separate counter is preferred.
So I'd suggest to go with NR_SLAB (which should have been named NR_KMEM)
as now and conditionally switch to a separate counter later.
I really do not think the overall usage matters when it comes to abusing
other counters. Changing this in future will be always tricky and there
always be our favorite "Can this break userspace" question. Yes we dared
to change meaning of some counters but this is not generally possible.
Just have a look how accounting shmem as a page cache has turned out
being much more tricky than many like.
Really if a separate counter is a big deal, for which I do not see any
big reason, then this should be accounted as unevictable (as suggested
by Matthew) and ideally pages of those mappings should be sitting in the
unevictable LRU as well unless there is a strong reason against.
Why not decide based on the movability of these pages? If movable then
unevictable LRU seems like the right way otherwise NR_SLAB.
That's not true -- Mlocked is also unreclaimable. And doesn't this
feel more like mlocked memory than unreclaimable slab? It's also
Unevictable, so could be counted there instead.
yes, that is indeed true, except the unreclaimable counter is tracking
the unevictable LRUs. These pages are not on any LRU and that can cause
some confusion. Maybe they shouldn't be so special and they should live
on unevistable LRU and get their stats automagically.
I definitely do agree that this would be a better fit than NR_SLAB
abuse. But considering that this is somehow even more special than mlock
then a dedicated counter sounds as even better fit.
I think it depends on how large these areas will be in practice.
If they will be measured in single or double digits MBs, a separate entry
is hardly a good choice: because of the batching the displayed value
will be in the noise range, plus every new vmstat item adds to the
struct mem_cgroup size.
If it will be measured in GBs, of course, a separate counter is preferred.
So I'd suggest to go with NR_SLAB (which should have been named NR_KMEM)
as now and conditionally switch to a separate counter later.
I really do not think the overall usage matters when it comes to abusing
other counters. Changing this in future will be always tricky and there
always be our favorite "Can this break userspace" question. Yes we dared
to change meaning of some counters but this is not generally possible.
Just have a look how accounting shmem as a page cache has turned out
being much more tricky than many like.
Really if a separate counter is a big deal, for which I do not see any
big reason, then this should be accounted as unevictable (as suggested
by Matthew) and ideally pages of those mappings should be sitting in the
unevictable LRU as well unless there is a strong reason against.
Why not decide based on the movability of these pages? If movable then
unevictable LRU seems like the right way otherwise NR_SLAB.
I really do not follow. If the page is unevictable then why movability
matters? I also fail to see why NR_SLAB is even considered considering
this is completely outside of slab proper.
Really what is the point? What are we trying to achieve by stats? Do we
want to know how much secret memory is used because that is an
interesting/important information or do we just want to make some
accounting?
Just think at it from a practical point of view. I want to know how much
slab memory is used because it can give me an idea whether kernel is
consuming unexpected amount of memory. Now I have to subtract _some_
number to get that information. Where do I get that some number?
We have been creative with counters and it tends to kick back much more
often than it helps.
I really do not want this to turn into an endless bike shed but either
this should be accounted as a general type of memory (unevictable would
be a good fit because that is a userspace memory which is not
reclaimable) or it needs its own counter to tell how much of this
specific type of memory is used for this purpose.
--
Michal Hocko
SUSE Labs
That's not true -- Mlocked is also unreclaimable. And doesn't this
feel more like mlocked memory than unreclaimable slab? It's also
Unevictable, so could be counted there instead.
yes, that is indeed true, except the unreclaimable counter is tracking
the unevictable LRUs. These pages are not on any LRU and that can cause
some confusion. Maybe they shouldn't be so special and they should live
on unevistable LRU and get their stats automagically.
I definitely do agree that this would be a better fit than NR_SLAB
abuse. But considering that this is somehow even more special than mlock
then a dedicated counter sounds as even better fit.
I think it depends on how large these areas will be in practice.
If they will be measured in single or double digits MBs, a separate entry
is hardly a good choice: because of the batching the displayed value
will be in the noise range, plus every new vmstat item adds to the
struct mem_cgroup size.
If it will be measured in GBs, of course, a separate counter is preferred.
So I'd suggest to go with NR_SLAB (which should have been named NR_KMEM)
as now and conditionally switch to a separate counter later.
I really do not think the overall usage matters when it comes to abusing
other counters. Changing this in future will be always tricky and there
always be our favorite "Can this break userspace" question. Yes we dared
to change meaning of some counters but this is not generally possible.
Just have a look how accounting shmem as a page cache has turned out
being much more tricky than many like.
Really if a separate counter is a big deal, for which I do not see any
big reason, then this should be accounted as unevictable (as suggested
by Matthew) and ideally pages of those mappings should be sitting in the
unevictable LRU as well unless there is a strong reason against.
Why not decide based on the movability of these pages? If movable then
unevictable LRU seems like the right way otherwise NR_SLAB.
I really do not follow. If the page is unevictable then why movability
matters?
My point was if these pages are very much similar to our existing
definition of unevictable LRU pages then it makes more sense to
account for these pages into unevictable stat.
I also fail to see why NR_SLAB is even considered considering
this is completely outside of slab proper.
Really what is the point? What are we trying to achieve by stats? Do we
want to know how much secret memory is used because that is an
interesting/important information or do we just want to make some
accounting?
Just think at it from a practical point of view. I want to know how much
slab memory is used because it can give me an idea whether kernel is
consuming unexpected amount of memory. Now I have to subtract _some_
number to get that information. Where do I get that some number?
We have been creative with counters and it tends to kick back much more
often than it helps.
I really do not want this to turn into an endless bike shed but either
this should be accounted as a general type of memory (unevictable would
be a good fit because that is a userspace memory which is not
reclaimable) or it needs its own counter to tell how much of this
specific type of memory is used for this purpose.
I suggested having a separate counter in the previous version but got
shot down based on the not-yet-clear benefit of a separate stat for
it.
There is also an option to not add new or use existing stat at this
moment. As there will be more clear use-cases and usage of secretmem,
adding a new stat at that time would be much simpler than changing the
definition of existing stats.
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-26 17:21:49
On Mon 25-01-21 23:38:17, Mike Rapoport wrote:
On Mon, Jan 25, 2021 at 05:54:51PM +0100, Michal Hocko wrote:
quoted
On Thu 21-01-21 14:27:20, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
What does this mean?
That means that the accounting is updated when secretmem does cma_alloc()
and cma_relase().
quoted
What are the lifetime rules?
Hmm, what do you mean by lifetime rules?
OK, so let's start by reservation time (mmap time right?) then the
instantiation time (faulting in memory). What if the calling process of
the former has a different memcg context than the later. E.g. when you
send your fd or inherited fd over fork will move to a different memcg.
What about freeing path? E.g. when you punch a hole in the middle of
a mapping?
Please make sure to document all this.
quoted
[...]
quoted
+static int secretmem_account_pages(struct page *page, gfp_t gfp, int order)
+{
+ int err;
+
+ err = memcg_kmem_charge_page(page, gfp, order);
+ if (err)
+ return err;
+
+ /*
+ * seceremem caches are unreclaimable kernel allocations, so treat
+ * them as unreclaimable slab memory for VM statistics purposes
+ */
+ mod_lruvec_page_state(page, NR_SLAB_UNRECLAIMABLE_B,
+ PAGE_SIZE << order);
A lot of memcg accounted memory is not reclaimable. Why do you abuse
SLAB counter when this is not a slab owned memory? Why do you use the
kmem accounting API when __GFP_ACCOUNT should give you the same without
this details?
I cannot use __GFP_ACCOUNT because cma_alloc() does not use gfp.
Other people are working on this to change. But OK, I do see that this
can be done later but it looks rather awkward.
charging and stats are two different things. You can still take care of
your stats without explicitly using the charging API. But this is a mere
detail. It just hit my eyes.
Those arguments should be a part of the changelof.
I think that a dedicated stats counter would be too much at the moment and
NR_SLAB_UNRECLAIMABLE_B is the only explicit stat for unreclaimable memory.
Why do you think it would be too much? If the secret memory becomes a
prevalent memory user because it will happen to back the whole virtual
machine then hiding it into any existing counter would be less than
useful.
Please note that this all is a user visible stuff that will become PITA
(if possible) to change later on. You should really have strong
arguments in your justification here.
--
Michal Hocko
SUSE Labs
From: Mike Rapoport <rppt@kernel.org> Date: 2021-01-26 18:39:31
On Tue, Jan 26, 2021 at 08:31:42AM +0100, Michal Hocko wrote:
On Mon 25-01-21 23:38:17, Mike Rapoport wrote:
quoted
On Mon, Jan 25, 2021 at 05:54:51PM +0100, Michal Hocko wrote:
quoted
On Thu 21-01-21 14:27:20, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
What does this mean?
That means that the accounting is updated when secretmem does cma_alloc()
and cma_relase().
quoted
What are the lifetime rules?
Hmm, what do you mean by lifetime rules?
OK, so let's start by reservation time (mmap time right?) then the
instantiation time (faulting in memory). What if the calling process of
the former has a different memcg context than the later. E.g. when you
send your fd or inherited fd over fork will move to a different memcg.
What about freeing path? E.g. when you punch a hole in the middle of
a mapping?
Please make sure to document all this.
So, does something like this answer your question:
---
The memory cgroup is charged when secremem allocates pages from CMA to
increase large pages pool during ->fault() processing.
The pages are uncharged from memory cgroup when they are released back to
CMA at the time secretme inode is evicted.
---
quoted
quoted
[...]
quoted
+static int secretmem_account_pages(struct page *page, gfp_t gfp, int order)
+{
+ int err;
+
+ err = memcg_kmem_charge_page(page, gfp, order);
+ if (err)
+ return err;
+
+ /*
+ * seceremem caches are unreclaimable kernel allocations, so treat
+ * them as unreclaimable slab memory for VM statistics purposes
+ */
+ mod_lruvec_page_state(page, NR_SLAB_UNRECLAIMABLE_B,
+ PAGE_SIZE << order);
A lot of memcg accounted memory is not reclaimable. Why do you abuse
SLAB counter when this is not a slab owned memory? Why do you use the
kmem accounting API when __GFP_ACCOUNT should give you the same without
this details?
I cannot use __GFP_ACCOUNT because cma_alloc() does not use gfp.
Other people are working on this to change. But OK, I do see that this
can be done later but it looks rather awkward.
charging and stats are two different things. You can still take care of
your stats without explicitly using the charging API. But this is a mere
detail. It just hit my eyes.
Those arguments should be a part of the changelof.
quoted
I think that a dedicated stats counter would be too much at the moment and
NR_SLAB_UNRECLAIMABLE_B is the only explicit stat for unreclaimable memory.
Why do you think it would be too much? If the secret memory becomes a
prevalent memory user because it will happen to back the whole virtual
machine then hiding it into any existing counter would be less than
useful.
Please note that this all is a user visible stuff that will become PITA
(if possible) to change later on. You should really have strong
arguments in your justification here.
I think that adding a dedicated counter for few 2M areas per container is
not worth the churn.
When we'll get to the point that secretmem can be used to back the entire
guest memory we can add a new counter and it does not seem to PITA to me.
--
Sincerely yours,
Mike.
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-26 09:25:04
On Tue 26-01-21 10:56:54, Mike Rapoport wrote:
On Tue, Jan 26, 2021 at 08:31:42AM +0100, Michal Hocko wrote:
quoted
On Mon 25-01-21 23:38:17, Mike Rapoport wrote:
quoted
On Mon, Jan 25, 2021 at 05:54:51PM +0100, Michal Hocko wrote:
quoted
On Thu 21-01-21 14:27:20, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
What does this mean?
That means that the accounting is updated when secretmem does cma_alloc()
and cma_relase().
quoted
What are the lifetime rules?
Hmm, what do you mean by lifetime rules?
OK, so let's start by reservation time (mmap time right?) then the
instantiation time (faulting in memory). What if the calling process of
the former has a different memcg context than the later. E.g. when you
send your fd or inherited fd over fork will move to a different memcg.
What about freeing path? E.g. when you punch a hole in the middle of
a mapping?
Please make sure to document all this.
So, does something like this answer your question:
---
The memory cgroup is charged when secremem allocates pages from CMA to
increase large pages pool during ->fault() processing.
OK so that is when the memory is faulted in. Good that is a standard
model we have. The memcg context of the creator of the secret memory is
not really important. So whoever has created is not charged.
The pages are uncharged from memory cgroup when they are released back to
CMA at the time secretme inode is evicted.
---
so effectivelly when they are unmapped, right? This is similar to
anonymous memory.
As I've said it would be really great to have this life cycle documented
properly.
quoted
Please note that this all is a user visible stuff that will become PITA
(if possible) to change later on. You should really have strong
arguments in your justification here.
I think that adding a dedicated counter for few 2M areas per container is
not worth the churn.
What kind of churn you have in mind? What is the downside?
When we'll get to the point that secretmem can be used to back the entire
guest memory we can add a new counter and it does not seem to PITA to me.
What does really prevent a larger use with this implementation?
--
Michal Hocko
SUSE Labs
From: Matthew Wilcox <willy@infradead.org> Date: 2021-01-26 02:40:06
On Thu, Jan 21, 2021 at 02:27:20PM +0200, Mike Rapoport wrote:
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
I think this is wrong. It fails to account subsequent allocators from
the same PMD. If you want to track like this, you need separate pools
per memcg.
I think you shouldn't try to track like this; better to just track on
a per-page basis. After all, the page allocator doesn't track order-10
pages to the memcg that initially caused them to be split.
quoted hunk
Signed-off-by: Mike Rapoport <redacted>
Acked-by: Roman Gushchin <redacted>
Reviewed-by: Shakeel Butt <redacted>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Christopher Lameter <redacted>
Cc: Dan Williams <redacted>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: David Hildenbrand <redacted>
Cc: Elena Reshetova <elena.reshetova@intel.com>
Cc: Hagen Paul Pfeifer <redacted>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Bottomley <redacted>
Cc: "Kirill A. Shutemov" <redacted>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Michael Kerrisk <redacted>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Palmer Dabbelt <redacted>
Cc: Paul Walmsley <redacted>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Thomas Gleixner <redacted>
Cc: Tycho Andersen <redacted>
Cc: Will Deacon <will@kernel.org>
---
mm/filemap.c | 3 ++-
mm/secretmem.c | 36 +++++++++++++++++++++++++++++++++++-
2 files changed, 37 insertions(+), 2 deletions(-)
On Mon, Jan 25, 2021 at 8:20 AM Matthew Wilcox [off-list ref] wrote:
On Thu, Jan 21, 2021 at 02:27:20PM +0200, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
I think this is wrong. It fails to account subsequent allocators from
the same PMD. If you want to track like this, you need separate pools
per memcg.
Are these secretmem pools shared between different jobs/memcgs?
From: Mike Rapoport <rppt@kernel.org> Date: 2021-01-25 21:56:53
On Mon, Jan 25, 2021 at 09:18:04AM -0800, Shakeel Butt wrote:
On Mon, Jan 25, 2021 at 8:20 AM Matthew Wilcox [off-list ref] wrote:
quoted
On Thu, Jan 21, 2021 at 02:27:20PM +0200, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
I though about doing per-page accounting, but then one would be able to
create a lot of secretmem file descriptors, use only a page from each while
actual memory consumption will be way higher.
quoted
I think this is wrong. It fails to account subsequent allocators from
the same PMD. If you want to track like this, you need separate pools
per memcg.
Are these secretmem pools shared between different jobs/memcgs?
A secretmem pool is per anonymous file descriptor and this file descriptor
can be shared only explicitly between several processes. So, the secretmem
pool should not be shared between different jobs/memcg. Of course, it's
possible to spread threads of a process across different memcgs, but in
that case the accounting will be similar to what's happening today with
sl*b. The first thread to cause kmalloc() will be charged for the
allocation of the entire slab and subsequent allocations from that slab
will not be accounted.
That said, having a pool per memcg will add ton of complexity with very
dubious value.
--
Sincerely yours,
Mike.
On Mon, Jan 25, 2021 at 1:35 PM Mike Rapoport [off-list ref] wrote:
On Mon, Jan 25, 2021 at 09:18:04AM -0800, Shakeel Butt wrote:
quoted
On Mon, Jan 25, 2021 at 8:20 AM Matthew Wilcox [off-list ref] wrote:
quoted
On Thu, Jan 21, 2021 at 02:27:20PM +0200, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Account memory consumed by secretmem to memcg. The accounting is updated
when the memory is actually allocated and freed.
I though about doing per-page accounting, but then one would be able to
create a lot of secretmem file descriptors, use only a page from each while
actual memory consumption will be way higher.
quoted
quoted
I think this is wrong. It fails to account subsequent allocators from
the same PMD. If you want to track like this, you need separate pools
per memcg.
Are these secretmem pools shared between different jobs/memcgs?
A secretmem pool is per anonymous file descriptor and this file descriptor
can be shared only explicitly between several processes. So, the secretmem
pool should not be shared between different jobs/memcg. Of course, it's
possible to spread threads of a process across different memcgs, but in
that case the accounting will be similar to what's happening today with
sl*b.
I don't think memcg accounting for sl*b works like that.
The first thread to cause kmalloc() will be charged for the
allocation of the entire slab and subsequent allocations from that slab
will not be accounted.
The latest kernel does object level memcg accounting. So, each
allocation from these threads will correctly charge their own memcgs.
From: Mike Rapoport <rppt@kernel.org> Date: 2021-01-21 12:31:28
From: Mike Rapoport <redacted>
Removing a PAGE_SIZE page from the direct map every time such page is
allocated for a secret memory mapping will cause severe fragmentation of
the direct map. This fragmentation can be reduced by using PMD-size pages
as a pool for small pages for secret memory mappings.
Add a gen_pool per secretmem inode and lazily populate this pool with
PMD-size pages.
As pages allocated by secretmem become unmovable, use CMA to back large
page caches so that page allocator won't be surprised by failing attempt to
migrate these pages.
The CMA area used by secretmem is controlled by the "secretmem=" kernel
parameter. This allows explicit control over the memory available for
secretmem and provides upper hard limit for secretmem consumption.
Signed-off-by: Mike Rapoport <redacted>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Christopher Lameter <redacted>
Cc: Dan Williams <redacted>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: David Hildenbrand <redacted>
Cc: Elena Reshetova <elena.reshetova@intel.com>
Cc: Hagen Paul Pfeifer <redacted>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Bottomley <redacted>
Cc: "Kirill A. Shutemov" <redacted>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Michael Kerrisk <redacted>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Palmer Dabbelt <redacted>
Cc: Paul Walmsley <redacted>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
Cc: Roman Gushchin <redacted>
Cc: Shakeel Butt <redacted>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Thomas Gleixner <redacted>
Cc: Tycho Andersen <redacted>
Cc: Will Deacon <will@kernel.org>
---
mm/Kconfig | 2 +
mm/secretmem.c | 175 +++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 150 insertions(+), 27 deletions(-)
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-26 11:48:27
On Thu 21-01-21 14:27:19, Mike Rapoport wrote:
From: Mike Rapoport <redacted>
Removing a PAGE_SIZE page from the direct map every time such page is
allocated for a secret memory mapping will cause severe fragmentation of
the direct map. This fragmentation can be reduced by using PMD-size pages
as a pool for small pages for secret memory mappings.
Add a gen_pool per secretmem inode and lazily populate this pool with
PMD-size pages.
As pages allocated by secretmem become unmovable, use CMA to back large
page caches so that page allocator won't be surprised by failing attempt to
migrate these pages.
The CMA area used by secretmem is controlled by the "secretmem=" kernel
parameter. This allows explicit control over the memory available for
secretmem and provides upper hard limit for secretmem consumption.
OK, so I have finally had a look at this closer and this is really not
acceptable. I have already mentioned that in a response to other patch
but any task is able to deprive access to secret memory to other tasks
and cause OOM killer which wouldn't really recover ever and potentially
panic the system. Now you could be less drastic and only make SIGBUS on
fault but that would be still quite terrible. There is a very good
reason why hugetlb implements is non-trivial reservation system to avoid
exactly these problems.
So unless I am really misreading the code
Nacked-by: Michal Hocko [off-list ref]
That doesn't mean I reject the whole idea. There are some details to
sort out as mentioned elsewhere but you cannot really depend on
pre-allocated pool which can fail at a fault time like that.
quoted hunk
Signed-off-by: Mike Rapoport <redacted>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Christopher Lameter <redacted>
Cc: Dan Williams <redacted>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: David Hildenbrand <redacted>
Cc: Elena Reshetova <elena.reshetova@intel.com>
Cc: Hagen Paul Pfeifer <redacted>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Bottomley <redacted>
Cc: "Kirill A. Shutemov" <redacted>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Michael Kerrisk <redacted>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Palmer Dabbelt <redacted>
Cc: Paul Walmsley <redacted>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
Cc: Roman Gushchin <redacted>
Cc: Shakeel Butt <redacted>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Thomas Gleixner <redacted>
Cc: Tycho Andersen <redacted>
Cc: Will Deacon <will@kernel.org>
---
mm/Kconfig | 2 +
mm/secretmem.c | 175 +++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 150 insertions(+), 27 deletions(-)
From: David Hildenbrand <hidden> Date: 2021-01-26 12:00:24
On 26.01.21 12:46, Michal Hocko wrote:
On Thu 21-01-21 14:27:19, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Removing a PAGE_SIZE page from the direct map every time such page is
allocated for a secret memory mapping will cause severe fragmentation of
the direct map. This fragmentation can be reduced by using PMD-size pages
as a pool for small pages for secret memory mappings.
Add a gen_pool per secretmem inode and lazily populate this pool with
PMD-size pages.
As pages allocated by secretmem become unmovable, use CMA to back large
page caches so that page allocator won't be surprised by failing attempt to
migrate these pages.
The CMA area used by secretmem is controlled by the "secretmem=" kernel
parameter. This allows explicit control over the memory available for
secretmem and provides upper hard limit for secretmem consumption.
OK, so I have finally had a look at this closer and this is really not
acceptable. I have already mentioned that in a response to other patch
but any task is able to deprive access to secret memory to other tasks
and cause OOM killer which wouldn't really recover ever and potentially
panic the system. Now you could be less drastic and only make SIGBUS on
fault but that would be still quite terrible. There is a very good
reason why hugetlb implements is non-trivial reservation system to avoid
exactly these problems.
So unless I am really misreading the code
Nacked-by: Michal Hocko [off-list ref]
That doesn't mean I reject the whole idea. There are some details to
sort out as mentioned elsewhere but you cannot really depend on
pre-allocated pool which can fail at a fault time like that.
So, to do it similar to hugetlbfs (e.g., with CMA), there would have to
be a mechanism to actually try pre-reserving (e.g., from the CMA area),
at which point in time the pages would get moved to the secretmem pool,
and a mechanism for mmap() etc. to "reserve" from these secretmem pool,
such that there are guarantees at fault time?
What we have right now feels like some kind of overcommit (reading, as
overcommiting huge pages, so we might get SIGBUS at fault time).
TBH, the SIGBUS thingy doesn't sound terrible to me - if this behavior
is to be expected right now by applications using it and they can handle
it - no guarantees. I fully agree that some kind of
reservation/guarantee mechanism would be preferable.
--
Thanks,
David / dhildenb
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-26 12:09:52
On Tue 26-01-21 12:56:48, David Hildenbrand wrote:
On 26.01.21 12:46, Michal Hocko wrote:
quoted
On Thu 21-01-21 14:27:19, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Removing a PAGE_SIZE page from the direct map every time such page is
allocated for a secret memory mapping will cause severe fragmentation of
the direct map. This fragmentation can be reduced by using PMD-size pages
as a pool for small pages for secret memory mappings.
Add a gen_pool per secretmem inode and lazily populate this pool with
PMD-size pages.
As pages allocated by secretmem become unmovable, use CMA to back large
page caches so that page allocator won't be surprised by failing attempt to
migrate these pages.
The CMA area used by secretmem is controlled by the "secretmem=" kernel
parameter. This allows explicit control over the memory available for
secretmem and provides upper hard limit for secretmem consumption.
OK, so I have finally had a look at this closer and this is really not
acceptable. I have already mentioned that in a response to other patch
but any task is able to deprive access to secret memory to other tasks
and cause OOM killer which wouldn't really recover ever and potentially
panic the system. Now you could be less drastic and only make SIGBUS on
fault but that would be still quite terrible. There is a very good
reason why hugetlb implements is non-trivial reservation system to avoid
exactly these problems.
So unless I am really misreading the code
Nacked-by: Michal Hocko [off-list ref]
That doesn't mean I reject the whole idea. There are some details to
sort out as mentioned elsewhere but you cannot really depend on
pre-allocated pool which can fail at a fault time like that.
So, to do it similar to hugetlbfs (e.g., with CMA), there would have to be a
mechanism to actually try pre-reserving (e.g., from the CMA area), at which
point in time the pages would get moved to the secretmem pool, and a
mechanism for mmap() etc. to "reserve" from these secretmem pool, such that
there are guarantees at fault time?
yes, reserve at mmap time and use during the fault. But this all sounds
like a self inflicted problem to me. Sure you can have a pre-allocated
or more dynamic pool to reduce the direct mapping fragmentation but you
can always fall back to regular allocatios. In other ways have the pool
as an optimization rather than a hard requirement. With a careful access
control this sounds like a manageable solution to me.
--
Michal Hocko
SUSE Labs
From: Mike Rapoport <rppt@kernel.org> Date: 2021-01-28 09:26:38
On Tue, Jan 26, 2021 at 01:08:23PM +0100, Michal Hocko wrote:
On Tue 26-01-21 12:56:48, David Hildenbrand wrote:
quoted
On 26.01.21 12:46, Michal Hocko wrote:
quoted
On Thu 21-01-21 14:27:19, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Removing a PAGE_SIZE page from the direct map every time such page is
allocated for a secret memory mapping will cause severe fragmentation of
the direct map. This fragmentation can be reduced by using PMD-size pages
as a pool for small pages for secret memory mappings.
Add a gen_pool per secretmem inode and lazily populate this pool with
PMD-size pages.
As pages allocated by secretmem become unmovable, use CMA to back large
page caches so that page allocator won't be surprised by failing attempt to
migrate these pages.
The CMA area used by secretmem is controlled by the "secretmem=" kernel
parameter. This allows explicit control over the memory available for
secretmem and provides upper hard limit for secretmem consumption.
OK, so I have finally had a look at this closer and this is really not
acceptable. I have already mentioned that in a response to other patch
but any task is able to deprive access to secret memory to other tasks
and cause OOM killer which wouldn't really recover ever and potentially
panic the system. Now you could be less drastic and only make SIGBUS on
fault but that would be still quite terrible. There is a very good
reason why hugetlb implements is non-trivial reservation system to avoid
exactly these problems.
So, if I understand your concerns correct this implementation has two
issues:
1) allocation failure at page fault that causes unrecoverable OOM and
2) a possibility for an unprivileged user to deplete secretmem pool and
cause (1) to others
I'm not really familiar with OOM internals, but when I simulated an
allocation failure in my testing only the allocating process and it's
parent were OOM-killed and then the system continued normally.
You are right, it would be better if we SIGBUS instead of OOM but I don't
agree SIGBUS is terrible. As we started to draw parallels with hugetlbfs
even despite it's complex reservation system, hugetlb_fault() may fail to
allocate pages from CMA and this still will cause SIGBUS.
And hugetlb pools may be also depleted by anybody by calling
mmap(MAP_HUGETLB) and there is no any limiting knob for this, while
secretmem has RLIMIT_MEMLOCK.
That said, simply replacing VM_FAULT_OOM with VM_FAULT_SIGBUS makes
secretmem at least as controllable and robust than hugeltbfs even without
complex reservation at mmap() time.
quoted
quoted
So unless I am really misreading the code
Nacked-by: Michal Hocko [off-list ref]
That doesn't mean I reject the whole idea. There are some details to
sort out as mentioned elsewhere but you cannot really depend on
pre-allocated pool which can fail at a fault time like that.
So, to do it similar to hugetlbfs (e.g., with CMA), there would have to be a
mechanism to actually try pre-reserving (e.g., from the CMA area), at which
point in time the pages would get moved to the secretmem pool, and a
mechanism for mmap() etc. to "reserve" from these secretmem pool, such that
there are guarantees at fault time?
yes, reserve at mmap time and use during the fault. But this all sounds
like a self inflicted problem to me. Sure you can have a pre-allocated
or more dynamic pool to reduce the direct mapping fragmentation but you
can always fall back to regular allocatios. In other ways have the pool
as an optimization rather than a hard requirement. With a careful access
control this sounds like a manageable solution to me.
I'd really wish we had this discussion for earlier spins of this series,
but since this didn't happen let's refresh the history a bit.
One of the major pushbacks on the first RFC [1] of the concept was about
the direct map fragmentation. I tried really hard to find data that shows
what is the performance difference with different page sizes in the direct
map and I didn't find anything.
So presuming that large pages do provide advantage the first implementation
of secretmem used PMD_ORDER allocations to amortise the effect of the
direct map fragmentation and then handed out 4k pages at each fault. In
addition there was an option to reserve a finite pool at boot time and
limit secretmem allocations only to that pool.
At some point David suggested to use CMA to improve overall flexibility
[3], so I switched secretmem to use CMA.
Now, with the data we have at hand (my benchmarks and Intel's report David
mentioned) I'm even not sure this whole pooling even required.
I like the idea to have a pool as an optimization rather than a hard
requirement but I don't see why would it need a careful access control. As
the direct map fragmentation is not necessarily degrades the performance
(and even sometimes it actually improves it) and even then the degradation
is small, trying a PMD_ORDER allocation for a pool and then falling back to
4K page may be just fine.
I think we could have something like this (error handling is mostly
omitted):
static int secretmem_pool_increase(struct secretmem_ctx *ctx, gfp_t gfp)
{
struct page *page = alloc_pages(gfp, PMD_PAGE_ORDER);
if (!page)
return -ENOMEM;
/* add large page to pool */
return 0;
}
static struct page *secretmem_alloc_page(struct secretmem_ctx *ctx,
gfp_t gfp)
{
struct page *page;
...
if (gen_pool_avail(pool) < PAGE_SIZE) {
err = secretmem_pool_increase(ctx, gfp);
if (!err) {
addr = gen_pool_alloc(pool, PAGE_SIZE);
if (addr)
page = virt_to_page(addr);
}
}
if (!page)
page = alloc_page(gfp);
return page;
}
[1] https://lore.kernel.org/lkml/1572171452-7958-1-git-send-email-rppt@kernel.org/
[2] https://lore.kernel.org/lkml/20200720092435.17469-1-rppt@kernel.org/
[3] https://lore.kernel.org/lkml/03ec586d-c00c-c57e-3118-7186acb7b823@redhat.com/#t
--
Sincerely yours,
Mike.
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-28 13:02:35
On Thu 28-01-21 11:22:59, Mike Rapoport wrote:
On Tue, Jan 26, 2021 at 01:08:23PM +0100, Michal Hocko wrote:
quoted
On Tue 26-01-21 12:56:48, David Hildenbrand wrote:
quoted
On 26.01.21 12:46, Michal Hocko wrote:
quoted
On Thu 21-01-21 14:27:19, Mike Rapoport wrote:
quoted
From: Mike Rapoport <redacted>
Removing a PAGE_SIZE page from the direct map every time such page is
allocated for a secret memory mapping will cause severe fragmentation of
the direct map. This fragmentation can be reduced by using PMD-size pages
as a pool for small pages for secret memory mappings.
Add a gen_pool per secretmem inode and lazily populate this pool with
PMD-size pages.
As pages allocated by secretmem become unmovable, use CMA to back large
page caches so that page allocator won't be surprised by failing attempt to
migrate these pages.
The CMA area used by secretmem is controlled by the "secretmem=" kernel
parameter. This allows explicit control over the memory available for
secretmem and provides upper hard limit for secretmem consumption.
OK, so I have finally had a look at this closer and this is really not
acceptable. I have already mentioned that in a response to other patch
but any task is able to deprive access to secret memory to other tasks
and cause OOM killer which wouldn't really recover ever and potentially
panic the system. Now you could be less drastic and only make SIGBUS on
fault but that would be still quite terrible. There is a very good
reason why hugetlb implements is non-trivial reservation system to avoid
exactly these problems.
So, if I understand your concerns correct this implementation has two
issues:
1) allocation failure at page fault that causes unrecoverable OOM and
2) a possibility for an unprivileged user to deplete secretmem pool and
cause (1) to others
I'm not really familiar with OOM internals, but when I simulated an
allocation failure in my testing only the allocating process and it's
parent were OOM-killed and then the system continued normally.
If you kill the allocating process then yes, it would work, but your
process might be the very last to be selected.
You are right, it would be better if we SIGBUS instead of OOM but I don't
agree SIGBUS is terrible. As we started to draw parallels with hugetlbfs
even despite it's complex reservation system, hugetlb_fault() may fail to
allocate pages from CMA and this still will cause SIGBUS.
This is an unexpected runtime error. Unless you make it an integral part
of the API design.
And hugetlb pools may be also depleted by anybody by calling
mmap(MAP_HUGETLB) and there is no any limiting knob for this, while
secretmem has RLIMIT_MEMLOCK.
Yes it can fail. But it would fail at the mmap time when the reservation
fails. Not during the #PF time which can be at any time.
That said, simply replacing VM_FAULT_OOM with VM_FAULT_SIGBUS makes
secretmem at least as controllable and robust than hugeltbfs even without
complex reservation at mmap() time.
Still sucks huge!
quoted
quoted
quoted
So unless I am really misreading the code
Nacked-by: Michal Hocko [off-list ref]
That doesn't mean I reject the whole idea. There are some details to
sort out as mentioned elsewhere but you cannot really depend on
pre-allocated pool which can fail at a fault time like that.
So, to do it similar to hugetlbfs (e.g., with CMA), there would have to be a
mechanism to actually try pre-reserving (e.g., from the CMA area), at which
point in time the pages would get moved to the secretmem pool, and a
mechanism for mmap() etc. to "reserve" from these secretmem pool, such that
there are guarantees at fault time?
yes, reserve at mmap time and use during the fault. But this all sounds
like a self inflicted problem to me. Sure you can have a pre-allocated
or more dynamic pool to reduce the direct mapping fragmentation but you
can always fall back to regular allocatios. In other ways have the pool
as an optimization rather than a hard requirement. With a careful access
control this sounds like a manageable solution to me.
I'd really wish we had this discussion for earlier spins of this series,
but since this didn't happen let's refresh the history a bit.
I am sorry but I am really fighting to find time to watch for all the
moving targets...
One of the major pushbacks on the first RFC [1] of the concept was about
the direct map fragmentation. I tried really hard to find data that shows
what is the performance difference with different page sizes in the direct
map and I didn't find anything.
So presuming that large pages do provide advantage the first implementation
of secretmem used PMD_ORDER allocations to amortise the effect of the
direct map fragmentation and then handed out 4k pages at each fault. In
addition there was an option to reserve a finite pool at boot time and
limit secretmem allocations only to that pool.
At some point David suggested to use CMA to improve overall flexibility
[3], so I switched secretmem to use CMA.
Now, with the data we have at hand (my benchmarks and Intel's report David
mentioned) I'm even not sure this whole pooling even required.
I would still like to understand whether that data is actually
representative. With some underlying reasoning rather than I have run
these XYZ benchmarks and numbers do not look terrible.
I like the idea to have a pool as an optimization rather than a hard
requirement but I don't see why would it need a careful access control. As
the direct map fragmentation is not necessarily degrades the performance
(and even sometimes it actually improves it) and even then the degradation
is small, trying a PMD_ORDER allocation for a pool and then falling back to
4K page may be just fine.
Well, as soon as this is a scarce resource then an access control seems
like a first thing to think of. Maybe it is not really necessary but
then this should be really justified.
I am also still not sure why this whole thing is not just a
ramdisk/ramfs which happens to unmap its pages from the direct
map. Wouldn't that be a much more easier model to work with? You would
get an access control for free as well.
--
Michal Hocko
SUSE Labs
From: Christoph Lameter <hidden> Date: 2021-01-28 13:38:41
On Thu, 28 Jan 2021, Michal Hocko wrote:
quoted
So, if I understand your concerns correct this implementation has two
issues:
1) allocation failure at page fault that causes unrecoverable OOM and
2) a possibility for an unprivileged user to deplete secretmem pool and
cause (1) to others
I'm not really familiar with OOM internals, but when I simulated an
allocation failure in my testing only the allocating process and it's
parent were OOM-killed and then the system continued normally.
If you kill the allocating process then yes, it would work, but your
process might be the very last to be selected.
OOMs are different if you have a "constrained allocation". In that case it
is the fault of the process who wanted memory with certain conditions.
That memory is not available. General memory is available though. In that
case the allocating process is killed.
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-28 13:50:43
On Thu 28-01-21 13:28:10, Cristopher Lameter wrote:
On Thu, 28 Jan 2021, Michal Hocko wrote:
quoted
quoted
So, if I understand your concerns correct this implementation has two
issues:
1) allocation failure at page fault that causes unrecoverable OOM and
2) a possibility for an unprivileged user to deplete secretmem pool and
cause (1) to others
I'm not really familiar with OOM internals, but when I simulated an
allocation failure in my testing only the allocating process and it's
parent were OOM-killed and then the system continued normally.
If you kill the allocating process then yes, it would work, but your
process might be the very last to be selected.
OOMs are different if you have a "constrained allocation". In that case it
is the fault of the process who wanted memory with certain conditions.
That memory is not available. General memory is available though. In that
case the allocating process is killed.
I do not see this implementation would do anything like that. Neither
anything like that implemented in the oom killer. Constrained
allocations (cpusets/memcg/mempolicy) only do restrict their selection
to processes which belong to the same domain. So I am not really sure
what you are referring to. The is only a global knob to _always_ kill
the allocating process on OOM.
--
Michal Hocko
SUSE Labs
From: Christoph Lameter <hidden> Date: 2021-01-28 15:57:52
On Thu, 28 Jan 2021, Michal Hocko wrote:
quoted
quoted
If you kill the allocating process then yes, it would work, but your
process might be the very last to be selected.
OOMs are different if you have a "constrained allocation". In that case it
is the fault of the process who wanted memory with certain conditions.
That memory is not available. General memory is available though. In that
case the allocating process is killed.
I do not see this implementation would do anything like that. Neither
anything like that implemented in the oom killer. Constrained
allocations (cpusets/memcg/mempolicy) only do restrict their selection
to processes which belong to the same domain. So I am not really sure
what you are referring to. The is only a global knob to _always_ kill
the allocating process on OOM.
Constrained allocations refer to allocations where the NUMA nodes are
restricted or something else does not allow the use of arbitrary memory.
The OOM killer changes its behavior. In the past we fell back to killing
the calling process.
See constrained_alloc() in mm/oom_kill.c
static const char * const oom_constraint_text[] = {
[CONSTRAINT_NONE] = "CONSTRAINT_NONE",
[CONSTRAINT_CPUSET] = "CONSTRAINT_CPUSET",
[CONSTRAINT_MEMORY_POLICY] = "CONSTRAINT_MEMORY_POLICY",
[CONSTRAINT_MEMCG] = "CONSTRAINT_MEMCG",
};
/*
* Determine the type of allocation constraint.
*/
static enum oom_constraint constrained_alloc(struct oom_control *oc)
{
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-28 16:25:04
On Thu 28-01-21 15:56:36, Cristopher Lameter wrote:
On Thu, 28 Jan 2021, Michal Hocko wrote:
quoted
quoted
quoted
If you kill the allocating process then yes, it would work, but your
process might be the very last to be selected.
OOMs are different if you have a "constrained allocation". In that case it
is the fault of the process who wanted memory with certain conditions.
That memory is not available. General memory is available though. In that
case the allocating process is killed.
I do not see this implementation would do anything like that. Neither
anything like that implemented in the oom killer. Constrained
allocations (cpusets/memcg/mempolicy) only do restrict their selection
to processes which belong to the same domain. So I am not really sure
what you are referring to. The is only a global knob to _always_ kill
the allocating process on OOM.
Constrained allocations refer to allocations where the NUMA nodes are
restricted or something else does not allow the use of arbitrary memory.
The OOM killer changes its behavior.
Yes as described in the above paragraph.
In the past we fell back to killing the calling process.
Yeah, but this is no longer the case since 6f48d0ebd907a (more than 10
years ago.
Anyway this is not really important because if you want to kill the
allocating task because there is no chance the fault can succed then
there is a SIGBUS as already mentioned.
--
Michal Hocko
SUSE Labs
From: James Bottomley <hidden> Date: 2021-01-28 15:31:55
On Thu, 2021-01-28 at 14:01 +0100, Michal Hocko wrote:
On Thu 28-01-21 11:22:59, Mike Rapoport wrote:
[...]
quoted
One of the major pushbacks on the first RFC [1] of the concept was
about the direct map fragmentation. I tried really hard to find
data that shows what is the performance difference with different
page sizes in the direct map and I didn't find anything.
So presuming that large pages do provide advantage the first
implementation of secretmem used PMD_ORDER allocations to amortise
the effect of the direct map fragmentation and then handed out 4k
pages at each fault. In addition there was an option to reserve a
finite pool at boot time and limit secretmem allocations only to
that pool.
At some point David suggested to use CMA to improve overall
flexibility [3], so I switched secretmem to use CMA.
Now, with the data we have at hand (my benchmarks and Intel's
report David mentioned) I'm even not sure this whole pooling even
required.
I would still like to understand whether that data is actually
representative. With some underlying reasoning rather than I have run
these XYZ benchmarks and numbers do not look terrible.
My theory, and the reason I made Mike run the benchmarks, is that our
fear of TLB miss has been alleviated by CPU speculation advances over
the years. You can appreciate this if you think that both Intel and
AMD have increased the number of levels in the page table to
accommodate larger virtual memory size 5 instead of 3. That increases
the length of the page walk nearly 2x in a physical system and even
more in a virtual system. Unless this were massively optimized,
systems would have slowed down significantly. Using 2M pages only
eliminates one level and 2G pages eliminates 2, so I theorized that
actually fragmentation wouldn't be the significant problem we once
thought it was and asked Mike to benchmark it.
The benchmarks show that indeed, it isn't a huge change in the data TLB
miss time, I suspect because data is nicely continuous nowadays and the
prediction that goes into the CPU optimizations quite easy. ITLB
fragmentation actually seems to be quite a bit worse, likely because we
still don't have branch prediction down to an exact science.
James
From: Mike Rapoport <rppt@kernel.org> Date: 2021-01-29 07:05:26
On Thu, Jan 28, 2021 at 07:28:57AM -0800, James Bottomley wrote:
On Thu, 2021-01-28 at 14:01 +0100, Michal Hocko wrote:
quoted
On Thu 28-01-21 11:22:59, Mike Rapoport wrote:
[...]
quoted
quoted
One of the major pushbacks on the first RFC [1] of the concept was
about the direct map fragmentation. I tried really hard to find
data that shows what is the performance difference with different
page sizes in the direct map and I didn't find anything.
So presuming that large pages do provide advantage the first
implementation of secretmem used PMD_ORDER allocations to amortise
the effect of the direct map fragmentation and then handed out 4k
pages at each fault. In addition there was an option to reserve a
finite pool at boot time and limit secretmem allocations only to
that pool.
At some point David suggested to use CMA to improve overall
flexibility [3], so I switched secretmem to use CMA.
Now, with the data we have at hand (my benchmarks and Intel's
report David mentioned) I'm even not sure this whole pooling even
required.
I would still like to understand whether that data is actually
representative. With some underlying reasoning rather than I have run
these XYZ benchmarks and numbers do not look terrible.
My theory, and the reason I made Mike run the benchmarks, is that our
fear of TLB miss has been alleviated by CPU speculation advances over
the years. You can appreciate this if you think that both Intel and
AMD have increased the number of levels in the page table to
accommodate larger virtual memory size 5 instead of 3. That increases
the length of the page walk nearly 2x in a physical system and even
more in a virtual system. Unless this were massively optimized,
systems would have slowed down significantly. Using 2M pages only
eliminates one level and 2G pages eliminates 2, so I theorized that
actually fragmentation wouldn't be the significant problem we once
thought it was and asked Mike to benchmark it.
The benchmarks show that indeed, it isn't a huge change in the data TLB
miss time, I suspect because data is nicely continuous nowadays and the
prediction that goes into the CPU optimizations quite easy. ITLB
fragmentation actually seems to be quite a bit worse, likely because we
still don't have branch prediction down to an exact science.
Another thing is that normally useful work done by userspace so data
accesses are dominated by userspace and any change in dTLB miss rate for
kernel data accesses is only a small fraction of all misses.
From: James Bottomley <hidden> Date: 2021-01-28 21:07:15
On Thu, 2021-01-28 at 14:01 +0100, Michal Hocko wrote:
On Thu 28-01-21 11:22:59, Mike Rapoport wrote:
[...]
quoted
I like the idea to have a pool as an optimization rather than a
hard requirement but I don't see why would it need a careful access
control. As the direct map fragmentation is not necessarily
degrades the performance (and even sometimes it actually improves
it) and even then the degradation is small, trying a PMD_ORDER
allocation for a pool and then falling back to 4K page may be just
fine.
Well, as soon as this is a scarce resource then an access control
seems like a first thing to think of. Maybe it is not really
necessary but then this should be really justified.
The control for the resource is effectively the rlimit today. I don't
think dividing the world into people who can and can't use secret
memory would be useful since the design is to be usable for anyone who
might have a secret to keep; it would become like the kvm group
permissions: something which is theoretically an access control but
which in practise is given to everyone on the system.
I am also still not sure why this whole thing is not just a
ramdisk/ramfs which happens to unmap its pages from the direct
map. Wouldn't that be a much more easier model to work with? You
would get an access control for free as well.
The original API was a memfd which does have this access control as
well. However, the decision was made after much discussion to go with
a new system call instead. Obviously the API choice could be revisited
but do you have anything to add over the previous discussion, or is
this just to get your access control?
James
From: Michal Hocko <mhocko@suse.com> Date: 2021-01-29 11:53:41
On Thu 28-01-21 13:05:02, James Bottomley wrote:
Obviously the API choice could be revisited
but do you have anything to add over the previous discussion, or is
this just to get your access control?
Well, access control is certainly one thing which I still believe is
missing. But if there is a general agreement that the direct map
manipulation is not that critical then this will become much less of a
problem of course.
It all boils down whether secret memory is a scarce resource. With the
existing implementation it really is. It is effectivelly repeating
same design errors as hugetlb did. And look now, we have a subtle and
convoluted reservation code to track mmap requests and we have a cgroup
controller to, guess what, have at least some control over distribution
if the preallocated pool. See where am I coming from?
If the secret memory is more in line with mlock without any imposed
limit (other than available memory) in the end then, sure, using the same
access control as mlock sounds reasonable. Btw. if this is really
just a more restrictive mlock then is there any reason to not hook this
into the existing mlock infrastructure (e.g. MCL_EXCLUSIVE)?
Implications would be that direct map would be handled on instantiation/tear
down paths, migration would deal with the same (if possible). Other than
that it would be mlock like.
--
Michal Hocko
SUSE Labs
From: James Bottomley <hidden> Date: 2021-02-01 16:58:23
On Fri, 2021-01-29 at 09:23 +0100, Michal Hocko wrote:
On Thu 28-01-21 13:05:02, James Bottomley wrote:
quoted
Obviously the API choice could be revisited
but do you have anything to add over the previous discussion, or is
this just to get your access control?
Well, access control is certainly one thing which I still believe is
missing. But if there is a general agreement that the direct map
manipulation is not that critical then this will become much less of
a problem of course.
The secret memory is a scarce resource but it's not a facility that
should only be available to some users.
It all boils down whether secret memory is a scarce resource. With
the existing implementation it really is. It is effectivelly
repeating same design errors as hugetlb did. And look now, we have a
subtle and convoluted reservation code to track mmap requests and we
have a cgroup controller to, guess what, have at least some control
over distribution if the preallocated pool. See where am I coming
from?
I'm fairly sure rlimit is the correct way to control this. The
subtlety in both rlimit and memcg tracking comes from deciding to
account under an existing category rather than having our own new one.
People don't like new stuff in accounting because it requires
modifications to everything in userspace. Accounting under and
existing limit keeps userspace the same but leads to endless arguments
about which limit it should be under. It took us several patch set
iterations to get to a fragile consensus on this which you're now
disrupting for reasons you're not making clear.
If the secret memory is more in line with mlock without any imposed
limit (other than available memory) in the end then, sure, using the
same access control as mlock sounds reasonable. Btw. if this is
really just a more restrictive mlock then is there any reason to not
hook this into the existing mlock infrastructure (e.g.
MCL_EXCLUSIVE)? Implications would be that direct map would be
handled on instantiation/tear down paths, migration would deal with
the same (if possible). Other than that it would be mlock like.
In the very first patch set we proposed a mmap flag to do this. Under
detailed probing it emerged that this suffers from several design
problems: the KVM people want VMM to be able to remove the secret
memory range from the process; there may be situations where sharing is
useful and some people want to be able to seal the operations. All of
this ended up convincing everyone that a file descriptor based approach
was better than a mmap one.
James
From: Michal Hocko <mhocko@suse.com> Date: 2021-02-02 09:37:47
On Mon 01-02-21 08:56:19, James Bottomley wrote:
On Fri, 2021-01-29 at 09:23 +0100, Michal Hocko wrote:
quoted
On Thu 28-01-21 13:05:02, James Bottomley wrote:
quoted
Obviously the API choice could be revisited
but do you have anything to add over the previous discussion, or is
this just to get your access control?
Well, access control is certainly one thing which I still believe is
missing. But if there is a general agreement that the direct map
manipulation is not that critical then this will become much less of
a problem of course.
The secret memory is a scarce resource but it's not a facility that
should only be available to some users.
How those two objectives go along? Or maybe our understanding of what
scrace really means here. If the pool of the secret memory is very limited
then you really need a way to stop one party from depriving others. More
on that below.
quoted
It all boils down whether secret memory is a scarce resource. With
the existing implementation it really is. It is effectivelly
repeating same design errors as hugetlb did. And look now, we have a
subtle and convoluted reservation code to track mmap requests and we
have a cgroup controller to, guess what, have at least some control
over distribution if the preallocated pool. See where am I coming
from?
I'm fairly sure rlimit is the correct way to control this. The
subtlety in both rlimit and memcg tracking comes from deciding to
account under an existing category rather than having our own new one.
People don't like new stuff in accounting because it requires
modifications to everything in userspace. Accounting under and
existing limit keeps userspace the same but leads to endless arguments
about which limit it should be under. It took us several patch set
iterations to get to a fragile consensus on this which you're now
disrupting for reasons you're not making clear.
I hoped I had made my points really clear. The existing scheme allows
one users (potentially adversary) to deplete the preallocated pool
and cause a shitstorm of OOM killer because there is no real way to
replenish the pool from the oom killer other than randomly keep killing
tasks until one happens to release its secret memory back to the
pool. Is that more clear now?
And no, rlimit and memcg limit will not save you from that because the
former is per process and later is hard to manage under a single limit
which might be order of magnitude larger than the secret memory pool
size. See the point?
I have also proposed potential ways out of this. Either the pool is not
fixed sized and you make it a regular unevictable memory (if direct map
fragmentation is not considered a major problem) or you need a careful
access control or you need SIGBUS on the mmap failure (to allow at least
some fallback mode to caller).
I do not see any other way around it. I might be missing some other
ways but so far I keep hearing that the existing scheme is just fine
because this has been discussed in the past and you have agreed it is
ok. Without any specifics...
Please keep in mind this is a user interface and it is due to careful
scrutiny. So rather than pushing back with "you are disrupting a
consensus" kinda feedback, please try to stay technical.
quoted
If the secret memory is more in line with mlock without any imposed
limit (other than available memory) in the end then, sure, using the
same access control as mlock sounds reasonable. Btw. if this is
really just a more restrictive mlock then is there any reason to not
hook this into the existing mlock infrastructure (e.g.
MCL_EXCLUSIVE)? Implications would be that direct map would be
handled on instantiation/tear down paths, migration would deal with
the same (if possible). Other than that it would be mlock like.
In the very first patch set we proposed a mmap flag to do this. Under
detailed probing it emerged that this suffers from several design
problems: the KVM people want VMM to be able to remove the secret
memory range from the process; there may be situations where sharing is
useful and some people want to be able to seal the operations. All of
this ended up convincing everyone that a file descriptor based approach
was better than a mmap one.
OK, fair enough. This belongs to the changelog IMHO. It is good to know
why existing interfaces do not match the need.
--
Michal Hocko
SUSE Labs
From: Mike Rapoport <rppt@kernel.org> Date: 2021-02-02 12:50:23
On Tue, Feb 02, 2021 at 10:35:05AM +0100, Michal Hocko wrote:
On Mon 01-02-21 08:56:19, James Bottomley wrote:
I have also proposed potential ways out of this. Either the pool is not
fixed sized and you make it a regular unevictable memory (if direct map
fragmentation is not considered a major problem)
I think that the direct map fragmentation is not a major problem, and the
data we have confirms it, so I'd be more than happy to entirely drop the
pool, allocate memory page by page and remove each page from the direct
map.
Still, we cannot prove negative and it could happen that there is a
workload that would suffer a lot from the direct map fragmentation, so
having a pool of large pages upfront is better than trying to fix it
afterwards. As we get more confidence that the direct map fragmentation is
not an issue as it is common to believe we may remove the pool altogether.
I think that using PMD_ORDER allocations for the pool with a fallback to
order 0 will do the job, but unfortunately I doubt we'll reach a consensus
about this because dogmatic beliefs are hard to shake...
A more restrictive possibility is to still use plain PMD_ORDER allocations
to fill the pool, without relying on CMA. In this case there will be no
global secretmem specific pool to exhaust, but then it's possible to drain
high order free blocks in a system, so CMA has an advantage of limiting
secretmem pools to certain amount of memory with somewhat higher
probability for high order allocation to succeed.
or you need a careful access control
Do you mind elaborating what do you mean by "careful access control"?
or you need SIGBUS on the mmap failure (to allow at least some fallback
mode to caller).
As I've already said, I agree that SIGBUS is way better than OOM at #PF
time.
And we can add some means to fail at mmap() time if the pools are running
low.
--
Sincerely yours,
Mike.
From: David Hildenbrand <hidden> Date: 2021-02-02 13:18:36
On 02.02.21 13:48, Mike Rapoport wrote:
On Tue, Feb 02, 2021 at 10:35:05AM +0100, Michal Hocko wrote:
quoted
On Mon 01-02-21 08:56:19, James Bottomley wrote:
I have also proposed potential ways out of this. Either the pool is not
fixed sized and you make it a regular unevictable memory (if direct map
fragmentation is not considered a major problem)
I think that the direct map fragmentation is not a major problem, and the
data we have confirms it, so I'd be more than happy to entirely drop the
pool, allocate memory page by page and remove each page from the direct
map.
Still, we cannot prove negative and it could happen that there is a
workload that would suffer a lot from the direct map fragmentation, so
having a pool of large pages upfront is better than trying to fix it
afterwards. As we get more confidence that the direct map fragmentation is
not an issue as it is common to believe we may remove the pool altogether.
I think that using PMD_ORDER allocations for the pool with a fallback to
order 0 will do the job, but unfortunately I doubt we'll reach a consensus
about this because dogmatic beliefs are hard to shake...
A more restrictive possibility is to still use plain PMD_ORDER allocations
to fill the pool, without relying on CMA. In this case there will be no
global secretmem specific pool to exhaust, but then it's possible to drain
high order free blocks in a system, so CMA has an advantage of limiting
secretmem pools to certain amount of memory with somewhat higher
probability for high order allocation to succeed.
I am not really concerned about fragmenting/breaking up the direct map
as long as the feature has to be explicitly enabled (similar to
fragmenting the vmemmap).
As already expressed, I dislike allowing user space to consume an
unlimited number unmovable/unmigratable allocations. We already have
that in some cases with huge pages (when the arch does not support
migration) - but there we can at least manage the consumption using the
whole max/reserved/free/... infrastructure. In addition, adding arch
support for migration shouldn't be too complicated.
The idea of using CMA is quite good IMHO, because there we can locally
limit the direct map fragmentation and don't have to bother about
migration at all. We own the area, so we can place as many unmovable
allocations on it as we can fit.
But it sounds like, we would also need some kind of reservation
mechanism in either scenario (CMA vs. no CMA).
If we don't want to go full-circle on max/reserved/free/..., allowing
for migration of secretmem pages would make sense. Then, these pages
become "less special". Map source, copy, unmap destination. The security
implementations are the ugly part. I wonder if we could temporarily map
somewhere else, so avoiding to touch the direct map during migration.
--
Thanks,
David / dhildenb
From: Michal Hocko <mhocko@suse.com> Date: 2021-02-02 13:34:53
On Tue 02-02-21 14:14:09, David Hildenbrand wrote:
[...]
As already expressed, I dislike allowing user space to consume an unlimited
number unmovable/unmigratable allocations. We already have that in some
cases with huge pages (when the arch does not support migration) - but there
we can at least manage the consumption using the whole max/reserved/free/...
infrastructure. In addition, adding arch support for migration shouldn't be
too complicated.
Well, mlock is not too different here as well. Hugepages are arguably an
easier model because it requires an explicit pre-configuration by an
admin. Mlock doesn't have anything like that. Please also note that
while mlock pages are migrateable by default, this is not the case in
general because they can be configured to disalow migration to prevent
from minor page faults as some workloads require that (e.g. RT).
Another example is ramdisk or even tmpfs (with swap storage depleted or
not configured). Both are PITA from the OOM POV but they are manageable
if people are careful. If secretmem behaves along those existing models
then we know what to expect at least.
--
Michal Hocko
SUSE Labs
From: David Hildenbrand <hidden> Date: 2021-02-02 14:16:34
On 02.02.21 14:32, Michal Hocko wrote:
On Tue 02-02-21 14:14:09, David Hildenbrand wrote:
[...]
quoted
As already expressed, I dislike allowing user space to consume an unlimited
number unmovable/unmigratable allocations. We already have that in some
cases with huge pages (when the arch does not support migration) - but there
we can at least manage the consumption using the whole max/reserved/free/...
infrastructure. In addition, adding arch support for migration shouldn't be
too complicated.
Well, mlock is not too different here as well. Hugepages are arguably an
easier model because it requires an explicit pre-configuration by an
admin. Mlock doesn't have anything like that. Please also note that
while mlock pages are migrateable by default, this is not the case in
general because they can be configured to disalow migration to prevent
from minor page faults as some workloads require that (e.g. RT).
Yeah, however that is a very special case. In most cases mlock() simply
prevents swapping, you still have movable pages you can place anywhere
you like (including on ZONE_MOVABLE).
Another example is ramdisk or even tmpfs (with swap storage depleted or
not configured). Both are PITA from the OOM POV but they are manageable
if people are careful.
Right, but again, special cases - e.g., tmpfs explicitly has to be resized.
If secretmem behaves along those existing models
then we know what to expect at least.
I think secretmem behaves much more like longterm GUP right now
("unmigratable", "lifetime controlled by user space", "cannot go on
CMA/ZONE_MOVABLE"). I'd either want to reasonably well control/limit it
or make it behave more like mlocked pages.
--
Thanks,
David / dhildenb
From: Michal Hocko <mhocko@suse.com> Date: 2021-02-02 14:25:53
On Tue 02-02-21 15:12:21, David Hildenbrand wrote:
[...]
I think secretmem behaves much more like longterm GUP right now
("unmigratable", "lifetime controlled by user space", "cannot go on
CMA/ZONE_MOVABLE"). I'd either want to reasonably well control/limit it or
make it behave more like mlocked pages.
I thought I have already asked but I must have forgotten. Is there any
actual reason why the memory is not movable? Timing attacks?
--
Michal Hocko
SUSE Labs
From: David Hildenbrand <hidden> Date: 2021-02-02 14:29:12
On 02.02.21 15:22, Michal Hocko wrote:
On Tue 02-02-21 15:12:21, David Hildenbrand wrote:
[...]
quoted
I think secretmem behaves much more like longterm GUP right now
("unmigratable", "lifetime controlled by user space", "cannot go on
CMA/ZONE_MOVABLE"). I'd either want to reasonably well control/limit it or
make it behave more like mlocked pages.
I thought I have already asked but I must have forgotten. Is there any
actual reason why the memory is not movable? Timing attacks?
I think the reason is simple: no direct map, no copying of memory.
As I mentioned, we would have to temporarily map in order to copy.
Mapping it somewhere else (like kmap), outside of the direct map might
reduce possible attacks.
--
Thanks,
David / dhildenb
From: Michal Hocko <mhocko@suse.com> Date: 2021-02-02 14:34:14
On Tue 02-02-21 15:26:20, David Hildenbrand wrote:
On 02.02.21 15:22, Michal Hocko wrote:
quoted
On Tue 02-02-21 15:12:21, David Hildenbrand wrote:
[...]
quoted
I think secretmem behaves much more like longterm GUP right now
("unmigratable", "lifetime controlled by user space", "cannot go on
CMA/ZONE_MOVABLE"). I'd either want to reasonably well control/limit it or
make it behave more like mlocked pages.
I thought I have already asked but I must have forgotten. Is there any
actual reason why the memory is not movable? Timing attacks?
I think the reason is simple: no direct map, no copying of memory.
This is an implementation detail though and not something terribly hard
to add on top later on. I was more worried there would be really
fundamental reason why this is not possible. E.g. security implications.
--
Michal Hocko
SUSE Labs
From: David Hildenbrand <hidden> Date: 2021-02-02 14:47:40
On 02.02.21 15:32, Michal Hocko wrote:
On Tue 02-02-21 15:26:20, David Hildenbrand wrote:
quoted
On 02.02.21 15:22, Michal Hocko wrote:
quoted
On Tue 02-02-21 15:12:21, David Hildenbrand wrote:
[...]
quoted
I think secretmem behaves much more like longterm GUP right now
("unmigratable", "lifetime controlled by user space", "cannot go on
CMA/ZONE_MOVABLE"). I'd either want to reasonably well control/limit it or
make it behave more like mlocked pages.
I thought I have already asked but I must have forgotten. Is there any
actual reason why the memory is not movable? Timing attacks?
I think the reason is simple: no direct map, no copying of memory.
This is an implementation detail though and not something terribly hard
to add on top later on. I was more worried there would be really
fundamental reason why this is not possible. E.g. security implications.
I don't remember all the details. Let's see what Mike thinks regarding
migration (e.g., security concerns).
--
Thanks,
David / dhildenb
From: Mike Rapoport <rppt@kernel.org> Date: 2021-02-02 18:19:37
On Tue, Feb 02, 2021 at 03:34:29PM +0100, David Hildenbrand wrote:
On 02.02.21 15:32, Michal Hocko wrote:
quoted
On Tue 02-02-21 15:26:20, David Hildenbrand wrote:
quoted
On 02.02.21 15:22, Michal Hocko wrote:
quoted
On Tue 02-02-21 15:12:21, David Hildenbrand wrote:
[...]
quoted
I think secretmem behaves much more like longterm GUP right now
("unmigratable", "lifetime controlled by user space", "cannot go on
CMA/ZONE_MOVABLE"). I'd either want to reasonably well control/limit it or
make it behave more like mlocked pages.
I thought I have already asked but I must have forgotten. Is there any
actual reason why the memory is not movable? Timing attacks?
I think the reason is simple: no direct map, no copying of memory.
This is an implementation detail though and not something terribly hard
to add on top later on. I was more worried there would be really
fundamental reason why this is not possible. E.g. security implications.
I don't remember all the details. Let's see what Mike thinks regarding
migration (e.g., security concerns).
Thanks for considering me a security expert :-)
Yet, I cannot estimate how dangerous is the temporal exposure of
this data to the kernel via the direct map in the simple map/copy/unmap
sequence.
More secure way would be to map source and destination in a different page table
rather than in the direct map, similarly to the way text_poke() on x86
does.
I've left the migration callback empty for now because it can be added on
top and its implementation would depend on the way we do (or do not do)
pooling.
--
Sincerely yours,
Mike.
From: James Bottomley <hidden> Date: 2021-02-02 19:20:30
On Tue, 2021-02-02 at 20:15 +0200, Mike Rapoport wrote:
On Tue, Feb 02, 2021 at 03:34:29PM +0100, David Hildenbrand wrote:
quoted
On 02.02.21 15:32, Michal Hocko wrote:
quoted
On Tue 02-02-21 15:26:20, David Hildenbrand wrote:
quoted
On 02.02.21 15:22, Michal Hocko wrote:
quoted
On Tue 02-02-21 15:12:21, David Hildenbrand wrote:
[...]
quoted
I think secretmem behaves much more like longterm GUP right
now
("unmigratable", "lifetime controlled by user space",
"cannot go on
CMA/ZONE_MOVABLE"). I'd either want to reasonably well
control/limit it or
make it behave more like mlocked pages.
I thought I have already asked but I must have forgotten. Is
there any
actual reason why the memory is not movable? Timing attacks?
I think the reason is simple: no direct map, no copying of
memory.
This is an implementation detail though and not something
terribly hard
to add on top later on. I was more worried there would be really
fundamental reason why this is not possible. E.g. security
implications.
I don't remember all the details. Let's see what Mike thinks
regarding
migration (e.g., security concerns).
Thanks for considering me a security expert :-)
Yet, I cannot estimate how dangerous is the temporal exposure of
this data to the kernel via the direct map in the simple
map/copy/unmap
sequence.
Well the safest security statement is that we never expose the data to
the kernel because it's a very clean security statement and easy to
enforce. It's also the easiest threat model to analyse. Once we do
start exposing the secret to the kernel it alters the threat profile
and the analysis and obviously potentially provides the ROP gadget to
an attacker to do the same. Instinct tells me that the loss of
security doesn't really make up for the ability to swap or migrate but
if there were a case for doing the latter, it would have to be a
security policy of the user (i.e. a user should be able to decide their
data is too sensitive to expose to the kernel).
More secure way would be to map source and destination in a different
page table rather than in the direct map, similarly to the way
text_poke() on x86 does.
I think doing this would have much less of an impact on the security
posture because it's already theoretically possible to have kmap
restore access to the kernel.
James
I've left the migration callback empty for now because it can be
added on top and its implementation would depend on the way we do (or
do not do) pooling.
From: Michal Hocko <mhocko@suse.com> Date: 2021-02-03 12:10:38
On Tue 02-02-21 10:55:40, James Bottomley wrote:
On Tue, 2021-02-02 at 20:15 +0200, Mike Rapoport wrote:
quoted
On Tue, Feb 02, 2021 at 03:34:29PM +0100, David Hildenbrand wrote:
quoted
On 02.02.21 15:32, Michal Hocko wrote:
quoted
On Tue 02-02-21 15:26:20, David Hildenbrand wrote:
quoted
On 02.02.21 15:22, Michal Hocko wrote:
quoted
On Tue 02-02-21 15:12:21, David Hildenbrand wrote:
[...]
quoted
I think secretmem behaves much more like longterm GUP right
now
("unmigratable", "lifetime controlled by user space",
"cannot go on
CMA/ZONE_MOVABLE"). I'd either want to reasonably well
control/limit it or
make it behave more like mlocked pages.
I thought I have already asked but I must have forgotten. Is
there any
actual reason why the memory is not movable? Timing attacks?
I think the reason is simple: no direct map, no copying of
memory.
This is an implementation detail though and not something
terribly hard
to add on top later on. I was more worried there would be really
fundamental reason why this is not possible. E.g. security
implications.
I don't remember all the details. Let's see what Mike thinks
regarding
migration (e.g., security concerns).
Thanks for considering me a security expert :-)
Yet, I cannot estimate how dangerous is the temporal exposure of
this data to the kernel via the direct map in the simple
map/copy/unmap
sequence.
Well the safest security statement is that we never expose the data to
the kernel because it's a very clean security statement and easy to
enforce. It's also the easiest threat model to analyse. Once we do
start exposing the secret to the kernel it alters the threat profile
and the analysis and obviously potentially provides the ROP gadget to
an attacker to do the same. Instinct tells me that the loss of
security doesn't really make up for the ability to swap or migrate but
if there were a case for doing the latter, it would have to be a
security policy of the user (i.e. a user should be able to decide their
data is too sensitive to expose to the kernel).
The security/threat model should be documented in the changelog as
well. I am not a security expert but I would tend to agree that not
allowing even temporal mapping for data copying (in the kernel) is the
most robust approach. Whether that is generally necessary for users I do
not know.
From the API POV I think it makes sense to have two
modes. NEVER_MAP_IN_KERNEL which would imply no migrateability, no
copy_{from,to}_user, no gup or any other way for the kernel to access
content of the memory. Maybe even zero the content on the last unmap to
never allow any data leak. ALLOW_TEMPORARY would unmap the page from
the direct mapping but it would still allow temporary mappings for
data copying inside the kernel (thus allow CoW, copy*user, migration).
Which one should be default and which an opt-in I do not know. A less
restrictive mode to be default and the more restrictive an opt-in via
flags makes a lot of sense to me though.
--
Michal Hocko
SUSE Labs
From: Mike Rapoport <rppt@kernel.org> Date: 2021-02-04 11:35:16
On Wed, Feb 03, 2021 at 01:09:30PM +0100, Michal Hocko wrote:
On Tue 02-02-21 10:55:40, James Bottomley wrote:
quoted
On Tue, 2021-02-02 at 20:15 +0200, Mike Rapoport wrote:
quoted
On Tue, Feb 02, 2021 at 03:34:29PM +0100, David Hildenbrand wrote:
quoted
On 02.02.21 15:32, Michal Hocko wrote:
Well the safest security statement is that we never expose the data to
the kernel because it's a very clean security statement and easy to
enforce. It's also the easiest threat model to analyse. Once we do
start exposing the secret to the kernel it alters the threat profile
and the analysis and obviously potentially provides the ROP gadget to
an attacker to do the same. Instinct tells me that the loss of
security doesn't really make up for the ability to swap or migrate but
if there were a case for doing the latter, it would have to be a
security policy of the user (i.e. a user should be able to decide their
data is too sensitive to expose to the kernel).
The security/threat model should be documented in the changelog as
well. I am not a security expert but I would tend to agree that not
allowing even temporal mapping for data copying (in the kernel) is the
most robust approach. Whether that is generally necessary for users I do
not know.
From the API POV I think it makes sense to have two
modes. NEVER_MAP_IN_KERNEL which would imply no migrateability, no
copy_{from,to}_user, no gup or any other way for the kernel to access
content of the memory. Maybe even zero the content on the last unmap to
never allow any data leak. ALLOW_TEMPORARY would unmap the page from
the direct mapping but it would still allow temporary mappings for
data copying inside the kernel (thus allow CoW, copy*user, migration).
Which one should be default and which an opt-in I do not know. A less
restrictive mode to be default and the more restrictive an opt-in via
flags makes a lot of sense to me though.
The default is already NEVER_MAP_IN_KERNEL, so there is no explicit flag
for this. ALLOW_TEMPORARY should be opt-in, IMHO, and we can add it on top
later on.
--
Sincerely yours,
Mike.
From: Michal Hocko <mhocko@suse.com> Date: 2021-02-02 13:28:27
On Tue 02-02-21 14:48:57, Mike Rapoport wrote:
On Tue, Feb 02, 2021 at 10:35:05AM +0100, Michal Hocko wrote:
quoted
On Mon 01-02-21 08:56:19, James Bottomley wrote:
I have also proposed potential ways out of this. Either the pool is not
fixed sized and you make it a regular unevictable memory (if direct map
fragmentation is not considered a major problem)
I think that the direct map fragmentation is not a major problem, and the
data we have confirms it, so I'd be more than happy to entirely drop the
pool, allocate memory page by page and remove each page from the direct
map.
Still, we cannot prove negative and it could happen that there is a
workload that would suffer a lot from the direct map fragmentation, so
having a pool of large pages upfront is better than trying to fix it
afterwards. As we get more confidence that the direct map fragmentation is
not an issue as it is common to believe we may remove the pool altogether.
I would drop the pool altogether and instantiate pages to the
unevictable LRU list and internally treat it as ramdisk/mlock so you
will get an accounting correctly. The feature should be still opt-in
(e.g. a kernel command line parameter) for now. The recent report by
Intel (http://lkml.kernel.org/r/213b4567-46ce-f116-9cdf-bbd0c884eb3c@linux.intel.com)
there is no clear win to have huge mappings in _general_ but there are
still workloads which benefit.
I think that using PMD_ORDER allocations for the pool with a fallback to
order 0 will do the job, but unfortunately I doubt we'll reach a consensus
about this because dogmatic beliefs are hard to shake...
If this is opt-in then those beliefs can be relaxed somehow. Long term
it makes a lot of sense to optimize for a better direct map management
but I do not think this is a hard requirement for an initial
implementation if it is not imposed to everybody by default.
A more restrictive possibility is to still use plain PMD_ORDER allocations
to fill the pool, without relying on CMA. In this case there will be no
global secretmem specific pool to exhaust, but then it's possible to drain
high order free blocks in a system, so CMA has an advantage of limiting
secretmem pools to certain amount of memory with somewhat higher
probability for high order allocation to succeed.
quoted
or you need a careful access control
Do you mind elaborating what do you mean by "careful access control"?
As already mentioned, a mechanism to control who can use this feature -
e.g. make it a special device which you can access control by
permissions or higher level security policies. But that is really needed
only if the pool is fixed sized.
quoted
or you need SIGBUS on the mmap failure (to allow at least some fallback
mode to caller).
As I've already said, I agree that SIGBUS is way better than OOM at #PF
time.
It would be better than OOM but it would still be a terrible interface.
So I would go that path only as a last resort. I do not even want to
think what kind of security consequences that would have. E.g. think of
somebody depleting the pool and pushing security sensitive workload into
fallback which is not backed by security memory.
And we can add some means to fail at mmap() time if the pools are running
low.
Welcome to hugetlb reservation world...
--
Michal Hocko
SUSE Labs
From: Mike Rapoport <rppt@kernel.org> Date: 2021-02-02 19:14:19
On Tue, Feb 02, 2021 at 02:27:14PM +0100, Michal Hocko wrote:
On Tue 02-02-21 14:48:57, Mike Rapoport wrote:
quoted
On Tue, Feb 02, 2021 at 10:35:05AM +0100, Michal Hocko wrote:
quoted
On Mon 01-02-21 08:56:19, James Bottomley wrote:
I have also proposed potential ways out of this. Either the pool is not
fixed sized and you make it a regular unevictable memory (if direct map
fragmentation is not considered a major problem)
I think that the direct map fragmentation is not a major problem, and the
data we have confirms it, so I'd be more than happy to entirely drop the
pool, allocate memory page by page and remove each page from the direct
map.
Still, we cannot prove negative and it could happen that there is a
workload that would suffer a lot from the direct map fragmentation, so
having a pool of large pages upfront is better than trying to fix it
afterwards. As we get more confidence that the direct map fragmentation is
not an issue as it is common to believe we may remove the pool altogether.
I would drop the pool altogether and instantiate pages to the
unevictable LRU list and internally treat it as ramdisk/mlock so you
will get an accounting correctly. The feature should be still opt-in
(e.g. a kernel command line parameter) for now. The recent report by
Intel (http://lkml.kernel.org/r/213b4567-46ce-f116-9cdf-bbd0c884eb3c@linux.intel.com)
there is no clear win to have huge mappings in _general_ but there are
still workloads which benefit.
quoted
I think that using PMD_ORDER allocations for the pool with a fallback to
order 0 will do the job, but unfortunately I doubt we'll reach a consensus
about this because dogmatic beliefs are hard to shake...
If this is opt-in then those beliefs can be relaxed somehow. Long term
it makes a lot of sense to optimize for a better direct map management
but I do not think this is a hard requirement for an initial
implementation if it is not imposed to everybody by default.
quoted
A more restrictive possibility is to still use plain PMD_ORDER allocations
to fill the pool, without relying on CMA. In this case there will be no
global secretmem specific pool to exhaust, but then it's possible to drain
high order free blocks in a system, so CMA has an advantage of limiting
secretmem pools to certain amount of memory with somewhat higher
probability for high order allocation to succeed.
quoted
or you need a careful access control
Do you mind elaborating what do you mean by "careful access control"?
As already mentioned, a mechanism to control who can use this feature -
e.g. make it a special device which you can access control by
permissions or higher level security policies. But that is really needed
only if the pool is fixed sized.
Let me reiterate to make sure I don't misread your suggestion.
If we make secretmem an opt-in feature with, e.g. kernel parameter, the
pooling of large pages is unnecessary. In this case there is no limited
resource we need to protect because secretmem will allocate page by page.
Since there is no limited resource, we don't need special permissions
to access secretmem so we can move forward with a system call that creates
a mmapable file descriptor and save the hassle of a chardev.
I cannot say I don't like this as it cuts roughly half of mm/secretmem.c :)
But I must say I am still a bit concerned about that we have no provisions
here for dealing with the direct map fragmentation even with the set goal
to improve the direct map management in the long run...
--
Sincerely yours,
Mike.