]> granicus.if.org Git - zfs/blob - module/zcommon/zfs_uio.c
Linux 5.0 compat: access_ok() drops 'type' parameter
[zfs] / 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                                 if (uio->uio_fault_disable) {
84                                         if (!zfs_access_ok(VERIFY_READ,
85                                             (iov->iov_base + skip), cnt)) {
86                                                 return (EFAULT);
87                                         }
88                                         pagefault_disable();
89                                         if (__copy_from_user_inatomic(p,
90                                             (iov->iov_base + skip), cnt)) {
91                                                 pagefault_enable();
92                                                 return (EFAULT);
93                                         }
94                                         pagefault_enable();
95                                 } else {
96                                         if (copy_from_user(p,
97                                             (iov->iov_base + skip), cnt))
98                                                 return (EFAULT);
99                                 }
100                         }
101                         break;
102                 case UIO_SYSSPACE:
103                         if (rw == UIO_READ)
104                                 bcopy(p, iov->iov_base + skip, cnt);
105                         else
106                                 bcopy(iov->iov_base + skip, p, cnt);
107                         break;
108                 default:
109                         ASSERT(0);
110                 }
111                 skip += cnt;
112                 if (skip == iov->iov_len) {
113                         skip = 0;
114                         uio->uio_iov = (++iov);
115                         uio->uio_iovcnt--;
116                 }
117                 uio->uio_skip = skip;
118                 uio->uio_resid -= cnt;
119                 uio->uio_loffset += cnt;
120                 p = (caddr_t)p + cnt;
121                 n -= cnt;
122         }
123         return (0);
124 }
125
126 static int
127 uiomove_bvec(void *p, size_t n, enum uio_rw rw, struct uio *uio)
128 {
129         const struct bio_vec *bv = uio->uio_bvec;
130         size_t skip = uio->uio_skip;
131         ulong_t cnt;
132
133         while (n && uio->uio_resid) {
134                 void *paddr;
135                 cnt = MIN(bv->bv_len - skip, n);
136
137                 paddr = zfs_kmap_atomic(bv->bv_page, KM_USER1);
138                 if (rw == UIO_READ)
139                         bcopy(p, paddr + bv->bv_offset + skip, cnt);
140                 else
141                         bcopy(paddr + bv->bv_offset + skip, p, cnt);
142                 zfs_kunmap_atomic(paddr, KM_USER1);
143
144                 skip += cnt;
145                 if (skip == bv->bv_len) {
146                         skip = 0;
147                         uio->uio_bvec = (++bv);
148                         uio->uio_iovcnt--;
149                 }
150                 uio->uio_skip = skip;
151                 uio->uio_resid -= cnt;
152                 uio->uio_loffset += cnt;
153                 p = (caddr_t)p + cnt;
154                 n -= cnt;
155         }
156         return (0);
157 }
158
159 int
160 uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
161 {
162         if (uio->uio_segflg != UIO_BVEC)
163                 return (uiomove_iov(p, n, rw, uio));
164         else
165                 return (uiomove_bvec(p, n, rw, uio));
166 }
167 EXPORT_SYMBOL(uiomove);
168
169 #define fuword8(uptr, vptr)     get_user((*vptr), (uptr))
170
171 /*
172  * Fault in the pages of the first n bytes specified by the uio structure.
173  * 1 byte in each page is touched and the uio struct is unmodified. Any
174  * error will terminate the process as this is only a best attempt to get
175  * the pages resident.
176  */
177 int
178 uio_prefaultpages(ssize_t n, struct uio *uio)
179 {
180         const struct iovec *iov;
181         ulong_t cnt, incr;
182         caddr_t p;
183         uint8_t tmp;
184         int iovcnt;
185         size_t skip;
186
187         /* no need to fault in kernel pages */
188         switch (uio->uio_segflg) {
189                 case UIO_SYSSPACE:
190                 case UIO_BVEC:
191                         return (0);
192                 case UIO_USERSPACE:
193                 case UIO_USERISPACE:
194                         break;
195                 default:
196                         ASSERT(0);
197         }
198
199         iov = uio->uio_iov;
200         iovcnt = uio->uio_iovcnt;
201         skip = uio->uio_skip;
202
203         for (; n > 0 && iovcnt > 0; iov++, iovcnt--, skip = 0) {
204                 cnt = MIN(iov->iov_len - skip, n);
205                 /* empty iov */
206                 if (cnt == 0)
207                         continue;
208                 n -= cnt;
209                 /*
210                  * touch each page in this segment.
211                  */
212                 p = iov->iov_base + skip;
213                 while (cnt) {
214                         if (fuword8((uint8_t *)p, &tmp))
215                                 return (EFAULT);
216                         incr = MIN(cnt, PAGESIZE);
217                         p += incr;
218                         cnt -= incr;
219                 }
220                 /*
221                  * touch the last byte in case it straddles a page.
222                  */
223                 p--;
224                 if (fuword8((uint8_t *)p, &tmp))
225                         return (EFAULT);
226         }
227
228         return (0);
229 }
230 EXPORT_SYMBOL(uio_prefaultpages);
231
232 /*
233  * same as uiomove() but doesn't modify uio structure.
234  * return in cbytes how many bytes were copied.
235  */
236 int
237 uiocopy(void *p, size_t n, enum uio_rw rw, struct uio *uio, size_t *cbytes)
238 {
239         struct uio uio_copy;
240         int ret;
241
242         bcopy(uio, &uio_copy, sizeof (struct uio));
243         ret = uiomove(p, n, rw, &uio_copy);
244         *cbytes = uio->uio_resid - uio_copy.uio_resid;
245         return (ret);
246 }
247 EXPORT_SYMBOL(uiocopy);
248
249 /*
250  * Drop the next n chars out of *uiop.
251  */
252 void
253 uioskip(uio_t *uiop, size_t n)
254 {
255         if (n > uiop->uio_resid)
256                 return;
257
258         uiop->uio_skip += n;
259         if (uiop->uio_segflg != UIO_BVEC) {
260                 while (uiop->uio_iovcnt &&
261                     uiop->uio_skip >= uiop->uio_iov->iov_len) {
262                         uiop->uio_skip -= uiop->uio_iov->iov_len;
263                         uiop->uio_iov++;
264                         uiop->uio_iovcnt--;
265                 }
266         } else {
267                 while (uiop->uio_iovcnt &&
268                     uiop->uio_skip >= uiop->uio_bvec->bv_len) {
269                         uiop->uio_skip -= uiop->uio_bvec->bv_len;
270                         uiop->uio_bvec++;
271                         uiop->uio_iovcnt--;
272                 }
273         }
274         uiop->uio_loffset += n;
275         uiop->uio_resid -= n;
276 }
277 EXPORT_SYMBOL(uioskip);
278 #endif /* _KERNEL */