2019-05-28 17:29:46 +08:00
|
|
|
/*
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
*
|
|
|
|
* Copyright © 2014-2016 Intel Corporation
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/shmem_fs.h>
|
|
|
|
#include <linux/swap.h>
|
|
|
|
|
|
|
|
#include <drm/drm.h> /* for drm_legacy.h! */
|
|
|
|
#include <drm/drm_cache.h>
|
|
|
|
#include <drm/drm_legacy.h> /* for drm_pci.h! */
|
|
|
|
#include <drm/drm_pci.h>
|
|
|
|
|
2019-06-21 15:08:02 +08:00
|
|
|
#include "gt/intel_gt.h"
|
2019-05-28 17:29:46 +08:00
|
|
|
#include "i915_drv.h"
|
|
|
|
#include "i915_gem_object.h"
|
2019-10-18 17:07:50 +08:00
|
|
|
#include "i915_gem_region.h"
|
2019-05-28 17:29:50 +08:00
|
|
|
#include "i915_scatterlist.h"
|
2019-05-28 17:29:46 +08:00
|
|
|
|
|
|
|
static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
|
|
|
|
{
|
|
|
|
struct address_space *mapping = obj->base.filp->f_mapping;
|
|
|
|
struct drm_dma_handle *phys;
|
|
|
|
struct sg_table *st;
|
|
|
|
struct scatterlist *sg;
|
|
|
|
char *vaddr;
|
|
|
|
int i;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (WARN_ON(i915_gem_object_needs_bit17_swizzle(obj)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Always aligning to the object size, allows a single allocation
|
|
|
|
* to handle all possible callers, and given typical object sizes,
|
|
|
|
* the alignment of the buddy allocation will naturally match.
|
|
|
|
*/
|
|
|
|
phys = drm_pci_alloc(obj->base.dev,
|
|
|
|
roundup_pow_of_two(obj->base.size),
|
|
|
|
roundup_pow_of_two(obj->base.size));
|
|
|
|
if (!phys)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
vaddr = phys->vaddr;
|
|
|
|
for (i = 0; i < obj->base.size / PAGE_SIZE; i++) {
|
|
|
|
struct page *page;
|
|
|
|
char *src;
|
|
|
|
|
|
|
|
page = shmem_read_mapping_page(mapping, i);
|
|
|
|
if (IS_ERR(page)) {
|
|
|
|
err = PTR_ERR(page);
|
|
|
|
goto err_phys;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = kmap_atomic(page);
|
|
|
|
memcpy(vaddr, src, PAGE_SIZE);
|
|
|
|
drm_clflush_virt_range(vaddr, PAGE_SIZE);
|
|
|
|
kunmap_atomic(src);
|
|
|
|
|
|
|
|
put_page(page);
|
|
|
|
vaddr += PAGE_SIZE;
|
|
|
|
}
|
|
|
|
|
2019-06-21 15:08:02 +08:00
|
|
|
intel_gt_chipset_flush(&to_i915(obj->base.dev)->gt);
|
2019-05-28 17:29:46 +08:00
|
|
|
|
|
|
|
st = kmalloc(sizeof(*st), GFP_KERNEL);
|
|
|
|
if (!st) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto err_phys;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sg_alloc_table(st, 1, GFP_KERNEL)) {
|
|
|
|
kfree(st);
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto err_phys;
|
|
|
|
}
|
|
|
|
|
|
|
|
sg = st->sgl;
|
|
|
|
sg->offset = 0;
|
|
|
|
sg->length = obj->base.size;
|
|
|
|
|
|
|
|
sg_dma_address(sg) = phys->busaddr;
|
|
|
|
sg_dma_len(sg) = obj->base.size;
|
|
|
|
|
|
|
|
obj->phys_handle = phys;
|
|
|
|
|
|
|
|
__i915_gem_object_set_pages(obj, st, sg->length);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_phys:
|
|
|
|
drm_pci_free(obj->base.dev, phys);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
|
|
|
|
struct sg_table *pages)
|
|
|
|
{
|
|
|
|
__i915_gem_object_release_shmem(obj, pages, false);
|
|
|
|
|
|
|
|
if (obj->mm.dirty) {
|
|
|
|
struct address_space *mapping = obj->base.filp->f_mapping;
|
|
|
|
char *vaddr = obj->phys_handle->vaddr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < obj->base.size / PAGE_SIZE; i++) {
|
|
|
|
struct page *page;
|
|
|
|
char *dst;
|
|
|
|
|
|
|
|
page = shmem_read_mapping_page(mapping, i);
|
|
|
|
if (IS_ERR(page))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dst = kmap_atomic(page);
|
|
|
|
drm_clflush_virt_range(vaddr, PAGE_SIZE);
|
|
|
|
memcpy(dst, vaddr, PAGE_SIZE);
|
|
|
|
kunmap_atomic(dst);
|
|
|
|
|
|
|
|
set_page_dirty(page);
|
|
|
|
if (obj->mm.madv == I915_MADV_WILLNEED)
|
|
|
|
mark_page_accessed(page);
|
|
|
|
put_page(page);
|
|
|
|
vaddr += PAGE_SIZE;
|
|
|
|
}
|
|
|
|
obj->mm.dirty = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
sg_free_table(pages);
|
|
|
|
kfree(pages);
|
|
|
|
|
|
|
|
drm_pci_free(obj->base.dev, obj->phys_handle);
|
|
|
|
}
|
|
|
|
|
2019-08-09 19:07:52 +08:00
|
|
|
static void phys_release(struct drm_i915_gem_object *obj)
|
|
|
|
{
|
|
|
|
fput(obj->base.filp);
|
|
|
|
}
|
|
|
|
|
2019-05-28 17:29:46 +08:00
|
|
|
static const struct drm_i915_gem_object_ops i915_gem_phys_ops = {
|
|
|
|
.get_pages = i915_gem_object_get_pages_phys,
|
|
|
|
.put_pages = i915_gem_object_put_pages_phys,
|
2019-08-09 19:07:52 +08:00
|
|
|
|
|
|
|
.release = phys_release,
|
2019-05-28 17:29:46 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
|
|
|
|
{
|
|
|
|
struct sg_table *pages;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (align > obj->base.size)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (obj->ops == &i915_gem_phys_ops)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (obj->ops != &i915_gem_shmem_ops)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-07-03 17:17:17 +08:00
|
|
|
err = i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE);
|
2019-05-28 17:29:46 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
drm/i915: Switch obj->mm.lock lockdep annotations on its head
The trouble with having a plain nesting flag for locks which do not
naturally nest (unlike block devices and their partitions, which is
the original motivation for nesting levels) is that lockdep will
never spot a true deadlock if you screw up.
This patch is an attempt at trying better, by highlighting a bit more
of the actual nature of the nesting that's going on. Essentially we
have two kinds of objects:
- objects without pages allocated, which cannot be on any lru and are
hence inaccessible to the shrinker.
- objects which have pages allocated, which are on an lru, and which
the shrinker can decide to throw out.
For the former type of object, memory allocations while holding
obj->mm.lock are permissible. For the latter they are not. And
get/put_pages transitions between the two types of objects.
This is still not entirely fool-proof since the rules might change.
But as long as we run such a code ever at runtime lockdep should be
able to observe the inconsistency and complain (like with any other
lockdep class that we've split up in multiple classes). But there are
a few clear benefits:
- We can drop the nesting flag parameter from
__i915_gem_object_put_pages, because that function by definition is
never going allocate memory, and calling it on an object which
doesn't have its pages allocated would be a bug.
- We strictly catch more bugs, since there's not only one place in the
entire tree which is annotated with the special class. All the
other places that had explicit lockdep nesting annotations we're now
going to leave up to lockdep again.
- Specifically this catches stuff like calling get_pages from
put_pages (which isn't really a good idea, if we can call get_pages
so could the shrinker). I've seen patches do exactly that.
Of course I fully expect CI will show me for the fool I am with this
one here :-)
v2: There can only be one (lockdep only has a cache for the first
subclass, not for deeper ones, and we don't want to make these locks
even slower). Still separate enums for better documentation.
Real fix: don't forget about phys objs and pin_map(), and fix the
shrinker to have the right annotations ... silly me.
v3: Forgot usertptr too ...
v4: Improve comment for pages_pin_count, drop the IMPORTANT comment
and instead prime lockdep (Chris).
v5: Appease checkpatch, no double empty lines (Chris)
v6: More rebasing over selftest changes. Also somehow I forgot to
push this patch :-/
Also format comments consistently while at it.
v7: Fix typo in commit message (Joonas)
Also drop the priming, with the lmem merge we now have allocations
while holding the lmem lock, which wreaks the generic priming I've
done in earlier patches. Should probably be resurrected when lmem is
fixed. See
commit 232a6ebae419193f5b8da4fa869ae5089ab105c2
Author: Matthew Auld <matthew.auld@intel.com>
Date: Tue Oct 8 17:01:14 2019 +0100
drm/i915: introduce intel_memory_region
I'm keeping the priming patch locally so it wont get lost.
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: "Tang, CQ" <cq.tang@intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> (v5)
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com> (v6)
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191105090148.30269-1-daniel.vetter@ffwll.ch
[mlankhorst: Fix commit typos pointed out by Michael Ruhl]
2019-11-05 17:01:48 +08:00
|
|
|
mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
|
2019-05-28 17:29:46 +08:00
|
|
|
|
|
|
|
if (obj->mm.madv != I915_MADV_WILLNEED) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto err_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj->mm.quirked) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto err_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj->mm.mapping) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto err_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
pages = __i915_gem_object_unset_pages(obj);
|
|
|
|
|
|
|
|
obj->ops = &i915_gem_phys_ops;
|
|
|
|
|
|
|
|
err = ____i915_gem_object_get_pages(obj);
|
|
|
|
if (err)
|
|
|
|
goto err_xfer;
|
|
|
|
|
|
|
|
/* Perma-pin (until release) the physical set of pages */
|
|
|
|
__i915_gem_object_pin_pages(obj);
|
|
|
|
|
2019-10-18 17:07:50 +08:00
|
|
|
if (!IS_ERR_OR_NULL(pages)) {
|
2019-05-28 17:29:46 +08:00
|
|
|
i915_gem_shmem_ops.put_pages(obj, pages);
|
2019-10-18 17:07:50 +08:00
|
|
|
i915_gem_object_release_memory_region(obj);
|
|
|
|
}
|
2019-05-28 17:29:46 +08:00
|
|
|
mutex_unlock(&obj->mm.lock);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_xfer:
|
|
|
|
obj->ops = &i915_gem_shmem_ops;
|
|
|
|
if (!IS_ERR_OR_NULL(pages)) {
|
|
|
|
unsigned int sg_page_sizes = i915_sg_page_sizes(pages->sgl);
|
|
|
|
|
|
|
|
__i915_gem_object_set_pages(obj, pages, sg_page_sizes);
|
|
|
|
}
|
|
|
|
err_unlock:
|
|
|
|
mutex_unlock(&obj->mm.lock);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
|
|
|
|
#include "selftests/i915_gem_phys.c"
|
|
|
|
#endif
|