diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-10-30 09:24:21 -1000 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-10-30 09:24:21 -1000 | 
| commit | df9c65b5fc7ef1caabdb7a01a2415cbb8a00908d (patch) | |
| tree | 42560cc7aca2e177332f46c50dfa6824aa10752d | |
| parent | 3b3f874cc1d074bdcffc224d683925fd11808fe7 (diff) | |
| parent | b5f0e20f444cd150121e0ce912ebd3f2dabd12bc (diff) | |
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
| -rw-r--r-- | arch/x86/include/asm/uaccess.h | 2 | ||||
| -rw-r--r-- | arch/x86/lib/copy_mc.c | 8 | ||||
| -rw-r--r-- | drivers/infiniband/hw/hfi1/file_ops.c | 2 | ||||
| -rw-r--r-- | drivers/infiniband/hw/qib/qib_file_ops.c | 2 | ||||
| -rw-r--r-- | include/linux/iov_iter.h | 274 | ||||
| -rw-r--r-- | include/linux/skbuff.h | 3 | ||||
| -rw-r--r-- | include/linux/uio.h | 34 | ||||
| -rw-r--r-- | lib/iov_iter.c | 437 | ||||
| -rw-r--r-- | net/core/datagram.c | 75 | ||||
| -rw-r--r-- | net/core/skbuff.c | 40 | ||||
| -rw-r--r-- | sound/core/pcm_native.c | 4 | 
11 files changed, 540 insertions, 341 deletions
| diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 8bae40a66282..5c367c1290c3 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -496,7 +496,7 @@ copy_mc_to_kernel(void *to, const void *from, unsigned len);  #define copy_mc_to_kernel copy_mc_to_kernel  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  /* diff --git a/arch/x86/lib/copy_mc.c b/arch/x86/lib/copy_mc.c index 80efd45a7761..6e8b7e600def 100644 --- a/arch/x86/lib/copy_mc.c +++ b/arch/x86/lib/copy_mc.c @@ -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); -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;  	if (copy_mc_fragile_enabled) {  		__uaccess_begin(); -		ret = copy_mc_fragile(dst, src, len); +		ret = copy_mc_fragile((__force void *)dst, src, len);  		__uaccess_end();  		return ret;  	}  	if (static_cpu_has(X86_FEATURE_ERMS)) {  		__uaccess_begin(); -		ret = copy_mc_enhanced_fast_string(dst, src, len); +		ret = copy_mc_enhanced_fast_string((__force void *)dst, src, len);  		__uaccess_end();  		return ret;  	} -	return copy_user_generic(dst, src, len); +	return copy_user_generic((__force void *)dst, src, len);  } diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index a5ab22cedd41..788fc249234f 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -267,7 +267,7 @@ static ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from)  	if (!HFI1_CAP_IS_KSET(SDMA))  		return -EINVAL; -	if (!from->user_backed) +	if (!user_backed_iter(from))  		return -EINVAL;  	idx = srcu_read_lock(&fd->pq_srcu);  	pq = srcu_dereference(fd->pq, &fd->pq_srcu); diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index 152952127f13..29e4c59aa23b 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -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_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 qib_user_sdma_writev(rcd, pq, iter_iov(from), from->nr_segs); diff --git a/include/linux/iov_iter.h b/include/linux/iov_iter.h new file mode 100644 index 000000000000..270454a6703d --- /dev/null +++ b/include/linux/iov_iter.h @@ -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 */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 97bfef071255..27998f73183e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.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);  } +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,  			       struct iov_iter *from, int copy)  { diff --git a/include/linux/uio.h b/include/linux/uio.h index 42bce38a8e87..b6214cbf2a43 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -21,12 +21,12 @@ struct kvec {  enum iter_type {  	/* iter types */ +	ITER_UBUF,  	ITER_IOVEC, -	ITER_KVEC,  	ITER_BVEC, +	ITER_KVEC,  	ITER_XARRAY,  	ITER_DISCARD, -	ITER_UBUF,  };  #define ITER_SOURCE	1	// == WRITE @@ -43,11 +43,7 @@ struct iov_iter {  	bool copy_mc;  	bool nofault;  	bool data_source; -	bool user_backed; -	union { -		size_t iov_offset; -		int last_offset; -	}; +	size_t iov_offset;  	/*  	 * Hack alert: overlay ubuf_iovec with iovec + count, so  	 * 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)  { -	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;  } -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,  		unsigned long nr_segs, unsigned long fast_segs,  		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) {  		.iter_type = ITER_UBUF,  		.copy_mc = false, -		.user_backed = true,  		.data_source = direction,  		.ubuf = buf,  		.count = count, diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 27234a820eeb..de7d11cf4c63 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1,5 +1,4 @@  // SPDX-License-Identifier: GPL-2.0-only -#include <crypto/hash.h>  #include <linux/export.h>  #include <linux/bvec.h>  #include <linux/fault-inject-usercopy.h> @@ -10,192 +9,71 @@  #include <linux/vmalloc.h>  #include <linux/splice.h>  #include <linux/compat.h> -#include <net/checksum.h>  #include <linux/scatterlist.h>  #include <linux/instrumented.h> +#include <linux/iov_iter.h> -/* covers ubuf and kbuf alike */ -#define iterate_buf(i, n, base, len, off, __p, STEP) {		\ -	size_t __maybe_unused off = 0;				\ -	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) +static __always_inline +size_t copy_to_user_iter(void __user *iter_to, size_t progress, +			 size_t len, void *from, void *priv2)  {  	if (should_fail_usercopy()) -		return n; -	if (access_ok(to, n)) { -		instrument_copy_to_user(to, from, n); -		n = raw_copy_to_user(to, from, n); +		return len; +	if (access_ok(iter_to, len)) { +		from += progress; +		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()) -		return n; - -	res = copy_to_user_nofault(to, from, n); +		return len; -	return res < 0 ? n : res; +	from += progress; +	res = copy_to_user_nofault(iter_to, from, len); +	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()) -		return n; -	if (access_ok(from, n)) { -		instrument_copy_from_user_before(to, from, n); -		res = raw_copy_from_user(to, from, n); -		instrument_copy_from_user_after(to, from, n, res); +		return len; +	if (access_ok(iter_from, len)) { +		to += progress; +		instrument_copy_from_user_before(to, iter_from, len); +		res = raw_copy_from_user(to, iter_from, len); +		instrument_copy_from_user_after(to, iter_from, len, 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   * @i: iterator @@ -290,7 +168,6 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction,  		.iter_type = ITER_IOVEC,  		.copy_mc = false,  		.nofault = false, -		.user_backed = true,  		.data_source = direction,  		.__iov = iov,  		.nr_segs = nr_segs, @@ -300,36 +177,35 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction,  }  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)  {  	if (WARN_ON_ONCE(i->data_source))  		return 0;  	if (user_backed_iter(i))  		might_fault(); -	iterate_and_advance(i, bytes, base, len, off, -		copyout(base, addr + off, len), -		memcpy(base, addr + off, len) -	) - -	return bytes; +	return iterate_and_advance(i, bytes, (void *)addr, +				   copy_to_user_iter, memcpy_to_iter);  }  EXPORT_SYMBOL(_copy_to_iter);  #ifdef CONFIG_ARCH_HAS_COPY_MC -static int copyout_mc(void __user *to, const void *from, size_t n) -{ -	if (access_ok(to, n)) { -		instrument_copy_to_user(to, from, n); -		n = copy_mc_to_user((__force void *) to, from, 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(iter_to, len)) { +		from += progress; +		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;  	if (user_backed_iter(i))  		might_fault(); -	__iterate_and_advance(i, bytes, base, len, off, -		copyout_mc(base, addr + off, len), -		copy_mc_to_kernel(base, addr + off, len) -	) - -	return bytes; +	return iterate_and_advance(i, bytes, (void *)addr, +				   copy_to_user_iter_mc, memcpy_to_iter_mc);  }  EXPORT_SYMBOL_GPL(_copy_mc_to_iter);  #endif /* CONFIG_ARCH_HAS_COPY_MC */ -static void *memcpy_from_iter(struct iov_iter *i, void *to, const void *from, -				 size_t size) +static __always_inline +size_t memcpy_from_iter_mc(void *iter_from, size_t progress, +			   size_t len, void *to, void *priv2) +{ +	return copy_mc_to_kernel(to + progress, iter_from, len); +} + +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 (iov_iter_is_copy_mc(i)) -		return (void *)copy_mc_to_kernel(to, from, size); -	return memcpy(to, from, size); +	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) @@ -387,30 +276,44 @@ size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)  	if (user_backed_iter(i))  		might_fault(); -	iterate_and_advance(i, bytes, base, len, off, -		copyin(addr + off, base, len), -		memcpy_from_iter(i, addr + off, base, len) -	) - -	return bytes; +	return __copy_from_iter(addr, bytes, i);  }  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)  {  	if (WARN_ON_ONCE(!i->data_source))  		return 0; -	iterate_and_advance(i, bytes, base, len, off, -		__copy_from_user_inatomic_nocache(addr + off, base, len), -		memcpy(addr + off, base, len) -	) - -	return bytes; +	return iterate_and_advance(i, bytes, addr, +				   copy_from_user_iter_nocache, +				   memcpy_from_iter);  }  EXPORT_SYMBOL(_copy_from_iter_nocache);  #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   * @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))  		return 0; -	iterate_and_advance(i, bytes, base, len, off, -		__copy_from_user_flushcache(addr + off, base, len), -		memcpy_flushcache(addr + off, base, len) -	) - -	return bytes; +	return iterate_and_advance(i, bytes, addr, +				   copy_from_user_iter_flushcache, +				   memcpy_from_iter_flushcache);  }  EXPORT_SYMBOL_GPL(_copy_from_iter_flushcache);  #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);  		size_t n = min(bytes, (size_t)PAGE_SIZE - offset); -		iterate_and_advance(i, n, base, len, off, -			copyout_nofault(base, kaddr + offset + off, len), -			memcpy(base, kaddr + offset + off, len) -		) +		n = iterate_and_advance(i, bytes, kaddr, +					copy_to_user_iter_nofault, +					memcpy_to_iter);  		kunmap_local(kaddr);  		res += 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); -size_t iov_iter_zero(size_t bytes, struct iov_iter *i) +static __always_inline +size_t zero_to_user_iter(void __user *iter_to, size_t progress, +			 size_t len, void *priv, void *priv2)  { -	iterate_and_advance(i, bytes, base, len, count, -		clear_user(base, len), -		memset(base, 0, len) -	) +	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; +} -	return bytes; +size_t iov_iter_zero(size_t bytes, struct iov_iter *i) +{ +	return iterate_and_advance(i, bytes, NULL, +				   zero_to_user_iter, zero_to_iter);  }  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; -		iterate_and_advance(i, n, base, len, off, -			copyin(p + off, base, len), -			memcpy_from_iter(i, p + off, base, len) -		) +		n = __copy_from_iter(p, n, i);  		kunmap_atomic(p);  		copied += 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); -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)  {  	size_t skip = i->iov_offset, size = i->count; diff --git a/net/core/datagram.c b/net/core/datagram.c index 176eb5834746..103d46fa0eeb 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -50,7 +50,7 @@  #include <linux/spinlock.h>  #include <linux/slab.h>  #include <linux/pagemap.h> -#include <linux/uio.h> +#include <linux/iov_iter.h>  #include <linux/indirect_call_wrapper.h>  #include <net/protocol.h> @@ -61,6 +61,7 @@  #include <net/tcp_states.h>  #include <trace/events/skb.h>  #include <net/busy_poll.h> +#include <crypto/hash.h>  /*   *	Is a socket 'connection oriented' ? @@ -489,6 +490,24 @@ short_copy:  	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   *          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); +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   *          and update a checksum. diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4eaf7ed0d1f4..2bfa6a7ba244 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -62,6 +62,7 @@  #include <linux/if_vlan.h>  #include <linux/mpls.h>  #include <linux/kcov.h> +#include <linux/iov_iter.h>  #include <net/protocol.h>  #include <net/dst.h> @@ -6931,3 +6932,42 @@ out:  	return spliced ?: ret;  }  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); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index bd9ddf412b46..9a69236fa207 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3527,7 +3527,7 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)  	if (runtime->state == SNDRV_PCM_STATE_OPEN ||  	    runtime->state == SNDRV_PCM_STATE_DISCONNECTED)  		return -EBADFD; -	if (!to->user_backed) +	if (!user_backed_iter(to))  		return -EINVAL;  	if (to->nr_segs > 1024 || to->nr_segs != runtime->channels)  		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 ||  	    runtime->state == SNDRV_PCM_STATE_DISCONNECTED)  		return -EBADFD; -	if (!from->user_backed) +	if (!user_backed_iter(from))  		return -EINVAL;  	if (from->nr_segs > 128 || from->nr_segs != runtime->channels ||  	    !frame_aligned(runtime, iov->iov_len)) | 
