From c22fb1db551ed49aabc924ca844b0d0392b83a18 Mon Sep 17 00:00:00 2001 From: scw Date: Wed, 16 May 2001 19:06:46 +0000 Subject: [PATCH] Finally nobble the bus_dmamap_sync() problem with osiop(4). Basically, bus_dmamap_sync() `PREREAD' needs to flush the cache for the start and end of the region if it is not aligned to a cacheline boundary, otherwise a subsequent POSTREAD can *purge* valid data which was in the cacheline but *outside* the region passed to bus_dmamap_sync(). Bus snooping doesn't always help here because osiop(4) calls bus_dmamap_sync() with POSTREAD even if no data was actually transferred! (And we can't rely on snooping on the 68060 models anyway). --- sys/arch/mvme68k/include/bus_dma.h | 4 +- sys/arch/mvme68k/include/pmap.h | 3 +- sys/arch/mvme68k/mvme68k/bus_dma.c | 95 ++++++++++++++++++++++++------ sys/arch/mvme68k/mvme68k/pmap.c | 20 ++++++- 4 files changed, 100 insertions(+), 22 deletions(-) diff --git a/sys/arch/mvme68k/include/bus_dma.h b/sys/arch/mvme68k/include/bus_dma.h index b84db951e1ac..e5d38361855c 100644 --- a/sys/arch/mvme68k/include/bus_dma.h +++ b/sys/arch/mvme68k/include/bus_dma.h @@ -1,4 +1,4 @@ -/* $NetBSD: bus_dma.h,v 1.7 2001/03/07 22:42:19 thorpej Exp $ */ +/* $NetBSD: bus_dma.h,v 1.8 2001/05/16 19:06:46 scw Exp $ */ /* * This file was extracted from from next68k/include/bus.h @@ -123,7 +123,7 @@ struct mvme68k_bus_dma_segment { /* PRIVATE */ bus_addr_t _ds_cpuaddr; /* CPU-relative phys addr of segment */ - int _ds_padding; + int _ds_flags; }; typedef struct mvme68k_bus_dma_segment bus_dma_segment_t; diff --git a/sys/arch/mvme68k/include/pmap.h b/sys/arch/mvme68k/include/pmap.h index a124f717a00c..dc83ee73a4d1 100644 --- a/sys/arch/mvme68k/include/pmap.h +++ b/sys/arch/mvme68k/include/pmap.h @@ -1,4 +1,4 @@ -/* $NetBSD: pmap.h,v 1.20 2001/04/22 23:19:28 thorpej Exp $ */ +/* $NetBSD: pmap.h,v 1.21 2001/05/16 19:06:46 scw Exp $ */ /* * Copyright (c) 1987 Carnegie-Mellon University @@ -135,6 +135,7 @@ extern struct pmap kernel_pmap_store; extern void _pmap_set_page_cacheable __P((struct pmap *, vaddr_t)); extern void _pmap_set_page_cacheinhibit __P((struct pmap *, vaddr_t)); +extern int _pmap_page_is_cacheable __P((struct pmap *, vaddr_t)); extern struct pv_entry *pv_table; /* array of entries, one per page */ diff --git a/sys/arch/mvme68k/mvme68k/bus_dma.c b/sys/arch/mvme68k/mvme68k/bus_dma.c index 468cfd768ddb..4a903de52cb2 100644 --- a/sys/arch/mvme68k/mvme68k/bus_dma.c +++ b/sys/arch/mvme68k/mvme68k/bus_dma.c @@ -1,4 +1,4 @@ -/* $NetBSD: bus_dma.c,v 1.13 2001/05/11 13:01:44 scw Exp $ */ +/* $NetBSD: bus_dma.c,v 1.14 2001/05/16 19:06:47 scw Exp $ */ /* * This file was taken from from next68k/dev/bus_dma.c, which was originally @@ -46,7 +46,7 @@ #include /* RCS ID & Copyright macro defns */ -__KERNEL_RCSID(0, "$NetBSD: bus_dma.c,v 1.13 2001/05/11 13:01:44 scw Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bus_dma.c,v 1.14 2001/05/16 19:06:47 scw Exp $"); #include #include @@ -71,12 +71,6 @@ int _bus_dmamap_load_buffer_direct_common __P((bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t, struct proc *, int, paddr_t *, int *, int)); -/* - * Initialised in mvme68k/machdep.c according to the host cpu type - */ -void (*_bus_dmamap_sync)(bus_dma_tag_t, bus_dmamap_t, bus_addr_t, - bus_size_t, int); - /* * Common function for DMA map creation. May be called by bus-specific * DMA map creation functions. @@ -162,7 +156,7 @@ _bus_dmamap_load_buffer_direct_common(t, map, buf, buflen, p, flags, bus_size_t sgsize; bus_addr_t curaddr, lastaddr, baddr, bmask; vaddr_t vaddr = (vaddr_t)buf; - int seg; + int seg, cacheable, coherent = BUS_DMA_COHERENT; lastaddr = *lastaddrp; bmask = ~(map->_dm_boundary - 1); @@ -171,11 +165,20 @@ _bus_dmamap_load_buffer_direct_common(t, map, buf, buflen, p, flags, /* * Get the physical address for this segment. */ - if (p != NULL) + if (p != NULL) { (void) pmap_extract(p->p_vmspace->vm_map.pmap, vaddr, &curaddr); - else + cacheable = + _pmap_page_is_cacheable(p->p_vmspace->vm_map.pmap, + vaddr); + } else { (void) pmap_extract(pmap_kernel(),vaddr, &curaddr); + cacheable = + _pmap_page_is_cacheable(pmap_kernel(), vaddr); + } + + if (cacheable) + coherent = 0; /* * Compute the segment size, and adjust counts. @@ -201,6 +204,8 @@ _bus_dmamap_load_buffer_direct_common(t, map, buf, buflen, p, flags, map->dm_segs[seg].ds_addr = map->dm_segs[seg]._ds_cpuaddr = curaddr; map->dm_segs[seg].ds_len = sgsize; + map->dm_segs[seg]._ds_flags = + cacheable ? 0 : BUS_DMA_COHERENT; first = 0; } else { if (curaddr == lastaddr && @@ -216,6 +221,8 @@ _bus_dmamap_load_buffer_direct_common(t, map, buf, buflen, p, flags, map->dm_segs[seg].ds_addr = map->dm_segs[seg]._ds_cpuaddr = curaddr; map->dm_segs[seg].ds_len = sgsize; + map->dm_segs[seg]._ds_flags = + cacheable ? 0 : BUS_DMA_COHERENT; } } @@ -226,6 +233,8 @@ _bus_dmamap_load_buffer_direct_common(t, map, buf, buflen, p, flags, *segp = seg; *lastaddrp = lastaddr; + map->_dm_flags &= ~BUS_DMA_COHERENT; + map->_dm_flags |= coherent; /* * Did we fit? @@ -438,6 +447,7 @@ _bus_dmamap_unload(t, map) */ map->dm_mapsize = 0; map->dm_nsegs = 0; + map->_dm_flags &= ~BUS_DMA_COHERENT; } /* @@ -467,11 +477,18 @@ _bus_dmamap_sync_0460(t, map, offset, len, ops) bus_size_t len; int ops; { - bus_addr_t p, e; + bus_addr_t p, e, ps, pe; bus_size_t seglen; int i; - if ((ops & (BUS_DMASYNC_PREWRITE | BUS_DMASYNC_POSTREAD)) == 0) + /* If the whole DMA map is uncached, do nothing. */ + if (map->_dm_flags & BUS_DMA_COHERENT) + return; + + /* Short-circuit for unsupported `ops' */ + if ((ops & (BUS_DMASYNC_PREREAD | + BUS_DMASYNC_PREWRITE | + BUS_DMASYNC_POSTREAD)) == 0) return; for (i = 0; i < map->dm_nsegs && len > 0; i++) { @@ -486,9 +503,16 @@ _bus_dmamap_sync_0460(t, map, offset, len, ops) seglen = len; len -= seglen; + /* Ignore cache-inhibited segments */ + if (map->dm_segs[i]._ds_flags & BUS_DMA_COHERENT) + continue; + + ps = map->dm_segs[i]._ds_cpuaddr + offset; + pe = ps + seglen; + if (ops & BUS_DMASYNC_PREWRITE) { - p = (map->dm_segs[i]._ds_cpuaddr + offset) & ~0xf; - e = p + ((seglen + 15) & ~0xf); + p = ps & ~0xf; + e = (pe + 15) & ~0xf; /* flush cache line (060 too) */ while((p < e) && (p % NBPG)) { @@ -509,9 +533,39 @@ _bus_dmamap_sync_0460(t, map, offset, len, ops) } } + /* + * Normally, the `PREREAD' flag instructs us to purge the + * cache for the specified offset and length. However, if + * the offset/length is not aligned to a cacheline boundary, + * we may end up purging some legitimate data from the + * start/end of the cache. In such a case, *flush* the + * cachelines at the start and end of the required region. + * We assume someone will do a `POSTREAD' afterwards to + * ensure the cache is purged for the remainder of the region. + * + * Note: Even though the high-end MVME boards support bus- + * snooping (well, the 060 isn't *quite* there), the osiop(4) + * driver *ALWAYS* issues a `POSTREAD' EVEN IF NO DATA WAS + * TRANSFERRED! + * + * This isn't necessarily a bug, since a SCSI target is free + * to disconnect part way through a data-in phase anyway. + * Thus, the CPU may never get to snoop the incoming data + * before we purge the dmamap region. + * + * Note #2: All this is necessary on mvme68k because we + * normally run the cache in Copy Back mode... + */ + if (ops & BUS_DMASYNC_PREREAD) { + if (ps & 0xf) + DCFL_40(ps); + if (pe & 0xf) + DCFL_40(pe); + } + if (ops & BUS_DMASYNC_POSTREAD) { - p = (map->dm_segs[i]._ds_cpuaddr + offset) & ~0xf; - e = p + ((seglen + 15) & ~0xf); + p = ps & ~0xf; + e = (pe + 15) & ~0xf; /* purge cache line */ while((p < e) && (p % NBPG)) { @@ -583,6 +637,7 @@ _bus_dmamem_alloc_common(t, low, high, size, alignment, boundary, lastaddr = VM_PAGE_TO_PHYS(m); segs[curseg].ds_addr = segs[curseg]._ds_cpuaddr = lastaddr; segs[curseg].ds_len = PAGE_SIZE; + segs[curseg]._ds_flags = 0; m = m->pageq.tqe_next; for (; m != NULL; m = m->pageq.tqe_next) { @@ -612,6 +667,7 @@ _bus_dmamem_alloc_common(t, low, high, size, alignment, boundary, segs[curseg].ds_addr = segs[curseg]._ds_cpuaddr = curaddr; segs[curseg].ds_len = PAGE_SIZE; + segs[curseg]._ds_flags = 0; } lastaddr = curaddr; } @@ -728,8 +784,11 @@ _bus_dmamem_map(t, segs, nsegs, size, kvap, flags) VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED); /* Cache-inhibit the page if necessary */ - if ( (flags & BUS_DMA_COHERENT) != 0 ) + if ((flags & BUS_DMA_COHERENT) != 0) _pmap_set_page_cacheinhibit(pmap_kernel(), va); + + segs[curseg]._ds_flags &= ~BUS_DMA_COHERENT; + segs[curseg]._ds_flags |= (flags & BUS_DMA_COHERENT); } } pmap_update(); diff --git a/sys/arch/mvme68k/mvme68k/pmap.c b/sys/arch/mvme68k/mvme68k/pmap.c index 38b99ae97121..d1e08b7d7bc0 100644 --- a/sys/arch/mvme68k/mvme68k/pmap.c +++ b/sys/arch/mvme68k/mvme68k/pmap.c @@ -1,4 +1,4 @@ -/* $NetBSD: pmap.c,v 1.59 2001/05/13 18:35:19 scw Exp $ */ +/* $NetBSD: pmap.c,v 1.60 2001/05/16 19:06:47 scw Exp $ */ /*- * Copyright (c) 1999 The NetBSD Foundation, Inc. @@ -2739,6 +2739,24 @@ _pmap_set_page_cacheinhibit(pm, va) } } +int +_pmap_page_is_cacheable(pm, va) + struct pmap *pm; + vaddr_t va; +{ + pt_entry_t *pte; + + if (!pmap_ste_v(pm, va)) + return (0); + + pte = pmap_pte(pm, va); + + if (pmap_pte_ci(pte)) + return (0); + else + return (1); +} + #ifdef DEBUG /* * pmap_pvdump: