Hi,
Following Keiths proposal on AGP memory manager design,
I have a first shot proposal of an interface that lets the memory
manager bind and unbind drm maps into the GTT, while at the same time
taking care of caching issues.
The key issues are
a) Is the drm able to keep track of _all_ vmas referencing a drm map,
even after a fork(), that is, will a vma_open() be called on a fork()?
b) Is it sufficient to first mark the kernel linear map PTEs uncacheable
and then mark the vma PTEs uncachable? Will this create a problem on
certain processors during the short time the mappings conflict? The
solution to this will probably be to define DRM_TTM_PARANOID in the code
below, but I guess that the try_unmap_page() function will generate some
significant overhead.
The interface proposal is in the attached file, comments are much
appreciated.
/Thomas
8< ------------------code snippet-------------------
if (!ttm->cache_adjusted && ttm->needs_cache_adjust) {
for (cur_page = ttm->pages; cur_page < last_page; ++cur_page) {
#ifdef DRM_TTM_PARANOID
if (try_to_unmap_page(page) != SWAP_SUCCESS) {
drm_unbind_ttm(ttm);
return -1;
}
#endif
if (PageHighMem(page) && page_addr(cur_page) != NULL) {
drm_unbind_ttm(ttm);
return -1;
}
change_page_attr(cur_page,1,PAGE_KERNEL_NOCACHE);
}
for (pt = ttm->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
/*
* Stolen from memprotect.c. Assumes we can keep track of
mapping vmas
* in a list for each ttm.
*/
drm_change_protection(pt->vma, pt->vma->vm_start,
pt->vma->vm_end,
pgprot_noncached(pt->vma->vm_prot));
pt->vma->vm_prot = pgprot_noncached(pt->vma->vm_prot);
}
global_flush_tlb();
}
Description of a drm_ttm API.
Concept - A drm_ttm_t is conceptually a drm map consisting of a physically
dicontinous page range mappable into userspace and which can be bound
to a hardware-dependant translation table. Typically that translation
table is the GART, but other similar devices could be considered.
(On-board PCIE translation tables ?). The limitation of the current
DRM agp map implementation is that the pages cannot be mapped into
userspace other than through the AGP aperture, and read access through
the AGP aperture is painfully slow.
In drm, a ttm is similar in concept to a normal drm shared memory map.
/*
* Registers a drm ttm referenced by handle. Memory is allocated and locked.
* The handle is used by drmMap to map the ttm into userspace, using nopage()
* on a page-fault basis. DRM internally keeps track of the vmas mapping the
ttm.
* Problem: What happens on fork? How can we register the copied vma?
* Called by the user through an IOCTL similar to drmAddMap(), or by the memory
manager.
*
* drm_ttm_t *drm_create_ttm(drm_device_t *dev, unsigned long size,
drm_handle_t *handle);
*/
/*
* Binds the ttm into the translation table at offset "offset". Used by the
memory manager.
*
* 1) Checks the backend (AGP driver) if we need to make memory uncacheable, if
so there are two
* solutions:
* a) We know that we keep track of all vmas (see "Problem" above regarding
copied vmas). We
* run a scheme similar to memprotect to mark all PTEs on all vmas
mapping this ttm as
* uncacheable. We also mark the vmas and the kernel mapping for each
page uncacheable.
* This is the preferred method.
* b) We don't keep track of vmas resulting from a fork. We need to unmap
each page in the ttm
* from all its mappings using try_unmap_page().
* We then mark all vmas calling nopage() uncacheable and
* set the correct caching policy when pages are remapped. The kernel
mapping for each page is
* marked uncaheble.
* 2) Calls backend bind function. (AGPBind)
*/
int drm_bind_ttm(drm_device_t *dev, drm_ttm_t *ttm, unsigned long offset);
/*
* Unbinds the ttm. Restores previous caching policy using either method a) or
b) above.
* If the ttm is already evicted, only the caching policy is restored.
*/
int drm_unbind_ttm(drm_device_t *dev, drm_ttm_t *ttm);
/*
* Unbinds the ttm. Keeps the backend caching policy. Used by the Aperture
memory manager.
*/
int drm_evict_ttm(drm_device_t *dev, drm_ttm_t *ttm);
/*
* Rebinds a previously evicted ttm. Used by the Aperture memory manager.
*/
int drm_rebind_ttm(drm_device_t *dev, drm_ttm_t *ttm, unsigned long offset);
/*
* Unbinds a ttm if it is bound, Then unregisters the ttm. The vmas continue to
live their life,
* but any page fault will create a segmentation violation.
*/
int drm_destroy_ttm(drm_device_t *dev, drm_ttm_t *ttm);