mutex_lock(&dev->struct_mutex);
if (!obj_priv->gtt_space) {
ret = i915_gem_object_bind_to_gtt(obj, 0);
- if (ret) {
- mutex_unlock(&dev->struct_mutex);
- return VM_FAULT_SIGBUS;
- }
+ if (ret)
+ goto unlock;
+
list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
ret = i915_gem_object_set_to_gtt_domain(obj, write);
- if (ret) {
- mutex_unlock(&dev->struct_mutex);
- return VM_FAULT_SIGBUS;
- }
+ if (ret)
+ goto unlock;
}
/* Need a new fence register? */
if (obj_priv->tiling_mode != I915_TILING_NONE) {
ret = i915_gem_object_get_fence_reg(obj);
- if (ret) {
- mutex_unlock(&dev->struct_mutex);
- return VM_FAULT_SIGBUS;
- }
+ if (ret)
+ goto unlock;
}
pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) +
/* Finally, remap it using the new GTT offset */
ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
-
+unlock:
mutex_unlock(&dev->struct_mutex);
switch (ret) {
+ case 0:
+ case -ERESTARTSYS:
+ return VM_FAULT_NOPAGE;
case -ENOMEM:
case -EAGAIN:
return VM_FAULT_OOM;
- case -EFAULT:
- case -EINVAL:
- return VM_FAULT_SIGBUS;
default:
- return VM_FAULT_NOPAGE;
+ return VM_FAULT_SIGBUS;
}
}
* i915_gem_release_mmap - remove physical page mappings
* @obj: obj in question
*
- * Preserve the reservation of the mmaping with the DRM core code, but
+ * Preserve the reservation of the mmapping with the DRM core code, but
* relinquish ownership of the pages back to the system.
*
* It is vital that we remove the page mapping if we have mapped a tiled
obj_priv = obj->driver_private;
+ if (obj_priv->madv != I915_MADV_WILLNEED) {
+ DRM_ERROR("Attempting to mmap a purgeable buffer\n");
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+
+
if (!obj_priv->mmap_offset) {
ret = i915_gem_create_mmap_offset(obj);
if (ret) {
int i;
BUG_ON(obj_priv->pages_refcount == 0);
+ BUG_ON(obj_priv->madv == __I915_MADV_PURGED);
if (--obj_priv->pages_refcount != 0)
return;
i915_gem_object_save_bit_17_swizzle(obj);
if (obj_priv->madv == I915_MADV_DONTNEED)
- obj_priv->dirty = 0;
+ obj_priv->dirty = 0;
for (i = 0; i < page_count; i++) {
if (obj_priv->pages[i] == NULL)
set_page_dirty(obj_priv->pages[i]);
if (obj_priv->madv == I915_MADV_WILLNEED)
- mark_page_accessed(obj_priv->pages[i]);
+ mark_page_accessed(obj_priv->pages[i]);
page_cache_release(obj_priv->pages[i]);
}
obj_priv->last_rendering_seqno = 0;
}
+/* Immediately discard the backing storage */
+static void
+i915_gem_object_truncate(struct drm_gem_object *obj)
+{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct inode *inode;
+
+ inode = obj->filp->f_path.dentry->d_inode;
+ if (inode->i_op->truncate)
+ inode->i_op->truncate (inode);
+
+ obj_priv->madv = __I915_MADV_PURGED;
+}
+
+static inline int
+i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj_priv)
+{
+ return obj_priv->madv == I915_MADV_DONTNEED;
+}
+
static void
i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
{
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t seqno;
- if (!dev_priv->hw_status_page)
+ if (!dev_priv->hw_status_page || list_empty(&dev_priv->mm.request_list))
return;
seqno = i915_get_gem_seqno(dev);
} else
break;
}
+
+ if (unlikely (dev_priv->trace_irq_seqno &&
+ i915_seqno_passed(dev_priv->trace_irq_seqno, seqno))) {
+ i915_user_irq_put(dev);
+ dev_priv->trace_irq_seqno = 0;
+ }
}
void
}
i915_gem_object_put_pages(obj);
+ BUG_ON(obj_priv->pages_refcount);
if (obj_priv->gtt_space) {
atomic_dec(&dev->gtt_count);
if (!list_empty(&obj_priv->list))
list_del_init(&obj_priv->list);
+ if (i915_gem_object_is_purgeable(obj_priv))
+ i915_gem_object_truncate(obj);
+
trace_i915_gem_object_unbind(obj);
return 0;
}
-static inline int
-i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj_priv)
-{
- return !obj_priv->dirty || obj_priv->madv == I915_MADV_DONTNEED;
-}
-
static struct drm_gem_object *
i915_gem_find_inactive_object(struct drm_device *dev, int min_size)
{
list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) {
struct drm_gem_object *obj = obj_priv->obj;
if (obj->size >= min_size) {
- if (i915_gem_object_is_purgeable(obj_priv) &&
+ if ((!obj_priv->dirty ||
+ i915_gem_object_is_purgeable(obj_priv)) &&
(!best || obj->size < best->size)) {
best = obj;
if (best->size == min_size)
int ret;
bool lists_empty;
- DRM_INFO("GTT full, evicting everything: "
- "%d objects [%d pinned], "
- "%d object bytes [%d pinned], "
- "%d/%d gtt bytes\n",
- atomic_read(&dev->object_count),
- atomic_read(&dev->pin_count),
- atomic_read(&dev->object_memory),
- atomic_read(&dev->pin_memory),
- atomic_read(&dev->gtt_memory),
- dev->gtt_total);
-
spin_lock(&dev_priv->mm.active_list_lock);
lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
list_empty(&dev_priv->mm.flushing_list) &&
list_empty(&dev_priv->mm.active_list));
spin_unlock(&dev_priv->mm.active_list_lock);
- if (lists_empty) {
- DRM_ERROR("GTT full, but lists empty!\n");
+ if (lists_empty)
return -ENOSPC;
- }
/* Flush everything (on to the inactive lists) and evict */
i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_gem_object *obj;
- int have_waited = 0;
int ret;
for (;;) {
return i915_gem_object_unbind(obj);
}
- if (have_waited)
- return 0;
-
/* If we didn't get anything, but the ring is still processing
* things, wait for the next to finish and hopefully leave us
* a buffer to evict.
if (ret)
return ret;
- have_waited = 1;
continue;
}
*/
if (!list_empty(&dev_priv->mm.flushing_list)) {
struct drm_i915_gem_object *obj_priv;
- uint32_t seqno;
- obj_priv = list_first_entry(&dev_priv->mm.flushing_list,
- struct drm_i915_gem_object,
- list);
- obj = obj_priv->obj;
+ /* Find an object that we can immediately reuse */
+ list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, list) {
+ obj = obj_priv->obj;
+ if (obj->size >= min_size)
+ break;
+
+ obj = NULL;
+ }
- i915_gem_flush(dev,
- obj->write_domain,
- obj->write_domain);
- seqno = i915_add_request(dev, NULL, obj->write_domain);
- if (seqno == 0)
- return -ENOMEM;
+ if (obj != NULL) {
+ uint32_t seqno;
- ret = i915_wait_request(dev, seqno);
- if (ret)
- return ret;
+ i915_gem_flush(dev,
+ obj->write_domain,
+ obj->write_domain);
+ seqno = i915_add_request(dev, NULL, obj->write_domain);
+ if (seqno == 0)
+ return -ENOMEM;
- have_waited = 1;
- continue;
+ ret = i915_wait_request(dev, seqno);
+ if (ret)
+ return ret;
+
+ continue;
+ }
}
/* If we didn't do any of the above, there's no single buffer
* large enough to swap out for the new one, so just evict
* everything and start again. (This should be rare.)
*/
- if (!list_empty (&dev_priv->mm.inactive_list)) {
- DRM_INFO("GTT full, evicting inactive buffers\n");
+ if (!list_empty (&dev_priv->mm.inactive_list))
return i915_gem_evict_from_inactive_list(dev);
- } else
+ else
return i915_gem_evict_everything(dev);
}
}
BUG_ON(obj_priv->pages != NULL);
obj_priv->pages = drm_calloc_large(page_count, sizeof(struct page *));
if (obj_priv->pages == NULL) {
- DRM_ERROR("Failed to allocate page list\n");
obj_priv->pages_refcount--;
return -ENOMEM;
}
if (dev_priv->mm.suspended)
return -EBUSY;
- if (obj_priv->madv == I915_MADV_DONTNEED) {
+ if (obj_priv->madv != I915_MADV_WILLNEED) {
DRM_ERROR("Attempting to bind a purgeable object\n");
return -EINVAL;
}
DRM_INFO("%s: GTT full, evicting something\n", __func__);
#endif
ret = i915_gem_evict_something(dev, obj->size);
- if (ret != 0) {
- if (ret != -ERESTARTSYS)
- DRM_ERROR("Failed to evict a buffer %d\n", ret);
+ if (ret)
return ret;
- }
+
goto search_free;
}
/* first try to clear up some space from the GTT */
ret = i915_gem_evict_something(dev, obj->size);
if (ret) {
- if (ret != -ERESTARTSYS)
- DRM_ERROR("Failed to allocate space for backing pages %d\n", ret);
-
/* now try to shrink everyone else */
if (! retry_alloc) {
retry_alloc = true;
obj_priv->gtt_space = NULL;
ret = i915_gem_evict_something(dev, obj->size);
- if (ret) {
- if (ret != -ERESTARTSYS)
- DRM_ERROR("Failed to allocate space to bind AGP: %d\n", ret);
+ if (ret)
return ret;
- }
goto search_free;
}
exec_start = (uint32_t) exec_offset + exec->batch_start_offset;
exec_len = (uint32_t) exec->batch_len;
- trace_i915_gem_request_submit(dev, dev_priv->mm.next_gem_seqno);
+ trace_i915_gem_request_submit(dev, dev_priv->mm.next_gem_seqno + 1);
count = nbox ? nbox : 1;
i915_verify_inactive(dev, __FILE__, __LINE__);
if (obj_priv->gtt_space == NULL) {
ret = i915_gem_object_bind_to_gtt(obj, alignment);
- if (ret != 0) {
- if (ret != -EBUSY && ret != -ERESTARTSYS)
- DRM_ERROR("Failure to bind: %d\n", ret);
+ if (ret)
return ret;
- }
}
/*
* Pre-965 chips need a fence register set up in order to
}
obj_priv = obj->driver_private;
- if (obj_priv->madv == I915_MADV_DONTNEED) {
- DRM_ERROR("Attempting to pin a I915_MADV_DONTNEED buffer\n");
+ if (obj_priv->madv != I915_MADV_WILLNEED) {
+ DRM_ERROR("Attempting to pin a purgeable buffer\n");
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
return -EINVAL;
}
- obj_priv->madv = args->madv;
- args->retained = obj_priv->gtt_space != NULL;
+ if (obj_priv->madv != __I915_MADV_PURGED)
+ obj_priv->madv = args->madv;
+
+ /* if the object is no longer bound, discard its backing storage */
+ if (i915_gem_object_is_purgeable(obj_priv) &&
+ obj_priv->gtt_space == NULL)
+ i915_gem_object_truncate(obj);
+
+ args->retained = obj_priv->madv != __I915_MADV_PURGED;
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- int ret;
-
if (drm_core_check_feature(dev, DRIVER_MODESET))
return 0;
- ret = i915_gem_idle(dev);
drm_irq_uninstall(dev);
-
- return ret;
+ return i915_gem_idle(dev);
}
void
mutex_unlock(&dev->struct_mutex);
}
-/* Immediately discard the backing storage */
-static void
-i915_gem_object_truncate(struct drm_gem_object *obj)
-{
- struct inode *inode;
-
- inode = obj->filp->f_path.dentry->d_inode;
-
- mutex_lock(&inode->i_mutex);
- truncate_inode_pages(inode->i_mapping, 0);
- mutex_unlock(&inode->i_mutex);
-}
-
static int
i915_gem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
&dev_priv->mm.inactive_list,
list) {
if (i915_gem_object_is_purgeable(obj_priv)) {
- struct drm_gem_object *obj = obj_priv->obj;
- i915_gem_object_unbind(obj);
- i915_gem_object_truncate(obj);
-
+ i915_gem_object_unbind(obj_priv->obj);
if (--nr_to_scan <= 0)
break;
}
spin_lock(&shrink_list_lock);
mutex_unlock(&dev->struct_mutex);
+ would_deadlock = 0;
+
if (nr_to_scan <= 0)
break;
}
&dev_priv->mm.inactive_list,
list) {
if (nr_to_scan > 0) {
- struct drm_gem_object *obj = obj_priv->obj;
- i915_gem_object_unbind(obj);
- if (i915_gem_object_is_purgeable(obj_priv))
- i915_gem_object_truncate(obj);
-
+ i915_gem_object_unbind(obj_priv->obj);
nr_to_scan--;
} else
cnt++;