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--