Line data Source code
1 : /* $OpenBSD: kern_physio.c,v 1.43 2015/03/14 03:38:50 jsg Exp $ */
2 : /* $NetBSD: kern_physio.c,v 1.28 1997/05/19 10:43:28 pk Exp $ */
3 :
4 : /*-
5 : * Copyright (c) 1994 Christopher G. Demetriou
6 : * Copyright (c) 1982, 1986, 1990, 1993
7 : * The Regents of the University of California. All rights reserved.
8 : * (c) UNIX System Laboratories, Inc.
9 : * All or some portions of this file are derived from material licensed
10 : * to the University of California by American Telephone and Telegraph
11 : * Co. or Unix System Laboratories, Inc. and are reproduced herein with
12 : * the permission of UNIX System Laboratories, Inc.
13 : *
14 : * Redistribution and use in source and binary forms, with or without
15 : * modification, are permitted provided that the following conditions
16 : * are met:
17 : * 1. Redistributions of source code must retain the above copyright
18 : * notice, this list of conditions and the following disclaimer.
19 : * 2. Redistributions in binary form must reproduce the above copyright
20 : * notice, this list of conditions and the following disclaimer in the
21 : * documentation and/or other materials provided with the distribution.
22 : * 3. Neither the name of the University nor the names of its contributors
23 : * may be used to endorse or promote products derived from this software
24 : * without specific prior written permission.
25 : *
26 : * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 : * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 : * SUCH DAMAGE.
37 : *
38 : * @(#)kern_physio.c 8.1 (Berkeley) 6/10/93
39 : */
40 :
41 : #include <sys/param.h>
42 : #include <sys/systm.h>
43 : #include <sys/buf.h>
44 : #include <sys/pool.h>
45 :
46 : #include <uvm/uvm_extern.h>
47 :
48 : /*
49 : * The routines implemented in this file are described in:
50 : * Leffler, et al.: The Design and Implementation of the 4.3BSD
51 : * UNIX Operating System (Addison Welley, 1989)
52 : * on pages 231-233.
53 : */
54 :
55 : /*
56 : * Do "physical I/O" on behalf of a user. "Physical I/O" is I/O directly
57 : * from the raw device to user buffers, and bypasses the buffer cache.
58 : *
59 : * Comments in brackets are from Leffler, et al.'s pseudo-code implementation.
60 : */
61 : int
62 0 : physio(void (*strategy)(struct buf *), dev_t dev, int flags,
63 : void (*minphys)(struct buf *), struct uio *uio)
64 : {
65 : struct iovec *iovp;
66 0 : struct proc *p = curproc;
67 : int error, done, i, s, todo;
68 : struct buf *bp;
69 :
70 0 : if ((uio->uio_offset % DEV_BSIZE) != 0)
71 0 : return (EINVAL);
72 :
73 : error = 0;
74 0 : flags &= B_READ | B_WRITE;
75 :
76 : /* Create a buffer. */
77 0 : s = splbio();
78 0 : bp = pool_get(&bufpool, PR_WAITOK | PR_ZERO);
79 :
80 : /* [set up the fixed part of the buffer for a transfer] */
81 0 : bp->b_vnbufs.le_next = NOLIST;
82 0 : bp->b_dev = dev;
83 0 : bp->b_error = 0;
84 0 : bp->b_proc = p;
85 0 : bp->b_flags = B_BUSY;
86 0 : LIST_INIT(&bp->b_dep);
87 0 : splx(s);
88 :
89 : /*
90 : * [while there are data to transfer and no I/O error]
91 : * Note that I/O errors are handled with a 'goto' at the bottom
92 : * of the 'while' loop.
93 : */
94 0 : for (i = 0; i < uio->uio_iovcnt; i++) {
95 0 : iovp = &uio->uio_iov[i];
96 0 : while (iovp->iov_len > 0) {
97 0 : void *map = NULL;
98 :
99 : /*
100 : * [mark the buffer busy for physical I/O]
101 : * (i.e. set B_PHYS (because it's an I/O to user
102 : * memory), and B_RAW, because B_RAW is to be
103 : * "Set by physio for raw transfers.", in addition
104 : * to the "busy" and read/write flag.)
105 : */
106 0 : CLR(bp->b_flags, B_DONE | B_ERROR);
107 0 : bp->b_flags |= (B_BUSY | B_PHYS | B_RAW | flags);
108 :
109 : /* [set up the buffer for a maximum-sized transfer] */
110 0 : bp->b_blkno = btodb(uio->uio_offset);
111 :
112 : /*
113 : * Because iov_len is unsigned but b_bcount is signed,
114 : * an overflow is possible. Therefore bound to MAXPHYS
115 : * before calling minphys.
116 : */
117 0 : if (iovp->iov_len > MAXPHYS)
118 0 : bp->b_bcount = MAXPHYS;
119 : else
120 0 : bp->b_bcount = iovp->iov_len;
121 :
122 : /*
123 : * [call minphys to bound the transfer size]
124 : * and remember the amount of data to transfer,
125 : * for later comparison.
126 : */
127 0 : (*minphys)(bp);
128 0 : todo = bp->b_bcount;
129 0 : KASSERTMSG(todo >= 0, "minphys broken");
130 0 : KASSERTMSG(todo <= MAXPHYS, "minphys broken");
131 :
132 : /*
133 : * [lock the part of the user address space involved
134 : * in the transfer]
135 : * Beware vmapbuf(); it clobbers b_data and
136 : * saves it in b_saveaddr. However, vunmapbuf()
137 : * restores it.
138 : */
139 0 : error = uvm_vslock_device(p, iovp->iov_base, todo,
140 0 : (flags & B_READ) ?
141 : PROT_READ | PROT_WRITE : PROT_READ, &map);
142 0 : if (error)
143 0 : goto done;
144 0 : if (map) {
145 0 : bp->b_data = map;
146 0 : } else {
147 0 : bp->b_data = iovp->iov_base;
148 0 : vmapbuf(bp, todo);
149 : }
150 :
151 : /* [call strategy to start the transfer] */
152 0 : (*strategy)(bp);
153 :
154 : /*
155 : * Note that the raise/wait/lower/get error
156 : * steps below would be done by biowait(), but
157 : * we want to unlock the address space before
158 : * we lower the priority.
159 : *
160 : * [raise the priority level to splbio]
161 : */
162 0 : s = splbio();
163 :
164 : /* [wait for the transfer to complete] */
165 0 : while ((bp->b_flags & B_DONE) == 0)
166 0 : tsleep(bp, PRIBIO + 1, "physio", 0);
167 :
168 : /* Mark it busy again, so nobody else will use it. */
169 0 : bp->b_flags |= B_BUSY;
170 :
171 : /* [lower the priority level] */
172 0 : splx(s);
173 :
174 : /*
175 : * [unlock the part of the address space previously
176 : * locked]
177 : */
178 0 : if (!map)
179 0 : vunmapbuf(bp, todo);
180 0 : uvm_vsunlock_device(p, iovp->iov_base, todo, map);
181 :
182 : /* remember error value (save a splbio/splx pair) */
183 0 : if (bp->b_flags & B_ERROR)
184 0 : error = (bp->b_error ? bp->b_error : EIO);
185 :
186 : /*
187 : * [deduct the transfer size from the total number
188 : * of data to transfer]
189 : */
190 0 : done = bp->b_bcount - bp->b_resid;
191 0 : KASSERTMSG(done >= 0, "strategy broken");
192 0 : KASSERTMSG(done <= todo, "strategy broken");
193 0 : iovp->iov_len -= done;
194 0 : iovp->iov_base = (caddr_t)iovp->iov_base + done;
195 0 : uio->uio_offset += done;
196 0 : uio->uio_resid -= done;
197 :
198 : /*
199 : * Now, check for an error.
200 : * Also, handle weird end-of-disk semantics.
201 : */
202 0 : if (error || done < todo)
203 0 : goto done;
204 0 : }
205 : }
206 :
207 : done:
208 : /*
209 : * [clean up the state of the buffer]
210 : */
211 0 : s = splbio();
212 : /* XXXCDC: is this necessary? */
213 0 : if (bp->b_vp)
214 0 : brelvp(bp);
215 0 : splx(s);
216 0 : pool_put(&bufpool, bp);
217 :
218 0 : return (error);
219 0 : }
220 :
221 : /*
222 : * Leffler, et al., says on p. 231:
223 : * "The minphys() routine is called by physio() to adjust the
224 : * size of each I/O transfer before the latter is passed to
225 : * the strategy routine..."
226 : *
227 : * so, just adjust the buffer's count accounting to MAXPHYS here,
228 : * and return the new count;
229 : */
230 : void
231 0 : minphys(struct buf *bp)
232 : {
233 :
234 0 : if (bp->b_bcount > MAXPHYS)
235 0 : bp->b_bcount = MAXPHYS;
236 0 : }
|