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 /sys | |
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.
Diffstat (limited to 'sys')
-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 */ |