blob: c01590110b5e468db35bb8a20d6fc6999b049fe5 [file] [log] [blame]
/*
* iovec manipulation routines.
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Fixes:
* Andrew Lunn : Errors in iovec copying.
* Pedro Roque : Added memcpy_fromiovecend and
* csum_..._fromiovecend.
* Andi Kleen : fixed error handling for 2.1
* Alexey Kuznetsov: 2.1 optimisations
* Andi Kleen : Fix csum*fromiovecend for IPv6.
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <sys/uio.h>
#include <kvm/iovec.h>
#include <string.h>
/*
* Copy kernel to iovec. Returns -EFAULT on error.
*
* Note: this modifies the original iovec.
*/
int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
{
while (len > 0) {
if (iov->iov_len) {
int copy = min_t(unsigned int, iov->iov_len, len);
memcpy(iov->iov_base, kdata, copy);
kdata += copy;
len -= copy;
iov->iov_len -= copy;
iov->iov_base += copy;
}
iov++;
}
return 0;
}
/*
* Copy kernel to iovec. Returns -EFAULT on error.
*/
int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
size_t offset, int len)
{
int copy;
for (; len > 0; ++iov) {
/* Skip over the finished iovecs */
if (unlikely(offset >= iov->iov_len)) {
offset -= iov->iov_len;
continue;
}
copy = min_t(unsigned int, iov->iov_len - offset, len);
memcpy(iov->iov_base + offset, kdata, copy);
offset = 0;
kdata += copy;
len -= copy;
}
return 0;
}
/*
* Copy iovec to kernel. Returns -EFAULT on error.
*
* Note: this modifies the original iovec.
*/
int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
{
while (len > 0) {
if (iov->iov_len) {
int copy = min_t(unsigned int, len, iov->iov_len);
memcpy(kdata, iov->iov_base, copy);
len -= copy;
kdata += copy;
iov->iov_base += copy;
iov->iov_len -= copy;
}
iov++;
}
return 0;
}
/*
* Copy at most @len bytes from iovec to buffer.
* Returns the remaining len.
*
* Note: this modifies the original iovec, the iov pointer, and the
* iovcount to describe the remaining buffer.
*/
ssize_t memcpy_fromiovec_safe(void *buf, struct iovec **iov, size_t len,
size_t *iovcount)
{
size_t copy;
while (len && *iovcount) {
copy = min(len, (*iov)->iov_len);
memcpy(buf, (*iov)->iov_base, copy);
buf += copy;
len -= copy;
/* Move iov cursor */
(*iov)->iov_base += copy;
(*iov)->iov_len -= copy;
if (!(*iov)->iov_len) {
(*iov)++;
(*iovcount)--;
}
}
return len;
}
/*
* Copy iovec from kernel. Returns -EFAULT on error.
*/
int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
size_t offset, int len)
{
/* Skip over the finished iovecs */
while (offset >= iov->iov_len) {
offset -= iov->iov_len;
iov++;
}
while (len > 0) {
char *base = iov->iov_base + offset;
int copy = min_t(unsigned int, len, iov->iov_len - offset);
offset = 0;
memcpy(kdata, base, copy);
len -= copy;
kdata += copy;
iov++;
}
return 0;
}