--- /dev/null
+From 6647e76ab623b2b3fb2efe03a86e9c9046c52c33 Mon Sep 17 00:00:00 2001
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Date: Wed, 30 Nov 2022 16:10:52 -0800
+Subject: v4l2: don't fall back to follow_pfn() if pin_user_pages_fast() fails
+
+From: Linus Torvalds <torvalds@linux-foundation.org>
+
+commit 6647e76ab623b2b3fb2efe03a86e9c9046c52c33 upstream.
+
+The V4L2_MEMORY_USERPTR interface is long deprecated and shouldn't be
+used (and is discouraged for any modern v4l drivers). And Seth Jenkins
+points out that the fallback to VM_PFNMAP/VM_IO is fundamentally racy
+and dangerous.
+
+Note that it's not even a case that should trigger, since any normal
+user pointer logic ends up just using the pin_user_pages_fast() call
+that does the proper page reference counting. That's not the problem
+case, only if you try to use special device mappings do you have any
+issues.
+
+Normally I'd just remove this during the merge window, but since Seth
+pointed out the problem cases, we really want to know as soon as
+possible if there are actually any users of this odd special case of a
+legacy interface. Neither Hans nor Mauro seem to think that such
+mis-uses of the old legacy interface should exist. As Mauro says:
+
+ "See, V4L2 has actually 4 streaming APIs:
+ - Kernel-allocated mmap (usually referred simply as just mmap);
+ - USERPTR mmap;
+ - read();
+ - dmabuf;
+
+ The USERPTR is one of the oldest way to use it, coming from V4L
+ version 1 times, and by far the least used one"
+
+And Hans chimed in on the USERPTR interface:
+
+ "To be honest, I wouldn't mind if it goes away completely, but that's a
+ bit of a pipe dream right now"
+
+but while removing this legacy interface entirely may be a pipe dream we
+can at least try to remove the unlikely (and actively broken) case of
+using special device mappings for USERPTR accesses.
+
+This replaces it with a WARN_ONCE() that we can remove once we've
+hopefully confirmed that no actual users exist.
+
+NOTE! Longer term, this means that a 'struct frame_vector' only ever
+contains proper page pointers, and all the games we have with converting
+them to pages can go away (grep for 'frame_vector_to_pages()' and the
+uses of 'vec->is_pfns'). But this is just the first step, to verify
+that this code really is all dead, and do so as quickly as possible.
+
+Reported-by: Seth Jenkins <sethjenkins@google.com>
+Acked-by: Hans Verkuil <hverkuil@xs4all.nl>
+Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+Cc: David Hildenbrand <david@redhat.com>
+Cc: Jan Kara <jack@suse.cz>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/frame_vector.c | 31 ++++++-------------------------
+ 1 file changed, 6 insertions(+), 25 deletions(-)
+
+--- a/mm/frame_vector.c
++++ b/mm/frame_vector.c
+@@ -37,7 +37,6 @@ int get_vaddr_frames(unsigned long start
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ int ret = 0;
+- int err;
+ int locked;
+
+ if (nr_frames == 0)
+@@ -74,32 +73,14 @@ int get_vaddr_frames(unsigned long start
+ vec->is_pfns = false;
+ ret = get_user_pages_locked(start, nr_frames,
+ gup_flags, (struct page **)(vec->ptrs), &locked);
+- goto out;
++ if (likely(ret > 0))
++ goto out;
+ }
+
+- vec->got_ref = false;
+- vec->is_pfns = true;
+- do {
+- unsigned long *nums = frame_vector_pfns(vec);
+-
+- while (ret < nr_frames && start + PAGE_SIZE <= vma->vm_end) {
+- err = follow_pfn(vma, start, &nums[ret]);
+- if (err) {
+- if (ret == 0)
+- ret = err;
+- goto out;
+- }
+- start += PAGE_SIZE;
+- ret++;
+- }
+- /*
+- * We stop if we have enough pages or if VMA doesn't completely
+- * cover the tail page.
+- */
+- if (ret >= nr_frames || start < vma->vm_end)
+- break;
+- vma = find_vma_intersection(mm, start, start + 1);
+- } while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP));
++ /* This used to (racily) return non-refcounted pfns. Let people know */
++ WARN_ONCE(1, "get_vaddr_frames() cannot follow VM_IO mapping");
++ vec->nr_frames = 0;
++
+ out:
+ if (locked)
+ up_read(&mm->mmap_sem);