Skip to content

Commit

Permalink
Merge branch 'msm-fixes-4.2' of git://people.freedesktop.org/~robclar…
Browse files Browse the repository at this point in the history
…k/linux into drm-fixes

Fix for nasty crash on mdp4 in disable path, fix for dma-buf export,
smb leak on mdp5 which could result in intermittent modeset fails, and
don't let interrupted system call disturb atomic commit once we are
past the point of no return.

* 'msm-fixes-4.2' of git://people.freedesktop.org/~robclark/linux:
  drm/msm/mdp5: release SMB (shared memory blocks) in various cases
  drm/msm: change to uninterruptible wait in atomic commit
  drm/msm: mdp4: Fix drm_framebuffer dereference crash
  drm/msm: fix msm_gem_prime_get_sg_table()
  • Loading branch information
Dave Airlie committed Jul 30, 2015
2 parents d698291 + b4cba04 commit bdce3e7
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 48 deletions.
4 changes: 3 additions & 1 deletion drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,15 @@ static int mdp4_plane_mode_set(struct drm_plane *plane,
uint32_t op_mode = 0;
uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
uint32_t phasey_step = MDP4_VG_PHASE_STEP_DEFAULT;
enum mdp4_frame_format frame_type = mdp4_get_frame_format(fb);
enum mdp4_frame_format frame_type;

if (!(crtc && fb)) {
DBG("%s: disabled!", mdp4_plane->name);
return 0;
}

frame_type = mdp4_get_frame_format(fb);

/* src values are in Q16 fixed point, convert to integer: */
src_x = src_x >> 16;
src_y = src_y >> 16;
Expand Down
13 changes: 13 additions & 0 deletions drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,20 @@ static void mdp5_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *st

static void mdp5_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state)
{
int i;
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
int nplanes = mdp5_kms->dev->mode_config.num_total_plane;

for (i = 0; i < nplanes; i++) {
struct drm_plane *plane = state->planes[i];
struct drm_plane_state *plane_state = state->plane_states[i];

if (!plane)
continue;

mdp5_plane_complete_commit(plane, plane_state);
}

mdp5_disable(mdp5_kms);
}

Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ void mdp5_plane_install_properties(struct drm_plane *plane,
struct drm_mode_object *obj);
uint32_t mdp5_plane_get_flush(struct drm_plane *plane);
void mdp5_plane_complete_flip(struct drm_plane *plane);
void mdp5_plane_complete_commit(struct drm_plane *plane,
struct drm_plane_state *state);
enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
struct drm_plane *mdp5_plane_init(struct drm_device *dev,
enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset);
Expand Down
33 changes: 14 additions & 19 deletions drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ struct mdp5_plane {

uint32_t nformats;
uint32_t formats[32];

bool enabled;
};
#define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base)

Expand All @@ -56,22 +54,6 @@ static bool plane_enabled(struct drm_plane_state *state)
return state->fb && state->crtc;
}

static int mdp5_plane_disable(struct drm_plane *plane)
{
struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
struct mdp5_kms *mdp5_kms = get_kms(plane);
enum mdp5_pipe pipe = mdp5_plane->pipe;

DBG("%s: disable", mdp5_plane->name);

if (mdp5_kms) {
/* Release the memory we requested earlier from the SMP: */
mdp5_smp_release(mdp5_kms->smp, pipe);
}

return 0;
}

