Subject: M_EXT_PAGES
To: None <tech-kern@netbsd.org>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: tech-kern
Date: 06/05/2004 16:47:17
--NextPart-20040605163633-1799000
Content-Type: Text/Plain; charset=us-ascii
hi,
currently, ext_pgs is merely used as a hint for sodopendfreel().
i'd like to change it to be used by bus_dmamap_load_mbuf() as well.
(x86 diff is attached)
while i think that the change itsself has its own benefits,
eventual goals are:
- delay pmap_kenter_pa() until it's really needed.
(likely on the first mtod()?)
if you're lucky enough, no pmap_kenter_pa() is needed at all.
- use mbufs (or something similar) for disk i/o.
comments?
YAMAMOTO Takashi
--NextPart-20040605163633-1799000
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="a.diff"
Index: x86/bus_dma.c
===================================================================
--- x86/bus_dma.c (revision 736)
+++ x86/bus_dma.c (working copy)
@@ -135,8 +135,9 @@ static int _bus_dma_alloc_bouncebuf(bus_
bus_size_t size, int flags);
static void _bus_dma_free_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map);
static int _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map,
- void *buf, bus_size_t buflen, struct proc *p, int flags,
- paddr_t *lastaddrp, int *segp, int first);
+ void *buf, bus_size_t buflen, struct proc *p, int flags);
+static __inline int _bus_dmamap_load_paddr(bus_dma_tag_t, bus_dmamap_t,
+ paddr_t, int);
/*
@@ -265,8 +266,7 @@ _bus_dmamap_load(t, map, buf, buflen, p,
int flags;
{
struct x86_bus_dma_cookie *cookie = map->_dm_cookie;
- int error, seg;
- paddr_t lastaddr;
+ int error;
STAT_INCR(bus_dma_stats_loads);
@@ -279,12 +279,9 @@ _bus_dmamap_load(t, map, buf, buflen, p,
if (buflen > map->_dm_size)
return EINVAL;
- seg = 0;
- error = _bus_dmamap_load_buffer(t, map, buf, buflen, p, flags,
- &lastaddr, &seg, 1);
+ error = _bus_dmamap_load_buffer(t, map, buf, buflen, p, flags);
if (error == 0) {
map->dm_mapsize = buflen;
- map->dm_nsegs = seg + 1;
return 0;
}
@@ -316,6 +313,7 @@ _bus_dmamap_load(t, map, buf, buflen, p,
cookie->id_origbuf = buf;
cookie->id_origbuflen = buflen;
cookie->id_buftype = X86_DMA_ID_BUFTYPE_LINEAR;
+ map->dm_nsegs = 0;
error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, buflen,
p, flags);
if (error)
@@ -326,6 +324,64 @@ _bus_dmamap_load(t, map, buf, buflen, p,
return (0);
}
+static __inline int
+_bus_dmamap_load_paddr(bus_dma_tag_t t, bus_dmamap_t map,
+ paddr_t paddr, int size)
+{
+ bus_dma_segment_t * const segs = map->dm_segs;
+ int nseg = map->dm_nsegs;
+ bus_addr_t bmask = ~(map->_dm_boundary - 1);
+ bus_addr_t lastaddr = 0xdead; /* XXX gcc */
+ int sgsize;
+ int error = 0;
+
+ if (nseg > 0)
+ lastaddr = segs[nseg-1].ds_addr + segs[nseg-1].ds_len;
+again:
+ sgsize = size;
+ /*
+ * Make sure we don't cross any boundaries.
+ */
+ if (map->_dm_boundary > 0) {
+ bus_addr_t baddr; /* next boundary address */
+
+ baddr = (paddr + map->_dm_boundary) & bmask;
+ if (sgsize > (baddr - paddr))
+ sgsize = (baddr - paddr);
+ }
+
+ /*
+ * Insert chunk into a segment, coalescing with
+ * previous segment if possible.
+ */
+ if (nseg > 0 && paddr == lastaddr &&
+ segs[nseg-1].ds_len + sgsize <= map->_dm_maxsegsz &&
+ (map->_dm_boundary == 0 ||
+ (segs[nseg-1].ds_addr & bmask) == (paddr & bmask))) {
+ /* coalesce */
+ segs[nseg-1].ds_len += sgsize;
+ } else if (nseg >= map->_dm_segcnt) {
+ return EFBIG;
+ } else {
+ /* new segment */
+ segs[nseg].ds_addr = paddr;
+ segs[nseg].ds_len = sgsize;
+ nseg++;
+ }
+
+ lastaddr = paddr + sgsize;
+ if (map->_dm_bounce_thresh != 0 && lastaddr > map->_dm_bounce_thresh)
+ return EINVAL;
+
+ paddr += sgsize;
+ size -= sgsize;
+ if (size > 0)
+ goto again;
+
+ map->dm_nsegs = nseg;
+ return error;
+}
+
/*
* Like _bus_dmamap_load(), but for mbufs.
*/
@@ -337,8 +393,7 @@ _bus_dmamap_load_mbuf(t, map, m0, flags)
int flags;
{
struct x86_bus_dma_cookie *cookie = map->_dm_cookie;
- int seg, error, first;
- paddr_t lastaddr;
+ int error;
struct mbuf *m;
/*
@@ -355,51 +410,81 @@ _bus_dmamap_load_mbuf(t, map, m0, flags)
if (m0->m_pkthdr.len > map->_dm_size)
return (EINVAL);
- first = 1;
- seg = 0;
error = 0;
for (m = m0; m != NULL && error == 0; m = m->m_next) {
+ int offset;
+ int remainbytes;
+ const struct vm_page * const *pgs;
+ paddr_t paddr;
+ int size;
+
if (m->m_len == 0)
continue;
/* XXX Could be better about coalescing. */
/* XXX Doesn't check boundaries. */
- switch (m->m_flags & (M_EXT|M_EXT_CLUSTER)) {
+ switch (m->m_flags & (M_EXT|M_EXT_CLUSTER|M_EXT_PAGES)) {
case M_EXT|M_EXT_CLUSTER:
/* XXX KDASSERT */
KASSERT(m->m_ext.ext_paddr != M_PADDR_INVALID);
- lastaddr = m->m_ext.ext_paddr +
+ paddr = m->m_ext.ext_paddr +
(m->m_data - m->m_ext.ext_buf);
- have_addr:
- if (first == 0 &&
- ++seg >= map->_dm_segcnt) {
- error = EFBIG;
- break;
+ size = m->m_len;
+ error = _bus_dmamap_load_paddr(t, map, paddr, size);
+ break;
+
+ case M_EXT|M_EXT_PAGES:
+ KASSERT(m->m_ext.ext_buf <= m->m_data);
+ KASSERT(m->m_data <=
+ m->m_ext.ext_buf + m->m_ext.ext_size);
+
+ offset = (vaddr_t)m->m_data -
+ trunc_page((vaddr_t)m->m_ext.ext_buf);
+ remainbytes = m->m_len;
+
+ /* skip uninteresting pages */
+ pgs = (const struct vm_page * const *)
+ m->m_ext.ext_pgs + (offset >> PAGE_SHIFT);
+
+ offset &= PAGE_MASK; /* offset in the first page */
+
+ /* load each pages */
+ while (remainbytes > 0) {
+ const struct vm_page *pg;
+
+ size = MIN(remainbytes, PAGE_SIZE - offset);
+
+ pg = *pgs++;
+ KASSERT(pg);
+ paddr = VM_PAGE_TO_PHYS(pg) + offset;
+
+ error = _bus_dmamap_load_paddr(t, map,
+ paddr, size);
+ if (error)
+ break;
+ offset = 0;
+ remainbytes -= size;
}
- map->dm_segs[seg].ds_addr = lastaddr;
- map->dm_segs[seg].ds_len = m->m_len;
- lastaddr += m->m_len;
- if (map->_dm_bounce_thresh != 0 &&
- lastaddr > map->_dm_bounce_thresh)
- error = EINVAL;
break;
case 0:
- lastaddr = m->m_paddr + M_BUFOFFSET(m) +
+ paddr = m->m_paddr + M_BUFOFFSET(m) +
(m->m_data - M_BUFADDR(m));
- goto have_addr;
+ size = m->m_len;
+ error = _bus_dmamap_load_paddr(t, map, paddr, size);
+ break;
default:
error = _bus_dmamap_load_buffer(t, map, m->m_data,
- m->m_len, NULL, flags, &lastaddr, &seg, first);
+ m->m_len, NULL, flags);
}
- first = 0;
}
if (error == 0) {
map->dm_mapsize = m0->m_pkthdr.len;
- map->dm_nsegs = seg + 1;
return 0;
}
+ map->dm_nsegs = 0;
+
if (cookie == NULL ||
((cookie->id_flags & X86_DMA_ID_MIGHT_NEED_BOUNCE) == 0))
return error;
@@ -447,8 +532,7 @@ _bus_dmamap_load_uio(t, map, uio, flags)
struct uio *uio;
int flags;
{
- paddr_t lastaddr;
- int seg, i, error, first;
+ int i, error;
bus_size_t minlen, resid;
struct proc *p = NULL;
struct iovec *iov;
@@ -472,8 +556,6 @@ _bus_dmamap_load_uio(t, map, uio, flags)
#endif
}
- first = 1;
- seg = 0;
error = 0;
for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) {
/*
@@ -484,17 +566,17 @@ _bus_dmamap_load_uio(t, map, uio, flags)
addr = (caddr_t)iov[i].iov_base;
error = _bus_dmamap_load_buffer(t, map, addr, minlen,
- p, flags, &lastaddr, &seg, first);
- first = 0;
+ p, flags);
resid -= minlen;
}
if (error == 0) {
map->dm_mapsize = uio->uio_resid;
- map->dm_nsegs = seg + 1;
return 0;
}
+ map->dm_nsegs = 0;
+
if (cookie == NULL ||
((cookie->id_flags & X86_DMA_ID_MIGHT_NEED_BOUNCE) == 0))
return error;
@@ -1057,21 +1139,17 @@ _bus_dmamem_mmap(t, segs, nsegs, off, pr
* first indicates if this is the first invocation of this function.
*/
static int
-_bus_dmamap_load_buffer(t, map, buf, buflen, p, flags, lastaddrp, segp, first)
+_bus_dmamap_load_buffer(t, map, buf, buflen, p, flags)
bus_dma_tag_t t;
bus_dmamap_t map;
void *buf;
bus_size_t buflen;
struct proc *p;
int flags;
- paddr_t *lastaddrp;
- int *segp;
- int first;
{
bus_size_t sgsize;
- bus_addr_t curaddr, lastaddr, baddr, bmask;
+ bus_addr_t curaddr;
vaddr_t vaddr = (vaddr_t)buf;
- int seg;
pmap_t pmap;
if (p != NULL)
@@ -1079,10 +1157,9 @@ _bus_dmamap_load_buffer(t, map, buf, buf
else
pmap = pmap_kernel();
- lastaddr = *lastaddrp;
- bmask = ~(map->_dm_boundary - 1);
+ while (buflen > 0) {
+ int error;
- for (seg = *segp; buflen > 0 ; ) {
/*
* Get the physical address for this segment.
*/
@@ -1103,52 +1180,14 @@ _bus_dmamap_load_buffer(t, map, buf, buf
if (buflen < sgsize)
sgsize = buflen;
- /*
- * Make sure we don't cross any boundaries.
- */
- if (map->_dm_boundary > 0) {
- baddr = (curaddr + map->_dm_boundary) & bmask;
- if (sgsize > (baddr - curaddr))
- sgsize = (baddr - curaddr);
- }
-
- /*
- * Insert chunk into a segment, coalescing with
- * previous segment if possible.
- */
- if (first) {
- map->dm_segs[seg].ds_addr = curaddr;
- map->dm_segs[seg].ds_len = sgsize;
- first = 0;
- } else {
- if (curaddr == lastaddr &&
- (map->dm_segs[seg].ds_len + sgsize) <=
- map->_dm_maxsegsz &&
- (map->_dm_boundary == 0 ||
- (map->dm_segs[seg].ds_addr & bmask) ==
- (curaddr & bmask)))
- map->dm_segs[seg].ds_len += sgsize;
- else {
- if (++seg >= map->_dm_segcnt)
- break;
- map->dm_segs[seg].ds_addr = curaddr;
- map->dm_segs[seg].ds_len = sgsize;
- }
- }
+ error = _bus_dmamap_load_paddr(t, map, curaddr, sgsize);
+ if (error)
+ return error;
- lastaddr = curaddr + sgsize;
vaddr += sgsize;
buflen -= sgsize;
}
- *segp = seg;
- *lastaddrp = lastaddr;
-
- /*
- * Did we fit?
- */
- if (buflen != 0)
- return (EFBIG); /* XXX better return value here? */
return (0);
}
--NextPart-20040605163633-1799000--