Index: share/man/man9/VOP_UBOP.9 =================================================================== --- /dev/null +++ share/man/man9/VOP_UBOP.9 @@ -0,0 +1,71 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 2020 Matthew Macy +.\" +.\" All rights reserved. +.\" +.\" This program is free software. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd October 13, 2020 +.Dt VOP_UBOP 9 +.Os +.Sh NAME +.Nm VOP_UBOP +.Nd read or write a file system buffer +.Sh SYNOPSIS +.In sys/param.h +.In sys/vnode.h +.In sys/uio.h +.Ft int +.Fn VOP_UBOP "struct vnode *vp" "struct uio_bio *uio" "int io_flags" +.Sh DESCRIPTION +The arguments are: +.Bl -tag -width 2n +.It Fa vp +The vnode that the operation is for. +.It Fa uio +The context for the operation. +.It Fa flags +The flags for the operation: +.Bl -tag -width ".Dv FOF_OFFSET" +.It Dv FOF_OFFSET +uio_offset is valid. +.It Dv FAPPEND +Write to the end of the file. +.El +.El +.Pp +This call either reads from, writes to, or syncs a file, depending on the value of +.Fa uio->uio_cmd . +.Sh RETURN VALUES +Zero if completed immediately, EINPROGRESS otherwise. +Errors should be signalled by setting UIO_BIO_ERROR on uio_flags field in struct uio_bio, +and setting uio_error to the appropriate errno value. +.Sh SEE ALSO +.Xr uio_bio 9 , +.Xr vnode 9 +.Sh AUTHORS +This manual page was written by +.An Matthew Macy . Index: share/man/man9/uio_bio.9 =================================================================== --- /dev/null +++ share/man/man9/uio_bio.9 @@ -0,0 +1,255 @@ +.\" +.\" Copyright (c) 2020 Matthew Macy +.\" +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 11, 2020 +.Dt UIO 9 +.Os +.Sh NAME +.Nm uio_bio , +.Nm uiobiomove , +.Nd asynchronous file system I/O routines +.Sh SYNOPSIS +.In sys/types.h +.In sys/uio.h +.Bd -literal +struct bio_vec { + vm_page_t bv_page; + uint32_t bv_len; + uint32_t bv_offset; +}; + +struct uio_bio { + uint8_t uio_cmd; /* operation */ + uint8_t uio_error; /* Errno for UIO_ERROR. */ + uint16_t uio_flags; /* General flags */ + uint16_t uio_bv_cnt; /* length of scatter/gather list */ + uint32_t uio_bv_offset; /* offset in to page list */ + off_t uio_offset; /* offset in target object */ + ssize_t uio_resid; /* remaining bytes to process */ + struct ucred *uio_cred; /* owner credentials */ + struct bio_vec *uio_bvec; /* caller buffer's pages */ + void (*uio_bio_done)(struct uio_bio *); + void *uio_arg; +}; +.Ed +.Ft int +.Fn uiomove "void *buf" "int howmuch" "struct uio_bio *uiop" +.Sh DESCRIPTION +The function +.Fn uiobiomove , +is used to transfer data between buffers and pages that might +possibly cross the user/kernel space boundary. +.Pp +As a result of any +.Xr aio_read 2 , +.Xr aio_write 2 , +or +.Xr lio_listio 2 +system call that is being passed to a supporting file system, +.Va VOP_UBOP +will be called with a pointer to a +.Vt "struct uio_bio" +being passed. +The transfer request is encoded in this structure. +The driver itself should use +.Fn uiobiomove +to get at the data in this structure. +.Pp +The fields in the +.Vt uio +structure are: +.Bl -tag -width ".Va uio_ma_offset" +.It Va uio_cmd +The operation to be performed: +.Bl -tag -width ".Dv UIO_BIO_WRITE" +.It Dv UIO_BIO_READ +Read from a file. +.It Dv UIO_BIO_WRITE +Write to a file. +.It Dv UIO_BIO_SYNC +Sync a file to backing storage. +.El +.It Va uio_error +The error code if the operation was not successful. +.It Va uio_bv_cnt +The number of entries in the passed bio_vec array. +.It Va uio_flags +.Bl -tag -width ".Dv UIO_BIO_SPARSE" +.It Dv UIO_BIO_ERROR +The uio_error field is valid. +.It Dv UIO_BIO_SPARSE +The page array is not completely populated. +.El +.It Va uio_bv_offset +The starting byte offset in to the bio_vec array. +.It Va uio_offset +The offset into the file. +.It Va uio_resid +The remaining number of bytes to process, +updated after transfer. +.It Va uio_cred +The pointer to a +.Vt "struct ucred" +for the associated thread. +.It Va uio_bio_done +The I/O completion routine. +.It Va uio_arg +The argument to pass to the I/O completion routine. +.It Va uio_bvec +A pointer to the caller's bio_vec array. +.El +.Pp +.Sh RETURN VALUES +On success +.Fn uiobiomove , +will return 0; on error it will return an appropriate error code. +.Sh EXAMPLES +The idea is that the file system maintains private buffers for its data, +and processes the request in chunks of maximal the size of these +buffers. +.Bd -literal +#include +#include +#include +#include + +static uint64_t +dmu_physmove(dmu_buf_set_t *dbs, dmu_buf_t *db, uint64_t off, uint64_t sz) +{ + struct uio_bio *uio = (struct uio_bio *)dbs->dbs_dc->dc_data_buf; + uint64_t adv = uio->uio_resid; + int err; + + err = uiobiomove((char *)db->db_data + off, sz, uio); + if (err) + dbs->dbs_err = err; + adv -= uio->uio_resid; + + return (adv); +} + +static int +aio_queue_vfs(struct kaiocb *job) +{ + struct aiocb *cb; + struct file *fp; + struct vnode *vp; + struct uio_bio *ubio, ubio_local; + vm_prot_t prot; + uint32_t io_size, bio_size; + int error, cmd; + vm_offset_t page_offset; + + cb = &job->uaiocb; + fp = job->fd_file; + ... + vp = fp->f_vnode; + + /* + * Zero length read should always succeed + * if supported. + */ + bzero(&ubio_local, sizeof(ubio_local)); + ubio_local.uio_cmd = UIO_BIO_READ; + if (VOP_UBOP(vp, &ubio_local, FOF_OFFSET, curthread->td_ucred) == EOPNOTSUPP) + return (-1); + ... + page_offset = ((vm_offset_t)cb->aio_buf) & PAGE_MASK; + cmd = cb->aio_lio_opcode == LIO_WRITE ? UIO_BIO_WRITE : UIO_BIO_READ; + map_bytes = btoc(cb->aio_nbytes + page_offset)*PAGE_SIZE; + io_pages = btoc(map_bytes); + if (io_pages <= (MAXPHYS >> PAGE_SHIFT)) + ma = job->pages; + else + ma = malloc(sizeof(vm_page_t )*io_pages, M_AIOS, M_WAITOK); + + bio_size = sizeof(*ubio) + sizeof(*bv)*io_pages; + ubio = malloc(bio_size, M_AIOS, M_WAITOK); + bv = ubio->uio_bvec = (struct bio_vec*)(ubio + 1); + ubio->uio_cmd = cmd; + ubio->uio_error = 0; + ubio->uio_flags = 0; + ubio->uio_bv_offset = 0; + ubio->uio_offset = cb->aio_offset; + ubio->uio_resid = cb->aio_nbytes; + ubio->uio_cred = crhold(curthread->td_ucred); + ubio->uio_bio_done = aio_ubiowakeup; + ubio->uio_arg = job; + + prot = VM_PROT_READ; + /* Reading from disk means writing to memory */ + if (cb->aio_lio_opcode == LIO_READ) + prot |= VM_PROT_WRITE; + ubio->uio_bv_cnt = vm_fault_quick_hold_pages(&curproc->p_vmspace->vm_map, + (vm_offset_t)cb->aio_buf, cb->aio_nbytes, prot, ma, io_pages); + + if (ubio->uio_bv_cnt != io_pages) { + vm_page_unhold_pages(ma, ubio->uio_bv_cnt); + /* + * Punt to synchronous path + */ + error = -1; + goto err; + } + if (ubio->uio_bv_cnt < 0) { + error = EFAULT; + goto err; + } + for (int i = 0; i < ubio->uio_bv_cnt; i++, bv++) { + bv->bv_offset = page_offset; + bv->bv_len = PAGE_SIZE - page_offset; + bv->bv_page = ma[i]; + page_offset = 0; + } + if (ma != job->pages) { + free(ma, M_AIOS); + ma = NULL; + } + error = VOP_UBOP(vp, ubio, FOF_OFFSET); + if (error == EINPROGRESS || error == 0) + return (0); +err: + if (ma != job->pages) + free(ma, M_AIOS); + free(ubio, M_AIOS); + return (error); +} + +.Ed +.El +.Sh SEE ALSO +.Xr aio_read 2 , +.Xr aio_write 2 , +.Xr lio_listio 2 , +.Xr VOP_UBOP 9 , +.Sh HISTORY +The +.Nm +mechanism was introduced to support asynchronous file system requests in OpenZFS. +.Sh AUTHORS +This manual page was written by +.An Matthew Macy . Index: sys/kern/subr_uio.c =================================================================== --- sys/kern/subr_uio.c +++ sys/kern/subr_uio.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,7 @@ #include #include #include +#include #include @@ -279,6 +281,73 @@ return (error); } +int +uiobiomove(void *cp, int n, struct uio_bio *uio) +{ + int bv_idx, bv_idx_start __unused; + off_t boff, off, off_start __unused, bv_start __unused; + struct bio_vec *bv; + struct sf_buf *sf; + caddr_t va; + + off = uio->uio_bv_offset; + bv = uio->uio_bvec; + boff = off & PAGE_MASK; + bv_idx = OFF_TO_IDX(off); + bv += bv_idx; + MPASS(boff < bv->bv_len); +#ifdef INVARIANTS + off_start = uio->uio_offset; + bv_start = off; + bv_idx_start = bv_idx; +#endif + while (n > 0 && uio->uio_resid > 0) { + int bytes = imin(bv->bv_len - boff, n); + + MPASS(boff + bytes <= bv->bv_len); + MPASS(bytes <= PAGE_SIZE - bv->bv_offset); + MPASS(bytes > 0); + + if ((uio->uio_flags & UIO_BIO_SPARSE) == 0 || + bv->bv_page != NULL) { + sf = sf_buf_alloc(bv->bv_page, 0); + va = ((caddr_t)sf_buf_kva(sf)) + boff + bv->bv_offset; + + switch(uio->uio_cmd) { + case UIO_BIO_READ: + memcpy(va, cp, bytes); + break; + case UIO_BIO_WRITE: + memcpy(cp, va, bytes); + break; + default: + panic("invalid command to uiobiomove: %d", uio->uio_cmd); + } + sf_buf_free(sf); + } + boff += bytes; + uio->uio_bv_offset += bytes; + if (boff == bv->bv_len) { + uio->uio_bv_offset += (PAGE_SIZE - bv->bv_len); + MPASS((uio->uio_bv_offset & PAGE_MASK) == 0); + boff = 0; + bv++; + bv_idx++; + } + uio->uio_resid -= bytes; + uio->uio_offset += bytes; + cp = (caddr_t)cp + bytes; + n -= bytes; + } +#ifdef INVARIANTS + MPASS(uio->uio_offset - off_start == + uio->uio_bv_offset - bv_start - uio->uio_bvec[bv_idx_start].bv_offset); + MPASS(OFF_TO_IDX(uio->uio_bv_offset) == bv_idx); + MPASS((uio->uio_bv_offset & PAGE_MASK) == boff); +#endif + return (0); +} + /* * Wrapper for uiomove() that validates the arguments against a known-good * kernel buffer. Currently, uiomove accepts a signed (n) argument, which Index: sys/kern/vfs_aio.c =================================================================== --- sys/kern/vfs_aio.c +++ sys/kern/vfs_aio.c @@ -101,6 +101,10 @@ #define MAX_BUF_AIO 16 #endif +#ifndef MAX_VFS_XFER +#define MAX_VFS_XFER (32 * 1024 * 1024) /* 32MB - DMU_MAX_ACCESS/2 */ +#endif + FEATURE(aio, "Asynchronous I/O"); SYSCTL_DECL(_p1003_1b); @@ -319,9 +323,11 @@ struct aioliojob *lio, int type, struct aiocb_ops *ops); static int aio_queue_file(struct file *fp, struct kaiocb *job); static void aio_biowakeup(struct bio *bp); +static void aio_ubiowakeup(struct uio_bio *ubio); static void aio_proc_rundown(void *arg, struct proc *p); static void aio_proc_rundown_exec(void *arg, struct proc *p, struct image_params *imgp); +static int aio_queue_vfs(struct kaiocb *job); static int aio_qbio(struct proc *p, struct kaiocb *job); static void aio_daemon(void *param); static void aio_bio_done_notify(struct proc *userp, struct kaiocb *job); @@ -1285,8 +1291,9 @@ bp->bio_caller1 = (void *)job; prot = VM_PROT_READ; + /* Reading from disk means writing to memory */ if (cb->aio_lio_opcode == LIO_READ) - prot |= VM_PROT_WRITE; /* Less backwards than it looks */ + prot |= VM_PROT_WRITE; job->npages = vm_fault_quick_hold_pages(&curproc->p_vmspace->vm_map, (vm_offset_t)cb->aio_buf, bp->bio_length, prot, job->pages, nitems(job->pages)); @@ -1328,6 +1335,115 @@ return (error); } +/* + * aio_queue_vfs works similarly to aio_qbio. It checks + * that it supports the aio operation in question and + * then if the vnode's file system support asynchronous + * requests. It then sets up the request by holding the + * user's pages with the appropriate permissions. If that + * succeeds it call VOP_UBOP. The uio_bio callback + * aio_ubiowakeup will be called when the operation completes. + */ +static int +aio_queue_vfs(struct kaiocb *job) +{ + struct aiocb *cb; + struct file *fp; + struct vnode *vp; + struct uio_bio *ubio, ubio_local; + vm_prot_t prot; + uint32_t io_pages, bio_size, map_bytes; + int error, cmd; + vm_offset_t page_offset; + vm_page_t *ma; + struct bio_vec *bv; + + cb = &job->uaiocb; + fp = job->fd_file; + + if (!(cb->aio_lio_opcode == LIO_WRITE || + cb->aio_lio_opcode == LIO_READ)) + return (-1); + if (fp == NULL || fp->f_type != DTYPE_VNODE) + return (-1); + + vp = fp->f_vnode; + + /* + * Zero length read should always succeed + * if supported. + */ + bzero(&ubio_local, sizeof(ubio_local)); + ubio_local.uio_cmd = UIO_BIO_READ; + if (VOP_UBOP(vp, &ubio_local, FOF_OFFSET) == EOPNOTSUPP) + return (-1); + /* + * Don't punt here - XXX + */ + if (cb->aio_nbytes > MAX_VFS_XFER) + return (-1); + + page_offset = ((vm_offset_t)cb->aio_buf) & PAGE_MASK; + cmd = cb->aio_lio_opcode == LIO_WRITE ? UIO_BIO_WRITE : UIO_BIO_READ; + map_bytes = btoc(cb->aio_nbytes + page_offset)*PAGE_SIZE; + io_pages = btoc(map_bytes); + if (io_pages <= (MAXPHYS >> PAGE_SHIFT)) + ma = job->pages; + else + ma = malloc(sizeof(vm_page_t )*io_pages, M_AIOS, M_WAITOK); + + bio_size = sizeof(*ubio) + sizeof(*bv)*io_pages; + ubio = malloc(bio_size, M_AIOS, M_WAITOK); + bv = ubio->uio_bvec = (struct bio_vec*)(ubio + 1); + ubio->uio_cmd = cmd; + ubio->uio_error = 0; + ubio->uio_flags = 0; + ubio->uio_bv_offset = 0; + ubio->uio_offset = cb->aio_offset; + ubio->uio_resid = cb->aio_nbytes; + ubio->uio_cred = crhold(curthread->td_ucred); + ubio->uio_bio_done = aio_ubiowakeup; + ubio->uio_arg = job; + + prot = VM_PROT_READ; + /* Reading from disk means writing to memory */ + if (cb->aio_lio_opcode == LIO_READ) + prot |= VM_PROT_WRITE; + ubio->uio_bv_cnt = vm_fault_quick_hold_pages(&curproc->p_vmspace->vm_map, + (vm_offset_t)cb->aio_buf, cb->aio_nbytes, prot, ma, io_pages); + + if (ubio->uio_bv_cnt != io_pages) { + vm_page_unhold_pages(ma, ubio->uio_bv_cnt); + /* + * Punt to synchronous path + */ + error = -1; + goto err; + } + if (ubio->uio_bv_cnt < 0) { + error = EFAULT; + goto err; + } + for (int i = 0; i < ubio->uio_bv_cnt; i++, bv++) { + bv->bv_offset = page_offset; + bv->bv_len = PAGE_SIZE - page_offset; + bv->bv_page = ma[i]; + page_offset = 0; + } + if (ma != job->pages) { + free(ma, M_AIOS); + ma = NULL; + } + error = VOP_UBOP(vp, ubio, FOF_OFFSET); + if (error == EINPROGRESS || error == 0) + return (0); +err: + if (ma != job->pages) + free(ma, M_AIOS); + free(ubio, M_AIOS); + return (error); +} + #ifdef COMPAT_FREEBSD6 static int convert_old_sigevent(struct osigevent *osig, struct sigevent *nsig) @@ -1696,6 +1812,9 @@ int error; bool safe; + error = aio_queue_vfs(job); + if (error >= 0) + return (error); ki = job->userproc->p_aioinfo; error = aio_qbio(job->userproc, job); if (error >= 0) @@ -2330,6 +2449,37 @@ return (error); } +/* + * aio_ubiowakeup is the uio_bio completion callback for + * aio_queue_vfs. It just drops the hold on the pages + * from aio_queue_vfs and marks the aio as completed. + */ +static void +aio_ubiowakeup(struct uio_bio *ubio) +{ + struct kaiocb *job = (struct kaiocb *)ubio->uio_arg; + struct bio_vec *bv; + size_t nbytes; + int error, cnt; + + bv = ubio->uio_bvec; + cnt = ubio->uio_bv_cnt; + for (; cnt != 0; cnt--, bv++) + vm_page_unwire(bv->bv_page, PQ_ACTIVE); + + nbytes = job->uaiocb.aio_nbytes - ubio->uio_resid; + error = 0; + if (ubio->uio_flags & UIO_BIO_ERROR) + error = ubio->uio_error; + + if (error) + aio_complete(job, -1, error); + else + aio_complete(job, nbytes, 0); + crfree(ubio->uio_cred); + free(ubio, M_AIOS); +} + static void aio_biowakeup(struct bio *bp) { Index: sys/kern/vnode_if.src =================================================================== --- sys/kern/vnode_if.src +++ sys/kern/vnode_if.src @@ -782,6 +782,14 @@ }; +%% ubop vp U U U + +vop_ubop { + IN struct vnode *vp; + INOUT struct uio_bio *uio; + IN int ioflag; +}; + # The VOPs below are spares at the end of the table to allow new VOPs to be # added in stable branches without breaking the KBI. New VOPs in HEAD should # be added above these spares. When merging a new VOP to a stable branch, Index: sys/sys/_uio.h =================================================================== --- sys/sys/_uio.h +++ sys/sys/_uio.h @@ -41,6 +41,12 @@ UIO_WRITE }; +enum uio_bio_cmd { + UIO_BIO_READ, + UIO_BIO_WRITE, + UIO_BIO_SYNC +}; + /* Segment flag values. */ enum uio_seg { UIO_USERSPACE, /* from user data space */ Index: sys/sys/uio.h =================================================================== --- sys/sys/uio.h +++ sys/sys/uio.h @@ -62,6 +62,35 @@ struct thread *uio_td; /* owner */ }; +#define HAVE_UBOP +enum uio_bio_flags { + UIO_BIO_ERROR = 1 << 0, + UIO_BIO_SPARSE = 1 << 1, + UIO_BIO_USER = 1 << 2, + UIO_BIO_PREEMPTED = 1 << 3, + UIO_BIO_SG = 1 << 4, +}; + +struct bio_vec { + vm_page_t bv_page; + uint32_t bv_len; + uint32_t bv_offset; +}; + +struct uio_bio { + uint8_t uio_cmd; /* operation */ + uint8_t uio_error; /* Errno for UIO_ERROR. */ + uint16_t uio_flags; /* General flags */ + uint16_t uio_bv_cnt; /* length of scatter/gather list */ + uint32_t uio_bv_offset; /* offset in to page list */ + off_t uio_offset; /* offset in target object */ + ssize_t uio_resid; /* remaining bytes to process */ + struct ucred *uio_cred; /* owner credentials */ + struct bio_vec *uio_bvec; /* caller buffer's pages */ + void (*uio_bio_done)(struct uio_bio *); + void *uio_arg; +}; + /* * Limits * @@ -92,6 +121,7 @@ int physcopyout_vlist(vm_paddr_t src, struct bus_dma_segment *dst, off_t offset, size_t len); int uiomove(void *cp, int n, struct uio *uio); +int uiobiomove(void *cp, int n, struct uio_bio *uio); int uiomove_frombuf(void *buf, int buflen, struct uio *uio); int uiomove_fromphys(struct vm_page *ma[], vm_offset_t offset, int n, struct uio *uio);