c52ddea161a267bcc66032f55a57bfd450f1c008
[dragonfly.git] / sys / kern / kern_xio.c
1 /*
2  * Copyright (c) 2004 Matthew Dillon <dillon@backplane.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/sys/kern/kern_xio.c,v 1.5 2004/05/13 17:40:15 dillon Exp $
27  */
28 /*
29  * Kernel XIO interface.  An initialized XIO is basically a collection of
30  * appropriately held vm_page_t's.  XIO buffers are vmspace agnostic and
31  * can represent userspace or kernelspace buffers, and can be passed to
32  * foreign threads outside of the originating vmspace.  XIO buffers are
33  * not mapped into KVM and thus can be manipulated and passed around with
34  * very low overheads.
35  *
36  * The intent is for XIO to be used in the I/O path, VFS, CAPS, and other
37  * places that need to pass (possibly userspace) data between threads.
38  *
39  * TODO: check for busy page when modifying, check writeable.
40  */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/malloc.h>
45 #include <sys/proc.h>
46 #include <sys/vmmeter.h>
47 #include <sys/vnode.h>
48 #include <sys/xio.h>
49 #include <sys/sfbuf.h>
50
51 #include <vm/vm.h>
52 #include <vm/vm_param.h>
53 #include <sys/lock.h>
54 #include <vm/vm_kern.h>
55 #include <vm/pmap.h>
56 #include <vm/vm_map.h>
57 #include <vm/vm_object.h>
58 #include <vm/vm_page.h>
59 #include <vm/vm_pageout.h>
60 #include <vm/vm_pager.h>
61 #include <vm/vm_extern.h>
62 #include <vm/vm_page2.h>
63
64 /*
65  * Initialize an XIO given a userspace buffer.  0 is returned on success,
66  * an error code on failure.  The actual number of bytes that could be
67  * accomodated in the XIO will be stored in xio_bytes.
68  *
69  * Note that you cannot legally accessed a previously cached linmap with 
70  * a newly initialized xio until after calling xio_linmap().
71  */
72 int
73 xio_init_ubuf(xio_t xio, void *ubase, size_t ubytes, int flags)
74 {
75     vm_offset_t addr;
76     vm_paddr_t paddr;
77     vm_page_t m;
78     int i;
79     int n;
80     int s;
81     int vmprot;
82
83     addr = trunc_page((vm_offset_t)ubase);
84     xio->xio_flags = flags;
85     xio->xio_bytes = 0;
86     xio->xio_error = 0;
87     if (ubytes == 0) {
88         xio->xio_offset = 0;
89         xio->xio_npages = 0;
90     } else {
91         vmprot = (flags & XIOF_WRITE) ? VM_PROT_WRITE : VM_PROT_READ;
92         xio->xio_offset = (vm_offset_t)ubase & PAGE_MASK;
93         xio->xio_pages = xio->xio_internal_pages;
94         if ((n = PAGE_SIZE - xio->xio_offset) > ubytes)
95             n = ubytes;
96         for (i = 0; n && i < XIO_INTERNAL_PAGES; ++i) {
97             if (vm_fault_quick((caddr_t)addr, vmprot) < 0)
98                 break;
99             if ((paddr = pmap_kextract(addr)) == 0)
100                 break;
101             s = splvm();
102             m = PHYS_TO_VM_PAGE(paddr);
103             vm_page_hold(m);
104             splx(s);
105             xio->xio_pages[i] = m;
106             ubytes -= n;
107             xio->xio_bytes += n;
108             if ((n = ubytes) > PAGE_SIZE)
109                 n = PAGE_SIZE;
110             addr += PAGE_SIZE;
111         }
112         xio->xio_npages = i;
113
114         /*
115          * If a failure occured clean out what we loaded and return EFAULT.
116          * Return 0 on success.
117          */
118         if (i < XIO_INTERNAL_PAGES && n) {
119             xio_release(xio);
120             xio->xio_error = EFAULT;
121         }
122     }
123     return(xio->xio_error);
124 }
125
126 /*
127  * Initialize an XIO given a kernelspace buffer.  0 is returned on success,
128  * an error code on failure.  The actual number of bytes that could be
129  * accomodated in the XIO will be stored in xio_bytes.
130  *
131  * vmprot is usually either VM_PROT_READ or VM_PROT_WRITE.
132  *
133  * Note that you cannot legally accessed a previously cached linmap with 
134  * a newly initialized xio until after calling xio_linmap().
135  */
136 int
137 xio_init_kbuf(xio_t xio, void *kbase, size_t kbytes)
138 {
139     vm_offset_t addr;
140     vm_paddr_t paddr;
141     vm_page_t m;
142     int i;
143     int n;
144     int s;
145
146     addr = trunc_page((vm_offset_t)kbase);
147     xio->xio_flags = 0;
148     xio->xio_offset = (vm_offset_t)kbase & PAGE_MASK;
149     xio->xio_bytes = 0;
150     xio->xio_pages = xio->xio_internal_pages;
151     xio->xio_error = 0;
152     if ((n = PAGE_SIZE - xio->xio_offset) > kbytes)
153         n = kbytes;
154     for (i = 0; n && i < XIO_INTERNAL_PAGES; ++i) {
155         if ((paddr = pmap_kextract(addr)) == 0)
156             break;
157         s = splvm();
158         m = PHYS_TO_VM_PAGE(paddr);
159         vm_page_hold(m);
160         splx(s);
161         xio->xio_pages[i] = m;
162         kbytes -= n;
163         xio->xio_bytes += n;
164         if ((n = kbytes) > PAGE_SIZE)
165             n = PAGE_SIZE;
166         addr += PAGE_SIZE;
167     }
168     xio->xio_npages = i;
169
170     /*
171      * If a failure occured clean out what we loaded and return EFAULT.
172      * Return 0 on success.
173      */
174     if (i < XIO_INTERNAL_PAGES && n) {
175         xio_release(xio);
176         xio->xio_error = EFAULT;
177     }
178     return(xio->xio_error);
179 }
180
181 /*
182  * Cleanup an XIO so it can be destroyed.  The pages associated with the
183  * XIO are released.  If a linear mapping buffer is active, it will be
184  * unlocked but the mappings will be left intact for optimal reconstitution
185  * in a later xio_linmap() call.
186  *
187  * Note that you cannot legally accessed the linmap on a released XIO.
188  */
189 void
190 xio_release(xio_t xio)
191 {
192     int i;
193     int s;
194     vm_page_t m;
195
196     s = splvm();
197     for (i = 0; i < xio->xio_npages; ++i) {
198         m = xio->xio_pages[i];
199         vm_page_unhold(m);
200     }
201     splx(s);
202     if (xio->xio_flags & XIOF_LINMAP) {
203         xio->xio_flags &= ~XIOF_LINMAP;
204         /* XXX */
205     }
206     xio->xio_offset = 0;
207     xio->xio_npages = 0;
208     xio->xio_bytes = 0;
209     xio->xio_error = ENOBUFS;
210 }
211
212 /*
213  * Copy data between an XIO and a UIO.  If the UIO represents userspace it
214  * must be relative to the current context.  Both the UIO and the XIO are
215  * modified, but the XIO's pages are not released when exhausted.
216  *
217  * UIO_READ     xio -> uio
218  * UIO_WRITE    uio -> xio
219  */
220 int
221 xio_uio_copy(xio_t xio, struct uio *uio, int *sizep)
222 {
223     int error;
224     int bytes;
225
226     if ((bytes = xio->xio_bytes) > uio->uio_resid)
227         bytes = uio->uio_resid;
228     error = uiomove_fromphys(xio->xio_pages, xio->xio_offset, bytes, uio);
229     if (error == 0) {
230         xio->xio_bytes -= bytes;
231         xio->xio_offset += bytes;
232         *sizep = bytes;
233     } else {
234         *sizep = 0;
235     }
236     return(error);
237 }
238
239 /*
240  * Copy the specified number of bytes from the xio to a userland
241  * buffer.  Return an error code or 0 on success.
242  *
243  * The XIO is modified, but the XIO's pages are not released when exhausted.
244  */
245 int
246 xio_copy_xtou(xio_t xio, void *uptr, int bytes)
247 {
248     int i;
249     int n;
250     int error;
251     int offset;
252     vm_page_t m;
253     struct sf_buf *sf;
254
255     if (bytes > xio->xio_bytes)
256         return(EFAULT);
257
258     offset = xio->xio_offset & PAGE_MASK;
259     if ((n = PAGE_SIZE - offset) > bytes)
260         n = bytes;
261
262     error = 0;
263     for (i = xio->xio_offset >> PAGE_SHIFT; i < xio->xio_npages; ++i) {
264         m = xio->xio_pages[i];
265         sf = sf_buf_alloc(m, SFBA_QUICK);
266         error = copyout((char *)sf_buf_kva(sf) + offset, uptr, n);
267         sf_buf_free(sf);
268         if (error)
269             break;
270         bytes -= n;
271         xio->xio_bytes -= n;
272         xio->xio_offset += n;
273         uptr = (char *)uptr + n;
274         if (bytes == 0)
275             break;
276         if ((n = bytes) > PAGE_SIZE)
277             n = PAGE_SIZE;
278         offset = 0;
279     }
280     return(error);
281 }
282
283 /*
284  * Copy the specified number of bytes from the xio to a kernel
285  * buffer.  Return an error code or 0 on success.
286  *
287  * The XIO is modified, but the XIO's pages are not released when exhausted.
288  */
289 int
290 xio_copy_xtok(xio_t xio, void *kptr, int bytes)
291 {
292     int i;
293     int n;
294     int error;
295     int offset;
296     vm_page_t m;
297     struct sf_buf *sf;
298
299     if (bytes > xio->xio_bytes)
300         return(EFAULT);
301
302     offset = xio->xio_offset & PAGE_MASK;
303     if ((n = PAGE_SIZE - offset) > bytes)
304         n = bytes;
305
306     error = 0;
307     for (i = xio->xio_offset >> PAGE_SHIFT; i < xio->xio_npages; ++i) {
308         m = xio->xio_pages[i];
309         sf = sf_buf_alloc(m, SFBA_QUICK);
310         bcopy((char *)sf_buf_kva(sf) + offset, kptr, n);
311         sf_buf_free(sf);
312         bytes -= n;
313         xio->xio_bytes -= n;
314         xio->xio_offset += n;
315         kptr = (char *)kptr + n;
316         if (bytes == 0)
317             break;
318         if ((n = bytes) > PAGE_SIZE)
319             n = PAGE_SIZE;
320         offset = 0;
321     }
322     return(error);
323 }
324