lxip: fix iovec iter operations

This commit is contained in:
Josef Söntgen 2017-05-18 16:22:46 +02:00 committed by Christian Helmuth
parent 028aeafabe
commit 5b6735e1c9
1 changed files with 97 additions and 110 deletions

View File

@ -535,60 +535,61 @@ extern "C" void lx_trace_event(char const *fmt, ...)
** linux/uio.h **
*****************/
size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
static inline size_t _copy_iter(void *addr, size_t bytes,
struct iov_iter *i, bool to_iter)
{
if (bytes > i->count)
bytes = i->count;
if (addr == nullptr) { return 0; }
if (bytes == 0)
if (i->count == 0 ||
i->iov == nullptr ||
i->iov->iov_len == 0) {
return 0;
char *kdata = reinterpret_cast<char*>(addr);
struct iovec const *iov = i->iov;
size_t len = bytes;
while (len > 0) {
if (iov->iov_len) {
size_t copy_len = (size_t)len < iov->iov_len ? len : iov->iov_len;
Genode::memcpy(kdata, iov->iov_base, copy_len);
len -= copy_len;
kdata += copy_len;
i->count -= copy_len; /* XXX the vanilla macro does that */
}
iov++;
}
return bytes;
if (i->nr_segs > 1) {
Genode::error(__func__, ": too many segments ", i->nr_segs);
return 0;
}
/* make sure the whole iter fits as there is only 1 iovec */
if (i->iov->iov_len < i->count) {
Genode::error(__func__, ": "
"iov->iov_len: ", i->iov->iov_len, " < "
"i->count: ", i->count);
return 0;
}
struct iovec const * const iov = i->iov;
size_t const iov_len = iov->iov_len;
void * const base = (iov->iov_base + i->iov_offset);
if (bytes > i->count) { bytes = i->count; }
size_t const len = (size_t)(bytes < iov_len ? bytes : iov_len);
void * const dst = to_iter ? base : addr;
void const * const src = to_iter ? addr : base;
/* actual function body */
{
Genode::memcpy(dst, src, len);
}
i->iov_offset += len;
i->count -= len;
return len;
}
size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
{
return _copy_iter(addr, bytes, i, false);
}
size_t copy_to_iter(void *addr, size_t bytes, struct iov_iter *i)
{
if (bytes > i->count)
bytes = i->count;
if (bytes == 0)
return 0;
char *kdata = reinterpret_cast<char*>(addr);
struct iovec const *iov = i->iov;
size_t len = bytes;
while (len > 0) {
if (iov->iov_len) {
size_t copy_len = (size_t)len < iov->iov_len ? len : iov->iov_len;
Genode::memcpy(iov->iov_base, kdata, copy_len);
len -= copy_len;
kdata += copy_len;
i->count -= copy_len; /* XXX the vanilla macro does that */
}
iov++;
}
return bytes;
return _copy_iter(addr, bytes, i, true);
}
@ -606,83 +607,69 @@ size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
}
size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i)
static size_t _csum_and_copy_iter(void *addr, size_t bytes, __wsum *csum,
struct iov_iter *i, bool to_iter)
{
if (bytes > i->count)
bytes = i->count;
if (addr == nullptr) { return 0; }
if (bytes == 0)
if (i->count == 0 ||
i->iov == nullptr ||
i->iov->iov_len == 0) {
return 0;
char *kdata = reinterpret_cast<char*>(addr);
struct iovec const *iov = i->iov;
__wsum sum = *csum;
size_t len = bytes;
while (len > 0) {
if (iov->iov_len) {
size_t copy_len = (size_t)len < iov->iov_len ? len : iov->iov_len;
int err = 0;
__wsum next = csum_and_copy_from_user(iov->iov_base, kdata, copy_len, 0, &err);
if (err) {
Genode::error(__func__, ": err: ", err, " - sleeping");
Genode::sleep_forever();
}
sum = csum_block_add(sum, next, bytes-len);
len -= copy_len;
kdata += copy_len;
i->count -= copy_len; /* XXX the vanilla macro does that */
}
iov++;
}
*csum = sum;
if (i->nr_segs > 1) {
Genode::error(__func__, ": too many segments ", i->nr_segs);
return 0;
}
return bytes;
/* make sure the whole iter fits as there is only 1 iovec */
if (i->iov->iov_len < i->count) {
Genode::error(__func__, ": "
"iov->iov_len: ", i->iov->iov_len, " < "
"i->count: ", i->count);
return 0;
}
struct iovec const * const iov = i->iov;
size_t const iov_len = iov->iov_len;
void * const base = (iov->iov_base + i->iov_offset);
if (bytes > i->count) { bytes = i->count; }
size_t const len = (size_t)(bytes < iov_len ? bytes : iov_len);
void * const dst = to_iter ? base : addr;
void const * const src = to_iter ? addr : base;
/* actual function body */
{
int err = 0;
__wsum next = csum_and_copy_from_user(src, dst, len, 0, &err);
if (err) {
Genode::error(__func__, ": err: ", err, " - sleeping");
Genode::sleep_forever();
}
*csum = csum_block_add(*csum, next, 0);
}
i->iov_offset += len;
i->count -= len;
return len;
}
size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i)
{
return _csum_and_copy_iter(addr, bytes, csum, i, false);
}
size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i)
{
if (bytes > i->count)
bytes = i->count;
if (bytes == 0)
return 0;
char *kdata = reinterpret_cast<char*>(addr);
struct iovec const *iov = i->iov;
__wsum sum = *csum;
size_t len = bytes;
while (len > 0) {
if (iov->iov_len) {
size_t copy_len = (size_t)len < iov->iov_len ? len : iov->iov_len;
int err = 0;
__wsum next = csum_and_copy_to_user(kdata, iov->iov_base, copy_len, 0, &err);
if (err) {
Genode::error(__func__, ": err: ", err, " - sleeping");
Genode::sleep_forever();
}
sum = csum_block_add(sum, next, bytes-len);
len -= copy_len;
kdata += copy_len;
i->count -= copy_len; /* XXX the vanilla macro does that */
}
iov++;
}
*csum = sum;
return bytes;
return _csum_and_copy_iter(addr, bytes, csum, i, true);
}