Commit 8e7d2b2c authored by Jesse Barnes's avatar Jesse Barnes Committed by Eric Anholt

drm/i915: allocate large pointer arrays with vmalloc

For awhile now, many of the GEM code paths have allocated page or
object arrays with the slab allocator.  This is nice and fast, but
won't work well if memory is fragmented, since the slab allocator works
with physically contiguous memory (i.e. order > 2 allocations are
likely to fail fairly early after booting and doing some work).

This patch works around the issue by falling back to vmalloc for
>PAGE_SIZE allocations.  This is ugly, but much less work than chaining
a bunch of pages together by hand (suprisingly there's not a bunch of
generic kernel helpers for this yet afaik).  vmalloc space is somewhat
precious on 32 bit kernels, but our allocations shouldn't be big enough
to cause problems, though they're routinely more than a page.

Note that this patch doesn't address the unchecked
alloc-based-on-ioctl-args in GEM; that needs to be fixed in a separate
patch.

Also, I've deliberately ignored the DRM's "area" junk.  I don't think
anyone actually uses it anymore and I'm hoping it gets ripped out soon.

[Updated: removed size arg to new free function.  We could unify the
free functions as well once the DRM mem tracking is ripped out.]

fd.o bug #20152 (part 1/3)
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: default avatarEric Anholt <eric@anholt.net>
parent 1406de8e
...@@ -349,7 +349,7 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj, ...@@ -349,7 +349,7 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE; last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1; num_pages = last_data_page - first_data_page + 1;
user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
if (user_pages == NULL) if (user_pages == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -429,7 +429,7 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj, ...@@ -429,7 +429,7 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj,
SetPageDirty(user_pages[i]); SetPageDirty(user_pages[i]);
page_cache_release(user_pages[i]); page_cache_release(user_pages[i]);
} }
kfree(user_pages); drm_free_large(user_pages);
return ret; return ret;
} }
...@@ -649,7 +649,7 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, ...@@ -649,7 +649,7 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE; last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1; num_pages = last_data_page - first_data_page + 1;
user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
if (user_pages == NULL) if (user_pages == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -719,7 +719,7 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, ...@@ -719,7 +719,7 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
out_unpin_pages: out_unpin_pages:
for (i = 0; i < pinned_pages; i++) for (i = 0; i < pinned_pages; i++)
page_cache_release(user_pages[i]); page_cache_release(user_pages[i]);
kfree(user_pages); drm_free_large(user_pages);
return ret; return ret;
} }
...@@ -824,7 +824,7 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, ...@@ -824,7 +824,7 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE; last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1; num_pages = last_data_page - first_data_page + 1;
user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
if (user_pages == NULL) if (user_pages == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -902,7 +902,7 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, ...@@ -902,7 +902,7 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
fail_put_user_pages: fail_put_user_pages:
for (i = 0; i < pinned_pages; i++) for (i = 0; i < pinned_pages; i++)
page_cache_release(user_pages[i]); page_cache_release(user_pages[i]);
kfree(user_pages); drm_free_large(user_pages);
return ret; return ret;
} }
...@@ -1408,9 +1408,7 @@ i915_gem_object_put_pages(struct drm_gem_object *obj) ...@@ -1408,9 +1408,7 @@ i915_gem_object_put_pages(struct drm_gem_object *obj)
} }
obj_priv->dirty = 0; obj_priv->dirty = 0;
drm_free(obj_priv->pages, drm_free_large(obj_priv->pages);
page_count * sizeof(struct page *),
DRM_MEM_DRIVER);
obj_priv->pages = NULL; obj_priv->pages = NULL;
} }
...@@ -2024,8 +2022,7 @@ i915_gem_object_get_pages(struct drm_gem_object *obj) ...@@ -2024,8 +2022,7 @@ i915_gem_object_get_pages(struct drm_gem_object *obj)
*/ */
page_count = obj->size / PAGE_SIZE; page_count = obj->size / PAGE_SIZE;
BUG_ON(obj_priv->pages != NULL); BUG_ON(obj_priv->pages != NULL);
obj_priv->pages = drm_calloc(page_count, sizeof(struct page *), obj_priv->pages = drm_calloc_large(page_count, sizeof(struct page *));
DRM_MEM_DRIVER);
if (obj_priv->pages == NULL) { if (obj_priv->pages == NULL) {
DRM_ERROR("Faled to allocate page list\n"); DRM_ERROR("Faled to allocate page list\n");
obj_priv->pages_refcount--; obj_priv->pages_refcount--;
...@@ -3111,7 +3108,7 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list, ...@@ -3111,7 +3108,7 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
reloc_count += exec_list[i].relocation_count; reloc_count += exec_list[i].relocation_count;
} }
*relocs = drm_calloc(reloc_count, sizeof(**relocs), DRM_MEM_DRIVER); *relocs = drm_calloc_large(reloc_count, sizeof(**relocs));
if (*relocs == NULL) if (*relocs == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -3125,8 +3122,7 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list, ...@@ -3125,8 +3122,7 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
exec_list[i].relocation_count * exec_list[i].relocation_count *
sizeof(**relocs)); sizeof(**relocs));
if (ret != 0) { if (ret != 0) {
drm_free(*relocs, reloc_count * sizeof(**relocs), drm_free_large(*relocs);
DRM_MEM_DRIVER);
*relocs = NULL; *relocs = NULL;
return -EFAULT; return -EFAULT;
} }
...@@ -3165,7 +3161,7 @@ i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object *exec_list, ...@@ -3165,7 +3161,7 @@ i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object *exec_list,
} }
err: err:
drm_free(relocs, reloc_count * sizeof(*relocs), DRM_MEM_DRIVER); drm_free_large(relocs);
return ret; return ret;
} }
...@@ -3198,10 +3194,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, ...@@ -3198,10 +3194,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
return -EINVAL; return -EINVAL;
} }
/* Copy in the exec list from userland */ /* Copy in the exec list from userland */
exec_list = drm_calloc(sizeof(*exec_list), args->buffer_count, exec_list = drm_calloc_large(sizeof(*exec_list), args->buffer_count);
DRM_MEM_DRIVER); object_list = drm_calloc_large(sizeof(*object_list), args->buffer_count);
object_list = drm_calloc(sizeof(*object_list), args->buffer_count,
DRM_MEM_DRIVER);
if (exec_list == NULL || object_list == NULL) { if (exec_list == NULL || object_list == NULL) {
DRM_ERROR("Failed to allocate exec or object list " DRM_ERROR("Failed to allocate exec or object list "
"for %d buffers\n", "for %d buffers\n",
...@@ -3462,10 +3456,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, ...@@ -3462,10 +3456,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
} }
pre_mutex_err: pre_mutex_err:
drm_free(object_list, sizeof(*object_list) * args->buffer_count, drm_free_large(object_list);
DRM_MEM_DRIVER); drm_free_large(exec_list);
drm_free(exec_list, sizeof(*exec_list) * args->buffer_count,
DRM_MEM_DRIVER);
drm_free(cliprects, sizeof(*cliprects) * args->num_cliprects, drm_free(cliprects, sizeof(*cliprects) * args->num_cliprects,
DRM_MEM_DRIVER); DRM_MEM_DRIVER);
......
...@@ -1519,6 +1519,30 @@ static __inline__ void *drm_calloc(size_t nmemb, size_t size, int area) ...@@ -1519,6 +1519,30 @@ static __inline__ void *drm_calloc(size_t nmemb, size_t size, int area)
{ {
return kcalloc(nmemb, size, GFP_KERNEL); return kcalloc(nmemb, size, GFP_KERNEL);
} }
static __inline__ void *drm_calloc_large(size_t nmemb, size_t size)
{
u8 *addr;
if (size <= PAGE_SIZE)
return kcalloc(nmemb, size, GFP_KERNEL);
addr = vmalloc(nmemb * size);
if (!addr)
return NULL;
memset(addr, 0, nmemb * size);
return addr;
}
static __inline void drm_free_large(void *ptr)
{
if (!is_vmalloc_addr(ptr))
return kfree(ptr);
vfree(ptr);
}
#else #else
extern void *drm_alloc(size_t size, int area); extern void *drm_alloc(size_t size, int area);
extern void drm_free(void *pt, size_t size, int area); extern void drm_free(void *pt, size_t size, int area);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment