Here's what I've been hacking on wrt GTT mapping. The kernel's mmap_region
function was almost exactly what we needed, but it doesn't give us a way to
avoid the backing store for the attached file, thus the new function. It
would obviously be much better to pull out the common functionality for both
functions into a helper and provide mmap_io_region and mmap_region as thin
wrappers around it, but even that's uglier than just using fops->mmap. The
latter would let us just check what kind of mapping we wanted to create and
do it; we'd already have the VMA and address space taken care of by the
parent do_mmap_pgoff function. I think that would mean messing with shmem.c
though, since it won't pass down an mmap call for us...
On the plus side, these patches seem to work and bring performance on
modesetting-gem back to reasonable levels.
--
Jesse Barnes, Intel Open Source Technology Center
diff --git a/mm/mmap.c b/mm/mmap.c
index 971d0ed..e94afde 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1244,6 +1244,171 @@ unacct_error:
return error;
}
+/**
+ * mmap_io_region - map an I/O region, creating a new VMA if necessary
+ * @file: file to account mapping against
+ * @addr: user address to map
+ * @len: size of mapping
+ * @flags: mmap flags
+ * @vm_flags: VM protection bits
+ * @pgoff: pfn of backing pages
+ * @accountable: account for these pages?
+ *
+ * Normally drivers can simply override ->mmap and use remap_pfn_range
+ * themselves, but if remapping needs to be done in other functions (say ioctl)
+ * some function has to provide VMA allocation & linking services, thus
+ * this function.
+ */
+unsigned long mmap_io_region(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long flags,
+ unsigned int vm_flags, unsigned long pgoff,
+ int accountable)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma, *prev;
+ int correct_wcount = 0;
+ int error;
+ struct rb_node **rb_link, *rb_parent;
+ unsigned long charged = 0;
+ struct inode *inode = file ? file->f_path.dentry->d_inode : NULL;
+
+ /* Clear old maps */
+ error = -ENOMEM;
+munmap_back:
+ vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);
+ if (vma && vma->vm_start < addr + len) {
+ if (do_munmap(mm, addr, len))
+ return -ENOMEM;
+ goto munmap_back;
+ }
+
+ /* Check against address space limit. */
+ if (!may_expand_vm(mm, len >> PAGE_SHIFT))
+ return -ENOMEM;
+
+ if (flags & MAP_NORESERVE)
+ vm_flags |= VM_NORESERVE;
+
+ if (accountable && (!(flags & MAP_NORESERVE) ||
+ sysctl_overcommit_memory == OVERCOMMIT_NEVER)) {
+ if (vm_flags & VM_SHARED) {
+ /* Check memory availability in shmem_file_setup? */
+ vm_flags |= VM_ACCOUNT;
+ } else if (vm_flags & VM_WRITE) {
+ /*
+ * Private writable mapping: check memory availability
+ */
+ charged = len >> PAGE_SHIFT;
+ if (security_vm_enough_memory(charged))
+ return -ENOMEM;
+ vm_flags |= VM_ACCOUNT;
+ }
+ }
+
+ printk(KERN_ERR "%s: using vma %p\n", __FUNCTION__, vma);
+
+ /*
+ * Determine the object being mapped and call the appropriate
+ * specific mapper. the address has already been validated, but
+ * not unmapped, but the maps are removed from the list.
+ */
+ vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
+ if (!vma) {
+ error = -ENOMEM;
+ goto unacct_error;
+ }
+
+ vma->vm_mm = mm;
+ vma->vm_start = addr;
+ vma->vm_end = addr + len;
+ vma->vm_flags = vm_flags;
+ vma->vm_page_prot = vm_get_page_prot(vm_flags);
+ vma->vm_pgoff = pgoff;
+
+ error = -EINVAL;
+ if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
+ goto free_vma;
+ if (vm_flags & VM_DENYWRITE) {
+ error = deny_write_access(file);
+ if (error)
+ goto free_vma;
+ correct_wcount = 1;
+ }
+
+ get_file(file);
+ printk(KERN_ERR "remap_pfn_range(%p, 0x%lx, 0x%lx, 0x%lx, 0x%lx)\n",
+ vma, vma->vm_start, vma->vm_pgoff, len, vma->vm_page_prot);
+ error = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, len,
+ vma->vm_page_prot);
+ if (error)
+ goto unmap_and_free_vma;
+ if (vm_flags & VM_EXECUTABLE)
+ added_exe_file_vma(mm);
+
+ /* We set VM_ACCOUNT in a shared mapping's vm_flags, to inform
+ * shmem_zero_setup (perhaps called through /dev/zero's ->mmap)
+ * that memory reservation must be checked; but that reservation
+ * belongs to shared memory object, not to vma: so now clear it.
+ */
+ if ((vm_flags & (VM_SHARED|VM_ACCOUNT)) == (VM_SHARED|VM_ACCOUNT))
+ vma->vm_flags &= ~VM_ACCOUNT;
+
+ /* Can addr have changed??
+ *
+ * Answer: Yes, several device drivers can do it in their
+ * f_op->mmap method. -DaveM
+ */
+ addr = vma->vm_start;
+ pgoff = vma->vm_pgoff;
+ vm_flags = vma->vm_flags;
+
+ if (vma_wants_writenotify(vma))
+ vma->vm_page_prot = vm_get_page_prot(vm_flags & ~VM_SHARED);
+
+ if (file && vma_merge(mm, prev, addr, vma->vm_end,
+ vma->vm_flags, NULL, file, pgoff, vma_policy(vma))) {
+ mpol_put(vma_policy(vma));
+ kmem_cache_free(vm_area_cachep, vma);
+ fput(file);
+ if (vm_flags & VM_EXECUTABLE)
+ removed_exe_file_vma(mm);
+ } else {
+ vma_link(mm, vma, prev, rb_link, rb_parent);
+ file = vma->vm_file;
+ }
+
+ /* Once vma denies write, undo our temporary denial count */
+ if (correct_wcount)
+ atomic_inc(&inode->i_writecount);
+
+ mm->total_vm += len >> PAGE_SHIFT;
+ vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
+ if (vm_flags & VM_LOCKED) {
+ mm->locked_vm += len >> PAGE_SHIFT;
+ make_pages_present(addr, addr + len);
+ }
+ if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK))
+ make_pages_present(addr, addr + len);
+ return addr;
+
+unmap_and_free_vma:
+ if (correct_wcount)
+ atomic_inc(&inode->i_writecount);
+ vma->vm_file = NULL;
+ fput(file);
+
+ /* Undo any partial mapping done by a device driver. */
+ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
+ charged = 0;
+free_vma:
+ kmem_cache_free(vm_area_cachep, vma);
+unacct_error:
+ if (charged)
+ vm_unacct_memory(charged);
+ return error;
+}
+EXPORT_SYMBOL(mmap_io_region);
+
/* Get an address range which is currently unmapped.
* For shmat() with addr=0.
*
diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c
index 6966c84..782a221 100644
--- a/linux-core/i915_gem.c
+++ b/linux-core/i915_gem.c
@@ -31,6 +31,7 @@
#include "i915_drm.h"
#include "i915_drv.h"
#include <linux/swap.h>
+#include <linux/pci.h>
static int
i915_gem_object_set_domain_range(struct drm_gem_object *obj,
@@ -428,6 +429,8 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
i915_gem_clflush_object(obj);
drm_agp_chipset_flush(dev);
}
+
+ /* unmap here? */
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
return ret;
@@ -474,6 +477,68 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
return 0;
}
+extern unsigned long mmap_io_region(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long flags,
+ unsigned int vm_flags, unsigned long pgoff,
+ int accountable);
+
+/**
+ * Maps the contents of an object into the GTT range, returning the address
+ * it is mapped into.
+ *
+ * While the mapping holds a reference on the contents of the object, it doesn't
+ * imply a ref on the object itself.
+ */
+int
+i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_mmap_gtt *args = data;
+ struct drm_gem_object *obj;
+ struct drm_i915_gem_object *obj_priv;
+ loff_t offset;
+ unsigned long addr;
+
+ if (!(dev->driver->driver_features & DRIVER_GEM))
+ return -ENODEV;
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EBADF;
+
+ obj_priv = obj->driver_private;
+ offset = args->offset;
+
+ /* No flags yet */
+ if (args->flags)
+ return -EINVAL;
+
+ /* Create a new mapping to be backed by a GTT range */
+ addr = get_unmapped_area(obj->filp, 0, args->size, 0,
+ MAP_SHARED);
+ DRM_ERROR("%s: found unmapped area at 0x%08lx\n", __FUNCTION__, addr);
+
+ //vma->vm_pgoff = (dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT;
+ /* Assuming the object is pinned, map it in the GTT range w/WC */
+// ret = pci_mmap_page_range(dev->pdev, vma, pci_mmap_mem, 1);
+ addr = mmap_io_region(obj->filp, addr, args->size, MAP_SHARED,
+ PROT_READ | PROT_WRITE | _PAGE_CACHE_WC,
+ (dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT,
+ 1);
+
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ if (IS_ERR((void *)addr)) {
+ DRM_ERROR("mmap_io_region failed: %ld\n", addr);
+ return addr;
+ }
+
+ args->addr_ptr = (uint64_t) addr;
+
+ return 0;
+}
+
static void
i915_gem_object_free_page_list(struct drm_gem_object *obj)
{
@@ -575,8 +640,6 @@ i915_add_request(struct drm_device *dev, uint32_t flush_domains)
OUT_RING(MI_USER_INTERRUPT);
ADVANCE_LP_RING();
- DRM_DEBUG("%d\n", seqno);
-
request->seqno = seqno;
request->emitted_jiffies = jiffies;
request->flush_domains = flush_domains;
@@ -1751,6 +1814,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv)
seqno = i915_file_priv->mm.last_gem_throttle_seqno;
i915_file_priv->mm.last_gem_throttle_seqno =
i915_file_priv->mm.last_gem_seqno;
+
if (seqno)
ret = i915_wait_request(dev, seqno);
mutex_unlock(&dev->struct_mutex);
@@ -2298,7 +2362,8 @@ i915_gem_init_hws(struct drm_device *dev)
int ret;
/* If we need a physical address for the status page, it's already
- * initialized at driver load time.
+ * initialized at driver load time, unless kernel modesetting is
+ * active.
*/
if (!I915_NEED_GFX_HWS(dev))
return 0;
diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c
index ddc9cd5..de5a600 100644
--- a/shared-core/i915_dma.c
+++ b/shared-core/i915_dma.c
@@ -1035,6 +1035,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF(DRM_I915_GEM_PREAD, i915_gem_pread_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, 0),
+ DRM_IOCTL_DEF(DRM_I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0),
diff --git a/shared-core/i915_drm.h b/shared-core/i915_drm.h
index ab13cd4..b97e4fd 100644
--- a/shared-core/i915_drm.h
+++ b/shared-core/i915_drm.h
@@ -192,6 +192,7 @@ typedef struct drm_i915_sarea {
#define DRM_I915_GEM_SW_FINISH 0x20
#define DRM_I915_GEM_SET_TILING 0x21
#define DRM_I915_GEM_GET_TILING 0x22
+#define DRM_I915_GEM_MMAP_GTT 0x23
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -223,6 +224,7 @@ typedef struct drm_i915_sarea {
#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread)
#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite)
#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap)
+#define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt)
#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain)
#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish)
#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
@@ -449,6 +451,23 @@ struct drm_i915_gem_mmap {
uint64_t addr_ptr; /* void *, but pointers are not 32/64 compatible */
};
+struct drm_i915_gem_mmap_gtt {
+ /** Handle for the object being mapped. */
+ uint32_t handle;
+ uint32_t pad;
+ /** Offset in the object to map. */
+ uint64_t offset;
+ /**
+ * Length of data to map.
+ *
+ * The value will be page-aligned.
+ */
+ uint64_t size;
+ /** Returned pointer the data was mapped at */
+ uint64_t addr_ptr; /* void *, but pointers are not 32/64 compatible */
+ uint32_t flags;
+};
+
struct drm_i915_gem_set_domain {
/** Handle for the object */
uint32_t handle;
diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h
index 04062c4..010c617 100644
--- a/shared-core/i915_drv.h
+++ b/shared-core/i915_drv.h
@@ -561,6 +561,8 @@ int i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+int i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
diff --git a/libdrm/intel/intel_bufmgr.h b/libdrm/intel/intel_bufmgr.h
index 4d33521..59def00 100644
--- a/libdrm/intel/intel_bufmgr.h
+++ b/libdrm/intel/intel_bufmgr.h
@@ -126,5 +126,7 @@ int intel_bo_set_tiling(dri_bo *buf, uint32_t *tiling_mode);
int intel_bo_flink(dri_bo *buf, uint32_t *name);
+int dri_gem_bo_map_gtt(dri_bo *bo);
+
#endif /* INTEL_BUFMGR_GEM_H */
diff --git a/libdrm/intel/intel_bufmgr_gem.c b/libdrm/intel/intel_bufmgr_gem.c
index af20efb..ba49b24 100644
--- a/libdrm/intel/intel_bufmgr_gem.c
+++ b/libdrm/intel/intel_bufmgr_gem.c
@@ -35,6 +35,7 @@
*/
#include <xf86drm.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -42,6 +43,8 @@
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include "errno.h"
#include "dri_bufmgr.h"
@@ -370,6 +373,7 @@ intel_bo_gem_create_from_name(dri_bufmgr *bufmgr, const char *name,
bo_gem->refcount = 1;
bo_gem->validate_index = -1;
bo_gem->gem_handle = open_arg.handle;
+ bo_gem->bo.handle = bo_gem->gem_handle;
DBG("bo_create_from_handle: %d (%s)\n", handle, bo_gem->name);
@@ -517,6 +521,90 @@ dri_gem_bo_map(dri_bo *bo, int write_enable)
return 0;
}
+int
+dri_gem_bo_map_gtt(dri_bo *bo)
+{
+ dri_bufmgr_gem *bufmgr_gem;
+ dri_bo_gem *bo_gem = (dri_bo_gem *)bo;
+ struct drm_i915_gem_set_domain set_domain;
+ int ret;
+ int fd;
+
+ bufmgr_gem = (dri_bufmgr_gem *)bo->bufmgr;
+
+ /* Allow recursive mapping. Mesa may recursively map buffers with
+ * nested display loops.
+ */
+ if (!bo_gem->mapped) {
+
+ assert(bo->virtual == NULL);
+
+ DBG("bo_map_gtt: %d (%s)\n", bo_gem->gem_handle, bo_gem->name);
+
+ if (bo_gem->virtual == NULL) {
+ struct drm_i915_gem_mmap_gtt mmap_arg;
+
+ memset(&mmap_arg, 0, sizeof(mmap_arg));
+ mmap_arg.handle = bo_gem->gem_handle;
+ mmap_arg.offset = 0;
+ mmap_arg.size = bo->size;
+ ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_MMAP_GTT,
+ &mmap_arg);
+ if (ret != 0) {
+ fprintf(stderr,
+ "%s:%d: Error mapping buffer %d (%s): %s .\n",
+ __FILE__, __LINE__,
+ bo_gem->gem_handle, bo_gem->name,
+ strerror(errno));
+ return ret;
+ }
+ bo_gem->virtual = (void *)(uintptr_t)mmap_arg.addr_ptr;
+ }
+#if 0
+ if (bo_gem->virtual == NULL) {
+ fd = open("/sys/devices/pci0000:00/0000:00:02.0/resource2_wc",
+ O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr, "failed to open GTT: %s\n",
+ strerror(errno));
+ return errno;
+ }
+
+ bo_gem->virtual = mmap(NULL, bo->size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, bo->offset);
+ if (bo_gem->virtual == MAP_FAILED) {
+ fprintf(stderr, "failed to map GTT: %s\n",
+ strerror(errno));
+ return errno;
+ }
+ close(fd);
+ }
+#endif
+ bo->virtual = bo_gem->virtual;
+ bo_gem->swrast = 0;
+ bo_gem->mapped = 1;
+ DBG("bo_map: %d (%s) -> %p\n", bo_gem->gem_handle, bo_gem->name,
+ bo_gem->virtual);
+ }
+
+ if (!bo_gem->swrast) {
+ set_domain.handle = bo_gem->gem_handle;
+ set_domain.read_domains = I915_GEM_DOMAIN_GTT;
+ set_domain.write_domain = I915_GEM_DOMAIN_GTT;
+ do {
+ ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN,
+ &set_domain);
+ } while (ret == -1 && errno == EINTR);
+ if (ret != 0) {
+ fprintf (stderr, "%s:%d: Error setting swrast %d: %s\n",
+ __FILE__, __LINE__, bo_gem->gem_handle, strerror (errno));
+ }
+ bo_gem->swrast = 1;
+ }
+
+ return 0;
+}
+
static int
dri_gem_bo_unmap(dri_bo *bo)
{
@@ -593,7 +681,7 @@ dri_gem_bo_get_subdata (dri_bo *bo, unsigned long offset,
return 0;
}
-static void
+void
dri_gem_bo_wait_rendering(dri_bo *bo)
{
dri_bufmgr_gem *bufmgr_gem = (dri_bufmgr_gem *)bo->bufmgr;
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel