Skip to content

Commit

Permalink
mm/mmu_notifier: use structure for invalidate_range_start/end callback
Browse files Browse the repository at this point in the history
Patch series "mmu notifier contextual informations", v2.

This patchset adds contextual information, why an invalidation is
happening, to mmu notifier callback.  This is necessary for user of mmu
notifier that wish to maintains their own data structure without having to
add new fields to struct vm_area_struct (vma).

For instance device can have they own page table that mirror the process
address space.  When a vma is unmap (munmap() syscall) the device driver
can free the device page table for the range.

Today we do not have any information on why a mmu notifier call back is
happening and thus device driver have to assume that it is always an
munmap().  This is inefficient at it means that it needs to re-allocate
device page table on next page fault and rebuild the whole device driver
data structure for the range.

Other use case beside munmap() also exist, for instance it is pointless
for device driver to invalidate the device page table when the
invalidation is for the soft dirtyness tracking.  Or device driver can
optimize away mprotect() that change the page table permission access for
the range.

This patchset enables all this optimizations for device drivers.  I do not
include any of those in this series but another patchset I am posting will
leverage this.

The patchset is pretty simple from a code point of view.  The first two
patches consolidate all mmu notifier arguments into a struct so that it is
easier to add/change arguments.  The last patch adds the contextual
information (munmap, protection, soft dirty, clear, ...).

This patch (of 3):

To avoid having to change many callback definition everytime we want to
add a parameter use a structure to group all parameters for the
mmu_notifier invalidate_range_start/end callback.  No functional changes
with this patch.

[akpm@linux-foundation.org: fix drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c kerneldoc]
Link: http://lkml.kernel.org/r/20181205053628.3210-2-jglisse@redhat.com
Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Acked-by: Jan Kara <jack@suse.cz>
Acked-by: Jason Gunthorpe <jgg@mellanox.com>	[infiniband]
Cc: Matthew Wilcox <mawilcox@microsoft.com>
Cc: Ross Zwisler <zwisler@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Radim Krcmar <rkrcmar@redhat.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Christian Koenig <christian.koenig@amd.com>
Cc: Felix Kuehling <felix.kuehling@amd.com>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: John Hubbard <jhubbard@nvidia.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 torvalds committed Dec 28, 2018
1 parent b15c872 commit 5d6527a
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 116 deletions.
47 changes: 20 additions & 27 deletions drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,44 +238,40 @@ static void amdgpu_mn_invalidate_node(struct amdgpu_mn_node *node,
* amdgpu_mn_invalidate_range_start_gfx - callback to notify about mm change
*
* @mn: our notifier
* @mm: the mm this callback is about
* @start: start of updated range
* @end: end of updated range
* @range: mmu notifier context
*
* Block for operations on BOs to finish and mark pages as accessed and
* potentially dirty.
*/
static int amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn);
struct interval_tree_node *it;
unsigned long end;

/* notification is exclusive, but interval is inclusive */
end -= 1;
end = range->end - 1;

/* TODO we should be able to split locking for interval tree and
* amdgpu_mn_invalidate_node
*/
if (amdgpu_mn_read_lock(amn, blockable))
if (amdgpu_mn_read_lock(amn, range->blockable))
return -EAGAIN;

it = interval_tree_iter_first(&amn->objects, start, end);
it = interval_tree_iter_first(&amn->objects, range->start, end);
while (it) {
struct amdgpu_mn_node *node;

if (!blockable) {
if (!range->blockable) {
amdgpu_mn_read_unlock(amn);
return -EAGAIN;
}

node = container_of(it, struct amdgpu_mn_node, it);
it = interval_tree_iter_next(it, start, end);
it = interval_tree_iter_next(it, range->start, end);

amdgpu_mn_invalidate_node(node, start, end);
amdgpu_mn_invalidate_node(node, range->start, end);
}

return 0;
Expand All @@ -294,39 +290,38 @@ static int amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn,
* are restorted in amdgpu_mn_invalidate_range_end_hsa.
*/
static int amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn);
struct interval_tree_node *it;
unsigned long end;

/* notification is exclusive, but interval is inclusive */
end -= 1;
end = range->end - 1;

if (amdgpu_mn_read_lock(amn, blockable))
if (amdgpu_mn_read_lock(amn, range->blockable))
return -EAGAIN;

it = interval_tree_iter_first(&amn->objects, start, end);
it = interval_tree_iter_first(&amn->objects, range->start, end);
while (it) {
struct amdgpu_mn_node *node;
struct amdgpu_bo *bo;

if (!blockable) {
if (!range->blockable) {
amdgpu_mn_read_unlock(amn);
return -EAGAIN;
}

node = container_of(it, struct amdgpu_mn_node, it);
it = interval_tree_iter_next(it, start, end);
it = interval_tree_iter_next(it, range->start, end);

list_for_each_entry(bo, &node->bos, mn_list) {
struct kgd_mem *mem = bo->kfd_bo;

if (amdgpu_ttm_tt_affect_userptr(bo->tbo.ttm,
start, end))
amdgpu_amdkfd_evict_userptr(mem, mm);
range->start,
end))
amdgpu_amdkfd_evict_userptr(mem, range->mm);
}
}

