summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/subr_bus_dma.c69
-rw-r--r--sys/kern/subr_uio.c54
2 files changed, 108 insertions, 15 deletions
diff --git a/sys/kern/subr_bus_dma.c b/sys/kern/subr_bus_dma.c
index a16d8c8..ae30276 100644
--- a/sys/kern/subr_bus_dma.c
+++ b/sys/kern/subr_bus_dma.c
@@ -54,19 +54,32 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
/*
- * Load a list of virtual addresses.
+ * Load up data starting at offset within a region specified by a
+ * list of virtual address ranges until either length or the region
+ * are exhausted.
*/
static int
_bus_dmamap_load_vlist(bus_dma_tag_t dmat, bus_dmamap_t map,
bus_dma_segment_t *list, int sglist_cnt, struct pmap *pmap, int *nsegs,
- int flags)
+ int flags, size_t offset, size_t length)
{
int error;
error = 0;
- for (; sglist_cnt > 0; sglist_cnt--, list++) {
- error = _bus_dmamap_load_buffer(dmat, map,
- (void *)(uintptr_t)list->ds_addr, list->ds_len, pmap,
+ for (; sglist_cnt > 0 && length != 0; sglist_cnt--, list++) {
+ char *addr;
+ size_t ds_len;
+
+ KASSERT((offset < list->ds_len),
+ ("Invalid mid-segment offset"));
+ addr = (char *)(uintptr_t)list->ds_addr + offset;
+ ds_len = list->ds_len - offset;
+ offset = 0;
+ if (ds_len > length)
+ ds_len = length;
+ length -= ds_len;
+ KASSERT((ds_len != 0), ("Segment length is zero"));
+ error = _bus_dmamap_load_buffer(dmat, map, addr, ds_len, pmap,
flags, NULL, nsegs);
if (error)
break;
@@ -118,22 +131,48 @@ _bus_dmamap_load_mbuf_sg(bus_dma_tag_t dmat, bus_dmamap_t map,
}
/*
+ * Load tlen data starting at offset within a region specified by a list of
+ * physical pages.
+ */
+static int
+_bus_dmamap_load_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
+ vm_page_t *pages, bus_size_t tlen, int offset, int *nsegs, int flags)
+{
+ vm_paddr_t paddr;
+ bus_size_t len;
+ int error, i;
+
+ for (i = 0, error = 0; error == 0 && tlen > 0; i++, tlen -= len) {
+ len = min(PAGE_SIZE - offset, tlen);
+ paddr = VM_PAGE_TO_PHYS(pages[i]) + offset;
+ error = _bus_dmamap_load_phys(dmat, map, paddr, len,
+ flags, NULL, nsegs);
+ offset = 0;
+ }
+ return (error);
+}
+
+/*
* Load from block io.
*/
static int
_bus_dmamap_load_bio(bus_dma_tag_t dmat, bus_dmamap_t map, struct bio *bio,
int *nsegs, int flags)
{
- int error;
- if ((bio->bio_flags & BIO_UNMAPPED) == 0) {
- error = _bus_dmamap_load_buffer(dmat, map, bio->bio_data,
- bio->bio_bcount, kernel_pmap, flags, NULL, nsegs);
- } else {
- error = _bus_dmamap_load_ma(dmat, map, bio->bio_ma,
- bio->bio_bcount, bio->bio_ma_offset, flags, NULL, nsegs);
+ if ((bio->bio_flags & BIO_VLIST) != 0) {
+ bus_dma_segment_t *segs = (bus_dma_segment_t *)bio->bio_data;
+ return (_bus_dmamap_load_vlist(dmat, map, segs, bio->bio_ma_n,
+ kernel_pmap, nsegs, flags, bio->bio_ma_offset,
+ bio->bio_bcount));
}
- return (error);
+
+ if ((bio->bio_flags & BIO_UNMAPPED) != 0)
+ return (_bus_dmamap_load_pages(dmat, map, bio->bio_ma,
+ bio->bio_bcount, bio->bio_ma_offset, nsegs, flags));
+
+ return (_bus_dmamap_load_buffer(dmat, map, bio->bio_data,
+ bio->bio_bcount, kernel_pmap, flags, NULL, nsegs));
}
int
@@ -219,7 +258,7 @@ _bus_dmamap_load_ccb(bus_dma_tag_t dmat, bus_dmamap_t map, union ccb *ccb,
case CAM_DATA_SG:
error = _bus_dmamap_load_vlist(dmat, map,
(bus_dma_segment_t *)data_ptr, sglist_cnt, kernel_pmap,
- nsegs, flags);
+ nsegs, flags, 0, dxfer_len);
break;
case CAM_DATA_SG_PADDR:
error = _bus_dmamap_load_plist(dmat, map,
@@ -494,7 +533,7 @@ bus_dmamap_load_mem(bus_dma_tag_t dmat, bus_dmamap_t map,
break;
case MEMDESC_VLIST:
error = _bus_dmamap_load_vlist(dmat, map, mem->u.md_list,
- mem->md_opaque, kernel_pmap, &nsegs, flags);
+ mem->md_opaque, kernel_pmap, &nsegs, flags, 0, SIZE_T_MAX);
break;
case MEMDESC_PLIST:
error = _bus_dmamap_load_plist(dmat, map, mem->u.md_list,
diff --git a/sys/kern/subr_uio.c b/sys/kern/subr_uio.c
index 87892fd..3712f92 100644
--- a/sys/kern/subr_uio.c
+++ b/sys/kern/subr_uio.c
@@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_pageout.h>
#include <vm/vm_map.h>
+#include <machine/bus.h>
+
SYSCTL_INT(_kern, KERN_IOV_MAX, iov_max, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, UIO_MAXIOV,
"Maximum number of elements in an I/O vector; sysconf(_SC_IOV_MAX)");
@@ -136,6 +138,58 @@ physcopyout(vm_paddr_t src, void *dst, size_t len)
#undef PHYS_PAGE_COUNT
int
+physcopyin_vlist(bus_dma_segment_t *src, off_t offset, vm_paddr_t dst,
+ size_t len)
+{
+ size_t seg_len;
+ int error;
+
+ error = 0;
+ while (offset >= src->ds_len) {
+ offset -= src->ds_len;
+ src++;
+ }
+
+ while (len > 0 && error == 0) {
+ seg_len = MIN(src->ds_len - offset, len);
+ error = physcopyin((void *)(uintptr_t)(src->ds_addr + offset),
+ dst, seg_len);
+ offset = 0;
+ src++;
+ len -= seg_len;
+ dst += seg_len;
+ }
+
+ return (error);
+}
+
+int
+physcopyout_vlist(vm_paddr_t src, bus_dma_segment_t *dst, off_t offset,
+ size_t len)
+{
+ size_t seg_len;
+ int error;
+
+ error = 0;
+ while (offset >= dst->ds_len) {
+ offset -= dst->ds_len;
+ dst++;
+ }
+
+ while (len > 0 && error == 0) {
+ seg_len = MIN(dst->ds_len - offset, len);
+ error = physcopyout(src, (void *)(uintptr_t)(dst->ds_addr +
+ offset), seg_len);
+ offset = 0;
+ dst++;
+ len -= seg_len;
+ src += seg_len;
+ }
+
+ return (error);
+}
+
+int
uiomove(void *cp, int n, struct uio *uio)
{
OpenPOWER on IntegriCloud