static void mdp5_plane_destroy(struct drm_plane *plane)
{
struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
Expand Down Expand Up @@ -224,7 +206,6 @@ static void mdp5_plane_atomic_update(struct drm_plane *plane,

if (!plane_enabled(state)) {
to_mdp5_plane_state(state)->pending = true;
mdp5_plane_disable(plane);
} else if (to_mdp5_plane_state(state)->mode_changed) {
int ret;
to_mdp5_plane_state(state)->pending = true;
Expand Down Expand Up @@ -602,6 +583,20 @@ uint32_t mdp5_plane_get_flush(struct drm_plane *plane)
return mdp5_plane->flush_mask;
}

/* called after vsync in thread context */
void mdp5_plane_complete_commit(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct mdp5_kms *mdp5_kms = get_kms(plane);
struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
enum mdp5_pipe pipe = mdp5_plane->pipe;

if (!plane_enabled(plane->state)) {
DBG("%s: free SMP", mdp5_plane->name);
mdp5_smp_release(mdp5_kms->smp, pipe);
}
}

/* initialize plane */
struct drm_plane *mdp5_plane_init(struct drm_device *dev,
enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset)
Expand Down
87 changes: 74 additions & 13 deletions drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,44 @@
* and CANNOT be re-allocated (eg: MMB0 and MMB1 both tied to RGB0).
*
* For each block that can be dynamically allocated, it can be either
* free, or pending/in-use by a client. The updates happen in three steps:
* free:
* The block is free.
*
* pending:
* The block is allocated to some client and not free.
*
* configured:
* The block is allocated to some client, and assigned to that
* client in MDP5_MDP_SMP_ALLOC registers.
*
* inuse:
* The block is being actively used by a client.
*
* The updates happen in the following steps:
*
* 1) mdp5_smp_request():
* When plane scanout is setup, calculate required number of
* blocks needed per client, and request. Blocks not inuse or
* pending by any other client are added to client's pending
* set.
* blocks needed per client, and request. Blocks neither inuse nor
* configured nor pending by any other client are added to client's
* pending set.
* For shrinking, blocks in pending but not in configured can be freed
* directly, but those already in configured will be freed later by
* mdp5_smp_commit.
*
* 2) mdp5_smp_configure():
* As hw is programmed, before FLUSH, MDP5_MDP_SMP_ALLOC registers
* are configured for the union(pending, inuse)
* Current pending is copied to configured.
* It is assumed that mdp5_smp_request and mdp5_smp_configure not run
* concurrently for the same pipe.
*
* 3) mdp5_smp_commit():
* After next vblank, copy pending -> inuse. Optionally update
* After next vblank, copy configured -> inuse. Optionally update
* MDP5_SMP_ALLOC registers if there are newly unused blocks
*
* 4) mdp5_smp_release():
* Must be called after the pipe is disabled and no longer uses any SMB
*
* On the next vblank after changes have been committed to hw, the
* client's pending blocks become it's in-use blocks (and no-longer
* in-use blocks become available to other clients).
Expand Down Expand Up @@ -77,6 +99,9 @@ struct mdp5_smp {
struct mdp5_client_smp_state client_state[MAX_CLIENTS];
};

static void update_smp_state(struct mdp5_smp *smp,
u32 cid, mdp5_smp_state_t *assigned);

static inline
struct mdp5_kms *get_kms(struct mdp5_smp *smp)
{
Expand Down Expand Up @@ -149,7 +174,12 @@ static int smp_request_block(struct mdp5_smp *smp,
for (i = cur_nblks; i > nblks; i--) {
int blk = find_first_bit(ps->pending, cnt);
clear_bit(blk, ps->pending);
/* don't clear in global smp_state until _commit() */

/* clear in global smp_state if not in configured
* otherwise until _commit()
*/
if (!test_bit(blk, ps->configured))
clear_bit(blk, smp->state);
}
}

Expand Down Expand Up @@ -223,10 +253,33 @@ int mdp5_smp_request(struct mdp5_smp *smp, enum mdp5_pipe pipe, u32 fmt, u32 wid
/* Release SMP blocks for all clients of the pipe */
void mdp5_smp_release(struct mdp5_smp *smp, enum mdp5_pipe pipe)
{
int i, nblks;
int i;
unsigned long flags;
int cnt = smp->blk_cnt;

for (i = 0; i < pipe2nclients(pipe); i++) {
mdp5_smp_state_t assigned;
u32 cid = pipe2client(pipe, i);
struct mdp5_client_smp_state *ps = &smp->client_state[cid];

spin_lock_irqsave(&smp->state_lock, flags);

/* clear hw assignment */
bitmap_or(assigned, ps->inuse, ps->configured, cnt);
update_smp_state(smp, CID_UNUSED, &assigned);

/* free to global pool */
bitmap_andnot(smp->state, smp->state, ps->pending, cnt);
bitmap_andnot(smp->state, smp->state, assigned, cnt);

/* clear client's infor */
bitmap_zero(ps->pending, cnt);
bitmap_zero(ps->configured, cnt);
bitmap_zero(ps->inuse, cnt);

spin_unlock_irqrestore(&smp->state_lock, flags);
}

for (i = 0, nblks = 0; i < pipe2nclients(pipe); i++)
smp_request_block(smp, pipe2client(pipe, i), 0);
set_fifo_thresholds(smp, pipe, 0);
}

Expand Down Expand Up @@ -274,12 +327,20 @@ void mdp5_smp_configure(struct mdp5_smp *smp, enum mdp5_pipe pipe)
u32 cid = pipe2client(pipe, i);
struct mdp5_client_smp_state *ps = &smp->client_state[cid];

bitmap_or(assigned, ps->inuse, ps->pending, cnt);
/*
* if vblank has not happened since last smp_configure
* skip the configure for now
*/
if (!bitmap_equal(ps->inuse, ps->configured, cnt))
continue;

bitmap_copy(ps->configured, ps->pending, cnt);
bitmap_or(assigned, ps->inuse, ps->configured, cnt);
update_smp_state(smp, cid, &assigned);
}
}

