diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2014-09-20 16:15:17 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2014-09-20 16:15:17 +0000 |
commit | ffe2e156cb1636f64046b180aab6bf8e6a812d35 (patch) | |
tree | 5b59bbc738acbc6e2d59d857ed0955cbde44b90a | |
parent | af6c0aa460d486cee6637557789a4cacc75fe4d6 (diff) |
On i386, agp_map_subregion might sleep, which is not allowed in some of
the inteldrm code. Fix this by adding new interfaces that can map a single
page without sleeping and use that in the execbuffer fast path that needs
this "atomic" behaviour. Should fix the panic I've seen under memory pressure
on i386.
-rw-r--r-- | sys/arch/amd64/pci/agp_machdep.c | 22 | ||||
-rw-r--r-- | sys/arch/i386/pci/agp_machdep.c | 87 | ||||
-rw-r--r-- | sys/dev/pci/agpvar.h | 4 | ||||
-rw-r--r-- | sys/dev/pci/drm/i915/i915_gem_execbuffer.c | 12 |
4 files changed, 70 insertions, 55 deletions
diff --git a/sys/arch/amd64/pci/agp_machdep.c b/sys/arch/amd64/pci/agp_machdep.c index 669b56d15f8..a09549469a4 100644 --- a/sys/arch/amd64/pci/agp_machdep.c +++ b/sys/arch/amd64/pci/agp_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: agp_machdep.c,v 1.12 2014/07/12 18:44:41 tedu Exp $ */ +/* $OpenBSD: agp_machdep.c,v 1.13 2014/09/20 16:15:16 kettenis Exp $ */ /* * Copyright (c) 2008 - 2009 Owain G. Ainsworth <oga@openbsd.org> @@ -90,7 +90,7 @@ agp_init_map(bus_space_tag_t tag, bus_addr_t address, bus_size_t size, map->size = size; if ((err = bus_space_map(tag, address, size, flags, &map->bsh)) != 0) { - free(map, M_AGP, 0); + free(map, M_AGP, sizeof(*map)); return (err); } *mapp = map; @@ -101,7 +101,7 @@ void agp_destroy_map(struct agp_map *map) { bus_space_unmap(map->bst, map->bsh, map->size); - free(map, M_AGP, 0); + free(map, M_AGP, sizeof(*map)); } @@ -109,10 +109,7 @@ int agp_map_subregion(struct agp_map *map, bus_size_t offset, bus_size_t size, bus_space_handle_t *bshp) { - if (offset > map->size || size > map->size || offset + size > map->size) - return (EINVAL); return (bus_space_subregion(map->bst, map->bsh, offset, size, bshp)); - } void @@ -121,3 +118,16 @@ agp_unmap_subregion(struct agp_map *map, bus_space_handle_t bsh, { /* subregion doesn't need unmapping, do nothing */ } + +void +agp_map_atomic(struct agp_map *map, bus_size_t offset, + bus_space_handle_t *bshp) +{ + agp_map_subregion(map, offset, PAGE_SIZE, bshp); +} + +void +agp_unmap_atomic(struct agp_map *map, bus_space_handle_t bsh) +{ + /* subregion doesn't need unmapping, do nothing */ +} diff --git a/sys/arch/i386/pci/agp_machdep.c b/sys/arch/i386/pci/agp_machdep.c index 5af66c3131a..c4e4ec37988 100644 --- a/sys/arch/i386/pci/agp_machdep.c +++ b/sys/arch/i386/pci/agp_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: agp_machdep.c,v 1.18 2014/07/12 18:44:42 tedu Exp $ */ +/* $OpenBSD: agp_machdep.c,v 1.19 2014/09/20 16:15:16 kettenis Exp $ */ /* * Copyright (c) 2008 - 2009 Owain G. Ainsworth <oga@openbsd.org> @@ -74,38 +74,25 @@ struct agp_map { bus_addr_t addr; bus_size_t size; int flags; + vaddr_t va; }; -extern struct extent *ioport_ex; extern struct extent *iomem_ex; int agp_init_map(bus_space_tag_t tag, bus_addr_t address, bus_size_t size, int flags, struct agp_map **mapp) { - struct extent *ex; - struct agp_map *map; - int error; - - switch (tag) { - case I386_BUS_SPACE_IO: - ex = ioport_ex; - if (flags & BUS_SPACE_MAP_LINEAR) - return (EINVAL); - break; - - case I386_BUS_SPACE_MEM: - ex = iomem_ex; - break; - - default: - panic("agp_init_map: bad bus space tag"); - } + struct agp_map *map; + int error; + + KASSERT(tag == I386_BUS_SPACE_MEM); + /* * We grab the extent out of the bus region ourselves * so we don't need to do these allocations every time. */ - error = extent_alloc_region(ex, address, size, + error = extent_alloc_region(iomem_ex, address, size, EX_NOWAIT | EX_MALLOCOK); if (error) return (error); @@ -119,6 +106,12 @@ agp_init_map(bus_space_tag_t tag, bus_addr_t address, bus_size_t size, map->size = size; map->flags = flags; + map->va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any, &kp_none, &kd_waitok); + if (map->va == 0) { + free(map, M_AGP, sizeof(*map)); + return (ENOMEM); + } + *mapp = map; return (0); } @@ -126,25 +119,11 @@ agp_init_map(bus_space_tag_t tag, bus_addr_t address, bus_size_t size, void agp_destroy_map(struct agp_map *map) { - struct extent *ex; - - switch (map->bst) { - case I386_BUS_SPACE_IO: - ex = ioport_ex; - break; - - case I386_BUS_SPACE_MEM: - ex = iomem_ex; - break; - - default: - panic("agp_destroy_map: bad bus space tag"); - } - - if (extent_free(ex, map->addr, map->size, + if (extent_free(iomem_ex, map->addr, map->size, EX_NOWAIT | EX_MALLOCOK )) - printf("agp_destroy_map: can't free region\n"); - free(map, M_AGP, 0); + printf("%s: can't free region\n",__func__); + km_free((void *)map->va, PAGE_SIZE, &kv_any, &kp_none); + free(map, M_AGP, sizeof(*map)); } @@ -162,3 +141,33 @@ agp_unmap_subregion(struct agp_map *map, bus_space_handle_t bsh, { return (_bus_space_unmap(map->bst, bsh, size, NULL)); } + +void +agp_map_atomic(struct agp_map *map, bus_size_t offset, + bus_space_handle_t *bshp) +{ + int pmap_flags = PMAP_NOCACHE; + paddr_t pa; + + KASSERT((offset & PGOFSET) == 0); + + if (map->flags & BUS_SPACE_MAP_CACHEABLE) + pmap_flags = 0; + else if (map->flags & BUS_SPACE_MAP_PREFETCHABLE) + pmap_flags = PMAP_WC; + + pa = bus_space_mmap(map->bst, map->addr, offset, 0, 0); + pmap_kenter_pa(map->va, pa | pmap_flags, VM_PROT_READ | VM_PROT_WRITE); + pmap_update(pmap_kernel()); + + *bshp = (bus_space_handle_t)map->va; +} + +void +agp_unmap_atomic(struct agp_map *map, bus_space_handle_t bsh) +{ + KASSERT(bsh == (bus_space_handle_t)map->va); + + pmap_kremove(map->va, PAGE_SIZE); + pmap_update(pmap_kernel()); +} diff --git a/sys/dev/pci/agpvar.h b/sys/dev/pci/agpvar.h index 1ac56b5c38c..3101875327a 100644 --- a/sys/dev/pci/agpvar.h +++ b/sys/dev/pci/agpvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: agpvar.h,v 1.30 2014/03/26 14:41:41 mpi Exp $ */ +/* $OpenBSD: agpvar.h,v 1.31 2014/09/20 16:15:16 kettenis Exp $ */ /* $NetBSD: agpvar.h,v 1.4 2001/10/01 21:54:48 fvdl Exp $ */ /*- @@ -177,6 +177,8 @@ int agp_map_subregion(struct agp_map *, bus_size_t, bus_size_t, bus_space_handle_t *); void agp_unmap_subregion(struct agp_map *, bus_space_handle_t, bus_size_t); +void agp_map_atomic(struct agp_map *, bus_size_t, bus_space_handle_t *); +void agp_unmap_atomic(struct agp_map *, bus_space_handle_t); int agp_alloc_dmamem(bus_dma_tag_t, size_t, bus_dmamap_t *, bus_addr_t *, bus_dma_segment_t *); diff --git a/sys/dev/pci/drm/i915/i915_gem_execbuffer.c b/sys/dev/pci/drm/i915/i915_gem_execbuffer.c index a3ef3dc6a0f..279596dca0a 100644 --- a/sys/dev/pci/drm/i915/i915_gem_execbuffer.c +++ b/sys/dev/pci/drm/i915/i915_gem_execbuffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: i915_gem_execbuffer.c,v 1.29 2014/07/12 18:48:52 tedu Exp $ */ +/* $OpenBSD: i915_gem_execbuffer.c,v 1.30 2014/09/20 16:15:16 kettenis Exp $ */ /* * Copyright (c) 2008-2009 Owain G. Ainsworth <oga@openbsd.org> * @@ -233,16 +233,10 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, /* Map the page containing the relocation we're going to perform. */ reloc->offset += obj->gtt_offset; - if ((ret = agp_map_subregion(dev_priv->agph, - trunc_page(reloc->offset), PAGE_SIZE, &bsh)) != 0) { - DRM_ERROR("map failed...\n"); - return -ret; - } - + agp_map_atomic(dev_priv->agph, trunc_page(reloc->offset), &bsh); bus_space_write_4(dev_priv->bst, bsh, reloc->offset & PAGE_MASK, reloc->delta); - - agp_unmap_subregion(dev_priv->agph, bsh, PAGE_SIZE); + agp_unmap_atomic(dev_priv->agph, bsh); } /* and update the user's relocation entry */ |