iovec.c revision b4bf07771faaf959b0a916d35b1b930c030e30a8
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	iovec manipulation routines.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		This program is free software; you can redistribute it and/or
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		modify it under the terms of the GNU General Public License
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		as published by the Free Software Foundation; either version
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		2 of the License, or (at your option) any later version.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Fixes:
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Andrew Lunn	:	Errors in iovec copying.
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Pedro Roque	:	Added memcpy_fromiovecend and
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *					csum_..._fromiovecend.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Andi Kleen	:	fixed error handling for 2.1
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Alexey Kuznetsov:	2.1 optimisations
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Andi Kleen	:	Fix csum*fromiovecend for IPv6.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mm.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/net.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/in6.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/checksum.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/sock.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Verify iovec. The caller must ensure that the iovec is big enough
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	to hold the message iovec.
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
34e49332bd12e92da2df6d002f857ec62675ba2648Jesper Juhl *	Save time not doing access_ok. copy_*_user will make this work
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	in any case.
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3843db362d3adda9e0a915ddb9a8d1a41186e19179Maciej Żenczykowskiint verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode)
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
408acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller	int size, ct, err;
414ec93edb14fe5fdee9fae6335f2cbba204627eacYOSHIFUJI Hideaki
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (m->msg_namelen) {
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (mode == VERIFY_READ) {
44a700d8be733bd593ea4797dfde17aed4f35213c0Namhyung Kim			void __user *namep;
45a700d8be733bd593ea4797dfde17aed4f35213c0Namhyung Kim			namep = (void __user __force *) m->msg_name;
46a700d8be733bd593ea4797dfde17aed4f35213c0Namhyung Kim			err = move_addr_to_kernel(namep, m->msg_namelen,
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  address);
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (err < 0)
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return err;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		m->msg_name = address;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		m->msg_name = NULL;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size = m->msg_iovlen * sizeof(struct iovec);
57a700d8be733bd593ea4797dfde17aed4f35213c0Namhyung Kim	if (copy_from_user(iov, (void __user __force *) m->msg_iov, size))
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	m->msg_iov = iov;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = 0;
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (ct = 0; ct < m->msg_iovlen; ct++) {
648acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller		size_t len = iov[ct].iov_len;
658acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller
668acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller		if (len > INT_MAX - err) {
678acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller			len = INT_MAX - err;
688acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller			iov[ct].iov_len = len;
698acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller		}
708acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller		err += len;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Copy kernel to iovec. Returns -EFAULT on error.
780a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin */
790a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin
800a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkinint memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
810a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin		      int offset, int len)
820a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin{
830a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin	int copy;
840a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin	for (; len > 0; ++iov) {
850a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin		/* Skip over the finished iovecs */
860a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin		if (unlikely(offset >= iov->iov_len)) {
870a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin			offset -= iov->iov_len;
880a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin			continue;
890a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin		}
900a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin		copy = min_t(unsigned int, iov->iov_len - offset, len);
912faef52b72958b8b1c08e927b9b0691c314cf6f4Sridhar Samudrala		if (copy_to_user(iov->iov_base + offset, kdata, copy))
920a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin			return -EFAULT;
932faef52b72958b8b1c08e927b9b0691c314cf6f4Sridhar Samudrala		offset = 0;
940a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin		kdata += copy;
950a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin		len -= copy;
960a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin	}
970a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin
980a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin	return 0;
990a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin}
1009e34a5b51684bc90ac827ec4ba339f3892632eacEric DumazetEXPORT_SYMBOL(memcpy_toiovecend);
1010a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin
1020a1ec07a67bd8b0033dace237249654d015efa21Michael S. Tsirkin/*
1036f26c9a7555e5bcca3560919db9b852015077daeMichael S. Tsirkin *	Copy iovec from kernel. Returns -EFAULT on error.
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1056f26c9a7555e5bcca3560919db9b852015077daeMichael S. Tsirkin
1066f26c9a7555e5bcca3560919db9b852015077daeMichael S. Tsirkinint memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
1076f26c9a7555e5bcca3560919db9b852015077daeMichael S. Tsirkin			int offset, int len)
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Skip over the finished iovecs */
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (offset >= iov->iov_len) {
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		offset -= iov->iov_len;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		iov++;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (len > 0) {
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u8 __user *base = iov->iov_base + offset;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int copy = min_t(unsigned int, len, iov->iov_len - offset);
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		offset = 0;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy_from_user(kdata, base, copy))
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len -= copy;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kdata += copy;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		iov++;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1299e34a5b51684bc90ac827ec4ba339f3892632eacEric DumazetEXPORT_SYMBOL(memcpy_fromiovecend);
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	And now for the all-in-one: copy and checksum from a user iovec
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	directly to a datagram
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Calls to csum_partial but the last must be in 32 bit chunks
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	ip_build_xmit must ensure that when fragmenting only the last
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	call to this function will be unaligned also.
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
14044bb93633f57a55979f3c2589b10fd6a2bfc7c08Al Viro				 int offset, unsigned int len, __wsum *csump)
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14244bb93633f57a55979f3c2589b10fd6a2bfc7c08Al Viro	__wsum csum = *csump;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int partial_cnt = 0, err = 0;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Skip over the finished iovecs */
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (offset >= iov->iov_len) {
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		offset -= iov->iov_len;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		iov++;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (len > 0) {
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u8 __user *base = iov->iov_base + offset;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int copy = min_t(unsigned int, len, iov->iov_len - offset);
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		offset = 0;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* There is a remnant from previous iov. */
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (partial_cnt) {
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int par_len = 4 - partial_cnt;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* iov component is too short ... */
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (par_len > copy) {
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (copy_from_user(kdata, base, copy))
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto out_fault;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				kdata += copy;
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				base += copy;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				partial_cnt += copy;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				len -= copy;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				iov++;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (len)
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					continue;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				*csump = csum_partial(kdata - partial_cnt,
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							 partial_cnt, csum);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (copy_from_user(kdata, base, par_len))
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out_fault;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			csum = csum_partial(kdata - partial_cnt, 4, csum);
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kdata += par_len;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			base  += par_len;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy  -= par_len;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len   -= par_len;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			partial_cnt = 0;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (len > copy) {
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			partial_cnt = copy % 4;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (partial_cnt) {
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				copy -= partial_cnt;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (copy_from_user(kdata + copy, base + copy,
1914ec93edb14fe5fdee9fae6335f2cbba204627eacYOSHIFUJI Hideaki						partial_cnt))
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto out_fault;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy) {
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			csum = csum_and_copy_from_user(base, kdata, copy,
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							csum, &err);
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (err)
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len   -= copy + partial_cnt;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kdata += copy + partial_cnt;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		iov++;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2064ec93edb14fe5fdee9fae6335f2cbba204627eacYOSHIFUJI Hideaki	*csump = csum;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_fault:
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = -EFAULT;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(csum_partial_copy_fromiovecend);
215b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang
216b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wangunsigned long iov_pages(const struct iovec *iov, int offset,
217b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang			unsigned long nr_segs)
218b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang{
219b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang	unsigned long seg, base;
220b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang	int pages = 0, len, size;
221b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang
222b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang	while (nr_segs && (offset >= iov->iov_len)) {
223b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang		offset -= iov->iov_len;
224b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang		++iov;
225b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang		--nr_segs;
226b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang	}
227b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang
228b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang	for (seg = 0; seg < nr_segs; seg++) {
229b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang		base = (unsigned long)iov[seg].iov_base + offset;
230b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang		len = iov[seg].iov_len - offset;
231b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang		size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
232b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang		pages += size;
233b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang		offset = 0;
234b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang	}
235b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang
236b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang	return pages;
237b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang}
238b4bf07771faaf959b0a916d35b1b930c030e30a8Jason WangEXPORT_SYMBOL(iov_pages);
239