Expand All @@ -344,9 +339,7 @@ static int amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn,
* Release the lock again to allow new command submissions.
*/
static void amdgpu_mn_invalidate_range_end(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end)
const struct mmu_notifier_range *range)
{
struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn);

Expand Down
14 changes: 6 additions & 8 deletions drivers/gpu/drm/i915/i915_gem_userptr.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,27 +113,25 @@ static void del_object(struct i915_mmu_object *mo)
}

static int i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct i915_mmu_notifier *mn =
container_of(_mn, struct i915_mmu_notifier, mn);
struct i915_mmu_object *mo;
struct interval_tree_node *it;
LIST_HEAD(cancelled);
unsigned long end;

if (RB_EMPTY_ROOT(&mn->objects.rb_root))
return 0;

/* interval ranges are inclusive, but invalidate range is exclusive */
end--;
end = range->end - 1;

spin_lock(&mn->lock);
it = interval_tree_iter_first(&mn->objects, start, end);
it = interval_tree_iter_first(&mn->objects, range->start, end);
while (it) {
if (!blockable) {
if (!range->blockable) {
spin_unlock(&mn->lock);
return -EAGAIN;
}
Expand All @@ -151,7 +149,7 @@ static int i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
queue_work(mn->wq, &mo->work);

list_add(&mo->link, &cancelled);
it = interval_tree_iter_next(it, start, end);
it = interval_tree_iter_next(it, range->start, end);
}
list_for_each_entry(mo, &cancelled, link)
del_object(mo);
Expand Down
16 changes: 7 additions & 9 deletions drivers/gpu/drm/radeon/radeon_mn.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,40 +119,38 @@ static void radeon_mn_release(struct mmu_notifier *mn,
* unmap them by move them into system domain again.
*/
static int radeon_mn_invalidate_range_start(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn);
struct ttm_operation_ctx ctx = { false, false };
struct interval_tree_node *it;
unsigned long end;
int ret = 0;

/* notification is exclusive, but interval is inclusive */
end -= 1;
end = range->end - 1;

/* TODO we should be able to split locking for interval tree and
* the tear down.
*/
if (blockable)
if (range->blockable)
mutex_lock(&rmn->lock);
else if (!mutex_trylock(&rmn->lock))
return -EAGAIN;

it = interval_tree_iter_first(&rmn->objects, start, end);
it = interval_tree_iter_first(&rmn->objects, range->start, end);
while (it) {
struct radeon_mn_node *node;
struct radeon_bo *bo;
long r;

if (!blockable) {
if (!range->blockable) {
ret = -EAGAIN;
goto out_unlock;
}

node = container_of(it, struct radeon_mn_node, it);
it = interval_tree_iter_next(it, start, end);
it = interval_tree_iter_next(it, range->start, end);

list_for_each_entry(bo, &node->bos, mn_list) {

Expand Down
20 changes: 8 additions & 12 deletions drivers/infiniband/core/umem_odp.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,12 @@ static int invalidate_range_start_trampoline(struct ib_umem_odp *item,
}

static int ib_umem_notifier_invalidate_range_start(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct ib_ucontext_per_mm *per_mm =
container_of(mn, struct ib_ucontext_per_mm, mn);

if (blockable)
if (range->blockable)
down_read(&per_mm->umem_rwsem);
else if (!down_read_trylock(&per_mm->umem_rwsem))
return -EAGAIN;
Expand All @@ -169,9 +166,10 @@ static int ib_umem_notifier_invalidate_range_start(struct mmu_notifier *mn,
return 0;
}

return rbt_ib_umem_for_each_in_range(&per_mm->umem_tree, start, end,
return rbt_ib_umem_for_each_in_range(&per_mm->umem_tree, range->start,
range->end,
invalidate_range_start_trampoline,
blockable, NULL);
range->blockable, NULL);
}

static int invalidate_range_end_trampoline(struct ib_umem_odp *item, u64 start,
Expand All @@ -182,18 +180,16 @@ static int invalidate_range_end_trampoline(struct ib_umem_odp *item, u64 start,
}

static void ib_umem_notifier_invalidate_range_end(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end)
const struct mmu_notifier_range *range)
{
struct ib_ucontext_per_mm *per_mm =
container_of(mn, struct ib_ucontext_per_mm, mn);

if (unlikely(!per_mm->active))
return;

rbt_ib_umem_for_each_in_range(&per_mm->umem_tree, start,
end,
rbt_ib_umem_for_each_in_range(&per_mm->umem_tree, range->start,
range->end,
invalidate_range_end_trampoline, true, NULL);
up_read(&per_mm->umem_rwsem);
}
Expand Down
13 changes: 5 additions & 8 deletions drivers/infiniband/hw/hfi1/mmu_rb.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ struct mmu_rb_handler {
static unsigned long mmu_node_start(struct mmu_rb_node *);
static unsigned long mmu_node_last(struct mmu_rb_node *);
static int mmu_notifier_range_start(struct mmu_notifier *,
struct mm_struct *,
unsigned long, unsigned long, bool);
const struct mmu_notifier_range *);
static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *,
unsigned long, unsigned long);
static void do_remove(struct mmu_rb_handler *handler,
Expand Down Expand Up @@ -284,10 +283,7 @@ void hfi1_mmu_rb_remove(struct mmu_rb_handler *handler,
}

static int mmu_notifier_range_start(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct mmu_rb_handler *handler =
container_of(mn, struct mmu_rb_handler, mn);
Expand All @@ -297,10 +293,11 @@ static int mmu_notifier_range_start(struct mmu_notifier *mn,
bool added = false;

spin_lock_irqsave(&handler->lock, flags);
for (node = __mmu_int_rb_iter_first(root, start, end - 1);
for (node = __mmu_int_rb_iter_first(root, range->start, range->end-1);
node; node = ptr) {
/* Guard against node removal. */
ptr = __mmu_int_rb_iter_next(node, start, end - 1);
ptr = __mmu_int_rb_iter_next(node, range->start,
range->end - 1);
trace_hfi1_mmu_mem_invalidate(node->addr, node->len);
if (handler->ops->invalidate(handler->ops_arg, node)) {
__mmu_int_rb_remove(node, root);
Expand Down
11 changes: 3 additions & 8 deletions drivers/misc/mic/scif/scif_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,23 +201,18 @@ static void scif_mmu_notifier_release(struct mmu_notifier *mn,
}

static int scif_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct scif_mmu_notif *mmn;

mmn = container_of(mn, struct scif_mmu_notif, ep_mmu_notifier);
scif_rma_destroy_tcw(mmn, start, end - start);
scif_rma_destroy_tcw(mmn, range->start, range->end - range->start);

return 0;
}

static void scif_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end)
const struct mmu_notifier_range *range)
{
/*
* Nothing to do here, everything needed was done in
Expand Down
14 changes: 6 additions & 8 deletions drivers/misc/sgi-gru/grutlbpurge.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,25 +220,22 @@ void gru_flush_all_tlb(struct gru_state *gru)
* MMUOPS notifier callout functions
*/
static int gru_invalidate_range_start(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start, unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct,
ms_notifier);

STAT(mmu_invalidate_range);
atomic_inc(&gms->ms_range_active);
gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx, act %d\n", gms,
start, end, atomic_read(&gms->ms_range_active));
gru_flush_tlb_range(gms, start, end - start);
range->start, range->end, atomic_read(&gms->ms_range_active));
gru_flush_tlb_range(gms, range->start, range->end - range->start);

return 0;
}

static void gru_invalidate_range_end(struct mmu_notifier *mn,
struct mm_struct *mm, unsigned long start,
unsigned long end)
const struct mmu_notifier_range *range)
{
struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct,
ms_notifier);
Expand All @@ -247,7 +244,8 @@ static void gru_invalidate_range_end(struct mmu_notifier *mn,
(void)atomic_dec_and_test(&gms->ms_range_active);

wake_up_all(&gms->ms_wait_queue);
gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx\n", gms, start, end);
gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx\n",
gms, range->start, range->end);
}

static void gru_release(struct mmu_notifier *mn, struct mm_struct *mm)
Expand Down
12 changes: 6 additions & 6 deletions drivers/xen/gntdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,26 +520,26 @@ static int unmap_if_in_range(struct gntdev_grant_map *map,
}

static int mn_invl_range_start(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start, unsigned long end,
bool blockable)
const struct mmu_notifier_range *range)
{
struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn);
struct gntdev_grant_map *map;
int ret = 0;

if (blockable)
if (range->blockable)
mutex_lock(&priv->lock);
else if (!mutex_trylock(&priv->lock))
return -EAGAIN;

list_for_each_entry(map, &priv->maps, next) {
ret = unmap_if_in_range(map, start, end, blockable);
ret = unmap_if_in_range(map, range->start, range->end,
range->blockable);
if (ret)
goto out_unlock;
}
list_for_each_entry(map, &priv->freeable_maps, next) {
ret = unmap_if_in_range(map, start, end, blockable);
ret = unmap_if_in_range(map, range->start, range->end,
range->blockable);
if (ret)
goto out_unlock;
}
Expand Down
Loading

0 comments on commit 5d6527a

Please sign in to comment.