Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2c0a97b authored by Jesse Barnes's avatar Jesse Barnes Committed by Dave Airlie
Browse files

drm: GEM mmap support



Add core support for mapping of GEM objects.  Drivers should provide a
vm_operations_struct if they want to support page faulting of objects.
The code for handling GEM object offsets was taken from TTM, which was
written by Thomas Hellström.

Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: default avatarEric Anholt <eric@anholt.net>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent a9587470
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -261,6 +261,9 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
		}
		DRM_DEBUG("AGP offset = 0x%08lx, size = 0x%08lx\n", map->offset, map->size);

		break;
	case _DRM_GEM:
		DRM_ERROR("tried to rmmap GEM object\n");
		break;
	}
	case _DRM_SCATTER_GATHER:
@@ -429,6 +432,9 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
		dmah.size = map->size;
		__drm_pci_free(dev, &dmah);
		break;
	case _DRM_GEM:
		DRM_ERROR("tried to rmmap GEM object\n");
		break;
	}
	drm_free(map, sizeof(*map), DRM_MEM_MAPS);

+6 −0
Original line number Diff line number Diff line
@@ -209,6 +209,7 @@ int drm_lastclose(struct drm_device * dev)
	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
		drm_dma_takedown(dev);

	dev->dev_mapping = NULL;
	mutex_unlock(&dev->struct_mutex);

	DRM_DEBUG("lastclose completed\n");
@@ -273,6 +274,8 @@ EXPORT_SYMBOL(drm_init);
 */
static void drm_cleanup(struct drm_device * dev)
{
	struct drm_driver *driver = dev->driver;

	DRM_DEBUG("\n");

	if (!dev) {
@@ -304,6 +307,9 @@ static void drm_cleanup(struct drm_device * dev)
	drm_ht_remove(&dev->map_hash);
	drm_ctxbitmap_cleanup(dev);

	if (driver->driver_features & DRIVER_GEM)
		drm_gem_destroy(dev);

	drm_put_minor(&dev->primary);
	if (drm_put_dev(dev))
		DRM_ERROR("Cannot unload module\n");
+11 −1
Original line number Diff line number Diff line
@@ -133,11 +133,21 @@ int drm_open(struct inode *inode, struct file *filp)
		spin_lock(&dev->count_lock);
		if (!dev->open_count++) {
			spin_unlock(&dev->count_lock);
			return drm_setup(dev);
			retcode = drm_setup(dev);
			goto out;
		}
		spin_unlock(&dev->count_lock);
	}

out:
	mutex_lock(&dev->struct_mutex);
	if (dev->dev_mapping == NULL)
		dev->dev_mapping = inode->i_mapping;
	else if (dev->dev_mapping != inode->i_mapping)
		WARN(1, "dev->dev_mapping not inode mapping (%p expected %p)\n",
		     dev->dev_mapping, inode->i_mapping);
	mutex_unlock(&dev->struct_mutex);

	return retcode;
}
EXPORT_SYMBOL(drm_open);
+109 −0
Original line number Diff line number Diff line
@@ -64,6 +64,13 @@
 * up at a later date, and as our interface with shmfs for memory allocation.
 */

/*
 * We make up offsets for buffer objects so we can recognize them at
 * mmap time.
 */
#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)

/**
 * Initialize the GEM device fields
 */
@@ -71,6 +78,8 @@
int
drm_gem_init(struct drm_device *dev)
{
	struct drm_gem_mm *mm;

	spin_lock_init(&dev->object_name_lock);
	idr_init(&dev->object_name_idr);
	atomic_set(&dev->object_count, 0);
@@ -79,9 +88,41 @@ drm_gem_init(struct drm_device *dev)
	atomic_set(&dev->pin_memory, 0);
	atomic_set(&dev->gtt_count, 0);
	atomic_set(&dev->gtt_memory, 0);

	mm = drm_calloc(1, sizeof(struct drm_gem_mm), DRM_MEM_MM);
	if (!mm) {
		DRM_ERROR("out of memory\n");
		return -ENOMEM;
	}

	dev->mm_private = mm;

	if (drm_ht_create(&mm->offset_hash, 19)) {
		drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
		return -ENOMEM;
	}

	if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
			DRM_FILE_PAGE_OFFSET_SIZE)) {
		drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
		drm_ht_remove(&mm->offset_hash);
		return -ENOMEM;
	}

	return 0;
}

void
drm_gem_destroy(struct drm_device *dev)
{
	struct drm_gem_mm *mm = dev->mm_private;

	drm_mm_takedown(&mm->offset_manager);
	drm_ht_remove(&mm->offset_hash);
	drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
	dev->mm_private = NULL;
}

/**
 * Allocate a GEM object of the specified size with shmfs backing store
 */
@@ -419,3 +460,71 @@ drm_gem_object_handle_free(struct kref *kref)
}
EXPORT_SYMBOL(drm_gem_object_handle_free);

/**
 * drm_gem_mmap - memory map routine for GEM objects
 * @filp: DRM file pointer
 * @vma: VMA for the area to be mapped
 *
 * If a driver supports GEM object mapping, mmap calls on the DRM file
 * descriptor will end up here.
 *
 * If we find the object based on the offset passed in (vma->vm_pgoff will
 * contain the fake offset we created when the GTT map ioctl was called on
 * the object), we set up the driver fault handler so that any accesses
 * to the object can be trapped, to perform migration, GTT binding, surface
 * register allocation, or performance monitoring.
 */
int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
	struct drm_file *priv = filp->private_data;
	struct drm_device *dev = priv->minor->dev;
	struct drm_gem_mm *mm = dev->mm_private;
	struct drm_map *map = NULL;
	struct drm_gem_object *obj;
	struct drm_hash_item *hash;
	unsigned long prot;
	int ret = 0;

	mutex_lock(&dev->struct_mutex);

	if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
		mutex_unlock(&dev->struct_mutex);
		return drm_mmap(filp, vma);
	}

	map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
	if (!map ||
	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
		ret =  -EPERM;
		goto out_unlock;
	}

	/* Check for valid size. */
	if (map->size < vma->vm_end - vma->vm_start) {
		ret = -EINVAL;
		goto out_unlock;
	}

	obj = map->handle;
	if (!obj->dev->driver->gem_vm_ops) {
		ret = -EINVAL;
		goto out_unlock;
	}

	vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND;
	vma->vm_ops = obj->dev->driver->gem_vm_ops;
	vma->vm_private_data = map->handle;
	/* FIXME: use pgprot_writecombine when available */
	prot = pgprot_val(vma->vm_page_prot);
	prot |= _PAGE_CACHE_WC;
	vma->vm_page_prot = __pgprot(prot);

	vma->vm_file = filp;	/* Needed for drm_vm_open() */
	drm_vm_open_locked(vma);

out_unlock:
	mutex_unlock(&dev->struct_mutex);

	return ret;
}
EXPORT_SYMBOL(drm_gem_mmap);
+2 −0
Original line number Diff line number Diff line
@@ -127,6 +127,7 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
	}
	return 0;
}
EXPORT_SYMBOL(drm_ht_insert_item);

/*
 * Just insert an item and return any "bits" bit key that hasn't been
@@ -188,6 +189,7 @@ int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
	ht->fill--;
	return 0;
}
EXPORT_SYMBOL(drm_ht_remove_item);

void drm_ht_remove(struct drm_open_hash *ht)
{
Loading