xref: /dragonfly/sys/kern/kern_xio.c (revision 13dd34d8)
181ee925dSMatthew Dillon /*
28c10bfcfSMatthew Dillon  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
38c10bfcfSMatthew Dillon  *
48c10bfcfSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
58c10bfcfSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
681ee925dSMatthew Dillon  *
781ee925dSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
881ee925dSMatthew Dillon  * modification, are permitted provided that the following conditions
981ee925dSMatthew Dillon  * are met:
108c10bfcfSMatthew Dillon  *
1181ee925dSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
1281ee925dSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
1381ee925dSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
148c10bfcfSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
158c10bfcfSMatthew Dillon  *    the documentation and/or other materials provided with the
168c10bfcfSMatthew Dillon  *    distribution.
178c10bfcfSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
188c10bfcfSMatthew Dillon  *    contributors may be used to endorse or promote products derived
198c10bfcfSMatthew Dillon  *    from this software without specific, prior written permission.
2081ee925dSMatthew Dillon  *
218c10bfcfSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
228c10bfcfSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
238c10bfcfSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
248c10bfcfSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
258c10bfcfSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
268c10bfcfSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
278c10bfcfSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
288c10bfcfSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
298c10bfcfSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
308c10bfcfSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
318c10bfcfSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3281ee925dSMatthew Dillon  * SUCH DAMAGE.
3381ee925dSMatthew Dillon  */
3481ee925dSMatthew Dillon /*
3581ee925dSMatthew Dillon  * Kernel XIO interface.  An initialized XIO is basically a collection of
3681ee925dSMatthew Dillon  * appropriately held vm_page_t's.  XIO buffers are vmspace agnostic and
3781ee925dSMatthew Dillon  * can represent userspace or kernelspace buffers, and can be passed to
3881ee925dSMatthew Dillon  * foreign threads outside of the originating vmspace.  XIO buffers are
3981ee925dSMatthew Dillon  * not mapped into KVM and thus can be manipulated and passed around with
4081ee925dSMatthew Dillon  * very low overheads.
4181ee925dSMatthew Dillon  *
4281ee925dSMatthew Dillon  * The intent is for XIO to be used in the I/O path, VFS, CAPS, and other
4381ee925dSMatthew Dillon  * places that need to pass (possibly userspace) data between threads.
4481ee925dSMatthew Dillon  *
4581ee925dSMatthew Dillon  * TODO: check for busy page when modifying, check writeable.
4681ee925dSMatthew Dillon  */
4781ee925dSMatthew Dillon 
4881ee925dSMatthew Dillon #include <sys/param.h>
4981ee925dSMatthew Dillon #include <sys/systm.h>
50*13dd34d8Szrj #include <sys/uio.h>
5181ee925dSMatthew Dillon #include <sys/malloc.h>
5281ee925dSMatthew Dillon #include <sys/proc.h>
5381ee925dSMatthew Dillon #include <sys/vmmeter.h>
5481ee925dSMatthew Dillon #include <sys/vnode.h>
5581ee925dSMatthew Dillon #include <sys/xio.h>
565c5185aeSSamuel J. Greear 
575c5185aeSSamuel J. Greear #include <cpu/lwbuf.h>
5881ee925dSMatthew Dillon 
5981ee925dSMatthew Dillon #include <vm/vm.h>
6081ee925dSMatthew Dillon #include <vm/vm_param.h>
6181ee925dSMatthew Dillon #include <sys/lock.h>
6281ee925dSMatthew Dillon #include <vm/vm_kern.h>
6381ee925dSMatthew Dillon #include <vm/pmap.h>
6481ee925dSMatthew Dillon #include <vm/vm_map.h>
6581ee925dSMatthew Dillon #include <vm/vm_object.h>
6681ee925dSMatthew Dillon #include <vm/vm_page.h>
6781ee925dSMatthew Dillon #include <vm/vm_pageout.h>
6881ee925dSMatthew Dillon #include <vm/vm_pager.h>
6981ee925dSMatthew Dillon #include <vm/vm_extern.h>
7081ee925dSMatthew Dillon #include <vm/vm_page2.h>
7181ee925dSMatthew Dillon 
7281ee925dSMatthew Dillon /*
73d6a46bb7SMatthew Dillon  * Just do basic initialization of an empty XIO
74d6a46bb7SMatthew Dillon  */
75d6a46bb7SMatthew Dillon void
xio_init(xio_t xio)76d6a46bb7SMatthew Dillon xio_init(xio_t xio)
77d6a46bb7SMatthew Dillon {
78d6a46bb7SMatthew Dillon     xio->xio_flags = 0;
79d6a46bb7SMatthew Dillon     xio->xio_bytes = 0;
80d6a46bb7SMatthew Dillon     xio->xio_error = 0;
81d6a46bb7SMatthew Dillon     xio->xio_offset = 0;
82d6a46bb7SMatthew Dillon     xio->xio_npages = 0;
83d6a46bb7SMatthew Dillon     xio->xio_pages = xio->xio_internal_pages;
84d6a46bb7SMatthew Dillon }
85d6a46bb7SMatthew Dillon 
86d6a46bb7SMatthew Dillon /*
8781ee925dSMatthew Dillon  * Initialize an XIO given a kernelspace buffer.  0 is returned on success,
8881ee925dSMatthew Dillon  * an error code on failure.  The actual number of bytes that could be
8903aa69bdSMatthew Dillon  * accomodated in the XIO will be stored in xio_bytes and the page offset
9003aa69bdSMatthew Dillon  * will be stored in xio_offset.
91a36803d2SMatthew Dillon  *
92a36803d2SMatthew Dillon  * WARNING! We cannot map user memory directly into an xio unless we also
93a36803d2SMatthew Dillon  *	    make the mapping use managed pages, otherwise modifications to
94a36803d2SMatthew Dillon  *	    the memory will race against pageouts and flushes.
9581ee925dSMatthew Dillon  */
9681ee925dSMatthew Dillon int
xio_init_kbuf(xio_t xio,void * kbase,size_t kbytes)9781ee925dSMatthew Dillon xio_init_kbuf(xio_t xio, void *kbase, size_t kbytes)
9881ee925dSMatthew Dillon {
9981ee925dSMatthew Dillon     vm_offset_t addr;
10081ee925dSMatthew Dillon     vm_paddr_t paddr;
10181ee925dSMatthew Dillon     vm_page_t m;
10281ee925dSMatthew Dillon     int i;
10381ee925dSMatthew Dillon     int n;
10481ee925dSMatthew Dillon 
10581ee925dSMatthew Dillon     addr = trunc_page((vm_offset_t)kbase);
10681ee925dSMatthew Dillon     xio->xio_flags = 0;
10781ee925dSMatthew Dillon     xio->xio_offset = (vm_offset_t)kbase & PAGE_MASK;
10881ee925dSMatthew Dillon     xio->xio_bytes = 0;
10981ee925dSMatthew Dillon     xio->xio_pages = xio->xio_internal_pages;
11081ee925dSMatthew Dillon     xio->xio_error = 0;
11181ee925dSMatthew Dillon     if ((n = PAGE_SIZE - xio->xio_offset) > kbytes)
11281ee925dSMatthew Dillon 	n = kbytes;
11381ee925dSMatthew Dillon     for (i = 0; n && i < XIO_INTERNAL_PAGES; ++i) {
11481ee925dSMatthew Dillon 	if ((paddr = pmap_kextract(addr)) == 0)
11581ee925dSMatthew Dillon 	    break;
11681ee925dSMatthew Dillon 	m = PHYS_TO_VM_PAGE(paddr);
11781ee925dSMatthew Dillon 	vm_page_hold(m);
11881ee925dSMatthew Dillon 	xio->xio_pages[i] = m;
11981ee925dSMatthew Dillon 	kbytes -= n;
12081ee925dSMatthew Dillon 	xio->xio_bytes += n;
12181ee925dSMatthew Dillon 	if ((n = kbytes) > PAGE_SIZE)
12281ee925dSMatthew Dillon 	    n = PAGE_SIZE;
12381ee925dSMatthew Dillon 	addr += PAGE_SIZE;
12481ee925dSMatthew Dillon     }
12581ee925dSMatthew Dillon     xio->xio_npages = i;
12681ee925dSMatthew Dillon 
12781ee925dSMatthew Dillon     /*
12881ee925dSMatthew Dillon      * If a failure occured clean out what we loaded and return EFAULT.
12981ee925dSMatthew Dillon      * Return 0 on success.
13081ee925dSMatthew Dillon      */
13181ee925dSMatthew Dillon     if (i < XIO_INTERNAL_PAGES && n) {
13281ee925dSMatthew Dillon 	xio_release(xio);
13381ee925dSMatthew Dillon 	xio->xio_error = EFAULT;
13481ee925dSMatthew Dillon     }
13581ee925dSMatthew Dillon     return(xio->xio_error);
13681ee925dSMatthew Dillon }
13781ee925dSMatthew Dillon 
13806ecca5aSMatthew Dillon /*
13917cde63eSMatthew Dillon  * Initialize an XIO given an array of vm_page pointers.  The caller is
14017cde63eSMatthew Dillon  * responsible for any modified state changes for the pages.
14183269e7dSMatthew Dillon  */
14283269e7dSMatthew Dillon int
xio_init_pages(xio_t xio,struct vm_page ** mbase,int npages,int xflags)14383269e7dSMatthew Dillon xio_init_pages(xio_t xio, struct vm_page **mbase, int npages, int xflags)
14483269e7dSMatthew Dillon {
14583269e7dSMatthew Dillon     int i;
14683269e7dSMatthew Dillon 
14783269e7dSMatthew Dillon     KKASSERT(npages <= XIO_INTERNAL_PAGES);
14883269e7dSMatthew Dillon 
14983269e7dSMatthew Dillon     xio->xio_flags = xflags;
15083269e7dSMatthew Dillon     xio->xio_offset = 0;
151255b6068SSamuel J. Greear     xio->xio_bytes = npages * PAGE_SIZE;
15283269e7dSMatthew Dillon     xio->xio_pages = xio->xio_internal_pages;
15383269e7dSMatthew Dillon     xio->xio_npages = npages;
15483269e7dSMatthew Dillon     xio->xio_error = 0;
15583269e7dSMatthew Dillon     for (i = 0; i < npages; ++i) {
15683269e7dSMatthew Dillon 	vm_page_hold(mbase[i]);
15783269e7dSMatthew Dillon 	xio->xio_pages[i] = mbase[i];
15883269e7dSMatthew Dillon     }
15983269e7dSMatthew Dillon     return(0);
16083269e7dSMatthew Dillon }
16183269e7dSMatthew Dillon 
16283269e7dSMatthew Dillon /*
16306ecca5aSMatthew Dillon  * Cleanup an XIO so it can be destroyed.  The pages associated with the
16403aa69bdSMatthew Dillon  * XIO are released.
16506ecca5aSMatthew Dillon  */
16681ee925dSMatthew Dillon void
xio_release(xio_t xio)16781ee925dSMatthew Dillon xio_release(xio_t xio)
16881ee925dSMatthew Dillon {
16981ee925dSMatthew Dillon     int i;
17081ee925dSMatthew Dillon     vm_page_t m;
17181ee925dSMatthew Dillon 
17281ee925dSMatthew Dillon     for (i = 0; i < xio->xio_npages; ++i) {
17381ee925dSMatthew Dillon 	m = xio->xio_pages[i];
17417cde63eSMatthew Dillon 	if (xio->xio_flags & XIOF_WRITE)
17517cde63eSMatthew Dillon 		vm_page_dirty(m);
17681ee925dSMatthew Dillon 	vm_page_unhold(m);
17781ee925dSMatthew Dillon     }
17881ee925dSMatthew Dillon     xio->xio_offset = 0;
17981ee925dSMatthew Dillon     xio->xio_npages = 0;
18081ee925dSMatthew Dillon     xio->xio_bytes = 0;
18181ee925dSMatthew Dillon     xio->xio_error = ENOBUFS;
18281ee925dSMatthew Dillon }
18381ee925dSMatthew Dillon 
18481ee925dSMatthew Dillon /*
18581ee925dSMatthew Dillon  * Copy data between an XIO and a UIO.  If the UIO represents userspace it
18603aa69bdSMatthew Dillon  * must be relative to the current context.
18703aa69bdSMatthew Dillon  *
18803aa69bdSMatthew Dillon  * uoffset is the abstracted starting offset in the XIO, not the actual
18903aa69bdSMatthew Dillon  * offset, and usually starts at 0.
19003aa69bdSMatthew Dillon  *
19103aa69bdSMatthew Dillon  * The XIO is not modified.  The UIO is updated to reflect the copy.
19281ee925dSMatthew Dillon  *
19381ee925dSMatthew Dillon  * UIO_READ	xio -> uio
19481ee925dSMatthew Dillon  * UIO_WRITE	uio -> xio
19581ee925dSMatthew Dillon  */
19681ee925dSMatthew Dillon int
xio_uio_copy(xio_t xio,int uoffset,struct uio * uio,size_t * sizep)197e54488bbSMatthew Dillon xio_uio_copy(xio_t xio, int uoffset, struct uio *uio, size_t *sizep)
19881ee925dSMatthew Dillon {
199e54488bbSMatthew Dillon     size_t bytes;
20081ee925dSMatthew Dillon     int error;
20181ee925dSMatthew Dillon 
20203aa69bdSMatthew Dillon     bytes = xio->xio_bytes - uoffset;
20303aa69bdSMatthew Dillon     if (bytes > uio->uio_resid)
20481ee925dSMatthew Dillon 	bytes = uio->uio_resid;
20503aa69bdSMatthew Dillon     KKASSERT(bytes >= 0);
20603aa69bdSMatthew Dillon     error = uiomove_fromphys(xio->xio_pages, xio->xio_offset + uoffset,
20703aa69bdSMatthew Dillon 				bytes, uio);
20803aa69bdSMatthew Dillon     if (error == 0)
20981ee925dSMatthew Dillon 	*sizep = bytes;
21003aa69bdSMatthew Dillon     else
21181ee925dSMatthew Dillon 	*sizep = 0;
21281ee925dSMatthew Dillon     return(error);
21381ee925dSMatthew Dillon }
21481ee925dSMatthew Dillon 
21581ee925dSMatthew Dillon /*
21681ee925dSMatthew Dillon  * Copy the specified number of bytes from the xio to a userland
21781ee925dSMatthew Dillon  * buffer.  Return an error code or 0 on success.
21881ee925dSMatthew Dillon  *
21903aa69bdSMatthew Dillon  * uoffset is the abstracted starting offset in the XIO, not the actual
22003aa69bdSMatthew Dillon  * offset, and usually starts at 0.
22103aa69bdSMatthew Dillon  *
22203aa69bdSMatthew Dillon  * The XIO is not modified.
22381ee925dSMatthew Dillon  */
22481ee925dSMatthew Dillon int
xio_copy_xtou(xio_t xio,int uoffset,void * uptr,int bytes)22503aa69bdSMatthew Dillon xio_copy_xtou(xio_t xio, int uoffset, void *uptr, int bytes)
22681ee925dSMatthew Dillon {
22781ee925dSMatthew Dillon     int i;
22881ee925dSMatthew Dillon     int n;
22981ee925dSMatthew Dillon     int error;
23081ee925dSMatthew Dillon     int offset;
23181ee925dSMatthew Dillon     vm_page_t m;
2325c5185aeSSamuel J. Greear     struct lwbuf *lwb;
2337a683a24SMatthew Dillon     struct lwbuf lwb_cache;
23481ee925dSMatthew Dillon 
2350a7648b9SMatthew Dillon     if (uoffset + bytes > xio->xio_bytes)
23681ee925dSMatthew Dillon 	return(EFAULT);
23781ee925dSMatthew Dillon 
23803aa69bdSMatthew Dillon     offset = (xio->xio_offset + uoffset) & PAGE_MASK;
23982f4c82aSMatthew Dillon     if ((n = PAGE_SIZE - offset) > bytes)
24081ee925dSMatthew Dillon 	n = bytes;
24181ee925dSMatthew Dillon 
24281ee925dSMatthew Dillon     error = 0;
24303aa69bdSMatthew Dillon     for (i = (xio->xio_offset + uoffset) >> PAGE_SHIFT;
24403aa69bdSMatthew Dillon 	 i < xio->xio_npages;
24503aa69bdSMatthew Dillon 	 ++i
24603aa69bdSMatthew Dillon     ) {
24781ee925dSMatthew Dillon 	m = xio->xio_pages[i];
2487a683a24SMatthew Dillon 	lwb = lwbuf_alloc(m, &lwb_cache);
2495c5185aeSSamuel J. Greear 	error = copyout((char *)lwbuf_kva(lwb) + offset, uptr, n);
2505c5185aeSSamuel J. Greear 	lwbuf_free(lwb);
25181ee925dSMatthew Dillon 	if (error)
25281ee925dSMatthew Dillon 	    break;
25381ee925dSMatthew Dillon 	bytes -= n;
25481ee925dSMatthew Dillon 	uptr = (char *)uptr + n;
25581ee925dSMatthew Dillon 	if (bytes == 0)
25681ee925dSMatthew Dillon 	    break;
25781ee925dSMatthew Dillon 	if ((n = bytes) > PAGE_SIZE)
25881ee925dSMatthew Dillon 	    n = PAGE_SIZE;
25981ee925dSMatthew Dillon 	offset = 0;
26081ee925dSMatthew Dillon     }
26181ee925dSMatthew Dillon     return(error);
26281ee925dSMatthew Dillon }
26381ee925dSMatthew Dillon 
26481ee925dSMatthew Dillon /*
26581ee925dSMatthew Dillon  * Copy the specified number of bytes from the xio to a kernel
26681ee925dSMatthew Dillon  * buffer.  Return an error code or 0 on success.
26781ee925dSMatthew Dillon  *
26803aa69bdSMatthew Dillon  * uoffset is the abstracted starting offset in the XIO, not the actual
26903aa69bdSMatthew Dillon  * offset, and usually starts at 0.
27003aa69bdSMatthew Dillon  *
27103aa69bdSMatthew Dillon  * The XIO is not modified.
27281ee925dSMatthew Dillon  */
27381ee925dSMatthew Dillon int
xio_copy_xtok(xio_t xio,int uoffset,void * kptr,int bytes)27403aa69bdSMatthew Dillon xio_copy_xtok(xio_t xio, int uoffset, void *kptr, int bytes)
27581ee925dSMatthew Dillon {
27681ee925dSMatthew Dillon     int i;
27781ee925dSMatthew Dillon     int n;
27881ee925dSMatthew Dillon     int error;
27981ee925dSMatthew Dillon     int offset;
28081ee925dSMatthew Dillon     vm_page_t m;
2815c5185aeSSamuel J. Greear     struct lwbuf *lwb;
2827a683a24SMatthew Dillon     struct lwbuf lwb_cache;
28381ee925dSMatthew Dillon 
28403aa69bdSMatthew Dillon     if (bytes + uoffset > xio->xio_bytes)
28581ee925dSMatthew Dillon 	return(EFAULT);
28681ee925dSMatthew Dillon 
28703aa69bdSMatthew Dillon     offset = (xio->xio_offset + uoffset) & PAGE_MASK;
28882f4c82aSMatthew Dillon     if ((n = PAGE_SIZE - offset) > bytes)
28981ee925dSMatthew Dillon 	n = bytes;
29081ee925dSMatthew Dillon 
29181ee925dSMatthew Dillon     error = 0;
29203aa69bdSMatthew Dillon     for (i = (xio->xio_offset + uoffset) >> PAGE_SHIFT;
29303aa69bdSMatthew Dillon 	 i < xio->xio_npages;
29403aa69bdSMatthew Dillon 	 ++i
29503aa69bdSMatthew Dillon     ) {
29681ee925dSMatthew Dillon 	m = xio->xio_pages[i];
2977a683a24SMatthew Dillon 	lwb = lwbuf_alloc(m, &lwb_cache);
2985c5185aeSSamuel J. Greear 	bcopy((char *)lwbuf_kva(lwb) + offset, kptr, n);
2995c5185aeSSamuel J. Greear 	lwbuf_free(lwb);
30081ee925dSMatthew Dillon 	bytes -= n;
30181ee925dSMatthew Dillon 	kptr = (char *)kptr + n;
30281ee925dSMatthew Dillon 	if (bytes == 0)
30381ee925dSMatthew Dillon 	    break;
30481ee925dSMatthew Dillon 	if ((n = bytes) > PAGE_SIZE)
30581ee925dSMatthew Dillon 	    n = PAGE_SIZE;
30681ee925dSMatthew Dillon 	offset = 0;
30781ee925dSMatthew Dillon     }
30881ee925dSMatthew Dillon     return(error);
30981ee925dSMatthew Dillon }
31081ee925dSMatthew Dillon 
3110a7648b9SMatthew Dillon /*
3120a7648b9SMatthew Dillon  * Copy the specified number of bytes from userland to the xio.
3130a7648b9SMatthew Dillon  * Return an error code or 0 on success.
3140a7648b9SMatthew Dillon  *
3150a7648b9SMatthew Dillon  * uoffset is the abstracted starting offset in the XIO, not the actual
3160a7648b9SMatthew Dillon  * offset, and usually starts at 0.
3170a7648b9SMatthew Dillon  *
3180a7648b9SMatthew Dillon  * Data in pages backing the XIO will be modified.
3190a7648b9SMatthew Dillon  */
3200a7648b9SMatthew Dillon int
xio_copy_utox(xio_t xio,int uoffset,const void * uptr,int bytes)3210a7648b9SMatthew Dillon xio_copy_utox(xio_t xio, int uoffset, const void *uptr, int bytes)
3220a7648b9SMatthew Dillon {
3230a7648b9SMatthew Dillon     int i;
3240a7648b9SMatthew Dillon     int n;
3250a7648b9SMatthew Dillon     int error;
3260a7648b9SMatthew Dillon     int offset;
3270a7648b9SMatthew Dillon     vm_page_t m;
3285c5185aeSSamuel J. Greear     struct lwbuf *lwb;
3297a683a24SMatthew Dillon     struct lwbuf lwb_cache;
3300a7648b9SMatthew Dillon 
3310a7648b9SMatthew Dillon     if (uoffset + bytes > xio->xio_bytes)
3320a7648b9SMatthew Dillon 	return(EFAULT);
3330a7648b9SMatthew Dillon 
3340a7648b9SMatthew Dillon     offset = (xio->xio_offset + uoffset) & PAGE_MASK;
3350a7648b9SMatthew Dillon     if ((n = PAGE_SIZE - offset) > bytes)
3360a7648b9SMatthew Dillon 	n = bytes;
3370a7648b9SMatthew Dillon 
3380a7648b9SMatthew Dillon     error = 0;
3390a7648b9SMatthew Dillon     for (i = (xio->xio_offset + uoffset) >> PAGE_SHIFT;
3400a7648b9SMatthew Dillon 	 i < xio->xio_npages;
3410a7648b9SMatthew Dillon 	 ++i
3420a7648b9SMatthew Dillon     ) {
3430a7648b9SMatthew Dillon 	m = xio->xio_pages[i];
3447a683a24SMatthew Dillon 	lwb = lwbuf_alloc(m, &lwb_cache);
3455c5185aeSSamuel J. Greear 	error = copyin(uptr, (char *)lwbuf_kva(lwb) + offset, n);
3465c5185aeSSamuel J. Greear 	lwbuf_free(lwb);
3470a7648b9SMatthew Dillon 	if (error)
3480a7648b9SMatthew Dillon 	    break;
3490a7648b9SMatthew Dillon 	bytes -= n;
3500a7648b9SMatthew Dillon 	uptr = (const char *)uptr + n;
3510a7648b9SMatthew Dillon 	if (bytes == 0)
3520a7648b9SMatthew Dillon 	    break;
3530a7648b9SMatthew Dillon 	if ((n = bytes) > PAGE_SIZE)
3540a7648b9SMatthew Dillon 	    n = PAGE_SIZE;
3550a7648b9SMatthew Dillon 	offset = 0;
3560a7648b9SMatthew Dillon     }
3570a7648b9SMatthew Dillon     return(error);
3580a7648b9SMatthew Dillon }
3590a7648b9SMatthew Dillon 
3600a7648b9SMatthew Dillon /*
3610a7648b9SMatthew Dillon  * Copy the specified number of bytes from the kernel to the xio.
3620a7648b9SMatthew Dillon  * Return an error code or 0 on success.
3630a7648b9SMatthew Dillon  *
3640a7648b9SMatthew Dillon  * uoffset is the abstracted starting offset in the XIO, not the actual
3650a7648b9SMatthew Dillon  * offset, and usually starts at 0.
3660a7648b9SMatthew Dillon  *
3670a7648b9SMatthew Dillon  * Data in pages backing the XIO will be modified.
3680a7648b9SMatthew Dillon  */
3690a7648b9SMatthew Dillon int
xio_copy_ktox(xio_t xio,int uoffset,const void * kptr,int bytes)3700a7648b9SMatthew Dillon xio_copy_ktox(xio_t xio, int uoffset, const void *kptr, int bytes)
3710a7648b9SMatthew Dillon {
3720a7648b9SMatthew Dillon     int i;
3730a7648b9SMatthew Dillon     int n;
3740a7648b9SMatthew Dillon     int error;
3750a7648b9SMatthew Dillon     int offset;
3760a7648b9SMatthew Dillon     vm_page_t m;
3775c5185aeSSamuel J. Greear     struct lwbuf *lwb;
3787a683a24SMatthew Dillon     struct lwbuf lwb_cache;
3790a7648b9SMatthew Dillon 
3800a7648b9SMatthew Dillon     if (uoffset + bytes > xio->xio_bytes)
3810a7648b9SMatthew Dillon 	return(EFAULT);
3820a7648b9SMatthew Dillon 
3830a7648b9SMatthew Dillon     offset = (xio->xio_offset + uoffset) & PAGE_MASK;
3840a7648b9SMatthew Dillon     if ((n = PAGE_SIZE - offset) > bytes)
3850a7648b9SMatthew Dillon 	n = bytes;
3860a7648b9SMatthew Dillon 
3870a7648b9SMatthew Dillon     error = 0;
3880a7648b9SMatthew Dillon     for (i = (xio->xio_offset + uoffset) >> PAGE_SHIFT;
3890a7648b9SMatthew Dillon 	 i < xio->xio_npages;
3900a7648b9SMatthew Dillon 	 ++i
3910a7648b9SMatthew Dillon     ) {
3920a7648b9SMatthew Dillon 	m = xio->xio_pages[i];
3937a683a24SMatthew Dillon 	lwb = lwbuf_alloc(m, &lwb_cache);
3945c5185aeSSamuel J. Greear 	bcopy(kptr, (char *)lwbuf_kva(lwb) + offset, n);
3955c5185aeSSamuel J. Greear 	lwbuf_free(lwb);
3960a7648b9SMatthew Dillon 	bytes -= n;
3970a7648b9SMatthew Dillon 	kptr = (const char *)kptr + n;
3980a7648b9SMatthew Dillon 	if (bytes == 0)
3990a7648b9SMatthew Dillon 	    break;
4000a7648b9SMatthew Dillon 	if ((n = bytes) > PAGE_SIZE)
4010a7648b9SMatthew Dillon 	    n = PAGE_SIZE;
4020a7648b9SMatthew Dillon 	offset = 0;
4030a7648b9SMatthew Dillon     }
4040a7648b9SMatthew Dillon     return(error);
4050a7648b9SMatthew Dillon }
406