]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - zfs/module/zcommon/zfs_uio.c
UBUNTU: SAUCE: Fix non-prefaulted page deadlock (LP: #1754584)
[mirror_ubuntu-bionic-kernel.git] / zfs / module / zcommon / zfs_uio.c
CommitLineData
70e083d2
TG
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 <linux/kmap_compat.h>
53
54/*
55 * Move "n" bytes at byte address "p"; "rw" indicates the direction
56 * of the move, and the I/O parameters are provided in "uio", which is
57 * update to reflect the data which was moved. Returns 0 on success or
58 * a non-zero errno on failure.
59 */
60static int
61uiomove_iov(void *p, size_t n, enum uio_rw rw, struct uio *uio)
62{
63 const struct iovec *iov = uio->uio_iov;
64 size_t skip = uio->uio_skip;
65 ulong_t cnt;
66
67 while (n && uio->uio_resid) {
68 cnt = MIN(iov->iov_len - skip, n);
69 switch (uio->uio_segflg) {
70 case UIO_USERSPACE:
71 case UIO_USERISPACE:
72 /*
73 * p = kernel data pointer
74 * iov->iov_base = user data pointer
75 */
76 if (rw == UIO_READ) {
77 if (copy_to_user(iov->iov_base+skip, p, cnt))
78 return (EFAULT);
79 } else {
80 if (copy_from_user(p, iov->iov_base+skip, cnt))
81 return (EFAULT);
82 }
83 break;
84 case UIO_SYSSPACE:
85 if (rw == UIO_READ)
86 bcopy(p, iov->iov_base + skip, cnt);
87 else
88 bcopy(iov->iov_base + skip, p, cnt);
89 break;
90 default:
91 ASSERT(0);
92 }
93 skip += cnt;
94 if (skip == iov->iov_len) {
95 skip = 0;
96 uio->uio_iov = (++iov);
97 uio->uio_iovcnt--;
98 }
99 uio->uio_skip = skip;
100 uio->uio_resid -= cnt;
101 uio->uio_loffset += cnt;
102 p = (caddr_t)p + cnt;
103 n -= cnt;
104 }
105 return (0);
106}
107
108static int
109uiomove_bvec(void *p, size_t n, enum uio_rw rw, struct uio *uio)
110{
111 const struct bio_vec *bv = uio->uio_bvec;
112 size_t skip = uio->uio_skip;
113 ulong_t cnt;
114
115 while (n && uio->uio_resid) {
116 void *paddr;
117 cnt = MIN(bv->bv_len - skip, n);
118
119 paddr = zfs_kmap_atomic(bv->bv_page, KM_USER1);
120 if (rw == UIO_READ)
121 bcopy(p, paddr + bv->bv_offset + skip, cnt);
122 else
123 bcopy(paddr + bv->bv_offset + skip, p, cnt);
124 zfs_kunmap_atomic(paddr, KM_USER1);
125
126 skip += cnt;
127 if (skip == bv->bv_len) {
128 skip = 0;
129 uio->uio_bvec = (++bv);
130 uio->uio_iovcnt--;
131 }
132 uio->uio_skip = skip;
133 uio->uio_resid -= cnt;
134 uio->uio_loffset += cnt;
135 p = (caddr_t)p + cnt;
136 n -= cnt;
137 }
138 return (0);
139}
140
141int
142uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
143{
144 if (uio->uio_segflg != UIO_BVEC)
145 return (uiomove_iov(p, n, rw, uio));
146 else
147 return (uiomove_bvec(p, n, rw, uio));
148}
149EXPORT_SYMBOL(uiomove);
150
70e083d2 151/*
bc0adba1
CIK
152 * Fault in the user space pages specified by the uio structure. Note that
153 * when faulting in pages with UIO_READ they may have zeros written to them
154 * which is OK because we know they'll be overwritten.
70e083d2 155 */
bc0adba1
CIK
156int
157uio_prefaultpages(ssize_t n, enum uio_rw rw, struct uio *uio)
70e083d2 158{
bc0adba1
CIK
159 const struct iovec *iov = uio->uio_iov;
160 size_t skip = uio->uio_skip;
161 int iovcnt = uio->uio_iovcnt;
162 uio_seg_t seg = uio->uio_segflg;
163 char __user *p;
164 ulong_t cnt;
165 int error;
166
167 /* No need to fault in kernel pages */
168 if (seg == UIO_SYSSPACE || seg == UIO_BVEC)
169 return (0);
70e083d2 170
bc0adba1 171 ASSERT(seg == UIO_USERSPACE || seg == UIO_USERISPACE);
70e083d2 172
bc0adba1 173 while ((n > 0) && (iovcnt > 0)) {
70e083d2 174 cnt = MIN(iov->iov_len - skip, n);
70e083d2 175 p = iov->iov_base + skip;
bc0adba1
CIK
176
177 if (rw == UIO_READ)
178 error = -fault_in_pages_writeable(p, cnt);
179 else
180 error = -fault_in_pages_readable(p, cnt);
181
182 if (error)
183 return (error);
184
185 skip += cnt;
186 if (skip == iov->iov_len) {
187 skip = 0;
188 iov++;
189 iovcnt--;
70e083d2 190 }
bc0adba1
CIK
191
192 n -= cnt;
70e083d2 193 }
bc0adba1
CIK
194
195 return (0);
70e083d2
TG
196}
197EXPORT_SYMBOL(uio_prefaultpages);
198
199/*
200 * same as uiomove() but doesn't modify uio structure.
201 * return in cbytes how many bytes were copied.
202 */
203int
204uiocopy(void *p, size_t n, enum uio_rw rw, struct uio *uio, size_t *cbytes)
205{
206 struct uio uio_copy;
207 int ret;
208
209 bcopy(uio, &uio_copy, sizeof (struct uio));
210 ret = uiomove(p, n, rw, &uio_copy);
211 *cbytes = uio->uio_resid - uio_copy.uio_resid;
212 return (ret);
213}
214EXPORT_SYMBOL(uiocopy);
215
216/*
217 * Drop the next n chars out of *uiop.
218 */
219void
220uioskip(uio_t *uiop, size_t n)
221{
222 if (n > uiop->uio_resid)
223 return;
224
225 uiop->uio_skip += n;
226 if (uiop->uio_segflg != UIO_BVEC) {
227 while (uiop->uio_iovcnt &&
228 uiop->uio_skip >= uiop->uio_iov->iov_len) {
229 uiop->uio_skip -= uiop->uio_iov->iov_len;
230 uiop->uio_iov++;
231 uiop->uio_iovcnt--;
232 }
233 } else {
234 while (uiop->uio_iovcnt &&
235 uiop->uio_skip >= uiop->uio_bvec->bv_len) {
236 uiop->uio_skip -= uiop->uio_bvec->bv_len;
237 uiop->uio_bvec++;
238 uiop->uio_iovcnt--;
239 }
240 }
241 uiop->uio_loffset += n;
242 uiop->uio_resid -= n;
243}
244EXPORT_SYMBOL(uioskip);
245#endif /* _KERNEL */