@@ -425,7 +425,8 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
void
drm_gem_object_release(struct drm_gem_object *obj)
{
- fput(obj->filp);
+ if (obj->filp)
+ fput(obj->filp);
}
EXPORT_SYMBOL(drm_gem_object_release);
@@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
i915_gem_gtt.o \
i915_gem_io.o \
i915_gem_tiling.o \
+ i915_gem_vmap.o \
i915_trace_points.o \
intel_display.o \
intel_crt.o \
@@ -783,7 +783,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
value = INTEL_INFO(dev)->gen >= 4;
break;
case I915_PARAM_HAS_2D_IO:
- /* depends on GEM */
+ case I915_PARAM_HAS_VMAP:
value = dev_priv->has_gem;
break;
default:
@@ -2256,6 +2256,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF_DRV(I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(I915_GEM_PREAD_2D, i915_gem_pread_2d_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(I915_GEM_PWRITE_2D, i915_gem_pwrite_2d_ioctl, DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(I915_GEM_VMAP, i915_gem_vmap_ioctl, DRM_UNLOCKED),
};
int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
@@ -695,6 +695,11 @@ typedef struct drm_i915_private {
struct intel_fbdev *fbdev;
} drm_i915_private_t;
+struct drm_i915_gem_object_ops {
+ int (*get_pages)(struct drm_i915_gem_object *, gfp_t, u32 *offset);
+ void (*put_pages)(struct drm_i915_gem_object *);
+};
+
struct drm_i915_gem_object {
struct drm_gem_object base;
@@ -782,6 +787,7 @@ struct drm_i915_gem_object {
unsigned int fenced_gpu_access:1;
struct page **pages;
+ int num_pages;
/**
* DMAR support
@@ -1097,6 +1103,7 @@ void i915_gem_flush_ring(struct drm_device *dev,
uint32_t flush_domains);
struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
size_t size);
+void i915_gem_object_init(struct drm_i915_gem_object *obj);
void i915_gem_free_object(struct drm_gem_object *obj);
int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
uint32_t alignment,
@@ -1113,6 +1120,11 @@ void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
struct intel_ring_buffer *ring,
u32 seqno);
+/* i915_gem_vmap.c */
+int
+i915_gem_vmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file);
+
/**
* Returns true if seq1 is later than seq2.
*/
@@ -513,6 +513,12 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
goto unlock;
}
+ if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+ /* XXX worth handling? */
+ ret = -EINVAL;
+ goto out;
+ }
+
/* Bounds check source. */
if (args->offset > obj->base.size ||
args->size > obj->base.size - args->offset) {
@@ -954,6 +960,12 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
goto unlock;
}
+ if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+ /* XXX worth handling? */
+ ret = -EINVAL;
+ goto out;
+ }
+
/* Bounds check destination. */
if (args->offset > obj->base.size ||
args->size > obj->base.size - args->offset) {
@@ -1125,6 +1137,11 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
if (obj == NULL)
return -ENOENT;
+ if (to_intel_bo(obj)->agp_type == AGP_USER_CACHED_MEMORY) {
+ drm_gem_object_unreference_unlocked(obj);
+ return -EINVAL;
+ }
+
if (obj->size > dev_priv->mm.gtt_mappable_end) {
drm_gem_object_unreference_unlocked(obj);
return -E2BIG;
@@ -1484,25 +1501,26 @@ unlock:
static int
i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
- gfp_t gfpmask)
+ gfp_t gfpmask,
+ u32 *offset)
{
- int page_count, i;
struct address_space *mapping;
struct inode *inode;
struct page *page;
+ int i;
/* Get the list of pages out of our struct file. They'll be pinned
* at this point until we release them.
*/
- page_count = obj->base.size / PAGE_SIZE;
+ obj->num_pages = obj->base.size / PAGE_SIZE;
BUG_ON(obj->pages != NULL);
- obj->pages = drm_malloc_ab(page_count, sizeof(struct page *));
+ obj->pages = drm_malloc_ab(obj->num_pages, sizeof(struct page *));
if (obj->pages == NULL)
return -ENOMEM;
inode = obj->base.filp->f_path.dentry->d_inode;
mapping = inode->i_mapping;
- for (i = 0; i < page_count; i++) {
+ for (i = 0; i < obj->num_pages; i++) {
page = read_cache_page_gfp(mapping, i,
GFP_HIGHUSER |
__GFP_COLD |
@@ -1517,6 +1535,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
if (obj->tiling_mode != I915_TILING_NONE)
i915_gem_object_do_bit_17_swizzle(obj);
+ *offset = 0;
return 0;
err_pages:
@@ -1531,7 +1550,6 @@ err_pages:
static void
i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
{
- int page_count = obj->base.size / PAGE_SIZE;
int i;
BUG_ON(obj->madv == __I915_MADV_PURGED);
@@ -1542,7 +1560,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
if (obj->madv == I915_MADV_DONTNEED)
obj->dirty = 0;
- for (i = 0; i < page_count; i++) {
+ for (i = 0; i < obj->num_pages; i++) {
if (obj->dirty)
set_page_dirty(obj->pages[i]);
@@ -1643,6 +1661,9 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj)
{
struct inode *inode;
+ if (obj->base.filp == NULL)
+ return;
+
/* Our goal here is to return as much of the memory as
* is possible back to the system as we are called from OOM.
* To do this we must instruct the shmfs to drop all of its
@@ -2090,6 +2111,7 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
int
i915_gem_object_unbind(struct drm_i915_gem_object *obj)
{
+ const struct drm_i915_gem_object_ops *ops = obj->base.driver_private;
int ret = 0;
if (obj->gtt_space == NULL)
@@ -2127,7 +2149,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
return ret;
i915_gem_gtt_unbind_object(obj);
- i915_gem_object_put_pages_gtt(obj);
+ ops->put_pages(obj);
list_del_init(&obj->gtt_list);
list_del_init(&obj->mm_list);
@@ -2667,11 +2689,13 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
unsigned alignment,
bool map_and_fenceable)
{
+ const struct drm_i915_gem_object_ops *ops = obj->base.driver_private;
struct drm_device *dev = obj->base.dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_mm_node *free_space;
gfp_t gfpmask = __GFP_NORETRY | __GFP_NOWARN;
u32 size, fence_size, fence_alignment, unfenced_alignment;
+ u32 offset;
bool mappable, fenceable;
int ret;
@@ -2737,7 +2761,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
goto search_free;
}
- ret = i915_gem_object_get_pages_gtt(obj, gfpmask);
+ ret = ops->get_pages(obj, gfpmask, &offset);
if (ret) {
drm_mm_put_block(obj->gtt_space);
obj->gtt_space = NULL;
@@ -2765,7 +2789,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
ret = i915_gem_gtt_bind_object(obj);
if (ret) {
- i915_gem_object_put_pages_gtt(obj);
+ ops->put_pages(obj);
drm_mm_put_block(obj->gtt_space);
obj->gtt_space = NULL;
@@ -2787,11 +2811,11 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
- obj->gtt_offset = obj->gtt_space->start;
+ obj->gtt_offset = obj->gtt_space->start + offset;
fenceable =
obj->gtt_space->size == fence_size &&
- (obj->gtt_space->start & (fence_alignment -1)) == 0;
+ (obj->gtt_offset & (fence_alignment -1)) == 0;
mappable =
obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end;
@@ -2809,7 +2833,7 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj)
* to GPU, and we can ignore the cache flush because it'll happen
* again at bind time.
*/
- if (obj->pages == NULL)
+ if (obj->pages == NULL || obj->agp_type == AGP_USER_CACHED_MEMORY)
return;
trace_i915_gem_object_clflush(obj);
@@ -3464,6 +3488,31 @@ unlock:
return ret;
}
+void
+i915_gem_object_init(struct drm_i915_gem_object *obj)
+{
+ obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+ obj->base.read_domains = I915_GEM_DOMAIN_CPU;
+
+ obj->agp_type = AGP_USER_MEMORY;
+
+ obj->fence_reg = I915_FENCE_REG_NONE;
+ INIT_LIST_HEAD(&obj->mm_list);
+ INIT_LIST_HEAD(&obj->gtt_list);
+ INIT_LIST_HEAD(&obj->ring_list);
+ INIT_LIST_HEAD(&obj->exec_list);
+ INIT_LIST_HEAD(&obj->gpu_write_list);
+ obj->madv = I915_MADV_WILLNEED;
+
+ /* Avoid an unnecessary call to unbind on the first bind. */
+ obj->map_and_fenceable = true;
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_object_ops = {
+ .get_pages = i915_gem_object_get_pages_gtt,
+ .put_pages = i915_gem_object_put_pages_gtt,
+};
+
struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
size_t size)
{
@@ -3479,22 +3528,10 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
return NULL;
}
- i915_gem_info_add_obj(dev_priv, size);
+ obj->base.driver_private = (void *)&i915_gem_object_ops;
- obj->base.write_domain = I915_GEM_DOMAIN_CPU;
- obj->base.read_domains = I915_GEM_DOMAIN_CPU;
-
- obj->agp_type = AGP_USER_MEMORY;
- obj->base.driver_private = NULL;
- obj->fence_reg = I915_FENCE_REG_NONE;
- INIT_LIST_HEAD(&obj->mm_list);
- INIT_LIST_HEAD(&obj->gtt_list);
- INIT_LIST_HEAD(&obj->ring_list);
- INIT_LIST_HEAD(&obj->exec_list);
- INIT_LIST_HEAD(&obj->gpu_write_list);
- obj->madv = I915_MADV_WILLNEED;
- /* Avoid an unnecessary call to unbind on the first bind. */
- obj->map_and_fenceable = true;
+ i915_gem_object_init(obj);
+ i915_gem_info_add_obj(dev_priv, size);
return obj;
}
@@ -64,7 +64,7 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj)
if (dev_priv->mm.gtt->needs_dmar) {
ret = intel_gtt_map_memory(obj->pages,
- obj->base.size >> PAGE_SHIFT,
+ obj->num_pages,
&obj->sg_list,
&obj->num_sg);
if (ret != 0)
@@ -76,7 +76,7 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj)
obj->agp_type);
} else
intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
- obj->base.size >> PAGE_SHIFT,
+ obj->num_pages,
obj->pages,
obj->agp_type);
@@ -549,6 +549,11 @@ i915_gem_pwrite_2d_ioctl(struct drm_device *dev,
goto unlock;
}
+ if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+ ret = -EINVAL;
+ goto unref;
+ }
+
/* Bounds check destination. */
offset = args->dst_x * args->cpp + args->dst_y * args->dst_stride;
size = args->dst_stride * (args->height-1) + args->width * args->cpp;
@@ -961,6 +966,11 @@ i915_gem_pread_2d_ioctl(struct drm_device *dev,
goto unlock;
}
+ if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+ ret = -EINVAL;
+ goto unref;
+ }
+
/* Bounds check source. */
offset = args->src_x * args->cpp + args->src_y * args->src_stride;
size = args->src_stride * (args->height-1) + args->width * args->cpp;
@@ -294,6 +294,11 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
if (obj == NULL)
return -ENOENT;
+ if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+ drm_gem_object_unreference_unlocked(&obj->base);
+ return -EINVAL;
+ }
+
if (!i915_tiling_ok(dev,
args->stride, obj->base.size, args->tiling_mode)) {
drm_gem_object_unreference_unlocked(&obj->base);
new file mode 100644
@@ -0,0 +1,145 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "i915_trace.h"
+#include "intel_drv.h"
+#include <linux/swap.h>
+
+struct i915_gem_vmap_object {
+ struct drm_i915_gem_object gem;
+ uintptr_t user_ptr;
+ int read_only;
+};
+
+static struct i915_gem_vmap_object *to_vmap_object(struct drm_i915_gem_object *obj)
+{
+ return container_of(obj, struct i915_gem_vmap_object, gem);
+}
+
+static int
+i915_gem_vmap_get_pages(struct drm_i915_gem_object *obj, gfp_t gfp, u32 *offset)
+{
+ struct i915_gem_vmap_object *vmap = to_vmap_object(obj);
+ loff_t first_data_page, last_data_page;
+ int pinned_pages, i;
+
+ if (!access_ok(vmap->read_only ? VERIFY_READ : VERIFY_WRITE,
+ (char __user *)vmap->user_ptr,
+ vmap->gem.base.size))
+ return -EFAULT;
+
+ first_data_page = vmap->user_ptr / PAGE_SIZE;
+ last_data_page = (vmap->user_ptr + vmap->gem.base.size - 1) / PAGE_SIZE;
+ vmap->gem.num_pages = last_data_page - first_data_page + 1;
+
+ vmap->gem.pages = drm_malloc_ab(vmap->gem.num_pages,
+ sizeof(struct page *));
+ if (vmap->gem.pages == NULL)
+ return -ENOMEM;
+
+ pinned_pages = get_user_pages_fast(vmap->user_ptr,
+ vmap->gem.num_pages,
+ !vmap->read_only,
+ vmap->gem.pages);
+ if (pinned_pages < vmap->gem.num_pages) {
+ for (i = 0; i < pinned_pages; i++)
+ page_cache_release(vmap->gem.pages[i]);
+ drm_free_large(vmap->gem.pages);
+ return -EFAULT;
+ }
+
+ *offset = vmap->user_ptr & ~PAGE_MASK;
+ return 0;
+}
+
+static void
+i915_gem_vmap_put_pages(struct drm_i915_gem_object *obj)
+{
+ int i;
+
+ for (i = 0; i < obj->num_pages; i++) {
+ if (obj->dirty)
+ set_page_dirty(obj->pages[i]);
+
+ mark_page_accessed(obj->pages[i]);
+ page_cache_release(obj->pages[i]);
+ }
+
+ obj->dirty = 0;
+ drm_free_large(obj->pages);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_vmap_ops = {
+ .get_pages = i915_gem_vmap_get_pages,
+ .put_pages = i915_gem_vmap_put_pages,
+};
+
+/**
+ * Creates a new mm object that wraps some user memory.
+ */
+int
+i915_gem_vmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_i915_gem_vmap *args = data;
+ struct i915_gem_vmap_object *obj;
+ int ret;
+ u32 handle;
+
+ /* Allocate the new object */
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (obj == NULL)
+ return -ENOMEM;
+
+ obj->gem.base.driver_private = (void *)&i915_gem_vmap_ops;
+
+ obj->gem.base.dev = dev;
+ obj->gem.base.size = args->user_size;
+
+ kref_init(&obj->gem.base.refcount);
+ atomic_set(&obj->gem.base.handle_count, 0);
+
+ i915_gem_object_init(&obj->gem);
+ obj->gem.agp_type = AGP_USER_CACHED_MEMORY;
+
+ obj->user_ptr = args->user_ptr;
+ obj->read_only = args->flags & I915_VMAP_READ_ONLY;
+
+ ret = drm_gem_handle_create(file, &obj->gem.base, &handle);
+ if (ret) {
+ drm_gem_object_release(&obj->gem.base);
+ kfree(obj);
+ return ret;
+ }
+
+ /* drop reference from allocate - handle holds it now */
+ drm_gem_object_unreference(&obj->gem.base);
+
+ args->handle = handle;
+ return 0;
+}
@@ -200,6 +200,7 @@ typedef struct _drm_i915_sarea {
#define DRM_I915_GEM_EXECBUFFER2 0x29
#define DRM_I915_GEM_PREAD_2D 0x2a
#define DRM_I915_GEM_PWRITE_2D 0x2b
+#define DRM_I915_GEM_VMAP 0x2c
#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)
@@ -243,6 +244,7 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
#define DRM_IOCTL_I915_GEM_PREAD_2D DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD_2D, struct drm_i915_gem_pread_2d)
#define DRM_IOCTL_I915_GEM_PWRITE_2D DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE_2D, struct drm_i915_gem_pwrite_2d)
+#define DRM_IOCTL_I915_GEM_VMAP DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_VMAP, struct drm_i915_gem_vmap)
/* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware.
@@ -295,6 +297,7 @@ typedef struct drm_i915_irq_wait {
#define I915_PARAM_HAS_COHERENT_RINGS 13
#define I915_PARAM_HAS_EXEC_CONSTANTS 14
#define I915_PARAM_HAS_2D_IO 15
+#define I915_PARAM_HAS_VMAP 16
typedef struct drm_i915_getparam {
int param;
@@ -392,6 +395,19 @@ struct drm_i915_gem_create {
__u32 pad;
};
+struct drm_i915_gem_vmap {
+ __u64 user_ptr;
+ __u32 user_size;
+ __u32 flags;
+#define I915_VMAP_READ_ONLY 0x1
+ /**
* Returned handle for the object.
+ *
+ * Object handles are nonzero.
+ */
+ __u32 handle;
+};
+
struct drm_i915_gem_pread {
/** Handle for the object being read. */
__u32 handle;