-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
mm/hmm: heterogeneous memory management (HMM for short)
HMM provides 3 separate types of functionality: - Mirroring: synchronize CPU page table and device page table - Device memory: allocating struct page for device memory - Migration: migrating regular memory to device memory This patch introduces some common helpers and definitions to all of those 3 functionality. Link: http://lkml.kernel.org/r/20170817000548.32038-3-jglisse@redhat.com Signed-off-by: Jérôme Glisse <jglisse@redhat.com> Signed-off-by: Evgeny Baskakov <ebaskakov@nvidia.com> Signed-off-by: John Hubbard <jhubbard@nvidia.com> Signed-off-by: Mark Hairgrove <mhairgrove@nvidia.com> Signed-off-by: Sherry Cheung <SCheung@nvidia.com> Signed-off-by: Subhash Gutti <sgutti@nvidia.com> Cc: Aneesh Kumar <aneesh.kumar@linux.vnet.ibm.com> Cc: Balbir Singh <bsingharora@gmail.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Dan Williams <dan.j.williams@intel.com> Cc: David Nellans <dnellans@nvidia.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Michal Hocko <mhocko@kernel.org> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Ross Zwisler <ross.zwisler@linux.intel.com> Cc: Vladimir Davydov <vdavydov.dev@gmail.com> Cc: Bob Liu <liubo95@huawei.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
- Loading branch information
Jérôme Glisse
authored and
Linus Torvalds
committed
Sep 9, 2017
1 parent
bffc33e
commit 133ff0e
Showing
6 changed files
with
249 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/* | ||
* Copyright 2013 Red Hat Inc. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* Authors: Jérôme Glisse <jglisse@redhat.com> | ||
*/ | ||
/* | ||
* Heterogeneous Memory Management (HMM) | ||
* | ||
* See Documentation/vm/hmm.txt for reasons and overview of what HMM is and it | ||
* is for. Here we focus on the HMM API description, with some explanation of | ||
* the underlying implementation. | ||
* | ||
* Short description: HMM provides a set of helpers to share a virtual address | ||
* space between CPU and a device, so that the device can access any valid | ||
* address of the process (while still obeying memory protection). HMM also | ||
* provides helpers to migrate process memory to device memory, and back. Each | ||
* set of functionality (address space mirroring, and migration to and from | ||
* device memory) can be used independently of the other. | ||
* | ||
* | ||
* HMM address space mirroring API: | ||
* | ||
* Use HMM address space mirroring if you want to mirror range of the CPU page | ||
* table of a process into a device page table. Here, "mirror" means "keep | ||
* synchronized". Prerequisites: the device must provide the ability to write- | ||
* protect its page tables (at PAGE_SIZE granularity), and must be able to | ||
* recover from the resulting potential page faults. | ||
* | ||
* HMM guarantees that at any point in time, a given virtual address points to | ||
* either the same memory in both CPU and device page tables (that is: CPU and | ||
* device page tables each point to the same pages), or that one page table (CPU | ||
* or device) points to no entry, while the other still points to the old page | ||
* for the address. The latter case happens when the CPU page table update | ||
* happens first, and then the update is mirrored over to the device page table. | ||
* This does not cause any issue, because the CPU page table cannot start | ||
* pointing to a new page until the device page table is invalidated. | ||
* | ||
* HMM uses mmu_notifiers to monitor the CPU page tables, and forwards any | ||
* updates to each device driver that has registered a mirror. It also provides | ||
* some API calls to help with taking a snapshot of the CPU page table, and to | ||
* synchronize with any updates that might happen concurrently. | ||
* | ||
* | ||
* HMM migration to and from device memory: | ||
* | ||
* HMM provides a set of helpers to hotplug device memory as ZONE_DEVICE, with | ||
* a new MEMORY_DEVICE_PRIVATE type. This provides a struct page for each page | ||
* of the device memory, and allows the device driver to manage its memory | ||
* using those struct pages. Having struct pages for device memory makes | ||
* migration easier. Because that memory is not addressable by the CPU it must | ||
* never be pinned to the device; in other words, any CPU page fault can always | ||
* cause the device memory to be migrated (copied/moved) back to regular memory. | ||
* | ||
* A new migrate helper (migrate_vma()) has been added (see mm/migrate.c) that | ||
* allows use of a device DMA engine to perform the copy operation between | ||
* regular system memory and device memory. | ||
*/ | ||
#ifndef LINUX_HMM_H | ||
#define LINUX_HMM_H | ||
|
||
#include <linux/kconfig.h> | ||
|
||
#if IS_ENABLED(CONFIG_HMM) | ||
|
||
|
||
/* | ||
* hmm_pfn_t - HMM uses its own pfn type to keep several flags per page | ||
* | ||
* Flags: | ||
* HMM_PFN_VALID: pfn is valid | ||
* HMM_PFN_WRITE: CPU page table has write permission set | ||
*/ | ||
typedef unsigned long hmm_pfn_t; | ||
|
||
#define HMM_PFN_VALID (1 << 0) | ||
#define HMM_PFN_WRITE (1 << 1) | ||
#define HMM_PFN_SHIFT 2 | ||
|
||
/* | ||
* hmm_pfn_t_to_page() - return struct page pointed to by a valid hmm_pfn_t | ||
* @pfn: hmm_pfn_t to convert to struct page | ||
* Returns: struct page pointer if pfn is a valid hmm_pfn_t, NULL otherwise | ||
* | ||
* If the hmm_pfn_t is valid (ie valid flag set) then return the struct page | ||
* matching the pfn value stored in the hmm_pfn_t. Otherwise return NULL. | ||
*/ | ||
static inline struct page *hmm_pfn_t_to_page(hmm_pfn_t pfn) | ||
{ | ||
if (!(pfn & HMM_PFN_VALID)) | ||
return NULL; | ||
return pfn_to_page(pfn >> HMM_PFN_SHIFT); | ||
} | ||
|
||
/* | ||
* hmm_pfn_t_to_pfn() - return pfn value store in a hmm_pfn_t | ||
* @pfn: hmm_pfn_t to extract pfn from | ||
* Returns: pfn value if hmm_pfn_t is valid, -1UL otherwise | ||
*/ | ||
static inline unsigned long hmm_pfn_t_to_pfn(hmm_pfn_t pfn) | ||
{ | ||
if (!(pfn & HMM_PFN_VALID)) | ||
return -1UL; | ||
return (pfn >> HMM_PFN_SHIFT); | ||
} | ||
|
||
/* | ||
* hmm_pfn_t_from_page() - create a valid hmm_pfn_t value from struct page | ||
* @page: struct page pointer for which to create the hmm_pfn_t | ||
* Returns: valid hmm_pfn_t for the page | ||
*/ | ||
static inline hmm_pfn_t hmm_pfn_t_from_page(struct page *page) | ||
{ | ||
return (page_to_pfn(page) << HMM_PFN_SHIFT) | HMM_PFN_VALID; | ||
} | ||
|
||
/* | ||
* hmm_pfn_t_from_pfn() - create a valid hmm_pfn_t value from pfn | ||
* @pfn: pfn value for which to create the hmm_pfn_t | ||
* Returns: valid hmm_pfn_t for the pfn | ||
*/ | ||
static inline hmm_pfn_t hmm_pfn_t_from_pfn(unsigned long pfn) | ||
{ | ||
return (pfn << HMM_PFN_SHIFT) | HMM_PFN_VALID; | ||
} | ||
|
||
|
||
/* Below are for HMM internal use only! Not to be used by device driver! */ | ||
void hmm_mm_destroy(struct mm_struct *mm); | ||
|
||
static inline void hmm_mm_init(struct mm_struct *mm) | ||
{ | ||
mm->hmm = NULL; | ||
} | ||
|
||
#else /* IS_ENABLED(CONFIG_HMM) */ | ||
|
||
/* Below are for HMM internal use only! Not to be used by device driver! */ | ||
static inline void hmm_mm_destroy(struct mm_struct *mm) {} | ||
static inline void hmm_mm_init(struct mm_struct *mm) {} | ||
|
||
#endif /* IS_ENABLED(CONFIG_HMM) */ | ||
#endif /* LINUX_HMM_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* Copyright 2013 Red Hat Inc. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* Authors: Jérôme Glisse <jglisse@redhat.com> | ||
*/ | ||
/* | ||
* Refer to include/linux/hmm.h for information about heterogeneous memory | ||
* management or HMM for short. | ||
*/ | ||
#include <linux/mm.h> | ||
#include <linux/hmm.h> | ||
#include <linux/slab.h> | ||
#include <linux/sched.h> | ||
|
||
|
||
#ifdef CONFIG_HMM | ||
/* | ||
* struct hmm - HMM per mm struct | ||
* | ||
* @mm: mm struct this HMM struct is bound to | ||
*/ | ||
struct hmm { | ||
struct mm_struct *mm; | ||
}; | ||
|
||
/* | ||
* hmm_register - register HMM against an mm (HMM internal) | ||
* | ||
* @mm: mm struct to attach to | ||
* | ||
* This is not intended to be used directly by device drivers. It allocates an | ||
* HMM struct if mm does not have one, and initializes it. | ||
*/ | ||
static struct hmm *hmm_register(struct mm_struct *mm) | ||
{ | ||
if (!mm->hmm) { | ||
struct hmm *hmm = NULL; | ||
|
||
hmm = kmalloc(sizeof(*hmm), GFP_KERNEL); | ||
if (!hmm) | ||
return NULL; | ||
hmm->mm = mm; | ||
|
||
spin_lock(&mm->page_table_lock); | ||
if (!mm->hmm) | ||
mm->hmm = hmm; | ||
else | ||
kfree(hmm); | ||
spin_unlock(&mm->page_table_lock); | ||
} | ||
|
||
/* | ||
* The hmm struct can only be freed once the mm_struct goes away, | ||
* hence we should always have pre-allocated an new hmm struct | ||
* above. | ||
*/ | ||
return mm->hmm; | ||
} | ||
|
||
void hmm_mm_destroy(struct mm_struct *mm) | ||
{ | ||
kfree(mm->hmm); | ||
} | ||
#endif /* CONFIG_HMM */ |