Date: Mon, 27 Oct 2014 22:10:58 +0100 From: Tijl Coosemans <tijl@FreeBSD.org> To: Konstantin Belousov <kostikbel@gmail.com> Cc: x11@FreeBSD.org, dumbbell@FreeBSD.org Subject: Re: [rfc] Radeon AGP support patches Message-ID: <20141027221058.23a188d0@kalimero.tijl.coosemans.org> In-Reply-To: <20141027162753.GB1877@kib.kiev.ua> References: <20141026162442.1330d4c3@kalimero.tijl.coosemans.org> <20141027141631.GX1877@kib.kiev.ua> <20141027170055.10af15e6@kalimero.tijl.coosemans.org> <20141027162753.GB1877@kib.kiev.ua>
next in thread | previous in thread | raw e-mail | index | archive | help
--MP_//lb9S+oP6/lR06gq+sL9IPI Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Content-Disposition: inline On Mon, 27 Oct 2014 18:27:53 +0200 Konstantin Belousov <kostikbel@gmail.com> wrote: > On Mon, Oct 27, 2014 at 05:00:55PM +0100, Tijl Coosemans wrote: >> In ttm_agp_bind the ttm->pages array is already populated. These are >> the pages that need to be put into the GTT. The patch modifies struct >> agp_memory in sys/dev/agp such that ttm->pages can be passed via >> agp_bind_memory. Maybe it would be better to add two new functions to >> sys/dev/agp/agp.c: agp_bind_pages and agp_unbind_pages. These would >> take a vm_page_t array as argument and bind/unbind the pages directly >> in the GTT. There's no need for ttm_agp_bind to call agp_alloc_memory >> then and struct agp_memory would not be involved at all. Does that >> sound better? > > Yes, this approach is much better IMO. Having discriminated storage > for the bound pages is too ugly; New patch 1 & 3 attached. > was the whole code audited for correctness after the change ? I'm fairly confident these patches are all that's needed yes. I made a first implementation on Sunday afternoon. It got to the point that X showed a mouse pointer and background colour and then it crashed. It took the rest of the week to figure out why (NULL dereference in ttm_bo_vm_fault) and how to solve it (mark aperture range fictitious). It's hard to debug something without a screen. I read the code front to back and back to front in that time and compared it with the old DRM code and with the Linux DRM code. That's where patch 2 & 4 come from. --MP_//lb9S+oP6/lR06gq+sL9IPI Content-Type: text/x-patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=radeon-agp-1.patch Index: sys/dev/agp/agp.c =================================================================== --- sys/dev/agp/agp.c (revision 273255) +++ sys/dev/agp/agp.c (working copy) @@ -996,3 +996,68 @@ void agp_memory_info(device_t dev, void mi->ami_offset = mem->am_offset; mi->ami_is_bound = mem->am_is_bound; } + +int agp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size, + vm_offset_t offset) { + struct agp_softc *sc = device_get_softc(dev); + vm_offset_t i, j, k, pa; + vm_page_t m; + int error; + + if ((size & (AGP_PAGE_SIZE - 1)) != 0 || + (offset & (AGP_PAGE_SIZE - 1)) != 0) + return (EINVAL); + + mtx_lock(&sc->as_lock); + for (i = 0; i < size; i += PAGE_SIZE) { + m = pages[i >> PAGE_SHIFT]; + + /* + * Install entries in the GATT, making sure that if + * AGP_PAGE_SIZE < PAGE_SIZE and size is not + * aligned to PAGE_SIZE, we don't modify too many GATT + * entries. + */ + for (j = 0; j < PAGE_SIZE && i + j < size; j += AGP_PAGE_SIZE) { + pa = VM_PAGE_TO_PHYS(m) + j; + AGP_DPF("binding offset %#jx to pa %#jx\n", + (uintmax_t)offset + i + j, (uintmax_t)pa); + error = AGP_BIND_PAGE(dev, offset + i + j, pa); + if (error) { + /* + * Bail out. Reverse all the mappings. + */ + for (k = 0; k < i + j; k += AGP_PAGE_SIZE) + AGP_UNBIND_PAGE(dev, offset + k); + + mtx_unlock(&sc->as_lock); + return (error); + } + } + } + + agp_flush_cache(); + AGP_FLUSH_TLB(dev); + + mtx_unlock(&sc->as_lock); + return (0); +} + +int agp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset) { + struct agp_softc *sc = device_get_softc(dev); + vm_offset_t i; + + if ((size & (AGP_PAGE_SIZE - 1)) != 0 || + (offset & (AGP_PAGE_SIZE - 1)) != 0) + return (EINVAL); + + mtx_lock(&sc->as_lock); + for (i = 0; i < size; i += AGP_PAGE_SIZE) + AGP_UNBIND_PAGE(dev, offset + i); + + agp_flush_cache(); + AGP_FLUSH_TLB(dev); + + mtx_unlock(&sc->as_lock); + return (0); +} Index: sys/dev/agp/agpvar.h =================================================================== --- sys/dev/agp/agpvar.h (revision 273255) +++ sys/dev/agp/agpvar.h (working copy) @@ -122,6 +122,19 @@ int agp_unbind_memory(device_t dev, void */ void agp_memory_info(device_t dev, void *handle, struct agp_memory_info *mi); +/* + * Bind a set of pages at a given offset within the AGP aperture. + * Returns EINVAL if the given size or offset is not at an AGP page boundary. + */ +int agp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size, + vm_offset_t offset); + +/* + * Unbind a set of pages from the AGP aperture. + * Returns EINVAL if the given size or offset is not at an AGP page boundary. + */ +int agp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset); + #define AGP_NORMAL_MEMORY 0 #define AGP_USER_TYPES (1 << 16) --MP_//lb9S+oP6/lR06gq+sL9IPI Content-Type: text/x-patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=radeon-agp-3.patch Index: sys/dev/drm2/drm_agpsupport.c =================================================================== --- sys/dev/drm2/drm_agpsupport.c (revision 273255) +++ sys/dev/drm2/drm_agpsupport.c (working copy) @@ -396,7 +396,7 @@ void *drm_agp_allocate_memory(size_t pag if (!agpdev) return NULL; - return agp_alloc_memory(agpdev, type, pages << AGP_PAGE_SHIFT); + return agp_alloc_memory(agpdev, type, pages << PAGE_SHIFT); } int drm_agp_free_memory(void *handle) Index: sys/dev/drm2/radeon/radeon.h =================================================================== --- sys/dev/drm2/radeon/radeon.h (revision 273255) +++ sys/dev/drm2/radeon/radeon.h (working copy) @@ -1618,6 +1618,7 @@ struct radeon_device { bool need_dma32; bool accel_working; bool fictitious_range_registered; + bool fictitious_agp_range_registered; struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES]; const struct firmware *me_fw; /* all family ME firmware */ const struct firmware *pfp_fw; /* r6/700 PFP firmware */ Index: sys/dev/drm2/radeon/radeon_device.c =================================================================== --- sys/dev/drm2/radeon/radeon_device.c (revision 273255) +++ sys/dev/drm2/radeon/radeon_device.c (working copy) @@ -1014,6 +1014,7 @@ int radeon_device_init(struct radeon_dev rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; rdev->accel_working = false; rdev->fictitious_range_registered = false; + rdev->fictitious_agp_range_registered = false; /* set up ring ids */ for (i = 0; i < RADEON_NUM_RINGS; i++) { rdev->ring[i].idx = i; @@ -1168,6 +1169,24 @@ int radeon_device_init(struct radeon_dev return (-r); } rdev->fictitious_range_registered = true; +#if __OS_HAS_AGP + if (rdev->flags & RADEON_IS_AGP) { + DRM_INFO("%s: Taking over the fictitious range 0x%jx-0x%jx\n", + __func__, (uintmax_t)rdev->mc.agp_base, + (uintmax_t)rdev->mc.agp_base + rdev->mc.gtt_size); + r = vm_phys_fictitious_reg_range( + rdev->mc.agp_base, + rdev->mc.agp_base + rdev->mc.gtt_size, + VM_MEMATTR_WRITE_COMBINING); + if (r != 0) { + DRM_ERROR("Failed to register fictitious range " + "0x%jx-0x%jx (%d).\n", (uintmax_t)rdev->mc.agp_base, + (uintmax_t)rdev->mc.agp_base + rdev->mc.gtt_size, r); + return (-r); + } + rdev->fictitious_agp_range_registered = true; + } +#endif if ((radeon_testing & 1)) { radeon_test_moves(rdev); @@ -1205,6 +1224,13 @@ void radeon_device_fini(struct radeon_de rdev->mc.aper_base, rdev->mc.aper_base + rdev->mc.visible_vram_size); } +#if __OS_HAS_AGP + if (rdev->fictitious_agp_range_registered) { + vm_phys_fictitious_unreg_range( + rdev->mc.agp_base, + rdev->mc.agp_base + rdev->mc.gtt_size); + } +#endif radeon_fini(rdev); #ifdef DUMBBELL_WIP Index: sys/dev/drm2/radeon/radeon_ttm.c =================================================================== --- sys/dev/drm2/radeon/radeon_ttm.c (revision 273255) +++ sys/dev/drm2/radeon/radeon_ttm.c (working copy) @@ -560,12 +560,10 @@ static struct ttm_tt *radeon_ttm_tt_crea rdev = radeon_get_rdev(bdev); #if __OS_HAS_AGP -#ifdef DUMBBELL_WIP if (rdev->flags & RADEON_IS_AGP) { return ttm_agp_tt_create(bdev, rdev->ddev->agp->agpdev, size, page_flags, dummy_read_page); } -#endif /* DUMBBELL_WIP */ #endif gtt = malloc(sizeof(struct radeon_ttm_tt), @@ -610,11 +608,9 @@ static int radeon_ttm_tt_populate(struct rdev = radeon_get_rdev(ttm->bdev); #if __OS_HAS_AGP -#ifdef DUMBBELL_WIP if (rdev->flags & RADEON_IS_AGP) { return ttm_agp_tt_populate(ttm); } -#endif /* DUMBBELL_WIP */ #endif #ifdef CONFIG_SWIOTLB @@ -660,12 +656,10 @@ static void radeon_ttm_tt_unpopulate(str rdev = radeon_get_rdev(ttm->bdev); #if __OS_HAS_AGP -#ifdef DUMBBELL_WIP if (rdev->flags & RADEON_IS_AGP) { ttm_agp_tt_unpopulate(ttm); return; } -#endif /* DUMBBELL_WIP */ #endif #ifdef CONFIG_SWIOTLB Index: sys/dev/drm2/ttm/ttm_agp_backend.c =================================================================== --- sys/dev/drm2/ttm/ttm_agp_backend.c (revision 273255) +++ sys/dev/drm2/ttm/ttm_agp_backend.c (working copy) @@ -41,7 +41,8 @@ __FBSDID("$FreeBSD$"); struct ttm_agp_backend { struct ttm_tt ttm; - struct agp_memory *mem; + vm_offset_t offset; + vm_page_t *pages; device_t bridge; }; @@ -51,31 +52,23 @@ static int ttm_agp_bind(struct ttm_tt *t { struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm); struct drm_mm_node *node = bo_mem->mm_node; - struct agp_memory *mem; - int ret, cached = (bo_mem->placement & TTM_PL_FLAG_CACHED); + int ret; unsigned i; - mem = agp_alloc_memory(agp_be->bridge, AGP_USER_MEMORY, ttm->num_pages); - if (unlikely(mem == NULL)) - return -ENOMEM; - - mem->page_count = 0; for (i = 0; i < ttm->num_pages; i++) { vm_page_t page = ttm->pages[i]; if (!page) page = ttm->dummy_read_page; - mem->pages[mem->page_count++] = page; + agp_be->pages[i] = page; } - agp_be->mem = mem; - - mem->is_flushed = 1; - mem->type = (cached) ? AGP_USER_CACHED_MEMORY : AGP_USER_MEMORY; - ret = agp_bind_memory(mem, node->start); + agp_be->offset = node->start * PAGE_SIZE; + ret = -agp_bind_pages(agp_be->bridge, agp_be->pages, + ttm->num_pages << PAGE_SHIFT, agp_be->offset); if (ret) - pr_err("AGP Bind memory failed\n"); + printf("[TTM] AGP Bind memory failed\n"); return ret; } @@ -84,22 +77,16 @@ static int ttm_agp_unbind(struct ttm_tt { struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm); - if (agp_be->mem) { - if (agp_be->mem->is_bound) - return agp_unbind_memory(agp_be->mem); - agp_free_memory(agp_be->mem); - agp_be->mem = NULL; - } - return 0; + return -agp_unbind_pages(agp_be->bridge, ttm->num_pages << PAGE_SHIFT, + agp_be->offset); } static void ttm_agp_destroy(struct ttm_tt *ttm) { struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm); - if (agp_be->mem) - ttm_agp_unbind(ttm); ttm_tt_fini(ttm); + free(agp_be->pages, M_TTM_AGP); free(agp_be, M_TTM_AGP); } @@ -118,14 +105,18 @@ struct ttm_tt *ttm_agp_tt_create(struct agp_be = malloc(sizeof(*agp_be), M_TTM_AGP, M_WAITOK | M_ZERO); - agp_be->mem = NULL; agp_be->bridge = bridge; agp_be->ttm.func = &ttm_agp_func; if (ttm_tt_init(&agp_be->ttm, bdev, size, page_flags, dummy_read_page)) { + free(agp_be, M_TTM_AGP); return NULL; } + agp_be->offset = 0; + agp_be->pages = malloc(agp_be->ttm.num_pages * sizeof(*agp_be->pages), + M_TTM_AGP, M_WAITOK); + return &agp_be->ttm; } Index: sys/dev/drm2/ttm/ttm_bo_driver.h =================================================================== --- sys/dev/drm2/ttm/ttm_bo_driver.h (revision 273255) +++ sys/dev/drm2/ttm/ttm_bo_driver.h (working copy) @@ -990,9 +990,8 @@ extern vm_memattr_t ttm_io_prot(uint32_t extern const struct ttm_mem_type_manager_func ttm_bo_manager_func; -#if (defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE))) +#if __OS_HAS_AGP #define TTM_HAS_AGP -#include <linux/agp_backend.h> /** * ttm_agp_tt_create @@ -1009,7 +1008,7 @@ extern const struct ttm_mem_type_manager * bind and unbind memory backing a ttm_tt. */ extern struct ttm_tt *ttm_agp_tt_create(struct ttm_bo_device *bdev, - struct agp_bridge_data *bridge, + device_t bridge, unsigned long size, uint32_t page_flags, struct vm_page *dummy_read_page); int ttm_agp_tt_populate(struct ttm_tt *ttm); Index: sys/dev/drm2/ttm/ttm_page_alloc.c =================================================================== --- sys/dev/drm2/ttm/ttm_page_alloc.c (revision 273255) +++ sys/dev/drm2/ttm/ttm_page_alloc.c (working copy) @@ -45,10 +45,6 @@ __FBSDID("$FreeBSD$"); #include <dev/drm2/ttm/ttm_bo_driver.h> #include <dev/drm2/ttm/ttm_page_alloc.h> -#ifdef TTM_HAS_AGP -#include <asm/agp.h> -#endif - #define NUM_PAGES_TO_ALLOC (PAGE_SIZE/sizeof(vm_page_t)) #define SMALL_ALLOCATION 16 #define FREE_ALL_PAGES (~0U) @@ -220,46 +216,34 @@ static struct ttm_pool_manager *_manager static int set_pages_array_wb(vm_page_t *pages, int addrinarray) { - vm_page_t m; +#ifdef TTM_HAS_AGP int i; - for (i = 0; i < addrinarray; i++) { - m = pages[i]; -#ifdef TTM_HAS_AGP - unmap_page_from_agp(m); + for (i = 0; i < addrinarray; i++) + pmap_page_set_memattr(pages[i], VM_MEMATTR_WRITE_BACK); #endif - pmap_page_set_memattr(m, VM_MEMATTR_WRITE_BACK); - } return 0; } static int set_pages_array_wc(vm_page_t *pages, int addrinarray) { - vm_page_t m; +#ifdef TTM_HAS_AGP int i; - for (i = 0; i < addrinarray; i++) { - m = pages[i]; -#ifdef TTM_HAS_AGP - map_page_into_agp(pages[i]); + for (i = 0; i < addrinarray; i++) + pmap_page_set_memattr(pages[i], VM_MEMATTR_WRITE_COMBINING); #endif - pmap_page_set_memattr(m, VM_MEMATTR_WRITE_COMBINING); - } return 0; } static int set_pages_array_uc(vm_page_t *pages, int addrinarray) { - vm_page_t m; +#ifdef TTM_HAS_AGP int i; - for (i = 0; i < addrinarray; i++) { - m = pages[i]; -#ifdef TTM_HAS_AGP - map_page_into_agp(pages[i]); + for (i = 0; i < addrinarray; i++) + pmap_page_set_memattr(pages[i], VM_MEMATTR_UNCACHEABLE); #endif - pmap_page_set_memattr(m, VM_MEMATTR_UNCACHEABLE); - } return 0; } --MP_//lb9S+oP6/lR06gq+sL9IPI--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20141027221058.23a188d0>