forked from mirrors/linux
		
	vfs-6.7.iov_iter
-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCZTppQwAKCRCRxhvAZXjc
 om2kAP4u+eLsrhJHfUPUttGUEkSkZE+5/s/f1A/1GcV51usLSgEAu8urxAnP49GW
 INaDABXaFfKx8/KI/H2YFZPKGwlNEwY=
 =PDDE
 -----END PGP SIGNATURE-----
Merge tag 'vfs-6.7.iov_iter' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs
Pull iov_iter updates from Christian Brauner:
 "This contain's David's iov_iter cleanup work to convert the iov_iter
  iteration macros to inline functions:
   - Remove last_offset from iov_iter as it was only used by ITER_PIPE
   - Add a __user tag on copy_mc_to_user()'s dst argument on x86 to
     match that on powerpc and get rid of a sparse warning
   - Convert iter->user_backed to user_backed_iter() in the sound PCM
     driver
   - Convert iter->user_backed to user_backed_iter() in a couple of
     infiniband drivers
   - Renumber the type enum so that the ITER_* constants match the order
     in iterate_and_advance*()
   - Since the preceding patch puts UBUF and IOVEC at 0 and 1, change
     user_backed_iter() to just use the type value and get rid of the
     extra flag
   - Convert the iov_iter iteration macros to always-inline functions to
     make the code easier to follow. It uses function pointers, but they
     get optimised away
   - Move the check for ->copy_mc to _copy_from_iter() and
     copy_page_from_iter_atomic() rather than in memcpy_from_iter_mc()
     where it gets repeated for every segment. Instead, we check once
     and invoke a side function that can use iterate_bvec() rather than
     iterate_and_advance() and supply a different step function
   - Move the copy-and-csum code to net/ where it can be in proximity
     with the code that uses it
   - Fold memcpy_and_csum() in to its two users
   - Move csum_and_copy_from_iter_full() out of line and merge in
     csum_and_copy_from_iter() since the former is the only caller of
     the latter
   - Move hash_and_copy_to_iter() to net/ where it can be with its only
     caller"
* tag 'vfs-6.7.iov_iter' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs:
  iov_iter, net: Move hash_and_copy_to_iter() to net/
  iov_iter, net: Merge csum_and_copy_from_iter{,_full}() together
  iov_iter, net: Fold in csum_and_memcpy()
  iov_iter, net: Move csum_and_copy_to/from_iter() to net/
  iov_iter: Don't deal with iter->copy_mc in memcpy_from_iter_mc()
  iov_iter: Convert iterate*() to inline funcs
  iov_iter: Derive user-backedness from the iterator type
  iov_iter: Renumber ITER_* constants
  infiniband: Use user_backed_iter() to see if iterator is UBUF/IOVEC
  sound: Fix snd_pcm_readv()/writev() to use iov access functions
  iov_iter, x86: Be consistent about the __user tag on copy_mc_to_user()
  iov_iter: Remove last_offset from iov_iter as it was for ITER_PIPE
			
			
This commit is contained in:
		
						commit
						df9c65b5fc
					
				
					 11 changed files with 539 additions and 340 deletions
				
			
		|  | @ -496,7 +496,7 @@ copy_mc_to_kernel(void *to, const void *from, unsigned len); | ||||||
| #define copy_mc_to_kernel copy_mc_to_kernel | #define copy_mc_to_kernel copy_mc_to_kernel | ||||||
| 
 | 
 | ||||||
