]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - module/zcommon/zfs_uio.c
Vendor import of openzfs master @ 184df27eef0abdc7ab2105b21257f753834b936b
[FreeBSD/FreeBSD.git] / module / zcommon / zfs_uio.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25
26 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /*        All Rights Reserved   */
28
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 /*
39  * Copyright (c) 2015 by Chunwei Chen. All rights reserved.
40  */
41
42 /*
43  * The uio support from OpenSolaris has been added as a short term
44  * work around.  The hope is to adopt native Linux type and drop the
45  * use of uio's entirely.  Under Linux they only add overhead and
46  * when possible we want to use native APIs for the ZPL layer.
47  */
48 #ifdef _KERNEL
49
50 #include <sys/types.h>
51 #include <sys/uio_impl.h>
52 #include <sys/sysmacros.h>
53 #include <sys/strings.h>
54 #include <linux/kmap_compat.h>
55 #include <linux/uaccess.h>
56
57 /*
58  * Move "n" bytes at byte address "p"; "rw" indicates the direction
59  * of the move, and the I/O parameters are provided in "uio", which is
60  * update to reflect the data which was moved.  Returns 0 on success or
61  * a non-zero errno on failure.
62  */
63 static int
64 uiomove_iov(void *p, size_t n, enum uio_rw rw, struct uio *uio)
65 {
66         const struct iovec *iov = uio->uio_iov;
67         size_t skip = uio->uio_skip;
68         ulong_t cnt;
69
70         while (n && uio->uio_resid) {
71                 cnt = MIN(iov->iov_len - skip, n);
72                 switch (uio->uio_segflg) {
73                 case UIO_USERSPACE:
74                 case UIO_USERISPACE:
75                         /*
76                          * p = kernel data pointer
77                          * iov->iov_base = user data pointer
78                          */
79                         if (rw == UIO_READ) {
80                                 if (copy_to_user(iov->iov_base+skip, p, cnt))
81                                         return (EFAULT);
82                         } else {
83                                 unsigned long b_left = 0;
84                                 if (uio->uio_fault_disable) {
85                                         if (!zfs_access_ok(VERIFY_READ,
86                                             (iov->iov_base + skip), cnt)) {
87                                                 return (EFAULT);
88                                         }
89                                         pagefault_disable();
90                                         b_left =
91                                             __copy_from_user_inatomic(p,
92                                             (iov->iov_base + skip), cnt);
93                                         pagefault_enable();
94                                 } else {
95                                         b_left =
96                                             copy_from_user(p,
97                                             (iov->iov_base + skip), cnt);
98                                 }
99                                 if (b_left > 0) {
100                                         unsigned long c_bytes =
101                                             cnt - b_left;
102                                         uio->uio_skip += c_bytes;
103                                         ASSERT3U(uio->uio_skip, <,
104                                             iov->iov_len);
105                                         uio->uio_resid -= c_bytes;
106                                         uio->uio_loffset += c_bytes;
107                                         return (EFAULT);
108                                 }
109                         }
110                         break;
111                 case UIO_SYSSPACE:
112                         if (rw == UIO_READ)
113                                 bcopy(p, iov->iov_base + skip, cnt);
114                         else
115                                 bcopy(iov->iov_base + skip, p, cnt);
116                         break;
117                 default:
118                         ASSERT(0);
119                 }
120                 skip += cnt;
121                 if (skip == iov->iov_len) {
122                         skip = 0;
123                         uio->uio_iov = (++iov);
124                         uio->uio_iovcnt--;
125                 }
126                 uio->uio_skip = skip;
127                 uio->uio_resid -= cnt;
128                 uio->uio_loffset += cnt;
129                 p = (caddr_t)p + cnt;
130                 n -= cnt;
131         }
132         return (0);
133 }
134
135 static int
136 uiomove_bvec(void *p, size_t n, enum uio_rw rw, struct uio *uio)
137 {
138         const struct bio_vec *bv = uio->uio_bvec;
139         size_t skip = uio->uio_skip;
140         ulong_t cnt;
141
142         while (n && uio->uio_resid) {
143                 void *paddr;
144                 cnt = MIN(bv->bv_len - skip, n);
145
146                 paddr = zfs_kmap_atomic(bv->bv_page, KM_USER1);
147                 if (rw == UIO_READ)
148                         bcopy(p, paddr + bv->bv_offset + skip, cnt);
149                 else
150                         bcopy(paddr + bv->bv_offset + skip, p, cnt);
151                 zfs_kunmap_atomic(paddr, KM_USER1);
152
153                 skip += cnt;
154                 if (skip == bv->bv_len) {
155                         skip = 0;
156                         uio->uio_bvec = (++bv);
157                         uio->uio_iovcnt--;
158                 }
159                 uio->uio_skip = skip;
160                 uio->uio_resid -= cnt;
161                 uio->uio_loffset += cnt;
162                 p = (caddr_t)p + cnt;
163                 n -= cnt;
164         }
165         return (0);
166 }
167
168 int
169 uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
170 {
171         if (uio->uio_segflg != UIO_BVEC)
172                 return (uiomove_iov(p, n, rw, uio));
173         else
174                 return (uiomove_bvec(p, n, rw, uio));
175 }
176 EXPORT_SYMBOL(uiomove);
177
178 #define fuword8(uptr, vptr)     get_user((*vptr), (uptr))
179
180 /*
181  * Fault in the pages of the first n bytes specified by the uio structure.
182  * 1 byte in each page is touched and the uio struct is unmodified. Any
183  * error will terminate the process as this is only a best attempt to get
184  * the pages resident.
185  */
186 int
187 uio_prefaultpages(ssize_t n, struct uio *uio)
188 {
189         const struct iovec *iov;
190         ulong_t cnt, incr;
191         caddr_t p;
192         uint8_t tmp;
193         int iovcnt;
194         size_t skip;
195
196         /* no need to fault in kernel pages */
197         switch (uio->uio_segflg) {
198                 case UIO_SYSSPACE:
199                 case UIO_BVEC:
200                         return (0);
201                 case UIO_USERSPACE:
202                 case UIO_USERISPACE:
203                         break;
204                 default:
205                         ASSERT(0);
206         }
207
208         iov = uio->uio_iov;
209         iovcnt = uio->uio_iovcnt;
210         skip = uio->uio_skip;
211
212         for (; n > 0 && iovcnt > 0; iov++, iovcnt--, skip = 0) {
213                 cnt = MIN(iov->iov_len - skip, n);
214                 /* empty iov */
215                 if (cnt == 0)
216                         continue;
217                 n -= cnt;
218                 /*
219                  * touch each page in this segment.
220                  */
221                 p = iov->iov_base + skip;
222                 while (cnt) {
223                         if (fuword8((uint8_t *)p, &tmp))
224                                 return (EFAULT);
225                         incr = MIN(cnt, PAGESIZE);
226                         p += incr;
227                         cnt -= incr;
228                 }
229                 /*
230                  * touch the last byte in case it straddles a page.
231                  */
232                 p--;
233                 if (fuword8((uint8_t *)p, &tmp))
234                         return (EFAULT);
235         }
236
237         return (0);
238 }
239 EXPORT_SYMBOL(uio_prefaultpages);
240
241 /*
242  * same as uiomove() but doesn't modify uio structure.
243  * return in cbytes how many bytes were copied.
244  */
245 int
246 uiocopy(void *p, size_t n, enum uio_rw rw, struct uio *uio, size_t *cbytes)
247 {
248         struct uio uio_copy;
249         int ret;
250
251         bcopy(uio, &uio_copy, sizeof (struct uio));
252         ret = uiomove(p, n, rw, &uio_copy);
253         *cbytes = uio->uio_resid - uio_copy.uio_resid;
254         return (ret);
255 }
256 EXPORT_SYMBOL(uiocopy);
257
258 /*
259  * Drop the next n chars out of *uiop.
260  */
261 void
262 uioskip(uio_t *uiop, size_t n)
263 {
264         if (n > uiop->uio_resid)
265                 return;
266
267         uiop->uio_skip += n;
268         if (uiop->uio_segflg != UIO_BVEC) {
269                 while (uiop->uio_iovcnt &&
270                     uiop->uio_skip >= uiop->uio_iov->iov_len) {
271                         uiop->uio_skip -= uiop->uio_iov->iov_len;
272                         uiop->uio_iov++;
273                         uiop->uio_iovcnt--;
274                 }
275         } else {
276                 while (uiop->uio_iovcnt &&
277                     uiop->uio_skip >= uiop->uio_bvec->bv_len) {
278                         uiop->uio_skip -= uiop->uio_bvec->bv_len;
279                         uiop->uio_bvec++;
280                         uiop->uio_iovcnt--;
281                 }
282         }
283         uiop->uio_loffset += n;
284         uiop->uio_resid -= n;
285 }
286 EXPORT_SYMBOL(uioskip);
287 #endif /* _KERNEL */