#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/vmmeter.h>
#include <sys/vnode.h>
#include <sys/xio.h>
#include <cpu/lwbuf.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.h>
#include <vm/vm_kern.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pageout.h>
#include <vm/vm_pager.h>
#include <vm/vm_extern.h>
#include <vm/vm_page2.h>
void
xio_init(xio_t xio)
{
xio->xio_flags = 0;
xio->xio_bytes = 0;
xio->xio_error = 0;
xio->xio_offset = 0;
xio->xio_npages = 0;
xio->xio_pages = xio->xio_internal_pages;
}
int
xio_init_kbuf(xio_t xio, void *kbase, size_t kbytes)
{
vm_offset_t addr;
vm_paddr_t paddr;
vm_page_t m;
int i;
int n;
addr = trunc_page((vm_offset_t)kbase);
xio->xio_flags = 0;
xio->xio_offset = (vm_offset_t)kbase & PAGE_MASK;
xio->xio_bytes = 0;
xio->xio_pages = xio->xio_internal_pages;
xio->xio_error = 0;
if ((n = PAGE_SIZE - xio->xio_offset) > kbytes)
n = kbytes;
for (i = 0; n && i < XIO_INTERNAL_PAGES; ++i) {
if ((paddr = pmap_kextract(addr)) == 0)
break;
m = PHYS_TO_VM_PAGE(paddr);
vm_page_hold(m);
xio->xio_pages[i] = m;
kbytes -= n;
xio->xio_bytes += n;
if ((n = kbytes) > PAGE_SIZE)
n = PAGE_SIZE;
addr += PAGE_SIZE;
}
xio->xio_npages = i;
if (i < XIO_INTERNAL_PAGES && n) {
xio_release(xio);
xio->xio_error = EFAULT;
}
return(xio->xio_error);
}
int
xio_init_pages(xio_t xio, struct vm_page **mbase, int npages, int xflags)
{
int i;
KKASSERT(npages <= XIO_INTERNAL_PAGES);
xio->xio_flags = xflags;
xio->xio_offset = 0;
xio->xio_bytes = npages * PAGE_SIZE;
xio->xio_pages = xio->xio_internal_pages;
xio->xio_npages = npages;
xio->xio_error = 0;
for (i = 0; i < npages; ++i) {
vm_page_hold(mbase[i]);
xio->xio_pages[i] = mbase[i];
}
return(0);
}
void
xio_release(xio_t xio)
{
int i;
vm_page_t m;
for (i = 0; i < xio->xio_npages; ++i) {
m = xio->xio_pages[i];
if (xio->xio_flags & XIOF_WRITE)
vm_page_dirty(m);
vm_page_unhold(m);
}
xio->xio_offset = 0;
xio->xio_npages = 0;
xio->xio_bytes = 0;
xio->xio_error = ENOBUFS;
}
int
xio_uio_copy(xio_t xio, int uoffset, struct uio *uio, size_t *sizep)
{
size_t bytes;
int error;
bytes = xio->xio_bytes - uoffset;
if (bytes > uio->uio_resid)
bytes = uio->uio_resid;
KKASSERT(bytes >= 0);
error = uiomove_fromphys(xio->xio_pages, xio->xio_offset + uoffset,
bytes, uio);
if (error == 0)
*sizep = bytes;
else
*sizep = 0;
return(error);
}
int
xio_copy_xtou(xio_t xio, int uoffset, void *uptr, int bytes)
{
int i;
int n;
int error;
int offset;
vm_page_t m;
struct lwbuf *lwb;
struct lwbuf lwb_cache;
if (uoffset + bytes > xio->xio_bytes)
return(EFAULT);
offset = (xio->xio_offset + uoffset) & PAGE_MASK;
if ((n = PAGE_SIZE - offset) > bytes)
n = bytes;
error = 0;
for (i = (xio->xio_offset + uoffset) >> PAGE_SHIFT;
i < xio->xio_npages;
++i
) {
m = xio->xio_pages[i];
lwb = lwbuf_alloc(m, &lwb_cache);
error = copyout((char *)lwbuf_kva(lwb) + offset, uptr, n);
lwbuf_free(lwb);
if (error)
break;
bytes -= n;
uptr = (char *)uptr + n;
if (bytes == 0)
break;
if ((n = bytes) > PAGE_SIZE)
n = PAGE_SIZE;
offset = 0;
}
return(error);
}
int
xio_copy_xtok(xio_t xio, int uoffset, void *kptr, int bytes)
{
int i;
int n;
int error;
int offset;
vm_page_t m;
struct lwbuf *lwb;
struct lwbuf lwb_cache;
if (bytes + uoffset > xio->xio_bytes)
return(EFAULT);
offset = (xio->xio_offset + uoffset) & PAGE_MASK;
if ((n = PAGE_SIZE - offset) > bytes)
n = bytes;
error = 0;
for (i = (xio->xio_offset + uoffset) >> PAGE_SHIFT;
i < xio->xio_npages;
++i
) {
m = xio->xio_pages[i];
lwb = lwbuf_alloc(m, &lwb_cache);
bcopy((char *)lwbuf_kva(lwb) + offset, kptr, n);
lwbuf_free(lwb);
bytes -= n;
kptr = (char *)kptr + n;
if (bytes == 0)
break;
if ((n = bytes) > PAGE_SIZE)
n = PAGE_SIZE;
offset = 0;
}
return(error);
}
int
xio_copy_utox(xio_t xio, int uoffset, const void *uptr, int bytes)
{
int i;
int n;
int error;
int offset;
vm_page_t m;
struct lwbuf *lwb;
struct lwbuf lwb_cache;
if (uoffset + bytes > xio->xio_bytes)
return(EFAULT);
offset = (xio->xio_offset + uoffset) & PAGE_MASK;
if ((n = PAGE_SIZE - offset) > bytes)
n = bytes;
error = 0;
for (i = (xio->xio_offset + uoffset) >> PAGE_SHIFT;
i < xio->xio_npages;
++i
) {
m = xio->xio_pages[i];
lwb = lwbuf_alloc(m, &lwb_cache);
error = copyin(uptr, (char *)lwbuf_kva(lwb) + offset, n);
lwbuf_free(lwb);
if (error)
break;
bytes -= n;
uptr = (const char *)uptr + n;
if (bytes == 0)
break;
if ((n = bytes) > PAGE_SIZE)
n = PAGE_SIZE;
offset = 0;
}
return(error);
}
int
xio_copy_ktox(xio_t xio, int uoffset, const void *kptr, int bytes)
{
int i;
int n;
int error;
int offset;
vm_page_t m;
struct lwbuf *lwb;
struct lwbuf lwb_cache;
if (uoffset + bytes > xio->xio_bytes)
return(EFAULT);
offset = (xio->xio_offset + uoffset) & PAGE_MASK;
if ((n = PAGE_SIZE - offset) > bytes)
n = bytes;
error = 0;
for (i = (xio->xio_offset + uoffset) >> PAGE_SHIFT;
i < xio->xio_npages;
++i
) {
m = xio->xio_pages[i];
lwb = lwbuf_alloc(m, &lwb_cache);
bcopy(kptr, (char *)lwbuf_kva(lwb) + offset, n);
lwbuf_free(lwb);
bytes -= n;
kptr = (const char *)kptr + n;
if (bytes == 0)
break;
if ((n = bytes) > PAGE_SIZE)
n = PAGE_SIZE;
offset = 0;
}
return(error);
}