| unsigned long __must_check | unsigned long __must_check | ||||||
| copy_mc_to_user(void *to, const void *from, unsigned len); | copy_mc_to_user(void __user *to, const void *from, unsigned len); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
|  | @ -70,23 +70,23 @@ unsigned long __must_check copy_mc_to_kernel(void *dst, const void *src, unsigne | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(copy_mc_to_kernel); | EXPORT_SYMBOL_GPL(copy_mc_to_kernel); | ||||||
| 
 | 
 | ||||||
| unsigned long __must_check copy_mc_to_user(void *dst, const void *src, unsigned len) | unsigned long __must_check copy_mc_to_user(void __user *dst, const void *src, unsigned len) | ||||||
| { | { | ||||||
| 	unsigned long ret; | 	unsigned long ret; | ||||||
| 
 | 
 | ||||||
| 	if (copy_mc_fragile_enabled) { | 	if (copy_mc_fragile_enabled) { | ||||||
| 		__uaccess_begin(); | 		__uaccess_begin(); | ||||||
| 		ret = copy_mc_fragile(dst, src, len); | 		ret = copy_mc_fragile((__force void *)dst, src, len); | ||||||
| 		__uaccess_end(); | 		__uaccess_end(); | ||||||
| 		return ret; | 		return ret; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (static_cpu_has(X86_FEATURE_ERMS)) { | 	if (static_cpu_has(X86_FEATURE_ERMS)) { | ||||||
| 		__uaccess_begin(); | 		__uaccess_begin(); | ||||||
| 		ret = copy_mc_enhanced_fast_string(dst, src, len); | 		ret = copy_mc_enhanced_fast_string((__force void *)dst, src, len); | ||||||
| 		__uaccess_end(); | 		__uaccess_end(); | ||||||
| 		return ret; | 		return ret; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return copy_user_generic(dst, src, len); | 	return copy_user_generic((__force void *)dst, src, len); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -267,7 +267,7 @@ static ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from) | ||||||
| 
 | 
 | ||||||
| 	if (!HFI1_CAP_IS_KSET(SDMA)) | 	if (!HFI1_CAP_IS_KSET(SDMA)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	if (!from->user_backed) | 	if (!user_backed_iter(from)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	idx = srcu_read_lock(&fd->pq_srcu); | 	idx = srcu_read_lock(&fd->pq_srcu); | ||||||
| 	pq = srcu_dereference(fd->pq, &fd->pq_srcu); | 	pq = srcu_dereference(fd->pq, &fd->pq_srcu); | ||||||
|  |  | ||||||
|  | @ -2244,7 +2244,7 @@ static ssize_t qib_write_iter(struct kiocb *iocb, struct iov_iter *from) | ||||||
| 	struct qib_ctxtdata *rcd = ctxt_fp(iocb->ki_filp); | 	struct qib_ctxtdata *rcd = ctxt_fp(iocb->ki_filp); | ||||||
| 	struct qib_user_sdma_queue *pq = fp->pq; | 	struct qib_user_sdma_queue *pq = fp->pq; | ||||||
| 
 | 
 | ||||||
| 	if (!from->user_backed || !from->nr_segs || !pq) | 	if (!user_backed_iter(from) || !from->nr_segs || !pq) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	return qib_user_sdma_writev(rcd, pq, iter_iov(from), from->nr_segs); | 	return qib_user_sdma_writev(rcd, pq, iter_iov(from), from->nr_segs); | ||||||
|  |  | ||||||
							
								
								
									
										274
									
								
								include/linux/iov_iter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								include/linux/iov_iter.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,274 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||||
|  | /* I/O iterator iteration building functions.
 | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved. | ||||||
|  |  * Written by David Howells (dhowells@redhat.com) | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _LINUX_IOV_ITER_H | ||||||
|  | #define _LINUX_IOV_ITER_H | ||||||
|  | 
 | ||||||
|  | #include <linux/uio.h> | ||||||
|  | #include <linux/bvec.h> | ||||||
|  | 
 | ||||||
|  | typedef size_t (*iov_step_f)(void *iter_base, size_t progress, size_t len, | ||||||
|  | 			     void *priv, void *priv2); | ||||||
|  | typedef size_t (*iov_ustep_f)(void __user *iter_base, size_t progress, size_t len, | ||||||
|  | 			      void *priv, void *priv2); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Handle ITER_UBUF. | ||||||
|  |  */ | ||||||
|  | static __always_inline | ||||||
|  | size_t iterate_ubuf(struct iov_iter *iter, size_t len, void *priv, void *priv2, | ||||||
|  | 		    iov_ustep_f step) | ||||||
|  | { | ||||||
|  | 	void __user *base = iter->ubuf; | ||||||
|  | 	size_t progress = 0, remain; | ||||||
|  | 
 | ||||||
|  | 	remain = step(base + iter->iov_offset, 0, len, priv, priv2); | ||||||
|  | 	progress = len - remain; | ||||||
|  | 	iter->iov_offset += progress; | ||||||
|  | 	iter->count -= progress; | ||||||
|  | 	return progress; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Handle ITER_IOVEC. | ||||||
|  |  */ | ||||||
|  | static __always_inline | ||||||
|  | size_t iterate_iovec(struct iov_iter *iter, size_t len, void *priv, void *priv2, | ||||||
|  | 		     iov_ustep_f step) | ||||||
|  | { | ||||||
|  | 	const struct iovec *p = iter->__iov; | ||||||
|  | 	size_t progress = 0, skip = iter->iov_offset; | ||||||
|  | 
 | ||||||
|  | 	do { | ||||||
|  | 		size_t remain, consumed; | ||||||
|  | 		size_t part = min(len, p->iov_len - skip); | ||||||
|  | 
 | ||||||
|  | 		if (likely(part)) { | ||||||
|  | 			remain = step(p->iov_base + skip, progress, part, priv, priv2); | ||||||
|  | 			consumed = part - remain; | ||||||
|  | 			progress += consumed; | ||||||
|  | 			skip += consumed; | ||||||
|  | 			len -= consumed; | ||||||
|  | 			if (skip < p->iov_len) | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 		p++; | ||||||
|  | 		skip = 0; | ||||||
|  | 	} while (len); | ||||||
|  | 
 | ||||||
|  | 	iter->nr_segs -= p - iter->__iov; | ||||||
|  | 	iter->__iov = p; | ||||||
|  | 	iter->iov_offset = skip; | ||||||
|  | 	iter->count -= progress; | ||||||
|  | 	return progress; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Handle ITER_KVEC. | ||||||
|  |  */ | ||||||
|  | static __always_inline | ||||||
|  | size_t iterate_kvec(struct iov_iter *iter, size_t len, void *priv, void *priv2, | ||||||
|  | 		    iov_step_f step) | ||||||
|  | { | ||||||
|  | 	const struct kvec *p = iter->kvec; | ||||||
|  | 	size_t progress = 0, skip = iter->iov_offset; | ||||||
|  | 
 | ||||||
|  | 	do { | ||||||
|  | 		size_t remain, consumed; | ||||||
|  | 		size_t part = min(len, p->iov_len - skip); | ||||||
|  | 
 | ||||||
|  | 		if (likely(part)) { | ||||||
|  | 			remain = step(p->iov_base + skip, progress, part, priv, priv2); | ||||||
|  | 			consumed = part - remain; | ||||||
|  | 			progress += consumed; | ||||||
|  | 			skip += consumed; | ||||||
|  | 			len -= consumed; | ||||||
|  | 			if (skip < p->iov_len) | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 		p++; | ||||||
|  | 		skip = 0; | ||||||
|  | 	} while (len); | ||||||
|  | 
 | ||||||
|  | 	iter->nr_segs -= p - iter->kvec; | ||||||
|  | 	iter->kvec = p; | ||||||
|  | 	iter->iov_offset = skip; | ||||||
|  | 	iter->count -= progress; | ||||||
|  | 	return progress; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Handle ITER_BVEC. | ||||||
|  |  */ | ||||||
|  | static __always_inline | ||||||
|  | size_t iterate_bvec(struct iov_iter *iter, size_t len, void *priv, void *priv2, | ||||||
|  | 		    iov_step_f step) | ||||||
|  | { | ||||||
|  | 	const struct bio_vec *p = iter->bvec; | ||||||
|  | 	size_t progress = 0, skip = iter->iov_offset; | ||||||
|  | 
 | ||||||
|  | 	do { | ||||||
|  | 		size_t remain, consumed; | ||||||
|  | 		size_t offset = p->bv_offset + skip, part; | ||||||
|  | 		void *kaddr = kmap_local_page(p->bv_page + offset / PAGE_SIZE); | ||||||
|  | 
 | ||||||
|  | 		part = min3(len, | ||||||
|  | 			   (size_t)(p->bv_len - skip), | ||||||
|  | 			   (size_t)(PAGE_SIZE - offset % PAGE_SIZE)); | ||||||
|  | 		remain = step(kaddr + offset % PAGE_SIZE, progress, part, priv, priv2); | ||||||
|  | 		kunmap_local(kaddr); | ||||||
|  | 		consumed = part - remain; | ||||||
|  | 		len -= consumed; | ||||||
|  | 		progress += consumed; | ||||||
|  | 		skip += consumed; | ||||||
|  | 		if (skip >= p->bv_len) { | ||||||
|  | 			skip = 0; | ||||||
|  | 			p++; | ||||||
|  | 		} | ||||||
|  | 		if (remain) | ||||||
|  | 			break; | ||||||
|  | 	} while (len); | ||||||
|  | 
 | ||||||
|  | 	iter->nr_segs -= p - iter->bvec; | ||||||
|  | 	iter->bvec = p; | ||||||
|  | 	iter->iov_offset = skip; | ||||||
|  | 	iter->count -= progress; | ||||||
|  | 	return progress; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Handle ITER_XARRAY. | ||||||
|  |  */ | ||||||
|  | static __always_inline | ||||||
|  | size_t iterate_xarray(struct iov_iter *iter, size_t len, void *priv, void *priv2, | ||||||
|  | 		      iov_step_f step) | ||||||
|  | { | ||||||
|  | 	struct folio *folio; | ||||||
|  | 	size_t progress = 0; | ||||||
|  | 	loff_t start = iter->xarray_start + iter->iov_offset; | ||||||
|  | 	pgoff_t index = start / PAGE_SIZE; | ||||||
|  | 	XA_STATE(xas, iter->xarray, index); | ||||||
|  | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 	xas_for_each(&xas, folio, ULONG_MAX) { | ||||||
|  | 		size_t remain, consumed, offset, part, flen; | ||||||
|  | 
 | ||||||
|  | 		if (xas_retry(&xas, folio)) | ||||||
|  | 			continue; | ||||||
|  | 		if (WARN_ON(xa_is_value(folio))) | ||||||
|  | 			break; | ||||||
|  | 		if (WARN_ON(folio_test_hugetlb(folio))) | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		offset = offset_in_folio(folio, start + progress); | ||||||
|  | 		flen = min(folio_size(folio) - offset, len); | ||||||
|  | 
 | ||||||
|  | 		while (flen) { | ||||||
|  | 			void *base = kmap_local_folio(folio, offset); | ||||||
|  | 
 | ||||||
|  | 			part = min_t(size_t, flen, | ||||||
|  | 				     PAGE_SIZE - offset_in_page(offset)); | ||||||
|  | 			remain = step(base, progress, part, priv, priv2); | ||||||
|  | 			kunmap_local(base); | ||||||
|  | 
 | ||||||
|  | 			consumed = part - remain; | ||||||
|  | 			progress += consumed; | ||||||
|  | 			len -= consumed; | ||||||
|  | 
 | ||||||
|  | 			if (remain || len == 0) | ||||||
|  | 				goto out; | ||||||
|  | 			flen -= consumed; | ||||||
|  | 			offset += consumed; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  | 	rcu_read_unlock(); | ||||||
|  | 	iter->iov_offset += progress; | ||||||
|  | 	iter->count -= progress; | ||||||
|  | 	return progress; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Handle ITER_DISCARD. | ||||||
|  |  */ | ||||||
|  | static __always_inline | ||||||
|  | size_t iterate_discard(struct iov_iter *iter, size_t len, void *priv, void *priv2, | ||||||
|  | 		      iov_step_f step) | ||||||
|  | { | ||||||
|  | 	size_t progress = len; | ||||||
|  | 
 | ||||||
|  | 	iter->count -= progress; | ||||||
|  | 	return progress; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * iterate_and_advance2 - Iterate over an iterator | ||||||
|  |  * @iter: The iterator to iterate over. | ||||||
|  |  * @len: The amount to iterate over. | ||||||
|  |  * @priv: Data for the step functions. | ||||||
|  |  * @priv2: More data for the step functions. | ||||||
|  |  * @ustep: Function for UBUF/IOVEC iterators; given __user addresses. | ||||||
|  |  * @step: Function for other iterators; given kernel addresses. | ||||||
|  |  * | ||||||
|  |  * Iterate over the next part of an iterator, up to the specified length.  The | ||||||
|  |  * buffer is presented in segments, which for kernel iteration are broken up by | ||||||
|  |  * physical pages and mapped, with the mapped address being presented. | ||||||
|  |  * | ||||||
|  |  * Two step functions, @step and @ustep, must be provided, one for handling | ||||||
|  |  * mapped kernel addresses and the other is given user addresses which have the | ||||||
|  |  * potential to fault since no pinning is performed. | ||||||
|  |  * | ||||||
|  |  * The step functions are passed the address and length of the segment, @priv, | ||||||
|  |  * @priv2 and the amount of data so far iterated over (which can, for example, | ||||||
|  |  * be added to @priv to point to the right part of a second buffer).  The step | ||||||
|  |  * functions should return the amount of the segment they didn't process (ie. 0 | ||||||
|  |  * indicates complete processsing). | ||||||
|  |  * | ||||||
|  |  * This function returns the amount of data processed (ie. 0 means nothing was | ||||||
|  |  * processed and the value of @len means processes to completion). | ||||||
|  |  */ | ||||||
|  | static __always_inline | ||||||
|  | size_t iterate_and_advance2(struct iov_iter *iter, size_t len, void *priv, | ||||||
|  | 			    void *priv2, iov_ustep_f ustep, iov_step_f step) | ||||||
|  | { | ||||||
|  | 	if (unlikely(iter->count < len)) | ||||||
|  | 		len = iter->count; | ||||||
|  | 	if (unlikely(!len)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	if (likely(iter_is_ubuf(iter))) | ||||||
|  | 		return iterate_ubuf(iter, len, priv, priv2, ustep); | ||||||
|  | 	if (likely(iter_is_iovec(iter))) | ||||||
|  | 		return iterate_iovec(iter, len, priv, priv2, ustep); | ||||||
|  | 	if (iov_iter_is_bvec(iter)) | ||||||
|  | 		return iterate_bvec(iter, len, priv, priv2, step); | ||||||
|  | 	if (iov_iter_is_kvec(iter)) | ||||||
|  | 		return iterate_kvec(iter, len, priv, priv2, step); | ||||||
|  | 	if (iov_iter_is_xarray(iter)) | ||||||
|  | 		return iterate_xarray(iter, len, priv, priv2, step); | ||||||
|  | 	return iterate_discard(iter, len, priv, priv2, step); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * iterate_and_advance - Iterate over an iterator | ||||||
|  |  * @iter: The iterator to iterate over. | ||||||
|  |  * @len: The amount to iterate over. | ||||||
|  |  * @priv: Data for the step functions. | ||||||
|  |  * @ustep: Function for UBUF/IOVEC iterators; given __user addresses. | ||||||
|  |  * @step: Function for other iterators; given kernel addresses. | ||||||
|  |  * | ||||||
|  |  * As iterate_and_advance2(), but priv2 is always NULL. | ||||||
|  |  */ | ||||||
|  | static __always_inline | ||||||
|  | size_t iterate_and_advance(struct iov_iter *iter, size_t len, void *priv, | ||||||
|  | 			   iov_ustep_f ustep, iov_step_f step) | ||||||
|  | { | ||||||
|  | 	return iterate_and_advance2(iter, len, priv, NULL, ustep, step); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif /* _LINUX_IOV_ITER_H */ | ||||||
|  | @ -3679,6 +3679,9 @@ static inline int __must_check skb_put_padto(struct sk_buff *skb, unsigned int l | ||||||
| 	return __skb_put_padto(skb, len, true); | 	return __skb_put_padto(skb, len, true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool csum_and_copy_from_iter_full(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i) | ||||||
|  | 	__must_check; | ||||||
|  | 
 | ||||||
| static inline int skb_add_data(struct sk_buff *skb, | static inline int skb_add_data(struct sk_buff *skb, | ||||||
| 			       struct iov_iter *from, int copy) | 			       struct iov_iter *from, int copy) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -21,12 +21,12 @@ struct kvec { | ||||||
| 
 | 
 | ||||||
| enum iter_type { | enum iter_type { | ||||||
| 	/* iter types */ | 	/* iter types */ | ||||||
|  | 	ITER_UBUF, | ||||||
| 	ITER_IOVEC, | 	ITER_IOVEC, | ||||||
| 	ITER_KVEC, |  | ||||||
| 	ITER_BVEC, | 	ITER_BVEC, | ||||||
|  | 	ITER_KVEC, | ||||||
| 	ITER_XARRAY, | 	ITER_XARRAY, | ||||||
| 	ITER_DISCARD, | 	ITER_DISCARD, | ||||||
| 	ITER_UBUF, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define ITER_SOURCE	1	// == WRITE
 | #define ITER_SOURCE	1	// == WRITE
 | ||||||
|  | @ -43,11 +43,7 @@ struct iov_iter { | ||||||
| 	bool copy_mc; | 	bool copy_mc; | ||||||
| 	bool nofault; | 	bool nofault; | ||||||
| 	bool data_source; | 	bool data_source; | ||||||
| 	bool user_backed; |  | ||||||
| 	union { |  | ||||||
| 	size_t iov_offset; | 	size_t iov_offset; | ||||||
| 		int last_offset; |  | ||||||
| 	}; |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Hack alert: overlay ubuf_iovec with iovec + count, so | 	 * Hack alert: overlay ubuf_iovec with iovec + count, so | ||||||
| 	 * that the members resolve correctly regardless of the type | 	 * that the members resolve correctly regardless of the type | ||||||
|  | @ -143,7 +139,7 @@ static inline unsigned char iov_iter_rw(const struct iov_iter *i) | ||||||
| 
 | 
 | ||||||
| static inline bool user_backed_iter(const struct iov_iter *i) | static inline bool user_backed_iter(const struct iov_iter *i) | ||||||
| { | { | ||||||
| 	return i->user_backed; | 	return iter_is_ubuf(i) || iter_is_iovec(i); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -342,27 +338,6 @@ iov_iter_npages_cap(struct iov_iter *i, int maxpages, size_t max_bytes) | ||||||
| 	return npages; | 	return npages; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct csum_state { |  | ||||||
| 	__wsum csum; |  | ||||||
| 	size_t off; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| size_t csum_and_copy_to_iter(const void *addr, size_t bytes, void *csstate, struct iov_iter *i); |  | ||||||
| size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i); |  | ||||||
| 
 |  | ||||||
| static __always_inline __must_check |  | ||||||
| bool csum_and_copy_from_iter_full(void *addr, size_t bytes, |  | ||||||
| 				  __wsum *csum, struct iov_iter *i) |  | ||||||
| { |  | ||||||
| 	size_t copied = csum_and_copy_from_iter(addr, bytes, csum, i); |  | ||||||
| 	if (likely(copied == bytes)) |  | ||||||
| 		return true; |  | ||||||
| 	iov_iter_revert(i, copied); |  | ||||||
| 	return false; |  | ||||||
| } |  | ||||||
| size_t hash_and_copy_to_iter(const void *addr, size_t bytes, void *hashp, |  | ||||||
| 		struct iov_iter *i); |  | ||||||
| 
 |  | ||||||
| struct iovec *iovec_from_user(const struct iovec __user *uvector, | struct iovec *iovec_from_user(const struct iovec __user *uvector, | ||||||
| 		unsigned long nr_segs, unsigned long fast_segs, | 		unsigned long nr_segs, unsigned long fast_segs, | ||||||
| 		struct iovec *fast_iov, bool compat); | 		struct iovec *fast_iov, bool compat); | ||||||
|  | @ -383,7 +358,6 @@ static inline void iov_iter_ubuf(struct iov_iter *i, unsigned int direction, | ||||||
| 	*i = (struct iov_iter) { | 	*i = (struct iov_iter) { | ||||||
| 		.iter_type = ITER_UBUF, | 		.iter_type = ITER_UBUF, | ||||||
| 		.copy_mc = false, | 		.copy_mc = false, | ||||||
| 		.user_backed = true, |  | ||||||
| 		.data_source = direction, | 		.data_source = direction, | ||||||
| 		.ubuf = buf, | 		.ubuf = buf, | ||||||
| 		.count = count, | 		.count = count, | ||||||
|  |  | ||||||
							
								
								
									
										435
									
								
								lib/iov_iter.c
									
									
									
									
									
								
							
							
						
						
									
										435
									
								
								lib/iov_iter.c
									
									
									
									
									
								
							|  | @ -1,5 +1,4 @@ | ||||||
| // SPDX-License-Identifier: GPL-2.0-only
 | // SPDX-License-Identifier: GPL-2.0-only
 | ||||||
| #include <crypto/hash.h> |  | ||||||
| #include <linux/export.h> | #include <linux/export.h> | ||||||
| #include <linux/bvec.h> | #include <linux/bvec.h> | ||||||
| #include <linux/fault-inject-usercopy.h> | #include <linux/fault-inject-usercopy.h> | ||||||
|  | @ -10,192 +9,71 @@ | ||||||
| #include <linux/vmalloc.h> | #include <linux/vmalloc.h> | ||||||
| #include <linux/splice.h> | #include <linux/splice.h> | ||||||
| #include <linux/compat.h> | #include <linux/compat.h> | ||||||
| #include <net/checksum.h> |  | ||||||
| #include <linux/scatterlist.h> | #include <linux/scatterlist.h> | ||||||
| #include <linux/instrumented.h> | #include <linux/instrumented.h> | ||||||
|  | #include <linux/iov_iter.h> | ||||||
| 
 | 
 | ||||||
| /* covers ubuf and kbuf alike */ | static __always_inline | ||||||
| #define iterate_buf(i, n, base, len, off, __p, STEP) {		\ | size_t copy_to_user_iter(void __user *iter_to, size_t progress, | ||||||
| 	size_t __maybe_unused off = 0;				\ | 			 size_t len, void *from, void *priv2) | ||||||
| 	len = n;						\ |  | ||||||
| 	base = __p + i->iov_offset;				\ |  | ||||||
| 	len -= (STEP);						\ |  | ||||||
| 	i->iov_offset += len;					\ |  | ||||||
| 	n = len;						\ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* covers iovec and kvec alike */ |  | ||||||
| #define iterate_iovec(i, n, base, len, off, __p, STEP) {	\ |  | ||||||
| 	size_t off = 0;						\ |  | ||||||
| 	size_t skip = i->iov_offset;				\ |  | ||||||
| 	do {							\ |  | ||||||
| 		len = min(n, __p->iov_len - skip);		\ |  | ||||||
| 		if (likely(len)) {				\ |  | ||||||
| 			base = __p->iov_base + skip;		\ |  | ||||||
| 			len -= (STEP);				\ |  | ||||||
| 			off += len;				\ |  | ||||||
| 			skip += len;				\ |  | ||||||
| 			n -= len;				\ |  | ||||||
| 			if (skip < __p->iov_len)		\ |  | ||||||
| 				break;				\ |  | ||||||
| 		}						\ |  | ||||||
| 		__p++;						\ |  | ||||||
| 		skip = 0;					\ |  | ||||||
| 	} while (n);						\ |  | ||||||
| 	i->iov_offset = skip;					\ |  | ||||||
| 	n = off;						\ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #define iterate_bvec(i, n, base, len, off, p, STEP) {		\ |  | ||||||
| 	size_t off = 0;						\ |  | ||||||
| 	unsigned skip = i->iov_offset;				\ |  | ||||||
| 	while (n) {						\ |  | ||||||
| 		unsigned offset = p->bv_offset + skip;		\ |  | ||||||
| 		unsigned left;					\ |  | ||||||
| 		void *kaddr = kmap_local_page(p->bv_page +	\ |  | ||||||
| 					offset / PAGE_SIZE);	\ |  | ||||||
| 		base = kaddr + offset % PAGE_SIZE;		\ |  | ||||||
| 		len = min(min(n, (size_t)(p->bv_len - skip)),	\ |  | ||||||
| 		     (size_t)(PAGE_SIZE - offset % PAGE_SIZE));	\ |  | ||||||
| 		left = (STEP);					\ |  | ||||||
| 		kunmap_local(kaddr);				\ |  | ||||||
| 		len -= left;					\ |  | ||||||
| 		off += len;					\ |  | ||||||
| 		skip += len;					\ |  | ||||||
| 		if (skip == p->bv_len) {			\ |  | ||||||
| 			skip = 0;				\ |  | ||||||
| 			p++;					\ |  | ||||||
| 		}						\ |  | ||||||
| 		n -= len;					\ |  | ||||||
| 		if (left)					\ |  | ||||||
| 			break;					\ |  | ||||||
| 	}							\ |  | ||||||
| 	i->iov_offset = skip;					\ |  | ||||||
| 	n = off;						\ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #define iterate_xarray(i, n, base, len, __off, STEP) {		\ |  | ||||||
| 	__label__ __out;					\ |  | ||||||
| 	size_t __off = 0;					\ |  | ||||||
| 	struct folio *folio;					\ |  | ||||||
| 	loff_t start = i->xarray_start + i->iov_offset;		\ |  | ||||||
| 	pgoff_t index = start / PAGE_SIZE;			\ |  | ||||||
| 	XA_STATE(xas, i->xarray, index);			\ |  | ||||||
| 								\ |  | ||||||
| 	len = PAGE_SIZE - offset_in_page(start);		\ |  | ||||||
| 	rcu_read_lock();					\ |  | ||||||
| 	xas_for_each(&xas, folio, ULONG_MAX) {			\ |  | ||||||
| 		unsigned left;					\ |  | ||||||
| 		size_t offset;					\ |  | ||||||
| 		if (xas_retry(&xas, folio))			\ |  | ||||||
| 			continue;				\ |  | ||||||
| 		if (WARN_ON(xa_is_value(folio)))		\ |  | ||||||
| 			break;					\ |  | ||||||
| 		if (WARN_ON(folio_test_hugetlb(folio)))		\ |  | ||||||
| 			break;					\ |  | ||||||
| 		offset = offset_in_folio(folio, start + __off);	\ |  | ||||||
| 		while (offset < folio_size(folio)) {		\ |  | ||||||
| 			base = kmap_local_folio(folio, offset);	\ |  | ||||||
| 			len = min(n, len);			\ |  | ||||||
| 			left = (STEP);				\ |  | ||||||
| 			kunmap_local(base);			\ |  | ||||||
| 			len -= left;				\ |  | ||||||
| 			__off += len;				\ |  | ||||||
| 			n -= len;				\ |  | ||||||
| 			if (left || n == 0)			\ |  | ||||||
| 				goto __out;			\ |  | ||||||
| 			offset += len;				\ |  | ||||||
| 			len = PAGE_SIZE;			\ |  | ||||||
| 		}						\ |  | ||||||
| 	}							\ |  | ||||||
| __out:								\ |  | ||||||
| 	rcu_read_unlock();					\ |  | ||||||
| 	i->iov_offset += __off;					\ |  | ||||||
| 	n = __off;						\ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #define __iterate_and_advance(i, n, base, len, off, I, K) {	\ |  | ||||||
| 	if (unlikely(i->count < n))				\ |  | ||||||
| 		n = i->count;					\ |  | ||||||
| 	if (likely(n)) {					\ |  | ||||||
| 		if (likely(iter_is_ubuf(i))) {			\ |  | ||||||
| 			void __user *base;			\ |  | ||||||
| 			size_t len;				\ |  | ||||||
| 			iterate_buf(i, n, base, len, off,	\ |  | ||||||
| 						i->ubuf, (I)) 	\ |  | ||||||
| 		} else if (likely(iter_is_iovec(i))) {		\ |  | ||||||
| 			const struct iovec *iov = iter_iov(i);	\ |  | ||||||
| 			void __user *base;			\ |  | ||||||
| 			size_t len;				\ |  | ||||||
| 			iterate_iovec(i, n, base, len, off,	\ |  | ||||||
| 						iov, (I))	\ |  | ||||||
| 			i->nr_segs -= iov - iter_iov(i);	\ |  | ||||||
| 			i->__iov = iov;				\ |  | ||||||
| 		} else if (iov_iter_is_bvec(i)) {		\ |  | ||||||
| 			const struct bio_vec *bvec = i->bvec;	\ |  | ||||||
| 			void *base;				\ |  | ||||||
| 			size_t len;				\ |  | ||||||
| 			iterate_bvec(i, n, base, len, off,	\ |  | ||||||
| 						bvec, (K))	\ |  | ||||||
| 			i->nr_segs -= bvec - i->bvec;		\ |  | ||||||
| 			i->bvec = bvec;				\ |  | ||||||
| 		} else if (iov_iter_is_kvec(i)) {		\ |  | ||||||
| 			const struct kvec *kvec = i->kvec;	\ |  | ||||||
| 			void *base;				\ |  | ||||||
| 			size_t len;				\ |  | ||||||
| 			iterate_iovec(i, n, base, len, off,	\ |  | ||||||
| 						kvec, (K))	\ |  | ||||||
| 			i->nr_segs -= kvec - i->kvec;		\ |  | ||||||
| 			i->kvec = kvec;				\ |  | ||||||
| 		} else if (iov_iter_is_xarray(i)) {		\ |  | ||||||
| 			void *base;				\ |  | ||||||
| 			size_t len;				\ |  | ||||||
| 			iterate_xarray(i, n, base, len, off,	\ |  | ||||||
| 							(K))	\ |  | ||||||
| 		}						\ |  | ||||||
| 		i->count -= n;					\ |  | ||||||
| 	}							\ |  | ||||||
| } |  | ||||||
| #define iterate_and_advance(i, n, base, len, off, I, K) \ |  | ||||||
| 	__iterate_and_advance(i, n, base, len, off, I, ((void)(K),0)) |  | ||||||
| 
 |  | ||||||
| static int copyout(void __user *to, const void *from, size_t n) |  | ||||||
| { | { | ||||||
| 	if (should_fail_usercopy()) | 	if (should_fail_usercopy()) | ||||||
| 		return n; | 		return len; | ||||||
| 	if (access_ok(to, n)) { | 	if (access_ok(iter_to, len)) { | ||||||
| 		instrument_copy_to_user(to, from, n); | 		from += progress; | ||||||
| 		n = raw_copy_to_user(to, from, n); | 		instrument_copy_to_user(iter_to, from, len); | ||||||
|  | 		len = raw_copy_to_user(iter_to, from, len); | ||||||
| 	} | 	} | ||||||
| 	return n; | 	return len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int copyout_nofault(void __user *to, const void *from, size_t n) | static __always_inline | ||||||
|  | size_t copy_to_user_iter_nofault(void __user *iter_to, size_t progress, | ||||||
|  | 				 size_t len, void *from, void *priv2) | ||||||
| { | { | ||||||
| 	long res; | 	ssize_t res; | ||||||
| 
 | 
 | ||||||
| 	if (should_fail_usercopy()) | 	if (should_fail_usercopy()) | ||||||
| 		return n; | 		return len; | ||||||
| 
 | 
 | ||||||
| 	res = copy_to_user_nofault(to, from, n); | 	from += progress; | ||||||
| 
 | 	res = copy_to_user_nofault(iter_to, from, len); | ||||||
| 	return res < 0 ? n : res; | 	return res < 0 ? len : res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int copyin(void *to, const void __user *from, size_t n) | static __always_inline | ||||||
|  | size_t copy_from_user_iter(void __user *iter_from, size_t progress, | ||||||
|  | 			   size_t len, void *to, void *priv2) | ||||||
| { | { | ||||||
| 	size_t res = n; | 	size_t res = len; | ||||||
| 
 | 
 | ||||||
| 	if (should_fail_usercopy()) | 	if (should_fail_usercopy()) | ||||||
| 		return n; | 		return len; | ||||||
| 	if (access_ok(from, n)) { | 	if (access_ok(iter_from, len)) { | ||||||
| 		instrument_copy_from_user_before(to, from, n); | 		to += progress; | ||||||
| 		res = raw_copy_from_user(to, from, n); | 		instrument_copy_from_user_before(to, iter_from, len); | ||||||
| 		instrument_copy_from_user_after(to, from, n, res); | 		res = raw_copy_from_user(to, iter_from, len); | ||||||
|  | 		instrument_copy_from_user_after(to, iter_from, len, res); | ||||||
| 	} | 	} | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t memcpy_to_iter(void *iter_to, size_t progress, | ||||||
|  | 		      size_t len, void *from, void *priv2) | ||||||
|  | { | ||||||
|  | 	memcpy(iter_to, from + progress, len); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t memcpy_from_iter(void *iter_from, size_t progress, | ||||||
|  | 			size_t len, void *to, void *priv2) | ||||||
|  | { | ||||||
|  | 	memcpy(to + progress, iter_from, len); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * fault_in_iov_iter_readable - fault in iov iterator for reading |  * fault_in_iov_iter_readable - fault in iov iterator for reading | ||||||
|  * @i: iterator |  * @i: iterator | ||||||
|  | @ -290,7 +168,6 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction, | ||||||
| 		.iter_type = ITER_IOVEC, | 		.iter_type = ITER_IOVEC, | ||||||
| 		.copy_mc = false, | 		.copy_mc = false, | ||||||
| 		.nofault = false, | 		.nofault = false, | ||||||
| 		.user_backed = true, |  | ||||||
| 		.data_source = direction, | 		.data_source = direction, | ||||||
| 		.__iov = iov, | 		.__iov = iov, | ||||||
| 		.nr_segs = nr_segs, | 		.nr_segs = nr_segs, | ||||||
|  | @ -300,36 +177,35 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(iov_iter_init); | EXPORT_SYMBOL(iov_iter_init); | ||||||
| 
 | 
 | ||||||
| static __wsum csum_and_memcpy(void *to, const void *from, size_t len, |  | ||||||
| 			      __wsum sum, size_t off) |  | ||||||
| { |  | ||||||
| 	__wsum next = csum_partial_copy_nocheck(from, to, len); |  | ||||||
| 	return csum_block_add(sum, next, off); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i) | size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i) | ||||||
| { | { | ||||||
| 	if (WARN_ON_ONCE(i->data_source)) | 	if (WARN_ON_ONCE(i->data_source)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	if (user_backed_iter(i)) | 	if (user_backed_iter(i)) | ||||||
| 		might_fault(); | 		might_fault(); | ||||||
| 	iterate_and_advance(i, bytes, base, len, off, | 	return iterate_and_advance(i, bytes, (void *)addr, | ||||||
| 		copyout(base, addr + off, len), | 				   copy_to_user_iter, memcpy_to_iter); | ||||||
| 		memcpy(base, addr + off, len) |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	return bytes; |  | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(_copy_to_iter); | EXPORT_SYMBOL(_copy_to_iter); | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_ARCH_HAS_COPY_MC | #ifdef CONFIG_ARCH_HAS_COPY_MC | ||||||
| static int copyout_mc(void __user *to, const void *from, size_t n) | static __always_inline | ||||||
|  | size_t copy_to_user_iter_mc(void __user *iter_to, size_t progress, | ||||||
|  | 			    size_t len, void *from, void *priv2) | ||||||
| { | { | ||||||
| 	if (access_ok(to, n)) { | 	if (access_ok(iter_to, len)) { | ||||||
| 		instrument_copy_to_user(to, from, n); | 		from += progress; | ||||||
| 		n = copy_mc_to_user((__force void *) to, from, n); | 		instrument_copy_to_user(iter_to, from, len); | ||||||
|  | 		len = copy_mc_to_user(iter_to, from, len); | ||||||
| 	} | 	} | ||||||
| 	return n; | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t memcpy_to_iter_mc(void *iter_to, size_t progress, | ||||||
|  | 			 size_t len, void *from, void *priv2) | ||||||
|  | { | ||||||
|  | 	return copy_mc_to_kernel(iter_to, from + progress, len); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -362,22 +238,35 @@ size_t _copy_mc_to_iter(const void *addr, size_t bytes, struct iov_iter *i) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	if (user_backed_iter(i)) | 	if (user_backed_iter(i)) | ||||||
| 		might_fault(); | 		might_fault(); | ||||||
| 	__iterate_and_advance(i, bytes, base, len, off, | 	return iterate_and_advance(i, bytes, (void *)addr, | ||||||
| 		copyout_mc(base, addr + off, len), | 				   copy_to_user_iter_mc, memcpy_to_iter_mc); | ||||||
| 		copy_mc_to_kernel(base, addr + off, len) |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	return bytes; |  | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(_copy_mc_to_iter); | EXPORT_SYMBOL_GPL(_copy_mc_to_iter); | ||||||
| #endif /* CONFIG_ARCH_HAS_COPY_MC */ | #endif /* CONFIG_ARCH_HAS_COPY_MC */ | ||||||
| 
 | 
 | ||||||
| static void *memcpy_from_iter(struct iov_iter *i, void *to, const void *from, | static __always_inline | ||||||
| 				 size_t size) | size_t memcpy_from_iter_mc(void *iter_from, size_t progress, | ||||||
|  | 			   size_t len, void *to, void *priv2) | ||||||
| { | { | ||||||
| 	if (iov_iter_is_copy_mc(i)) | 	return copy_mc_to_kernel(to + progress, iter_from, len); | ||||||
| 		return (void *)copy_mc_to_kernel(to, from, size); | } | ||||||
| 	return memcpy(to, from, size); | 
 | ||||||
|  | static size_t __copy_from_iter_mc(void *addr, size_t bytes, struct iov_iter *i) | ||||||
|  | { | ||||||
|  | 	if (unlikely(i->count < bytes)) | ||||||
|  | 		bytes = i->count; | ||||||
|  | 	if (unlikely(!bytes)) | ||||||
|  | 		return 0; | ||||||
|  | 	return iterate_bvec(i, bytes, addr, NULL, memcpy_from_iter_mc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t __copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) | ||||||
|  | { | ||||||
|  | 	if (unlikely(iov_iter_is_copy_mc(i))) | ||||||
|  | 		return __copy_from_iter_mc(addr, bytes, i); | ||||||
|  | 	return iterate_and_advance(i, bytes, addr, | ||||||
|  | 				   copy_from_user_iter, memcpy_from_iter); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) | size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) | ||||||
|  | @ -387,30 +276,44 @@ size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) | ||||||
| 
 | 
 | ||||||
| 	if (user_backed_iter(i)) | 	if (user_backed_iter(i)) | ||||||
| 		might_fault(); | 		might_fault(); | ||||||
| 	iterate_and_advance(i, bytes, base, len, off, | 	return __copy_from_iter(addr, bytes, i); | ||||||
| 		copyin(addr + off, base, len), |  | ||||||
| 		memcpy_from_iter(i, addr + off, base, len) |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	return bytes; |  | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(_copy_from_iter); | EXPORT_SYMBOL(_copy_from_iter); | ||||||
| 
 | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t copy_from_user_iter_nocache(void __user *iter_from, size_t progress, | ||||||
|  | 				   size_t len, void *to, void *priv2) | ||||||
|  | { | ||||||
|  | 	return __copy_from_user_inatomic_nocache(to + progress, iter_from, len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| size_t _copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i) | size_t _copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i) | ||||||
| { | { | ||||||
| 	if (WARN_ON_ONCE(!i->data_source)) | 	if (WARN_ON_ONCE(!i->data_source)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	iterate_and_advance(i, bytes, base, len, off, | 	return iterate_and_advance(i, bytes, addr, | ||||||
| 		__copy_from_user_inatomic_nocache(addr + off, base, len), | 				   copy_from_user_iter_nocache, | ||||||
| 		memcpy(addr + off, base, len) | 				   memcpy_from_iter); | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	return bytes; |  | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(_copy_from_iter_nocache); | EXPORT_SYMBOL(_copy_from_iter_nocache); | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE | #ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE | ||||||
|  | static __always_inline | ||||||
|  | size_t copy_from_user_iter_flushcache(void __user *iter_from, size_t progress, | ||||||
|  | 				      size_t len, void *to, void *priv2) | ||||||
|  | { | ||||||
|  | 	return __copy_from_user_flushcache(to + progress, iter_from, len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t memcpy_from_iter_flushcache(void *iter_from, size_t progress, | ||||||
|  | 				   size_t len, void *to, void *priv2) | ||||||
|  | { | ||||||
|  | 	memcpy_flushcache(to + progress, iter_from, len); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * _copy_from_iter_flushcache - write destination through cpu cache |  * _copy_from_iter_flushcache - write destination through cpu cache | ||||||
|  * @addr: destination kernel address |  * @addr: destination kernel address | ||||||
|  | @ -432,12 +335,9 @@ size_t _copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i) | ||||||
| 	if (WARN_ON_ONCE(!i->data_source)) | 	if (WARN_ON_ONCE(!i->data_source)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	iterate_and_advance(i, bytes, base, len, off, | 	return iterate_and_advance(i, bytes, addr, | ||||||
| 		__copy_from_user_flushcache(addr + off, base, len), | 				   copy_from_user_iter_flushcache, | ||||||
| 		memcpy_flushcache(addr + off, base, len) | 				   memcpy_from_iter_flushcache); | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	return bytes; |  | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(_copy_from_iter_flushcache); | EXPORT_SYMBOL_GPL(_copy_from_iter_flushcache); | ||||||
| #endif | #endif | ||||||
|  | @ -509,10 +409,9 @@ size_t copy_page_to_iter_nofault(struct page *page, unsigned offset, size_t byte | ||||||
| 		void *kaddr = kmap_local_page(page); | 		void *kaddr = kmap_local_page(page); | ||||||
| 		size_t n = min(bytes, (size_t)PAGE_SIZE - offset); | 		size_t n = min(bytes, (size_t)PAGE_SIZE - offset); | ||||||
| 
 | 
 | ||||||
| 		iterate_and_advance(i, n, base, len, off, | 		n = iterate_and_advance(i, bytes, kaddr, | ||||||
| 			copyout_nofault(base, kaddr + offset + off, len), | 					copy_to_user_iter_nofault, | ||||||
| 			memcpy(base, kaddr + offset + off, len) | 					memcpy_to_iter); | ||||||
| 		) |  | ||||||
| 		kunmap_local(kaddr); | 		kunmap_local(kaddr); | ||||||
| 		res += n; | 		res += n; | ||||||
| 		bytes -= n; | 		bytes -= n; | ||||||
|  | @ -555,14 +454,25 @@ size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(copy_page_from_iter); | EXPORT_SYMBOL(copy_page_from_iter); | ||||||
| 
 | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t zero_to_user_iter(void __user *iter_to, size_t progress, | ||||||
|  | 			 size_t len, void *priv, void *priv2) | ||||||
|  | { | ||||||
|  | 	return clear_user(iter_to, len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t zero_to_iter(void *iter_to, size_t progress, | ||||||
|  | 		    size_t len, void *priv, void *priv2) | ||||||
|  | { | ||||||
|  | 	memset(iter_to, 0, len); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| size_t iov_iter_zero(size_t bytes, struct iov_iter *i) | size_t iov_iter_zero(size_t bytes, struct iov_iter *i) | ||||||
| { | { | ||||||
| 	iterate_and_advance(i, bytes, base, len, count, | 	return iterate_and_advance(i, bytes, NULL, | ||||||
| 		clear_user(base, len), | 				   zero_to_user_iter, zero_to_iter); | ||||||
| 		memset(base, 0, len) |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	return bytes; |  | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(iov_iter_zero); | EXPORT_SYMBOL(iov_iter_zero); | ||||||
| 
 | 
 | ||||||
|  | @ -587,10 +497,7 @@ size_t copy_page_from_iter_atomic(struct page *page, size_t offset, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		p = kmap_atomic(page) + offset; | 		p = kmap_atomic(page) + offset; | ||||||
| 		iterate_and_advance(i, n, base, len, off, | 		n = __copy_from_iter(p, n, i); | ||||||
| 			copyin(p + off, base, len), |  | ||||||
| 			memcpy_from_iter(i, p + off, base, len) |  | ||||||
| 		) |  | ||||||
| 		kunmap_atomic(p); | 		kunmap_atomic(p); | ||||||
| 		copied += n; | 		copied += n; | ||||||
| 		offset += n; | 		offset += n; | ||||||
|  | @ -1181,78 +1088,6 @@ ssize_t iov_iter_get_pages_alloc2(struct iov_iter *i, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(iov_iter_get_pages_alloc2); | EXPORT_SYMBOL(iov_iter_get_pages_alloc2); | ||||||
| 
 | 
 | ||||||
| size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, |  | ||||||
| 			       struct iov_iter *i) |  | ||||||
| { |  | ||||||
| 	__wsum sum, next; |  | ||||||
| 	sum = *csum; |  | ||||||
| 	if (WARN_ON_ONCE(!i->data_source)) |  | ||||||
| 		return 0; |  | ||||||
| 
 |  | ||||||
| 	iterate_and_advance(i, bytes, base, len, off, ({ |  | ||||||
| 		next = csum_and_copy_from_user(base, addr + off, len); |  | ||||||
| 		sum = csum_block_add(sum, next, off); |  | ||||||
| 		next ? 0 : len; |  | ||||||
| 	}), ({ |  | ||||||
| 		sum = csum_and_memcpy(addr + off, base, len, sum, off); |  | ||||||
| 	}) |  | ||||||
| 	) |  | ||||||
| 	*csum = sum; |  | ||||||
| 	return bytes; |  | ||||||
| } |  | ||||||
| EXPORT_SYMBOL(csum_and_copy_from_iter); |  | ||||||
| 
 |  | ||||||
| size_t csum_and_copy_to_iter(const void *addr, size_t bytes, void *_csstate, |  | ||||||
| 			     struct iov_iter *i) |  | ||||||
| { |  | ||||||
| 	struct csum_state *csstate = _csstate; |  | ||||||
| 	__wsum sum, next; |  | ||||||
| 
 |  | ||||||
| 	if (WARN_ON_ONCE(i->data_source)) |  | ||||||
| 		return 0; |  | ||||||
| 	if (unlikely(iov_iter_is_discard(i))) { |  | ||||||
| 		// can't use csum_memcpy() for that one - data is not copied
 |  | ||||||
| 		csstate->csum = csum_block_add(csstate->csum, |  | ||||||
| 					       csum_partial(addr, bytes, 0), |  | ||||||
| 					       csstate->off); |  | ||||||
| 		csstate->off += bytes; |  | ||||||
| 		return bytes; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	sum = csum_shift(csstate->csum, csstate->off); |  | ||||||
| 	iterate_and_advance(i, bytes, base, len, off, ({ |  | ||||||
| 		next = csum_and_copy_to_user(addr + off, base, len); |  | ||||||
| 		sum = csum_block_add(sum, next, off); |  | ||||||
| 		next ? 0 : len; |  | ||||||
| 	}), ({ |  | ||||||
| 		sum = csum_and_memcpy(base, addr + off, len, sum, off); |  | ||||||
| 	}) |  | ||||||
| 	) |  | ||||||
| 	csstate->csum = csum_shift(sum, csstate->off); |  | ||||||
| 	csstate->off += bytes; |  | ||||||
| 	return bytes; |  | ||||||
| } |  | ||||||
| EXPORT_SYMBOL(csum_and_copy_to_iter); |  | ||||||
| 
 |  | ||||||
| size_t hash_and_copy_to_iter(const void *addr, size_t bytes, void *hashp, |  | ||||||
| 		struct iov_iter *i) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_CRYPTO_HASH |  | ||||||
| 	struct ahash_request *hash = hashp; |  | ||||||
| 	struct scatterlist sg; |  | ||||||
| 	size_t copied; |  | ||||||
| 
 |  | ||||||
| 	copied = copy_to_iter(addr, bytes, i); |  | ||||||
| 	sg_init_one(&sg, addr, copied); |  | ||||||
| 	ahash_request_set_crypt(hash, &sg, NULL, copied); |  | ||||||
| 	crypto_ahash_update(hash); |  | ||||||
| 	return copied; |  | ||||||
| #else |  | ||||||
| 	return 0; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| EXPORT_SYMBOL(hash_and_copy_to_iter); |  | ||||||
| 
 |  | ||||||
| static int iov_npages(const struct iov_iter *i, int maxpages) | static int iov_npages(const struct iov_iter *i, int maxpages) | ||||||
| { | { | ||||||
| 	size_t skip = i->iov_offset, size = i->count; | 	size_t skip = i->iov_offset, size = i->count; | ||||||
|  |  | ||||||
|  | @ -50,7 +50,7 @@ | ||||||
| #include <linux/spinlock.h> | #include <linux/spinlock.h> | ||||||
| #include <linux/slab.h> | #include <linux/slab.h> | ||||||
| #include <linux/pagemap.h> | #include <linux/pagemap.h> | ||||||
| #include <linux/uio.h> | #include <linux/iov_iter.h> | ||||||
| #include <linux/indirect_call_wrapper.h> | #include <linux/indirect_call_wrapper.h> | ||||||
| 
 | 
 | ||||||
| #include <net/protocol.h> | #include <net/protocol.h> | ||||||
|  | @ -61,6 +61,7 @@ | ||||||
| #include <net/tcp_states.h> | #include <net/tcp_states.h> | ||||||
| #include <trace/events/skb.h> | #include <trace/events/skb.h> | ||||||
| #include <net/busy_poll.h> | #include <net/busy_poll.h> | ||||||
|  | #include <crypto/hash.h> | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  *	Is a socket 'connection oriented' ? |  *	Is a socket 'connection oriented' ? | ||||||
|  | @ -489,6 +490,24 @@ static int __skb_datagram_iter(const struct sk_buff *skb, int offset, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static size_t hash_and_copy_to_iter(const void *addr, size_t bytes, void *hashp, | ||||||
|  | 				    struct iov_iter *i) | ||||||
|  | { | ||||||
|  | #ifdef CONFIG_CRYPTO_HASH | ||||||
|  | 	struct ahash_request *hash = hashp; | ||||||
|  | 	struct scatterlist sg; | ||||||
|  | 	size_t copied; | ||||||
|  | 
 | ||||||
|  | 	copied = copy_to_iter(addr, bytes, i); | ||||||
|  | 	sg_init_one(&sg, addr, copied); | ||||||
|  | 	ahash_request_set_crypt(hash, &sg, NULL, copied); | ||||||
|  | 	crypto_ahash_update(hash); | ||||||
|  | 	return copied; | ||||||
|  | #else | ||||||
|  | 	return 0; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  *	skb_copy_and_hash_datagram_iter - Copy datagram to an iovec iterator |  *	skb_copy_and_hash_datagram_iter - Copy datagram to an iovec iterator | ||||||
|  *          and update a hash. |  *          and update a hash. | ||||||
|  | @ -716,6 +735,60 @@ int zerocopy_sg_from_iter(struct sk_buff *skb, struct iov_iter *from) | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(zerocopy_sg_from_iter); | EXPORT_SYMBOL(zerocopy_sg_from_iter); | ||||||
| 
 | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t copy_to_user_iter_csum(void __user *iter_to, size_t progress, | ||||||
|  | 			      size_t len, void *from, void *priv2) | ||||||
|  | { | ||||||
|  | 	__wsum next, *csum = priv2; | ||||||
|  | 
 | ||||||
|  | 	next = csum_and_copy_to_user(from + progress, iter_to, len); | ||||||
|  | 	*csum = csum_block_add(*csum, next, progress); | ||||||
|  | 	return next ? 0 : len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t memcpy_to_iter_csum(void *iter_to, size_t progress, | ||||||
|  | 			   size_t len, void *from, void *priv2) | ||||||
|  | { | ||||||
|  | 	__wsum *csum = priv2; | ||||||
|  | 	__wsum next = csum_partial_copy_nocheck(from, iter_to, len); | ||||||
|  | 
 | ||||||
|  | 	*csum = csum_block_add(*csum, next, progress); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct csum_state { | ||||||
|  | 	__wsum csum; | ||||||
|  | 	size_t off; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static size_t csum_and_copy_to_iter(const void *addr, size_t bytes, void *_csstate, | ||||||
|  | 				    struct iov_iter *i) | ||||||
|  | { | ||||||
|  | 	struct csum_state *csstate = _csstate; | ||||||
|  | 	__wsum sum; | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON_ONCE(i->data_source)) | ||||||
|  | 		return 0; | ||||||
|  | 	if (unlikely(iov_iter_is_discard(i))) { | ||||||
|  | 		// can't use csum_memcpy() for that one - data is not copied
 | ||||||
|  | 		csstate->csum = csum_block_add(csstate->csum, | ||||||
|  | 					       csum_partial(addr, bytes, 0), | ||||||
|  | 					       csstate->off); | ||||||
|  | 		csstate->off += bytes; | ||||||
|  | 		return bytes; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sum = csum_shift(csstate->csum, csstate->off); | ||||||
|  | 
 | ||||||
|  | 	bytes = iterate_and_advance2(i, bytes, (void *)addr, &sum, | ||||||
|  | 				     copy_to_user_iter_csum, | ||||||
|  | 				     memcpy_to_iter_csum); | ||||||
|  | 	csstate->csum = csum_shift(sum, csstate->off); | ||||||
|  | 	csstate->off += bytes; | ||||||
|  | 	return bytes; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  *	skb_copy_and_csum_datagram - Copy datagram to an iovec iterator |  *	skb_copy_and_csum_datagram - Copy datagram to an iovec iterator | ||||||
|  *          and update a checksum. |  *          and update a checksum. | ||||||
|  |  | ||||||
|  | @ -62,6 +62,7 @@ | ||||||
| #include <linux/if_vlan.h> | #include <linux/if_vlan.h> | ||||||
| #include <linux/mpls.h> | #include <linux/mpls.h> | ||||||
| #include <linux/kcov.h> | #include <linux/kcov.h> | ||||||
|  | #include <linux/iov_iter.h> | ||||||
| 
 | 
 | ||||||
| #include <net/protocol.h> | #include <net/protocol.h> | ||||||
| #include <net/dst.h> | #include <net/dst.h> | ||||||
|  | @ -6931,3 +6932,42 @@ ssize_t skb_splice_from_iter(struct sk_buff *skb, struct iov_iter *iter, | ||||||
| 	return spliced ?: ret; | 	return spliced ?: ret; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(skb_splice_from_iter); | EXPORT_SYMBOL(skb_splice_from_iter); | ||||||
|  | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t memcpy_from_iter_csum(void *iter_from, size_t progress, | ||||||
|  | 			     size_t len, void *to, void *priv2) | ||||||
|  | { | ||||||
|  | 	__wsum *csum = priv2; | ||||||
|  | 	__wsum next = csum_partial_copy_nocheck(iter_from, to + progress, len); | ||||||
|  | 
 | ||||||
|  | 	*csum = csum_block_add(*csum, next, progress); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __always_inline | ||||||
|  | size_t copy_from_user_iter_csum(void __user *iter_from, size_t progress, | ||||||
|  | 				size_t len, void *to, void *priv2) | ||||||
|  | { | ||||||
|  | 	__wsum next, *csum = priv2; | ||||||
|  | 
 | ||||||
|  | 	next = csum_and_copy_from_user(iter_from, to + progress, len); | ||||||
|  | 	*csum = csum_block_add(*csum, next, progress); | ||||||
|  | 	return next ? 0 : len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool csum_and_copy_from_iter_full(void *addr, size_t bytes, | ||||||
|  | 				  __wsum *csum, struct iov_iter *i) | ||||||
|  | { | ||||||
|  | 	size_t copied; | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON_ONCE(!i->data_source)) | ||||||
|  | 		return false; | ||||||
|  | 	copied = iterate_and_advance2(i, bytes, addr, csum, | ||||||
|  | 				      copy_from_user_iter_csum, | ||||||
|  | 				      memcpy_from_iter_csum); | ||||||
|  | 	if (likely(copied == bytes)) | ||||||
|  | 		return true; | ||||||
|  | 	iov_iter_revert(i, copied); | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(csum_and_copy_from_iter_full); | ||||||
|  |  | ||||||
|  | @ -3527,7 +3527,7 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) | ||||||
| 	if (runtime->state == SNDRV_PCM_STATE_OPEN || | 	if (runtime->state == SNDRV_PCM_STATE_OPEN || | ||||||
| 	    runtime->state == SNDRV_PCM_STATE_DISCONNECTED) | 	    runtime->state == SNDRV_PCM_STATE_DISCONNECTED) | ||||||
| 		return -EBADFD; | 		return -EBADFD; | ||||||
| 	if (!to->user_backed) | 	if (!user_backed_iter(to)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	if (to->nr_segs > 1024 || to->nr_segs != runtime->channels) | 	if (to->nr_segs > 1024 || to->nr_segs != runtime->channels) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | @ -3567,7 +3567,7 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) | ||||||
| 	if (runtime->state == SNDRV_PCM_STATE_OPEN || | 	if (runtime->state == SNDRV_PCM_STATE_OPEN || | ||||||
| 	    runtime->state == SNDRV_PCM_STATE_DISCONNECTED) | 	    runtime->state == SNDRV_PCM_STATE_DISCONNECTED) | ||||||
| 		return -EBADFD; | 		return -EBADFD; | ||||||
| 	if (!from->user_backed) | 	if (!user_backed_iter(from)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	if (from->nr_segs > 128 || from->nr_segs != runtime->channels || | 	if (from->nr_segs > 128 || from->nr_segs != runtime->channels || | ||||||
| 	    !frame_aligned(runtime, iov->iov_len)) | 	    !frame_aligned(runtime, iov->iov_len)) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Linus Torvalds
						Linus Torvalds