/* step #3: after vblank, copy pending -> inuse: */
/* step #3: after vblank, copy configured -> inuse: */
void mdp5_smp_commit(struct mdp5_smp *smp, enum mdp5_pipe pipe)
{
int cnt = smp->blk_cnt;
Expand All @@ -295,7 +356,7 @@ void mdp5_smp_commit(struct mdp5_smp *smp, enum mdp5_pipe pipe)
* using, which can be released and made available to other
* clients:
*/
if (bitmap_andnot(released, ps->inuse, ps->pending, cnt)) {
if (bitmap_andnot(released, ps->inuse, ps->configured, cnt)) {
unsigned long flags;

spin_lock_irqsave(&smp->state_lock, flags);
Expand All @@ -306,7 +367,7 @@ void mdp5_smp_commit(struct mdp5_smp *smp, enum mdp5_pipe pipe)
update_smp_state(smp, CID_UNUSED, &released);
}

bitmap_copy(ps->inuse, ps->pending, cnt);
bitmap_copy(ps->inuse, ps->configured, cnt);
}
}

Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

struct mdp5_client_smp_state {
mdp5_smp_state_t inuse;
mdp5_smp_state_t configured;
mdp5_smp_state_t pending;
};

Expand Down
8 changes: 2 additions & 6 deletions drivers/gpu/drm/msm/msm_atomic.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,8 @@ int msm_atomic_commit(struct drm_device *dev,

timeout = ktime_add_ms(ktime_get(), 1000);

ret = msm_wait_fence_interruptable(dev, c->fence, &timeout);
if (ret) {
WARN_ON(ret); // TODO unswap state back? or??
commit_destroy(c);
return ret;
}
/* uninterruptible wait */
msm_wait_fence(dev, c->fence, &timeout, false);

complete_commit(c);

Expand Down
13 changes: 9 additions & 4 deletions drivers/gpu/drm/msm/msm_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,8 @@ static void msm_debugfs_cleanup(struct drm_minor *minor)
* Fences:
*/

int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
ktime_t *timeout)
int msm_wait_fence(struct drm_device *dev, uint32_t fence,
ktime_t *timeout , bool interruptible)
{
struct msm_drm_private *priv = dev->dev_private;
int ret;
Expand Down Expand Up @@ -667,7 +667,12 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
remaining_jiffies = timespec_to_jiffies(&ts);
}

ret = wait_event_interruptible_timeout(priv->fence_event,
if (interruptible)
ret = wait_event_interruptible_timeout(priv->fence_event,
fence_completed(dev, fence),
remaining_jiffies);
else
ret = wait_event_timeout(priv->fence_event,
fence_completed(dev, fence),
remaining_jiffies);

Expand Down Expand Up @@ -853,7 +858,7 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data,
return -EINVAL;
}

return msm_wait_fence_interruptable(dev, args->fence, &timeout);
return msm_wait_fence(dev, args->fence, &timeout, true);
}

static const struct drm_ioctl_desc msm_ioctls[] = {
Expand Down
4 changes: 2 additions & 2 deletions drivers/gpu/drm/msm/msm_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ int msm_atomic_commit(struct drm_device *dev,

int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu);

int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
ktime_t *timeout);
int msm_wait_fence(struct drm_device *dev, uint32_t fence,
ktime_t *timeout, bool interruptible);
int msm_queue_fence_cb(struct drm_device *dev,
struct msm_fence_cb *cb, uint32_t fence);
void msm_update_fence(struct drm_device *dev, uint32_t fence);
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/msm/msm_gem.c
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout)
if (op & MSM_PREP_NOSYNC)
timeout = NULL;

ret = msm_wait_fence_interruptable(dev, fence, timeout);
ret = msm_wait_fence(dev, fence, timeout, true);
}

/* TODO cache maintenance */
Expand Down
8 changes: 6 additions & 2 deletions drivers/gpu/drm/msm/msm_gem_prime.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@
struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
BUG_ON(!msm_obj->sgt); /* should have already pinned! */
return msm_obj->sgt;
int npages = obj->size >> PAGE_SHIFT;

if (WARN_ON(!msm_obj->pages)) /* should have already pinned! */
return NULL;

return drm_prime_pages_to_sg(msm_obj->pages, npages);
}

void *msm_gem_prime_vmap(struct drm_gem_object *obj)
Expand Down

0 comments on commit bdce3e7

Please sign in to comment.