Commit 6f4134b3 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm-intel-next-fixes-2020-02-13' of...

Merge tag 'drm-intel-next-fixes-2020-02-13' of git://anongit.freedesktop.org/drm/drm-intel into drm-fixes

drm/i915 fixes for v5.6-rc2

Most of these were aimed at a "next fixes" pull already during the merge
window, but there were issues with the baseline I used, which resulted
in a lot of issues in CI. I've regenerated this stuff piecemeal now,
adding gradually to it, and it seems healthy now.

Due to the issues this is much bigger than I'd like. But it was
obviously necessary to take the time to ensure it's not garbage...
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/878sl6yfrn.fsf@intel.com
parents e44c1e3a 2aaaa5ee
......@@ -357,14 +357,16 @@ parse_generic_dtd(struct drm_i915_private *dev_priv,
panel_fixed_mode->hdisplay + dtd->hfront_porch;
panel_fixed_mode->hsync_end =
panel_fixed_mode->hsync_start + dtd->hsync;
panel_fixed_mode->htotal = panel_fixed_mode->hsync_end;
panel_fixed_mode->htotal =
panel_fixed_mode->hdisplay + dtd->hblank;
panel_fixed_mode->vdisplay = dtd->vactive;
panel_fixed_mode->vsync_start =
panel_fixed_mode->vdisplay + dtd->vfront_porch;
panel_fixed_mode->vsync_end =
panel_fixed_mode->vsync_start + dtd->vsync;
panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end;
panel_fixed_mode->vtotal =
panel_fixed_mode->vdisplay + dtd->vblank;
panel_fixed_mode->clock = dtd->pixel_clock;
panel_fixed_mode->width_mm = dtd->width_mm;
......
......@@ -12366,6 +12366,7 @@ static int icl_check_nv12_planes(struct intel_crtc_state *crtc_state)
/* Copy parameters to slave plane */
linked_state->ctl = plane_state->ctl | PLANE_CTL_YUV420_Y_PLANE;
linked_state->color_ctl = plane_state->color_ctl;
linked_state->view = plane_state->view;
memcpy(linked_state->color_plane, plane_state->color_plane,
sizeof(linked_state->color_plane));
......@@ -14476,37 +14477,23 @@ static int intel_atomic_check_crtcs(struct intel_atomic_state *state)
return 0;
}
static bool intel_cpu_transcoder_needs_modeset(struct intel_atomic_state *state,
enum transcoder transcoder)
static bool intel_cpu_transcoders_need_modeset(struct intel_atomic_state *state,
u8 transcoders)
{
struct intel_crtc_state *new_crtc_state;
const struct intel_crtc_state *new_crtc_state;
struct intel_crtc *crtc;
int i;
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i)
if (new_crtc_state->cpu_transcoder == transcoder)
return needs_modeset(new_crtc_state);
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) {
if (new_crtc_state->hw.enable &&
transcoders & BIT(new_crtc_state->cpu_transcoder) &&
needs_modeset(new_crtc_state))
return true;
}
return false;
}
static void
intel_modeset_synced_crtcs(struct intel_atomic_state *state,
u8 transcoders)
{
struct intel_crtc_state *new_crtc_state;
struct intel_crtc *crtc;
int i;
for_each_new_intel_crtc_in_state(state, crtc,
new_crtc_state, i) {
if (transcoders & BIT(new_crtc_state->cpu_transcoder)) {
new_crtc_state->uapi.mode_changed = true;
new_crtc_state->update_pipe = false;
}
}
}
static int
intel_modeset_all_tiles(struct intel_atomic_state *state, int tile_grp_id)
{
......@@ -14662,15 +14649,20 @@ static int intel_atomic_check(struct drm_device *dev,
if (intel_dp_mst_is_slave_trans(new_crtc_state)) {
enum transcoder master = new_crtc_state->mst_master_transcoder;
if (intel_cpu_transcoder_needs_modeset(state, master)) {
if (intel_cpu_transcoders_need_modeset(state, BIT(master))) {
new_crtc_state->uapi.mode_changed = true;
new_crtc_state->update_pipe = false;
}
} else if (is_trans_port_sync_mode(new_crtc_state)) {
}
if (is_trans_port_sync_mode(new_crtc_state)) {
u8 trans = new_crtc_state->sync_mode_slaves_mask |
BIT(new_crtc_state->master_transcoder);
intel_modeset_synced_crtcs(state, trans);
if (intel_cpu_transcoders_need_modeset(state, trans)) {
new_crtc_state->uapi.mode_changed = true;
new_crtc_state->update_pipe = false;
}
}
}
......
......@@ -384,6 +384,7 @@ static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data)
return data;
}
#ifdef CONFIG_ACPI
static int i2c_adapter_lookup(struct acpi_resource *ares, void *data)
{
struct i2c_adapter_lookup *lookup = data;
......@@ -393,8 +394,7 @@ static int i2c_adapter_lookup(struct acpi_resource *ares, void *data)
acpi_handle adapter_handle;
acpi_status status;
if (intel_dsi->i2c_bus_num >= 0 ||
!i2c_acpi_get_i2c_resource(ares, &sb))
if (!i2c_acpi_get_i2c_resource(ares, &sb))
return 1;
if (lookup->slave_addr != sb->slave_address)
......@@ -413,14 +413,41 @@ static int i2c_adapter_lookup(struct acpi_resource *ares, void *data)
return 1;
}
static const u8 *mipi_exec_i2c(struct intel_dsi *intel_dsi, const u8 *data)
static void i2c_acpi_find_adapter(struct intel_dsi *intel_dsi,
const u16 slave_addr)
{
struct drm_device *drm_dev = intel_dsi->base.base.dev;
struct device *dev = &drm_dev->pdev->dev;
struct i2c_adapter *adapter;
struct acpi_device *acpi_dev;
struct list_head resource_list;
struct i2c_adapter_lookup lookup;
acpi_dev = ACPI_COMPANION(dev);
if (acpi_dev) {
memset(&lookup, 0, sizeof(lookup));
lookup.slave_addr = slave_addr;
lookup.intel_dsi = intel_dsi;
lookup.dev_handle = acpi_device_handle(acpi_dev);
INIT_LIST_HEAD(&resource_list);
acpi_dev_get_resources(acpi_dev, &resource_list,
i2c_adapter_lookup,
&lookup);
acpi_dev_free_resource_list(&resource_list);
}
}
#else
static inline void i2c_acpi_find_adapter(struct intel_dsi *intel_dsi,
const u16 slave_addr)
{
}
#endif
static const u8 *mipi_exec_i2c(struct intel_dsi *intel_dsi, const u8 *data)
{
struct drm_device *drm_dev = intel_dsi->base.base.dev;
struct device *dev = &drm_dev->pdev->dev;
struct i2c_adapter *adapter;
struct i2c_msg msg;
int ret;
u8 vbt_i2c_bus_num = *(data + 2);
......@@ -431,20 +458,7 @@ static const u8 *mipi_exec_i2c(struct intel_dsi *intel_dsi, const u8 *data)
if (intel_dsi->i2c_bus_num < 0) {
intel_dsi->i2c_bus_num = vbt_i2c_bus_num;
acpi_dev = ACPI_COMPANION(dev);
if (acpi_dev) {
memset(&lookup, 0, sizeof(lookup));
lookup.slave_addr = slave_addr;
lookup.intel_dsi = intel_dsi;
lookup.dev_handle = acpi_device_handle(acpi_dev);
INIT_LIST_HEAD(&resource_list);
acpi_dev_get_resources(acpi_dev, &resource_list,
i2c_adapter_lookup,
&lookup);
acpi_dev_free_resource_list(&resource_list);
}
i2c_acpi_find_adapter(intel_dsi, slave_addr);
}
adapter = i2c_get_adapter(intel_dsi->i2c_bus_num);
......
......@@ -1981,9 +1981,20 @@ static int __eb_parse(struct dma_fence_work *work)
pw->trampoline);
}
static void __eb_parse_release(struct dma_fence_work *work)
{
struct eb_parse_work *pw = container_of(work, typeof(*pw), base);
if (pw->trampoline)
i915_active_release(&pw->trampoline->active);
i915_active_release(&pw->shadow->active);
i915_active_release(&pw->batch->active);
}
static const struct dma_fence_work_ops eb_parse_ops = {
.name = "eb_parse",
.work = __eb_parse,
.release = __eb_parse_release,
};
static int eb_parse_pipeline(struct i915_execbuffer *eb,
......@@ -1997,6 +2008,20 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
if (!pw)
return -ENOMEM;
err = i915_active_acquire(&eb->batch->active);
if (err)
goto err_free;
err = i915_active_acquire(&shadow->active);
if (err)
goto err_batch;
if (trampoline) {
err = i915_active_acquire(&trampoline->active);
if (err)
goto err_shadow;
}
dma_fence_work_init(&pw->base, &eb_parse_ops);
pw->engine = eb->engine;
......@@ -2006,7 +2031,9 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
pw->shadow = shadow;
pw->trampoline = trampoline;
dma_resv_lock(pw->batch->resv, NULL);
err = dma_resv_lock_interruptible(pw->batch->resv, NULL);
if (err)
goto err_trampoline;
err = dma_resv_reserve_shared(pw->batch->resv, 1);
if (err)
......@@ -2034,6 +2061,14 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
err_batch_unlock:
dma_resv_unlock(pw->batch->resv);
err_trampoline:
if (trampoline)
i915_active_release(&trampoline->active);
err_shadow:
i915_active_release(&shadow->active);
err_batch:
i915_active_release(&eb->batch->active);
err_free:
kfree(pw);
return err;
}
......
......@@ -455,10 +455,11 @@ static void i915_gem_object_release_mmap_gtt(struct drm_i915_gem_object *obj)
void i915_gem_object_release_mmap_offset(struct drm_i915_gem_object *obj)
{
struct i915_mmap_offset *mmo;
struct i915_mmap_offset *mmo, *mn;
spin_lock(&obj->mmo.lock);
list_for_each_entry(mmo, &obj->mmo.offsets, offset) {
rbtree_postorder_for_each_entry_safe(mmo, mn,
&obj->mmo.offsets, offset) {
/*
* vma_node_unmap for GTT mmaps handled already in
* __i915_gem_object_release_mmap_gtt
......@@ -487,6 +488,67 @@ void i915_gem_object_release_mmap(struct drm_i915_gem_object *obj)
i915_gem_object_release_mmap_offset(obj);
}
static struct i915_mmap_offset *
lookup_mmo(struct drm_i915_gem_object *obj,
enum i915_mmap_type mmap_type)
{
struct rb_node *rb;
spin_lock(&obj->mmo.lock);
rb = obj->mmo.offsets.rb_node;
while (rb) {
struct i915_mmap_offset *mmo =
rb_entry(rb, typeof(*mmo), offset);
if (mmo->mmap_type == mmap_type) {
spin_unlock(&obj->mmo.lock);
return mmo;
}
if (mmo->mmap_type < mmap_type)
rb = rb->rb_right;
else
rb = rb->rb_left;
}
spin_unlock(&obj->mmo.lock);
return NULL;
}
static struct i915_mmap_offset *
insert_mmo(struct drm_i915_gem_object *obj, struct i915_mmap_offset *mmo)
{
struct rb_node *rb, **p;
spin_lock(&obj->mmo.lock);
rb = NULL;
p = &obj->mmo.offsets.rb_node;
while (*p) {
struct i915_mmap_offset *pos;
rb = *p;
pos = rb_entry(rb, typeof(*pos), offset);
if (pos->mmap_type == mmo->mmap_type) {
spin_unlock(&obj->mmo.lock);
drm_vma_offset_remove(obj->base.dev->vma_offset_manager,
&mmo->vma_node);
kfree(mmo);
return pos;
}
if (pos->mmap_type < mmo->mmap_type)
p = &rb->rb_right;
else
p = &rb->rb_left;
}
rb_link_node(&mmo->offset, rb, p);
rb_insert_color(&mmo->offset, &obj->mmo.offsets);
spin_unlock(&obj->mmo.lock);
return mmo;
}
static struct i915_mmap_offset *
mmap_offset_attach(struct drm_i915_gem_object *obj,
enum i915_mmap_type mmap_type,
......@@ -496,20 +558,22 @@ mmap_offset_attach(struct drm_i915_gem_object *obj,
struct i915_mmap_offset *mmo;
int err;
mmo = lookup_mmo(obj, mmap_type);
if (mmo)
goto out;
mmo = kmalloc(sizeof(*mmo), GFP_KERNEL);
if (!mmo)
return ERR_PTR(-ENOMEM);
mmo->obj = obj;
mmo->dev = obj->base.dev;
mmo->file = file;
mmo->mmap_type = mmap_type;
drm_vma_node_reset(&mmo->vma_node);
err = drm_vma_offset_add(mmo->dev->vma_offset_manager, &mmo->vma_node,
obj->base.size / PAGE_SIZE);
err = drm_vma_offset_add(obj->base.dev->vma_offset_manager,
&mmo->vma_node, obj->base.size / PAGE_SIZE);
if (likely(!err))
goto out;
goto insert;
/* Attempt to reap some mmap space from dead objects */
err = intel_gt_retire_requests_timeout(&i915->gt, MAX_SCHEDULE_TIMEOUT);
......@@ -517,19 +581,17 @@ mmap_offset_attach(struct drm_i915_gem_object *obj,
goto err;
i915_gem_drain_freed_objects(i915);
err = drm_vma_offset_add(mmo->dev->vma_offset_manager, &mmo->vma_node,
obj->base.size / PAGE_SIZE);
err = drm_vma_offset_add(obj->base.dev->vma_offset_manager,
&mmo->vma_node, obj->base.size / PAGE_SIZE);
if (err)
goto err;
insert:
mmo = insert_mmo(obj, mmo);
GEM_BUG_ON(lookup_mmo(obj, mmap_type) != mmo);
out:
if (file)
drm_vma_node_allow(&mmo->vma_node, file);
spin_lock(&obj->mmo.lock);
list_add(&mmo->offset, &obj->mmo.offsets);
spin_unlock(&obj->mmo.lock);
return mmo;
err:
......@@ -745,60 +807,43 @@ int i915_gem_mmap(struct file *filp, struct vm_area_struct *vma)
struct drm_vma_offset_node *node;
struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
struct drm_i915_gem_object *obj = NULL;
struct i915_mmap_offset *mmo = NULL;
struct drm_gem_object *obj = NULL;
struct file *anon;
if (drm_dev_is_unplugged(dev))
return -ENODEV;
rcu_read_lock();
drm_vma_offset_lock_lookup(dev->vma_offset_manager);
node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
vma->vm_pgoff,
vma_pages(vma));
if (likely(node)) {
mmo = container_of(node, struct i915_mmap_offset,
vma_node);
/*
* In our dependency chain, the drm_vma_offset_node
* depends on the validity of the mmo, which depends on
* the gem object. However the only reference we have
* at this point is the mmo (as the parent of the node).
* Try to check if the gem object was at least cleared.
*/
if (!mmo || !mmo->obj) {
drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
return -EINVAL;
}
if (node && drm_vma_node_is_allowed(node, priv)) {
/*
* Skip 0-refcnted objects as it is in the process of being
* destroyed and will be invalid when the vma manager lock
* is released.
*/
obj = &mmo->obj->base;
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
mmo = container_of(node, struct i915_mmap_offset, vma_node);
obj = i915_gem_object_get_rcu(mmo->obj);
}
drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
rcu_read_unlock();
if (!obj)
return -EINVAL;
if (!drm_vma_node_is_allowed(node, priv)) {
drm_gem_object_put_unlocked(obj);
return -EACCES;
}
return node ? -EACCES : -EINVAL;
if (i915_gem_object_is_readonly(to_intel_bo(obj))) {
if (i915_gem_object_is_readonly(obj)) {
if (vma->vm_flags & VM_WRITE) {
drm_gem_object_put_unlocked(obj);
i915_gem_object_put(obj);
return -EINVAL;
}
vma->vm_flags &= ~VM_MAYWRITE;
}
anon = mmap_singleton(to_i915(obj->dev));
anon = mmap_singleton(to_i915(dev));
if (IS_ERR(anon)) {
drm_gem_object_put_unlocked(obj);
i915_gem_object_put(obj);
return PTR_ERR(anon);
}
......
......@@ -63,7 +63,7 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
INIT_LIST_HEAD(&obj->lut_list);
spin_lock_init(&obj->mmo.lock);
INIT_LIST_HEAD(&obj->mmo.offsets);
obj->mmo.offsets = RB_ROOT;
init_rcu_head(&obj->rcu);
......@@ -100,8 +100,8 @@ void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
{
struct drm_i915_gem_object *obj = to_intel_bo(gem);
struct drm_i915_file_private *fpriv = file->driver_priv;
struct i915_mmap_offset *mmo, *mn;
struct i915_lut_handle *lut, *ln;
struct i915_mmap_offset *mmo;
LIST_HEAD(close);
i915_gem_object_lock(obj);
......@@ -117,14 +117,8 @@ void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
i915_gem_object_unlock(obj);
spin_lock(&obj->mmo.lock);
list_for_each_entry(mmo, &obj->mmo.offsets, offset) {
if (mmo->file != file)
continue;
spin_unlock(&obj->mmo.lock);
rbtree_postorder_for_each_entry_safe(mmo, mn, &obj->mmo.offsets, offset)
drm_vma_node_revoke(&mmo->vma_node, file);
spin_lock(&obj->mmo.lock);
}
spin_unlock(&obj->mmo.lock);
list_for_each_entry_safe(lut, ln, &close, obj_link) {
......@@ -203,12 +197,14 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
i915_gem_object_release_mmap(obj);
list_for_each_entry_safe(mmo, mn, &obj->mmo.offsets, offset) {
rbtree_postorder_for_each_entry_safe(mmo, mn,
&obj->mmo.offsets,
offset) {
drm_vma_offset_remove(obj->base.dev->vma_offset_manager,
&mmo->vma_node);
kfree(mmo);
}
INIT_LIST_HEAD(&obj->mmo.offsets);
obj->mmo.offsets = RB_ROOT;
GEM_BUG_ON(atomic_read(&obj->bind_count));
GEM_BUG_ON(obj->userfault_count);
......
......@@ -69,6 +69,15 @@ i915_gem_object_lookup_rcu(struct drm_file *file, u32 handle)
return idr_find(&file->object_idr, handle);
}
static inline struct drm_i915_gem_object *
i915_gem_object_get_rcu(struct drm_i915_gem_object *obj)
{
if (obj && !kref_get_unless_zero(&obj->base.refcount))
obj = NULL;
return obj;
}
static inline struct drm_i915_gem_object *
i915_gem_object_lookup(struct drm_file *file, u32 handle)
{
......@@ -76,8 +85,7 @@ i915_gem_object_lookup(struct drm_file *file, u32 handle)
rcu_read_lock();
obj = i915_gem_object_lookup_rcu(file, handle);
if (obj && !kref_get_unless_zero(&obj->base.refcount))
obj = NULL;
obj = i915_gem_object_get_rcu(obj);
rcu_read_unlock();
return obj;
......
......@@ -71,13 +71,11 @@ enum i915_mmap_type {
};
struct i915_mmap_offset {
struct drm_device *dev;
struct drm_vma_offset_node vma_node;
struct drm_i915_gem_object *obj;
struct drm_file *file;
enum i915_mmap_type mmap_type;
struct list_head offset;
struct rb_node offset;
};
struct drm_i915_gem_object {
......@@ -137,7 +135,7 @@ struct drm_i915_gem_object {
struct {
spinlock_t lock; /* Protects access to mmo offsets */
struct list_head offsets;
struct rb_root offsets;
} mmo;
I915_SELFTEST_DECLARE(struct list_head st_link);
......
......@@ -67,21 +67,18 @@ static int intel_context_active_acquire(struct intel_context *ce)
{
int err;
err = i915_active_acquire(&ce->active);
if (err)
return err;
__i915_active_acquire(&ce->active);
if (intel_context_is_barrier(ce))
return 0;
/* Preallocate tracking nodes */
if (!intel_context_is_barrier(ce)) {
err = i915_active_acquire_preallocate_barrier(&ce->active,
ce->engine);
if (err) {
i915_active_release(&ce->active);
return err;
}
}
err = i915_active_acquire_preallocate_barrier(&ce->active,
ce->engine);
if (err)
i915_active_release(&ce->active);
return 0;
return err;
}
static void intel_context_active_release(struct intel_context *ce)
......@@ -101,13 +98,19 @@ int __intel_context_do_pin(struct intel_context *ce)
return err;
}
if (mutex_lock_interruptible(&ce->pin_mutex))
return -EINTR;
err = i915_active_acquire(&ce->active);
if (err)
return err;
if (mutex_lock_interruptible(&ce->pin_mutex)) {
err = -EINTR;
goto out_release;
}
if (likely(!atomic_read(&ce->pin_count))) {
if (likely(!atomic_add_unless(&ce->pin_count, 1, 0))) {
err = intel_context_active_acquire(ce);
if (unlikely(err))
goto err;
goto out_unlock;
err = ce->ops->pin(ce);
if (unlikely(err))
......@@ -117,18 +120,19 @@ int __intel_context_do_pin(struct intel_context *ce)
ce->ring->head, ce->ring->tail);
smp_mb__before_atomic(); /* flush pin before it is visible */
atomic_inc(&ce->pin_count);
}
atomic_inc(&ce->pin_count);
GEM_BUG_ON(!intel_context_is_pinned(ce)); /* no overflow! */
mutex_unlock(&ce->pin_mutex);
return 0;
GEM_BUG_ON(i915_active_is_idle(&ce->active));
goto out_unlock;
err_active:
intel_context_active_release(ce);
err:
out_unlock:
mutex_unlock(&ce->pin_mutex);
out_release:
i915_active_release(&ce->active);
return err;
}
......
......@@ -671,6 +671,7 @@ void
intel_engine_init_active(struct intel_engine_cs *engine, unsigned int subclass)
{
INIT_LIST_HEAD(&engine->active.requests);
INIT_LIST_HEAD(&engine->active.hold);
spin_lock_init(&engine->active.lock);
lockdep_set_subclass(&engine->active.lock, subclass);
......@@ -1422,6 +1423,17 @@ static void print_request_ring(struct drm_printer *m, struct i915_request *rq)
}
}
static unsigned long list_count(struct list_head *list)
{
struct list_head *pos;
unsigned long count = 0;
list_for_each(pos, list)
count++;
return count;
}
void intel_engine_dump(struct intel_engine_cs *engine,
struct drm_printer *m,
const char *header, ...)
......@@ -1491,6 +1503,7 @@ void intel_engine_dump(struct intel_engine_cs *engine,
hexdump(m, rq->context->lrc_reg_state, PAGE_SIZE);
}
}
drm_printf(m, "\tOn hold?: %lu\n", list_count(&engine->active.hold));
spin_unlock_irqrestore(&engine->active.lock, flags);
drm_printf(m, "\tMMIO base: 0x%08x\n", engine->mmio_base);
......
......@@ -295,6 +295,7 @@ struct intel_engine_cs {
struct {
spinlock_t lock;
struct list_head requests;
struct list_head hold; /* ready requests, but on hold */
} active;
struct llist_head barrier_tasks;
......
This diff is collapsed.
......@@ -59,11 +59,26 @@ static struct intel_ring *mock_ring(struct intel_engine_cs *engine)
ring->vaddr = (void *)(ring + 1);
atomic_set(&ring->pin_count, 1);
ring->vma = i915_vma_alloc();
if (!ring->vma) {
kfree(ring);
return NULL;
}
i915_active_init(&ring->vma->active, NULL, NULL);
intel_ring_update_space(ring);
return ring;
}
static void mock_ring_free(struct intel_ring *ring)
{
i915_active_fini(&ring->vma->active);
i915_vma_free(ring->vma);
kfree(ring);
}
static struct i915_request *first_request(struct mock_engine *engine)
{
return list_first_entry_or_null(&engine->hw_queue,
......@@ -121,7 +136,7 @@ static void mock_context_destroy(struct kref *ref)
GEM_BUG_ON(intel_context_is_pinned(ce));
if (test_bit(CONTEXT_ALLOC_BIT, &ce->flags)) {
kfree(ce->ring);
mock_ring_free(ce->ring);
mock_timeline_unpin(ce->timeline);
}
......
......@@ -285,6 +285,107 @@ static int live_unlite_preempt(void *arg)
return live_unlite_restore(arg, I915_USER_PRIORITY(I915_PRIORITY_MAX));
}
static int live_hold_reset(void *arg)
{
struct intel_gt *gt = arg;
struct intel_engine_cs *engine;
enum intel_engine_id id;
struct igt_spinner spin;
int err = 0;
/*
* In order to support offline error capture for fast preempt reset,
* we need to decouple the guilty request and ensure that it and its
* descendents are not executed while the capture is in progress.
*/
if (!intel_has_reset_engine(gt))
return 0;
if (igt_spinner_init(&spin, gt))
return -ENOMEM;
for_each_engine(engine, gt, id) {
struct intel_context *ce;
unsigned long heartbeat;
struct i915_request *rq;
ce = intel_context_create(engine);
if (IS_ERR(ce)) {
err = PTR_ERR(ce);
break;
}
engine_heartbeat_disable(engine, &heartbeat);
rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto out;
}
i915_request_add(rq);
if (!igt_wait_for_spinner(&spin, rq)) {
intel_gt_set_wedged(gt);
err = -ETIME;
goto out;
}
/* We have our request executing, now remove it and reset */
if (test_and_set_bit(I915_RESET_ENGINE + id,
&gt->reset.flags)) {
intel_gt_set_wedged(gt);
err = -EBUSY;
goto out;
}
tasklet_disable(&engine->execlists.tasklet);
engine->execlists.tasklet.func(engine->execlists.tasklet.data);
GEM_BUG_ON(execlists_active(&engine->execlists) != rq);
i915_request_get(rq);
execlists_hold(engine, rq);
GEM_BUG_ON(!i915_request_on_hold(rq));
intel_engine_reset(engine, NULL);
GEM_BUG_ON(rq->fence.error != -EIO);
tasklet_enable(&engine->execlists.tasklet);
clear_and_wake_up_bit(I915_RESET_ENGINE + id,
&gt->reset.flags);
/* Check that we do not resubmit the held request */
if (!i915_request_wait(rq, 0, HZ / 5)) {
pr_err("%s: on hold request completed!\n",
engine->name);
i915_request_put(rq);
err = -EIO;
goto out;
}
GEM_BUG_ON(!i915_request_on_hold(rq));
/* But is resubmitted on release */
execlists_unhold(engine, rq);
if (i915_request_wait(rq, 0, HZ / 5) < 0) {
pr_err("%s: held request did not complete!\n",
engine->name);
intel_gt_set_wedged(gt);
err = -ETIME;
}
i915_request_put(rq);
out:
engine_heartbeat_enable(engine, heartbeat);
intel_context_put(ce);
if (err)
break;
}
igt_spinner_fini(&spin);
return err;
}
static int
emit_semaphore_chain(struct i915_request *rq, struct i915_vma *vma, int idx)
{
......@@ -3309,12 +3410,168 @@ static int live_virtual_bond(void *arg)
return 0;
}
static int reset_virtual_engine(struct intel_gt *gt,
struct intel_engine_cs **siblings,
unsigned int nsibling)
{
struct intel_engine_cs *engine;
struct intel_context *ve;
unsigned long *heartbeat;
struct igt_spinner spin;
struct i915_request *rq;
unsigned int n;
int err = 0;
/*
* In order to support offline error capture for fast preempt reset,
* we need to decouple the guilty request and ensure that it and its
* descendents are not executed while the capture is in progress.
*/
heartbeat = kmalloc_array(nsibling, sizeof(*heartbeat), GFP_KERNEL);
if (!heartbeat)
return -ENOMEM;
if (igt_spinner_init(&spin, gt)) {
err = -ENOMEM;
goto out_free;
}
ve = intel_execlists_create_virtual(siblings, nsibling);
if (IS_ERR(ve)) {
err = PTR_ERR(ve);
goto out_spin;
}
for (n = 0; n < nsibling; n++)
engine_heartbeat_disable(siblings[n], &heartbeat[n]);
rq = igt_spinner_create_request(&spin, ve, MI_ARB_CHECK);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto out_heartbeat;
}
i915_request_add(rq);
if (!igt_wait_for_spinner(&spin, rq)) {
intel_gt_set_wedged(gt);
err = -ETIME;
goto out_heartbeat;
}
engine = rq->engine;
GEM_BUG_ON(engine == ve->engine);
/* Take ownership of the reset and tasklet */
if (test_and_set_bit(I915_RESET_ENGINE + engine->id,
&gt->reset.flags)) {
intel_gt_set_wedged(gt);
err = -EBUSY;
goto out_heartbeat;
}
tasklet_disable(&engine->execlists.tasklet);
engine->execlists.tasklet.func(engine->execlists.tasklet.data);
GEM_BUG_ON(execlists_active(&engine->execlists) != rq);
/* Fake a preemption event; failed of course */
spin_lock_irq(&engine->active.lock);
__unwind_incomplete_requests(engine);
spin_unlock_irq(&engine->active.lock);
GEM_BUG_ON(rq->engine != ve->engine);
/* Reset the engine while keeping our active request on hold */
execlists_hold(engine, rq);
GEM_BUG_ON(!i915_request_on_hold(rq));
intel_engine_reset(engine, NULL);
GEM_BUG_ON(rq->fence.error != -EIO);
/* Release our grasp on the engine, letting CS flow again */
tasklet_enable(&engine->execlists.tasklet);
clear_and_wake_up_bit(I915_RESET_ENGINE + engine->id, &gt->reset.flags);
/* Check that we do not resubmit the held request */
i915_request_get(rq);
if (!i915_request_wait(rq, 0, HZ / 5)) {
pr_err("%s: on hold request completed!\n",
engine->name);
intel_gt_set_wedged(gt);
err = -EIO;
goto out_rq;
}
GEM_BUG_ON(!i915_request_on_hold(rq));
/* But is resubmitted on release */
execlists_unhold(engine, rq);
if (i915_request_wait(rq, 0, HZ / 5) < 0) {
pr_err("%s: held request did not complete!\n",
engine->name);
intel_gt_set_wedged(gt);
err = -ETIME;
}
out_rq:
i915_request_put(rq);
out_heartbeat:
for (n = 0; n < nsibling; n++)
engine_heartbeat_enable(siblings[n], heartbeat[n]);
intel_context_put(ve);
out_spin:
igt_spinner_fini(&spin);
out_free:
kfree(heartbeat);
return err;
}
static int live_virtual_reset(void *arg)
{
struct intel_gt *gt = arg;
struct intel_engine_cs *siblings[MAX_ENGINE_INSTANCE + 1];
unsigned int class, inst;
/*
* Check that we handle a reset event within a virtual engine.
* Only the physical engine is reset, but we have to check the flow
* of the virtual requests around the reset, and make sure it is not
* forgotten.
*/
if (USES_GUC_SUBMISSION(gt->i915))
return 0;
if (!intel_has_reset_engine(gt))
return 0;
for (class = 0; class <= MAX_ENGINE_CLASS; class++) {
int nsibling, err;
nsibling = 0;
for (inst = 0; inst <= MAX_ENGINE_INSTANCE; inst++) {
if (!gt->engine_class[class][inst])
continue;
siblings[nsibling++] = gt->engine_class[class][inst];
}
if (nsibling < 2)
continue;
err = reset_virtual_engine(gt, siblings, nsibling);
if (err)
return err;
}
return 0;
}
int intel_execlists_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(live_sanitycheck),
SUBTEST(live_unlite_switch),
SUBTEST(live_unlite_preempt),
SUBTEST(live_hold_reset),
SUBTEST(live_timeslice_preempt),
SUBTEST(live_timeslice_queue),
SUBTEST(live_busywait_preempt),
......@@ -3333,6 +3590,7 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915)
SUBTEST(live_virtual_mask),
SUBTEST(live_virtual_preserved),
SUBTEST(live_virtual_bond),
SUBTEST(live_virtual_reset),
};
if (!HAS_EXECLISTS(i915))
......
......@@ -146,7 +146,7 @@ void intel_gvt_free_firmware(struct intel_gvt *gvt)
clean_firmware_sysfs(gvt);
kfree(gvt->firmware.cfg_space);
kfree(gvt->firmware.mmio);
vfree(gvt->firmware.mmio);
}
static int verify_firmware(struct intel_gvt *gvt,
......@@ -229,7 +229,7 @@ int intel_gvt_load_firmware(struct intel_gvt *gvt)
firmware->cfg_space = mem;
mem = kmalloc(info->mmio_size, GFP_KERNEL);
mem = vmalloc(info->mmio_size);
if (!mem) {
kfree(path);
kfree(firmware->cfg_space);
......
......@@ -1956,7 +1956,11 @@ void _intel_vgpu_mm_release(struct kref *mm_ref)
if (mm->type == INTEL_GVT_MM_PPGTT) {
list_del(&mm->ppgtt_mm.list);
mutex_lock(&mm->vgpu->gvt->gtt.ppgtt_mm_lock);
list_del(&mm->ppgtt_mm.lru_list);
mutex_unlock(&mm->vgpu->gvt->gtt.ppgtt_mm_lock);
invalidate_ppgtt_mm(mm);
} else {
vfree(mm->ggtt_mm.virtual_ggtt);
......
......@@ -416,13 +416,15 @@ int i915_active_acquire(struct i915_active *ref)
if (err)
return err;
if (!atomic_read(&ref->count) && ref->active)
err = ref->active(ref);
if (!err) {
spin_lock_irq(&ref->tree_lock); /* vs __active_retire() */
debug_active_activate(ref);
atomic_inc(&ref->count);
spin_unlock_irq(&ref->tree_lock);
if (likely(!i915_active_acquire_if_busy(ref))) {
if (ref->active)
err = ref->active(ref);
if (!err) {
spin_lock_irq(&ref->tree_lock); /* __active_retire() */
debug_active_activate(ref);
atomic_inc(&ref->count);
spin_unlock_irq(&ref->tree_lock);
}
}
mutex_unlock(&ref->mutex);
......@@ -605,7 +607,7 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
struct intel_engine_cs *engine)
{
intel_engine_mask_t tmp, mask = engine->mask;
struct llist_node *pos = NULL, *next;
struct llist_node *first = NULL, *last = NULL;
struct intel_gt *gt = engine->gt;
int err;
......@@ -623,6 +625,7 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
*/
for_each_engine_masked(engine, gt, mask, tmp) {
u64 idx = engine->kernel_context->timeline->fence_context;
struct llist_node *prev = first;
struct active_node *node;
node = reuse_idle_barrier(ref, idx);
......@@ -656,23 +659,23 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
GEM_BUG_ON(rcu_access_pointer(node->base.fence) != ERR_PTR(-EAGAIN));
GEM_BUG_ON(barrier_to_engine(node) != engine);
next = barrier_to_ll(node);
next->next = pos;
if (!pos)
pos = next;
first = barrier_to_ll(node);
first->next = prev;
if (!last)
last = first;
intel_engine_pm_get(engine);
}
GEM_BUG_ON(!llist_empty(&ref->preallocated_barriers));
llist_add_batch(next, pos, &ref->preallocated_barriers);
llist_add_batch(first, last, &ref->preallocated_barriers);
return 0;
unwind:
while (pos) {
struct active_node *node = barrier_from_ll(pos);
while (first) {
struct active_node *node = barrier_from_ll(first);
pos = pos->next;
first = first->next;
atomic_dec(&ref->count);
intel_engine_pm_put(barrier_to_engine(node));
......
......@@ -188,6 +188,12 @@ int i915_active_acquire(struct i915_active *ref);
bool i915_active_acquire_if_busy(struct i915_active *ref);
void i915_active_release(struct i915_active *ref);
static inline void __i915_active_acquire(struct i915_active *ref)
{
GEM_BUG_ON(!atomic_read(&ref->count));
atomic_inc(&ref->count);
}
static inline bool
i915_active_is_idle(const struct i915_active *ref)
{
......
......@@ -265,7 +265,10 @@ i915_gem_dumb_create(struct drm_file *file,
DRM_FORMAT_MOD_LINEAR))
args->pitch = ALIGN(args->pitch, 4096);
args->size = args->pitch * args->height;
if (args->pitch < args->width)
return -EINVAL;
args->size = mul_u32_u32(args->pitch, args->height);
mem_type = INTEL_MEMORY_SYSTEM;
if (HAS_LMEM(to_i915(dev)))
......
......@@ -1681,7 +1681,7 @@ static const char *error_msg(struct i915_gpu_coredump *error)
"GPU HANG: ecode %d:%x:%08x",
INTEL_GEN(error->i915), engines,
generate_ecode(first));
if (first) {
if (first && first->context.pid) {
/* Just show the first executing process, more is confusing */
len += scnprintf(error->error_msg + len,
sizeof(error->error_msg) - len,
......
......@@ -314,8 +314,11 @@ i915_vma_capture_finish(struct intel_gt_coredump *gt,
}
static inline void
i915_error_state_store(struct drm_i915_private *i915,
struct i915_gpu_coredump *error)
i915_error_state_store(struct i915_gpu_coredump *error)
{
}
static inline void i915_gpu_coredump_put(struct i915_gpu_coredump *gpu)
{
}
......
......@@ -637,8 +637,10 @@ static void i915_pmu_enable(struct perf_event *event)
container_of(event->pmu, typeof(*i915), pmu.base);
unsigned int bit = event_enabled_bit(event);
struct i915_pmu *pmu = &i915->pmu;
intel_wakeref_t wakeref;
unsigned long flags;
wakeref = intel_runtime_pm_get(&i915->runtime_pm);
spin_lock_irqsave(&pmu->lock, flags);
/*
......@@ -648,6 +650,14 @@ static void i915_pmu_enable(struct perf_event *event)
BUILD_BUG_ON(ARRAY_SIZE(pmu->enable_count) != I915_PMU_MASK_BITS);
GEM_BUG_ON(bit >= ARRAY_SIZE(pmu->enable_count));
GEM_BUG_ON(pmu->enable_count[bit] == ~0);
if (pmu->enable_count[bit] == 0 &&
config_enabled_mask(I915_PMU_RC6_RESIDENCY) & BIT_ULL(bit)) {
pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur = 0;
pmu->sample[__I915_SAMPLE_RC6].cur = __get_rc6(&i915->gt);
pmu->sleep_last = ktime_get();
}
pmu->enable |= BIT_ULL(bit);
pmu->enable_count[bit]++;
......@@ -688,6 +698,8 @@ static void i915_pmu_enable(struct perf_event *event)
* an existing non-zero value.
*/
local64_set(&event->hw.prev_count, __i915_pmu_event_read(event));
intel_runtime_pm_put(&i915->runtime_pm, wakeref);
}
static void i915_pmu_disable(struct perf_event *event)
......
......@@ -221,6 +221,8 @@ static void remove_from_engine(struct i915_request *rq)
locked = engine;
}
list_del_init(&rq->sched.link);
clear_bit(I915_FENCE_FLAG_PQUEUE, &rq->fence.flags);
clear_bit(I915_FENCE_FLAG_HOLD, &rq->fence.flags);
spin_unlock_irq(&locked->active.lock);
}
......@@ -408,8 +410,10 @@ bool __i915_request_submit(struct i915_request *request)
xfer: /* We may be recursing from the signal callback of another i915 fence */
spin_lock_nested(&request->lock, SINGLE_DEPTH_NESTING);
if (!test_and_set_bit(I915_FENCE_FLAG_ACTIVE, &request->fence.flags))
if (!test_and_set_bit(I915_FENCE_FLAG_ACTIVE, &request->fence.flags)) {
list_move_tail(&request->sched.link, &engine->active.requests);
clear_bit(I915_FENCE_FLAG_PQUEUE, &request->fence.flags);
}
if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &request->fence.flags) &&
!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &request->fence.flags) &&
......
......@@ -70,6 +70,18 @@ enum {
*/
I915_FENCE_FLAG_ACTIVE = DMA_FENCE_FLAG_USER_BITS,
/*
* I915_FENCE_FLAG_PQUEUE - this request is ready for execution
*
* Using the scheduler, when a request is ready for execution it is put
* into the priority queue, and removed from that queue when transferred
* to the HW runlists. We want to track its membership within the
* priority queue so that we can easily check before rescheduling.
*
* See i915_request_in_priority_queue()
*/
I915_FENCE_FLAG_PQUEUE,
/*
* I915_FENCE_FLAG_SIGNAL - this request is currently on signal_list
*
......@@ -78,6 +90,13 @@ enum {
*/
I915_FENCE_FLAG_SIGNAL,
/*
* I915_FENCE_FLAG_HOLD - this request is currently on hold
*
* This request has been suspended, pending an ongoing investigation.
*/
I915_FENCE_FLAG_HOLD,
/*
* I915_FENCE_FLAG_NOPREEMPT - this request should not be preempted
*
......@@ -361,6 +380,11 @@ static inline bool i915_request_is_active(const struct i915_request *rq)
return test_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags);
}
static inline bool i915_request_in_priority_queue(const struct i915_request *rq)
{
return test_bit(I915_FENCE_FLAG_PQUEUE, &rq->fence.flags);
}
/**
* Returns true if seq1 is later than seq2.
*/
......@@ -454,6 +478,27 @@ static inline bool i915_request_is_running(const struct i915_request *rq)
return __i915_request_has_started(rq);
}
/**
* i915_request_is_running - check if the request is ready for execution
* @rq: the request
*
* Upon construction, the request is instructed to wait upon various
* signals before it is ready to be executed by the HW. That is, we do
* not want to start execution and read data before it is written. In practice,
* this is controlled with a mixture of interrupts and semaphores. Once
* the submit fence is completed, the backend scheduler will place the
* request into its queue and from there submit it for execution. So we
* can detect when a request is eligible for execution (and is under control
* of the scheduler) by querying where it is in any of the scheduler's lists.
*
* Returns true if the request is ready for execution (it may be inflight),
* false otherwise.
*/
static inline bool i915_request_is_ready(const struct i915_request *rq)
{
return !list_empty(&rq->sched.link);
}
static inline bool i915_request_completed(const struct i915_request *rq)
{
if (i915_request_signaled(rq))
......@@ -483,6 +528,21 @@ static inline bool i915_request_has_sentinel(const struct i915_request *rq)
return unlikely(test_bit(I915_FENCE_FLAG_SENTINEL, &rq->fence.flags));
}
static inline bool i915_request_on_hold(const struct i915_request *rq)
{
return unlikely(test_bit(I915_FENCE_FLAG_HOLD, &rq->fence.flags));
}
static inline void i915_request_set_hold(struct i915_request *rq)
{
set_bit(I915_FENCE_FLAG_HOLD, &rq->fence.flags);
}
static inline void i915_request_clear_hold(struct i915_request *rq)
{
clear_bit(I915_FENCE_FLAG_HOLD, &rq->fence.flags);
}
static inline struct intel_timeline *
i915_request_timeline(struct i915_request *rq)
{
......
......@@ -326,20 +326,18 @@ static void __i915_schedule(struct i915_sched_node *node,
node->attr.priority = prio;
if (list_empty(&node->link)) {
/*
* If the request is not in the priolist queue because
* it is not yet runnable, then it doesn't contribute
* to our preemption decisions. On the other hand,
* if the request is on the HW, it too is not in the
* queue; but in that case we may still need to reorder
* the inflight requests.
*/
/*
* Once the request is ready, it will be placed into the
* priority lists and then onto the HW runlist. Before the
* request is ready, it does not contribute to our preemption
* decisions and we can safely ignore it, as it will, and
* any preemption required, be dealt with upon submission.
* See engine->submit_request()
*/
if (list_empty(&node->link))
continue;
}
if (!intel_engine_is_virtual(engine) &&
!i915_request_is_active(node_to_request(node))) {
if (i915_request_in_priority_queue(node_to_request(node))) {
if (!cache.priolist)
cache.priolist =
i915_sched_lookup_priolist(engine,
......
......@@ -1202,16 +1202,26 @@ int __i915_vma_unbind(struct i915_vma *vma)
if (ret)
return ret;
GEM_BUG_ON(i915_vma_is_active(vma));
if (i915_vma_is_pinned(vma)) {
vma_print_allocator(vma, "is pinned");
return -EAGAIN;
}
GEM_BUG_ON(i915_vma_is_active(vma));
/*
* After confirming that no one else is pinning this vma, wait for
* any laggards who may have crept in during the wait (through
* a residual pin skipping the vm->mutex) to complete.
*/
ret = i915_vma_sync(vma);
if (ret)
return ret;
if (!drm_mm_node_allocated(&vma->node))
return 0;
GEM_BUG_ON(i915_vma_is_pinned(vma));
GEM_BUG_ON(i915_vma_is_active(vma));
if (i915_vma_is_map_and_fenceable(vma)) {
/*
* Check that we have flushed all writes through the GGTT
......
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