Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iov_iter: add copy_page_to_iter_nofault()

Provide a means to copy a page to user space from an iterator, aborting if
a page fault would occur. This supports compound pages, but may be passed
a tail page with an offset extending further into the compound page, so we
cannot pass a folio.

This allows for this function to be called from atomic context and _try_
to user pages if they are faulted in, aborting if not.

The function does not use _copy_to_iter() in order to not specify
might_fault(), this is similar to copy_page_from_iter_atomic().

This is being added in order that an iteratable form of vread() can be
implemented while holding spinlocks.

Link: https://lkml.kernel.org/r/19734729defb0f498a76bdec1bef3ac48a3af3e8.1679511146.git.lstoakes@gmail.com
Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
Reviewed-by: Baoquan He <bhe@redhat.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Liu Shixin <liushixin2@huawei.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Uladzislau Rezki (Sony) <urezki@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Lorenzo Stoakes and committed by
Andrew Morton
4f80818b 46c0d6d0

+50
+2
include/linux/uio.h
··· 173 173 { 174 174 return copy_page_to_iter(&folio->page, offset, bytes, i); 175 175 } 176 + size_t copy_page_to_iter_nofault(struct page *page, unsigned offset, 177 + size_t bytes, struct iov_iter *i); 176 178 177 179 static __always_inline __must_check 178 180 size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
+48
lib/iov_iter.c
··· 172 172 return n; 173 173 } 174 174 175 + static int copyout_nofault(void __user *to, const void *from, size_t n) 176 + { 177 + long res; 178 + 179 + if (should_fail_usercopy()) 180 + return n; 181 + 182 + res = copy_to_user_nofault(to, from, n); 183 + 184 + return res < 0 ? n : res; 185 + } 186 + 175 187 static int copyin(void *to, const void __user *from, size_t n) 176 188 { 177 189 size_t res = n; ··· 745 733 return res; 746 734 } 747 735 EXPORT_SYMBOL(copy_page_to_iter); 736 + 737 + size_t copy_page_to_iter_nofault(struct page *page, unsigned offset, size_t bytes, 738 + struct iov_iter *i) 739 + { 740 + size_t res = 0; 741 + 742 + if (!page_copy_sane(page, offset, bytes)) 743 + return 0; 744 + if (WARN_ON_ONCE(i->data_source)) 745 + return 0; 746 + if (unlikely(iov_iter_is_pipe(i))) 747 + return copy_page_to_iter_pipe(page, offset, bytes, i); 748 + page += offset / PAGE_SIZE; // first subpage 749 + offset %= PAGE_SIZE; 750 + while (1) { 751 + void *kaddr = kmap_local_page(page); 752 + size_t n = min(bytes, (size_t)PAGE_SIZE - offset); 753 + 754 + iterate_and_advance(i, n, base, len, off, 755 + copyout_nofault(base, kaddr + offset + off, len), 756 + memcpy(base, kaddr + offset + off, len) 757 + ) 758 + kunmap_local(kaddr); 759 + res += n; 760 + bytes -= n; 761 + if (!bytes || !n) 762 + break; 763 + offset += n; 764 + if (offset == PAGE_SIZE) { 765 + page++; 766 + offset = 0; 767 + } 768 + } 769 + return res; 770 + } 771 + EXPORT_SYMBOL(copy_page_to_iter_nofault); 748 772 749 773 size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, 750 774 struct iov_iter *i)