blob: c01590110b5e468db35bb8a20d6fc6999b049fe5 [file] [log] [blame]
Sasha Levind8f36172013-05-03 16:29:15 -04001/*
2 * iovec manipulation routines.
3 *
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
10 * Fixes:
11 * Andrew Lunn : Errors in iovec copying.
12 * Pedro Roque : Added memcpy_fromiovecend and
13 * csum_..._fromiovecend.
14 * Andi Kleen : fixed error handling for 2.1
15 * Alexey Kuznetsov: 2.1 optimisations
16 * Andi Kleen : Fix csum*fromiovecend for IPv6.
17 */
18
19#include <linux/errno.h>
Sasha Levind8f36172013-05-03 16:29:15 -040020#include <linux/kernel.h>
21#include <linux/compiler.h>
22#include <sys/uio.h>
23#include <kvm/iovec.h>
24#include <string.h>
25
26/*
27 * Copy kernel to iovec. Returns -EFAULT on error.
28 *
29 * Note: this modifies the original iovec.
30 */
31
32int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
33{
34 while (len > 0) {
35 if (iov->iov_len) {
36 int copy = min_t(unsigned int, iov->iov_len, len);
37 memcpy(iov->iov_base, kdata, copy);
38 kdata += copy;
39 len -= copy;
40 iov->iov_len -= copy;
41 iov->iov_base += copy;
42 }
43 iov++;
44 }
45
46 return 0;
47}
Sasha Levind8f36172013-05-03 16:29:15 -040048
49/*
50 * Copy kernel to iovec. Returns -EFAULT on error.
51 */
52
53int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
54 size_t offset, int len)
55{
56 int copy;
57 for (; len > 0; ++iov) {
58 /* Skip over the finished iovecs */
59 if (unlikely(offset >= iov->iov_len)) {
60 offset -= iov->iov_len;
61 continue;
62 }
63 copy = min_t(unsigned int, iov->iov_len - offset, len);
64 memcpy(iov->iov_base + offset, kdata, copy);
65 offset = 0;
66 kdata += copy;
67 len -= copy;
68 }
69
70 return 0;
71}
Sasha Levind8f36172013-05-03 16:29:15 -040072
73/*
74 * Copy iovec to kernel. Returns -EFAULT on error.
75 *
76 * Note: this modifies the original iovec.
77 */
78
79int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
80{
81 while (len > 0) {
82 if (iov->iov_len) {
83 int copy = min_t(unsigned int, len, iov->iov_len);
84 memcpy(kdata, iov->iov_base, copy);
85 len -= copy;
86 kdata += copy;
87 iov->iov_base += copy;
88 iov->iov_len -= copy;
89 }
90 iov++;
91 }
92
93 return 0;
94}
Sasha Levind8f36172013-05-03 16:29:15 -040095
96/*
Jean-Philippe Bruckerc4925342022-06-07 18:02:25 +010097 * Copy at most @len bytes from iovec to buffer.
98 * Returns the remaining len.
99 *
100 * Note: this modifies the original iovec, the iov pointer, and the
101 * iovcount to describe the remaining buffer.
102 */
103ssize_t memcpy_fromiovec_safe(void *buf, struct iovec **iov, size_t len,
104 size_t *iovcount)
105{
106 size_t copy;
107
108 while (len && *iovcount) {
109 copy = min(len, (*iov)->iov_len);
110 memcpy(buf, (*iov)->iov_base, copy);
111 buf += copy;
112 len -= copy;
113
114 /* Move iov cursor */
115 (*iov)->iov_base += copy;
116 (*iov)->iov_len -= copy;
117
118 if (!(*iov)->iov_len) {
119 (*iov)++;
120 (*iovcount)--;
121 }
122 }
123
124 return len;
125}
126
127/*
Sasha Levind8f36172013-05-03 16:29:15 -0400128 * Copy iovec from kernel. Returns -EFAULT on error.
129 */
130
131int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
132 size_t offset, int len)
133{
134 /* Skip over the finished iovecs */
135 while (offset >= iov->iov_len) {
136 offset -= iov->iov_len;
137 iov++;
138 }
139
140 while (len > 0) {
141 char *base = iov->iov_base + offset;
142 int copy = min_t(unsigned int, len, iov->iov_len - offset);
143
144 offset = 0;
145 memcpy(kdata, base, copy);
146 len -= copy;
147 kdata += copy;
148 iov++;
149 }
150
151 return 0;
152}