summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2019-12-02 03:41:13 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2019-12-02 03:41:13 +0000
commit50de43aba5d4474723b4571b57f533310777b15a (patch)
tree69c4a32458151c2f49c2784344be6903a83ac8a4
parenta47c980f74e2fdc21c1cb38a9476b13ab1a668b4 (diff)
drm/i915/userptr: Try to acquire the page lock around set_page_dirty()
From Chris Wilson e80e88ef6057c7947409bda9898387d25e54aaa9 in linux 4.19.y/4.19.87 2d691aeca4aecbb8d0414a777a46981a8e142b05 in mainline linux
-rw-r--r--sys/dev/pci/drm/i915/i915_gem_userptr.c22
1 files changed, 21 insertions, 1 deletions
diff --git a/sys/dev/pci/drm/i915/i915_gem_userptr.c b/sys/dev/pci/drm/i915/i915_gem_userptr.c
index 9a65830ede0..538b7415605 100644
--- a/sys/dev/pci/drm/i915/i915_gem_userptr.c
+++ b/sys/dev/pci/drm/i915/i915_gem_userptr.c
@@ -693,8 +693,28 @@ i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj,
i915_gem_gtt_finish_pages(obj, pages);
for_each_sgt_page(page, sgt_iter, pages) {
- if (obj->mm.dirty)
+ if (obj->mm.dirty && trylock_page(page)) {
+ /*
+ * As this may not be anonymous memory (e.g. shmem)
+ * but exist on a real mapping, we have to lock
+ * the page in order to dirty it -- holding
+ * the page reference is not sufficient to
+ * prevent the inode from being truncated.
+ * Play safe and take the lock.
+ *
+ * However...!
+ *
+ * The mmu-notifier can be invalidated for a
+ * migrate_page, that is alreadying holding the lock
+ * on the page. Such a try_to_unmap() will result
+ * in us calling put_pages() and so recursively try
+ * to lock the page. We avoid that deadlock with
+ * a trylock_page() and in exchange we risk missing
+ * some page dirtying.
+ */
set_page_dirty(page);
+ unlock_page(page);
+ }
mark_page_accessed(page);
put_page(page);