* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $DragonFly: src/sys/kern/kern_xio.c,v 1.11 2006/05/07 00:23:08 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_xio.c,v 1.16 2008/05/09 07:24:45 dillon Exp $
*/
/*
* Kernel XIO interface. An initialized XIO is basically a collection of
#include <sys/vmmeter.h>
#include <sys/vnode.h>
#include <sys/xio.h>
-#include <sys/sfbuf.h>
+
+#include <cpu/lwbuf.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
xio_init_ubuf(xio_t xio, void *ubase, size_t ubytes, int flags)
{
vm_offset_t addr;
- vm_paddr_t paddr;
vm_page_t m;
+ vm_page_t m0;
+ int error;
int i;
int n;
int vmprot;
xio->xio_pages = xio->xio_internal_pages;
if ((n = PAGE_SIZE - xio->xio_offset) > ubytes)
n = ubytes;
+ m0 = NULL;
for (i = 0; n && i < XIO_INTERNAL_PAGES; ++i) {
- if (vm_fault_quick((caddr_t)addr, vmprot) < 0)
+ m = vm_fault_page_quick(addr, vmprot, &error);
+ if (m == NULL)
break;
- if ((paddr = pmap_extract(&curproc->p_vmspace->vm_pmap, addr)) == 0)
- break;
- crit_enter();
- m = PHYS_TO_VM_PAGE(paddr);
- vm_page_hold(m);
- crit_exit();
xio->xio_pages[i] = m;
ubytes -= n;
xio->xio_bytes += n;
if ((n = ubytes) > PAGE_SIZE)
n = PAGE_SIZE;
addr += PAGE_SIZE;
+
+ /*
+ * Check linearity, used by syslink to memory map DMA buffers.
+ */
+ if (flags & XIOF_VMLINEAR) {
+ if (i == 0) {
+ m0 = m;
+ } else
+ if (m->object != m0->object || m->pindex != m0->pindex + i) {
+ error = EINVAL;
+ break;
+ }
+ }
}
xio->xio_npages = i;
/*
* If a failure occured clean out what we loaded and return EFAULT.
- * Return 0 on success.
+ * Return 0 on success. Do not dirty the pages.
*/
if (i < XIO_INTERNAL_PAGES && n) {
+ xio->xio_flags &= ~XIOF_WRITE;
xio_release(xio);
xio->xio_error = EFAULT;
}
for (i = 0; n && i < XIO_INTERNAL_PAGES; ++i) {
if ((paddr = pmap_kextract(addr)) == 0)
break;
- crit_enter();
m = PHYS_TO_VM_PAGE(paddr);
vm_page_hold(m);
- crit_exit();
xio->xio_pages[i] = m;
kbytes -= n;
xio->xio_bytes += n;
}
/*
+ * Initialize an XIO given an array of vm_page pointers. The caller is
+ * responsible for any modified state changes for the pages.
+ */
+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);
+}
+
+/*
* Cleanup an XIO so it can be destroyed. The pages associated with the
* XIO are released.
*/
int i;
vm_page_t m;
- crit_enter();
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);
}
- crit_exit();
xio->xio_offset = 0;
xio->xio_npages = 0;
xio->xio_bytes = 0;
* UIO_WRITE uio -> xio
*/
int
-xio_uio_copy(xio_t xio, int uoffset, struct uio *uio, int *sizep)
+xio_uio_copy(xio_t xio, int uoffset, struct uio *uio, size_t *sizep)
{
+ size_t bytes;
int error;
- int bytes;
bytes = xio->xio_bytes - uoffset;
if (bytes > uio->uio_resid)
int error;
int offset;
vm_page_t m;
- struct sf_buf *sf;
+ struct lwbuf *lwb;
+ struct lwbuf lwb_cache;
- if (bytes > xio->xio_bytes)
+ if (uoffset + bytes > xio->xio_bytes)
return(EFAULT);
offset = (xio->xio_offset + uoffset) & PAGE_MASK;
++i
) {
m = xio->xio_pages[i];
- sf = sf_buf_alloc(m, SFB_CPUPRIVATE);
- error = copyout((char *)sf_buf_kva(sf) + offset, uptr, n);
- sf_buf_free(sf);
+ lwb = lwbuf_alloc(m, &lwb_cache);
+ error = copyout((char *)lwbuf_kva(lwb) + offset, uptr, n);
+ lwbuf_free(lwb);
if (error)
break;
bytes -= n;
int error;
int offset;
vm_page_t m;
- struct sf_buf *sf;
+ struct lwbuf *lwb;
+ struct lwbuf lwb_cache;
if (bytes + uoffset > xio->xio_bytes)
return(EFAULT);
++i
) {
m = xio->xio_pages[i];
- sf = sf_buf_alloc(m, SFB_CPUPRIVATE);
- bcopy((char *)sf_buf_kva(sf) + offset, kptr, n);
- sf_buf_free(sf);
+ 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)
return(error);
}
+/*
+ * Copy the specified number of bytes from userland to the xio.
+ * Return an error code or 0 on success.
+ *
+ * uoffset is the abstracted starting offset in the XIO, not the actual
+ * offset, and usually starts at 0.
+ *
+ * Data in pages backing the XIO will be modified.
+ */
+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);
+}
+
+/*
+ * Copy the specified number of bytes from the kernel to the xio.
+ * Return an error code or 0 on success.
+ *
+ * uoffset is the abstracted starting offset in the XIO, not the actual
+ * offset, and usually starts at 0.
+ *
+ * Data in pages backing the XIO will be modified.
+ */
+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);
+}