summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2014-09-20 16:15:17 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2014-09-20 16:15:17 +0000
commitffe2e156cb1636f64046b180aab6bf8e6a812d35 (patch)
tree5b59bbc738acbc6e2d59d857ed0955cbde44b90a
parentaf6c0aa460d486cee6637557789a4cacc75fe4d6 (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.c22
-rw-r--r--sys/arch/i386/pci/agp_machdep.c87
-rw-r--r--sys/dev/pci/agpvar.h4
-rw-r--r--sys/dev/pci/drm/i915/i915_gem_execbuffer.c12
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 */