From 475d231be9bae4b997eb7e5a9a3cb214259cf90a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 10 Apr 2015 16:22:39 +0200 Subject: drm/atomic-helper: Don't call atomic_update_plane when it stays off It's a silly thing to do and surprises driver writers. Most likely this did already blow up for exynos. It's also a silly thing to change plane state when it's off, but fbdev is silly (it does an unconditional modeset over all planes). And userspace can be evil. So I think we need this. With this check in the helpers we can remove the one in i915 code for the same conditions (becuase ->crtc iff ->fb). Cc: Gustavo Padovan Cc: dri-devel@lists.freedesktop.org Cc: Inki Dae Cc: Matt Roper Acked-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 3 ++- drivers/gpu/drm/i915/intel_atomic_plane.c | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index a2fb0d45217a..d536817699c1 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1138,7 +1138,8 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (drm_atomic_plane_disabling(plane, old_plane_state) && funcs->atomic_disable) funcs->atomic_disable(plane, old_plane_state); - else + else if (plane->state->crtc || + drm_atomic_plane_disabling(plane, old_plane_state)) funcs->atomic_update(plane, old_plane_state); } diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c index 976b89156570..cb383a0fc392 100644 --- a/drivers/gpu/drm/i915/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/intel_atomic_plane.c @@ -172,10 +172,6 @@ static void intel_plane_atomic_update(struct drm_plane *plane, struct intel_plane_state *intel_state = to_intel_plane_state(plane->state); - /* Don't disable an already disabled plane */ - if (!plane->state->fb && !old_state->fb) - return; - intel_plane->commit_plane(plane, intel_state); } -- cgit v1.2.3 From 747552b947e1013276851b6a19a9867a94ff1c4f Mon Sep 17 00:00:00 2001 From: Todd Previte Date: Wed, 15 Apr 2015 08:38:47 -0700 Subject: drm: Fix the 'native defer' message in drm_dp_i2c_do_msg() The debug message is missing a newline at the end and it makes the logs hard to read when a device defers a lot. Simple 2-character fix adds the newline at the end. Signed-off-by: Todd Previte Cc: dri-devel@lists.freedesktop.org Reviewed-by: Paulo Zanoni Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_dp_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 71dcbc64ae98..575172ed6c32 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -466,7 +466,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) return -EREMOTEIO; case DP_AUX_NATIVE_REPLY_DEFER: - DRM_DEBUG_KMS("native defer"); + DRM_DEBUG_KMS("native defer\n"); /* * We could check for I2C bit rate capabilities and if * available adjust this interval. We could also be -- cgit v1.2.3 From 396aa4451e865d1e36d6d4e0686a9303c038b606 Mon Sep 17 00:00:00 2001 From: Todd Previte Date: Sat, 18 Apr 2015 00:04:18 -0700 Subject: drm: Fix for DP CTS test 4.2.2.5 - I2C DEFER handling For test 4.2.2.5 to pass per the Link CTS Core 1.2 rev1.1 spec, the source device must attempt at least 7 times to read the EDID when it receives an I2C defer. The normal DRM code makes only 7 retries, regardless of whether or not the response is a native defer or an I2C defer. Test 4.2.2.5 fails since there are native defers interspersed with the I2C defers which results in less than 7 EDID read attempts. The solution is to add the numer of defers to the retry counter when an I2C DEFER is returned such that another read attempt will be made. This situation should normally only occur in compliance testing, however, as a worse case real-world scenario, it would result in 13 attempts ( 6 native defers, 7 I2C defers) for a single transaction to complete. The net result is a slightly slower response to an EDID read that shouldn't significantly impact overall performance. V2: - Added a check on the number of I2C Defers to limit the number of times that the retries variable will be decremented. This is to address review feedback regarding possible infinite loops from misbehaving sink devices. V3: - Fixed the limit value to 7 instead of 8 to get the correct retry count. - Combined the increment of the defer count into the if-statement V4: - Removed i915 tag from subject as the patch is not i915-specific V5: - Updated the for-loop to add the number of i2c defers to the retry counter such that the correct number of retry attempts will be made Signed-off-by: Todd Previte Cc: dri-devel@lists.freedesktop.org Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_dp_helper.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 575172ed6c32..80a02a412607 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -432,7 +432,7 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) */ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { - unsigned int retry; + unsigned int retry, defer_i2c; int ret; /* @@ -440,7 +440,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) * is required to retry at least seven times upon receiving AUX_DEFER * before giving up the AUX transaction. */ - for (retry = 0; retry < 7; retry++) { + for (retry = 0, defer_i2c = 0; retry < (7 + defer_i2c); retry++) { mutex_lock(&aux->hw_mutex); ret = aux->transfer(aux, msg); mutex_unlock(&aux->hw_mutex); @@ -499,7 +499,13 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) case DP_AUX_I2C_REPLY_DEFER: DRM_DEBUG_KMS("I2C defer\n"); + /* DP Compliance Test 4.2.2.5 Requirement: + * Must have at least 7 retries for I2C defers on the + * transaction to pass this test + */ aux->i2c_defer_count++; + if (defer_i2c < 7) + defer_i2c++; usleep_range(400, 500); continue; -- cgit v1.2.3 From a3c6d686443e912e33cebdf9cd80f94df3ded7b0 Mon Sep 17 00:00:00 2001 From: Josef Holzmayr Date: Thu, 16 Apr 2015 14:16:29 +0200 Subject: DRM: Don't re-poll connector for disconnect DRM probe should not repoll a connector if it is already connected and the DRM_CONNECTOR_POLL_DISCONNECT flag is not set. Signed-off-by: Josef Holzmayr Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_probe_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index d44437fc42b1..2fb2fbe8c2de 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -322,8 +322,6 @@ static void output_poll_execute(struct work_struct *work) if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD) continue; - repoll = true; - old_status = connector->status; /* if we are connected and don't want to poll for disconnect skip it */ @@ -331,6 +329,8 @@ static void output_poll_execute(struct work_struct *work) !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT)) continue; + repoll = true; + connector->status = connector->funcs->detect(connector, false); if (old_status != connector->status) { const char *old, *new; -- cgit v1.2.3 From 99264a61dfcda41d86d0960cf2d4c0fc2758a773 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 15 Apr 2015 19:34:43 +0200 Subject: drm/vblank: Fixup and document timestamp update/read barriers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was a bit too much cargo-culted, so lets make it solid: - vblank->count doesn't need to be an atomic, writes are always done under the protection of dev->vblank_time_lock. Switch to an unsigned long instead and update comments. Note that atomic_read is just a normal read of a volatile variable, so no need to audit all the read-side access specifically. - The barriers for the vblank counter seqlock weren't complete: The read-side was missing the first barrier between the counter read and the timestamp read, it only had a barrier between the ts and the counter read. We need both. - Barriers weren't properly documented. Since barriers only work if you have them on boths sides of the transaction it's prudent to reference where the other side is. To avoid duplicating the write-side comment 3 times extract a little store_vblank() helper. In that helper also assert that we do indeed hold dev->vblank_time_lock, since in some cases the lock is acquired a few functions up in the callchain. Spotted while reviewing a patch from Chris Wilson to add a fastpath to the vblank_wait ioctl. v2: Add comment to better explain how store_vblank works, suggested by Chris. v3: Peter noticed that as-is the 2nd smp_wmb is redundant with the implicit barrier in the spin_unlock. But that can only be proven by auditing all callers and my point in extracting this little helper was to localize all the locking into just one place. Hence I think that additional optimization is too risky. Cc: Chris Wilson Cc: Mario Kleiner Cc: Ville Syrjälä Cc: Michel Dänzer Cc: Peter Hurley Reviewed-by: Chris Wilson Reviewed-and-tested-by: Mario Kleiner Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 95 +++++++++++++++++++++++++---------------------- include/drm/drmP.h | 8 +++- 2 files changed, 57 insertions(+), 46 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index c8a34476570a..8694b77d0002 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -74,6 +74,36 @@ module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); +static void store_vblank(struct drm_device *dev, int crtc, + unsigned vblank_count_inc, + struct timeval *t_vblank) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + u32 tslot; + + assert_spin_locked(&dev->vblank_time_lock); + + if (t_vblank) { + /* All writers hold the spinlock, but readers are serialized by + * the latching of vblank->count below. + */ + tslot = vblank->count + vblank_count_inc; + vblanktimestamp(dev, crtc, tslot) = *t_vblank; + } + + /* + * vblank timestamp updates are protected on the write side with + * vblank_time_lock, but on the read side done locklessly using a + * sequence-lock on the vblank counter. Ensure correct ordering using + * memory barrriers. We need the barrier both before and also after the + * counter update to synchronize with the next timestamp write. + * The read-side barriers for this are in drm_vblank_count_and_time. + */ + smp_wmb(); + vblank->count += vblank_count_inc; + smp_wmb(); +} + /** * drm_update_vblank_count - update the master vblank counter * @dev: DRM device @@ -93,7 +123,7 @@ module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); static void drm_update_vblank_count(struct drm_device *dev, int crtc) { struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; - u32 cur_vblank, diff, tslot; + u32 cur_vblank, diff; bool rc; struct timeval t_vblank; @@ -129,18 +159,12 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) if (diff == 0) return; - /* Reinitialize corresponding vblank timestamp if high-precision query - * available. Skip this step if query unsupported or failed. Will - * reinitialize delayed at next vblank interrupt in that case. + /* + * Only reinitialize corresponding vblank timestamp if high-precision query + * available and didn't fail. Will reinitialize delayed at next vblank + * interrupt in that case. */ - if (rc) { - tslot = atomic_read(&vblank->count) + diff; - vblanktimestamp(dev, crtc, tslot) = t_vblank; - } - - smp_mb__before_atomic(); - atomic_add(diff, &vblank->count); - smp_mb__after_atomic(); + store_vblank(dev, crtc, diff, rc ? &t_vblank : NULL); } /* @@ -218,7 +242,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) /* Compute time difference to stored timestamp of last vblank * as updated by last invocation of drm_handle_vblank() in vblank irq. */ - vblcount = atomic_read(&vblank->count); + vblcount = vblank->count; diff_ns = timeval_to_ns(&tvblank) - timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount)); @@ -234,17 +258,8 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) * available. In that case we can't account for this and just * hope for the best. */ - if (vblrc && (abs64(diff_ns) > 1000000)) { - /* Store new timestamp in ringbuffer. */ - vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; - - /* Increment cooked vblank count. This also atomically commits - * the timestamp computed above. - */ - smp_mb__before_atomic(); - atomic_inc(&vblank->count); - smp_mb__after_atomic(); - } + if (vblrc && (abs64(diff_ns) > 1000000)) + store_vblank(dev, crtc, 1, &tvblank); spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); } @@ -852,7 +867,7 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc) if (WARN_ON(crtc >= dev->num_crtcs)) return 0; - return atomic_read(&vblank->count); + return vblank->count; } EXPORT_SYMBOL(drm_vblank_count); @@ -897,16 +912,17 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, if (WARN_ON(crtc >= dev->num_crtcs)) return 0; - /* Read timestamp from slot of _vblank_time ringbuffer - * that corresponds to current vblank count. Retry if - * count has incremented during readout. This works like - * a seqlock. + /* + * Vblank timestamps are read lockless. To ensure consistency the vblank + * counter is rechecked and ordering is ensured using memory barriers. + * This works like a seqlock. The write-side barriers are in store_vblank. */ do { - cur_vblank = atomic_read(&vblank->count); + cur_vblank = vblank->count; + smp_rmb(); *vblanktime = vblanktimestamp(dev, crtc, cur_vblank); smp_rmb(); - } while (cur_vblank != atomic_read(&vblank->count)); + } while (cur_vblank != vblank->count); return cur_vblank; } @@ -1715,7 +1731,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) */ /* Get current timestamp and count. */ - vblcount = atomic_read(&vblank->count); + vblcount = vblank->count; drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ); /* Compute time difference to timestamp of last vblank */ @@ -1731,20 +1747,11 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) * e.g., due to spurious vblank interrupts. We need to * ignore those for accounting. */ - if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) { - /* Store new timestamp in ringbuffer. */ - vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; - - /* Increment cooked vblank count. This also atomically commits - * the timestamp computed above. - */ - smp_mb__before_atomic(); - atomic_inc(&vblank->count); - smp_mb__after_atomic(); - } else { + if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) + store_vblank(dev, crtc, 1, &tvblank); + else DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", crtc, (int) diff_ns); - } spin_unlock(&dev->vblank_time_lock); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 62c40777c009..4c31a2cc5a33 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -686,9 +686,13 @@ struct drm_pending_vblank_event { struct drm_vblank_crtc { struct drm_device *dev; /* pointer to the drm_device */ wait_queue_head_t queue; /**< VBLANK wait queue */ - struct timeval time[DRM_VBLANKTIME_RBSIZE]; /**< timestamp of current count */ struct timer_list disable_timer; /* delayed disable timer */ - atomic_t count; /**< number of VBLANK interrupts */ + + /* vblank counter, protected by dev->vblank_time_lock for writes */ + unsigned long count; + /* vblank timestamps, protected by dev->vblank_time_lock for writes */ + struct timeval time[DRM_VBLANKTIME_RBSIZE]; + atomic_t refcount; /* number of users of vblank interruptsper crtc */ u32 last; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ -- cgit v1.2.3 From 5a8b21b222296d6bc9be0783456838e6ab6e48e4 Mon Sep 17 00:00:00 2001 From: Mario Kleiner Date: Mon, 4 May 2015 06:29:45 +0200 Subject: drm: Prevent invalid use of vblank_disable_immediate. (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a kms driver to support immediate disable of vblank irq's reliably without introducing off by one errors or other mayhem for clients, it must not only support a hardware vblank counter query, but also high precision vblank timestamping, so vblank count and timestamp can be instantaneously reinitialzed to valid values. Additionally the exposed hardware counter must behave as if it is incrementing at leading edge of vblank to avoid off by one errors during reinitialization of the counter while the display happens to be inside or close to vblank. Check during drm_vblank_init that a driver which claims to be capable of vblank_disable_immediate at least supports high precision timestamping and prevent use of instant disable if that isn't present as a minimum requirement. v2: Changed from DRM_ERROR to DRM_INFO and made message more clear, as suggested by Michel Dänzer. Signed-off-by: Mario Kleiner Reviewed-by: Michel Dänzer Cc: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 8694b77d0002..e756485805b6 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -352,6 +352,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) else DRM_INFO("No driver support for vblank timestamp query.\n"); + /* Must have precise timestamping for reliable vblank instant disable */ + if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) { + dev->vblank_disable_immediate = false; + DRM_INFO("Setting vblank_disable_immediate to false because " + "get_vblank_timestamp == NULL\n"); + } + dev->vblank_disable_allowed = false; return 0; -- cgit v1.2.3 From d66a1e38280c42a06691e3df23f896273996255c Mon Sep 17 00:00:00 2001 From: Mario Kleiner Date: Mon, 4 May 2015 06:29:46 +0200 Subject: drm: Zero out invalid vblank timestamp in drm_update_vblank_count. (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 844b03f27739135fe1fed2fef06da0ffc4c7a081 we make sure that after vblank irq off, we return the last valid (vblank count, vblank timestamp) pair to clients, e.g., during modesets, which is good. An overlooked side effect of that commit for kms drivers without support for precise vblank timestamping is that at vblank irq enable, when we update the vblank counter from the hw counter, we can't update the corresponding vblank timestamp, so now we have a totally mismatched timestamp for the new count to confuse clients. Restore old client visible behaviour from before Linux 3.18, but zero out the timestamp at vblank counter update (instead of disable as in original implementation) if we can't generate a meaningful timestamp immediately for the new vblank counter. This will fix this regression, so callers know they need to retry again later if they need a valid timestamp, but at the same time preserves the improvements made in the commit mentioned above. v2: Rebased on top of Daniel Vetter's fixup and documentation patch for timestamp updates. Drop request for stable kernel backport as this would be more difficult, unless the original patch would get applied to stable kernels. Signed-off-by: Mario Kleiner Cc: Ville Syrjälä Cc: Daniel Vetter Cc: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index e756485805b6..1967e7fc9805 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -161,10 +161,13 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) /* * Only reinitialize corresponding vblank timestamp if high-precision query - * available and didn't fail. Will reinitialize delayed at next vblank - * interrupt in that case. + * available and didn't fail. Otherwise reinitialize delayed at next vblank + * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. */ - store_vblank(dev, crtc, diff, rc ? &t_vblank : NULL); + if (!rc) + t_vblank = (struct timeval) {0, 0}; + + store_vblank(dev, crtc, diff, &t_vblank); } /* -- cgit v1.2.3 From 337eb43c8d0000fef80b88e43fee7752a84afab2 Mon Sep 17 00:00:00 2001 From: Mario Kleiner Date: Mon, 4 May 2015 06:29:47 +0200 Subject: drm/qxl: Fix qxl_noop_get_vblank_counter() This breaks under the vblank timestamp cleanup patch by Daniel Vetter. Also it is pointless to return anything but zero (or any other constant) if the function doesn't actually query a hw vblank counter. The bogus return of the current drm vblank counter via direct readout or via drm_vblank_count() is found in many of the new kms drivers, but it does exactly nothing different from returning any arbitrary constant - it's a no operation. Let's simply return 0 - Easy and fast. Signed-off-by: Mario Kleiner Cc: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/qxl/qxl_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index e2d07085b6a5..83f6f0b5e9ef 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -198,7 +198,7 @@ static int qxl_pm_restore(struct device *dev) static u32 qxl_noop_get_vblank_counter(struct drm_device *dev, int crtc) { - return dev->vblank[crtc].count.counter; + return 0; } static int qxl_noop_enable_vblank(struct drm_device *dev, int crtc) -- cgit v1.2.3 From 3671c580e55955e61072a6e6d0d9567abdd80b5c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 4 May 2015 15:40:52 +0200 Subject: drm/atomic-helper: Really recover pre-atomic plane/cursor behavior I've fumbled this in commit f02ad907cd9e7fe3a6405d2d005840912f1ed258 Author: Daniel Vetter Date: Thu Jan 22 16:36:23 2015 +0100 drm/atomic-helpers: Recover full cursor plane behaviour and accidentally put the assignment for legacy_cursor_upate after the atomic commit, where it is pretty useless. Reported-by: Maarten Lankhorst Cc: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index d536817699c1..9f216ff61af3 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1310,13 +1310,13 @@ retry: plane_state->src_h = src_h; plane_state->src_w = src_w; + if (plane == crtc->cursor) + state->legacy_cursor_update = true; + ret = drm_atomic_commit(state); if (ret != 0) goto fail; - if (plane == crtc->cursor) - state->legacy_cursor_update = true; - /* Driver takes ownership of state on successful commit. */ return 0; fail: -- cgit v1.2.3 From ed1817036bd6d83829039543c9fe4aa94acc7c39 Mon Sep 17 00:00:00 2001 From: Oleg Drokin Date: Mon, 27 Apr 2015 11:36:46 -0400 Subject: drm: fix a memleak on mutex failure path Need to free just allocated ctx allocation if we cannot get our config mutex. This one has been flagged by kbuild bot all the way back in August, but somehow nobody picked it up: https://lists.01.org/pipermail/kbuild/2014-August/001691.html In addition there is another failure path that leaks the same ctx reference that is fixed. Found with smatch. Signed-off-by: Oleg Drokin CC: Daniel Vetter Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_modeset_lock.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 51cc47d827d8..c0a5cd8c5262 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -80,8 +80,10 @@ int __drm_modeset_lock_all(struct drm_device *dev, return -ENOMEM; if (trylock) { - if (!mutex_trylock(&config->mutex)) - return -EBUSY; + if (!mutex_trylock(&config->mutex)) { + ret = -EBUSY; + goto out; + } } else { mutex_lock(&config->mutex); } @@ -114,6 +116,8 @@ fail: goto retry; } +out: + kfree(ctx); return ret; } EXPORT_SYMBOL(__drm_modeset_lock_all); -- cgit v1.2.3 From acab18b5c3a7025640abc84ace5e94c76ebd3d10 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 4 May 2015 16:05:12 +0200 Subject: drm: drop unused 'magicfree' list This list is write-only. It's never used for read-access, so no reason to keep it around. Drop it! Signed-off-by: David Herrmann Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_auth.c | 3 --- drivers/gpu/drm/drm_drv.c | 1 - include/drm/drmP.h | 2 -- 3 files changed, 6 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index fc8e8aaa34fb..8a37524d0867 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -37,7 +37,6 @@ #include "drm_internal.h" struct drm_magic_entry { - struct list_head head; struct drm_hash_item hash_item; struct drm_file *priv; }; @@ -93,7 +92,6 @@ static int drm_add_magic(struct drm_master *master, struct drm_file *priv, entry->hash_item.key = (unsigned long)magic; mutex_lock(&dev->struct_mutex); drm_ht_insert_item(&master->magiclist, &entry->hash_item); - list_add_tail(&entry->head, &master->magicfree); mutex_unlock(&dev->struct_mutex); return 0; @@ -123,7 +121,6 @@ int drm_remove_magic(struct drm_master *master, drm_magic_t magic) } pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); drm_ht_remove_item(&master->magiclist, hash); - list_del(&pt->head); mutex_unlock(&dev->struct_mutex); kfree(pt); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 48f7359e2a6b..26ed9feb3cfd 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -109,7 +109,6 @@ struct drm_master *drm_master_create(struct drm_minor *minor) kfree(master); return NULL; } - INIT_LIST_HEAD(&master->magicfree); master->minor = minor; return master; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4c31a2cc5a33..e2b101b49112 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -356,7 +356,6 @@ struct drm_lock_data { * @unique: Unique identifier: e.g. busid. Protected by drm_global_mutex. * @unique_len: Length of unique field. Protected by drm_global_mutex. * @magiclist: Hash of used authentication tokens. Protected by struct_mutex. - * @magicfree: List of used authentication tokens. Protected by struct_mutex. * @lock: DRI lock information. * @driver_priv: Pointer to driver-private information. */ @@ -366,7 +365,6 @@ struct drm_master { char *unique; int unique_len; struct drm_open_hash magiclist; - struct list_head magicfree; struct drm_lock_data lock; void *driver_priv; }; -- cgit v1.2.3 From 32e7b94a3fa8e137aab9f2c65dff86be73245fc8 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 4 May 2015 21:01:30 +0200 Subject: drm: simplify authentication management The magic auth tokens we have are a simple map from cyclic IDs to drm_file objects. Remove all the old bulk of code and replace it with a simple, direct IDR. The previous behavior is kept. Especially calling authmagic multiple times on the same magic results in EINVAL except on the first call. The only difference in behavior is that we never allocate IDs multiple times as long as a client has its FD open. v2: - Fix return code of GetMagic() - Use non-cyclic IDR allocator - fix off-by-one in "magic > INT_MAX" sanity check v3: - drop redundant "magic > INT_MAX" check Signed-off-by: David Herrmann Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_auth.c | 175 +++++++++-------------------------------- drivers/gpu/drm/drm_drv.c | 12 +-- drivers/gpu/drm/drm_fops.c | 7 +- drivers/gpu/drm/drm_internal.h | 1 - include/drm/drmP.h | 4 +- 5 files changed, 42 insertions(+), 157 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 8a37524d0867..50d0baa06db0 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -1,11 +1,3 @@ -/** - * \file drm_auth.c - * IOCTLs for authentication - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - */ - /* * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com * @@ -13,6 +5,9 @@ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * + * Author Rickard E. (Rik) Faith + * Author Gareth Hughes + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -36,151 +31,47 @@ #include #include "drm_internal.h" -struct drm_magic_entry { - struct drm_hash_item hash_item; - struct drm_file *priv; -}; - -/** - * Find the file with the given magic number. - * - * \param dev DRM device. - * \param magic magic number. - * - * Searches in drm_device::magiclist within all files with the same hash key - * the one with matching magic number, while holding the drm_device::struct_mutex - * lock. - */ -static struct drm_file *drm_find_file(struct drm_master *master, drm_magic_t magic) -{ - struct drm_file *retval = NULL; - struct drm_magic_entry *pt; - struct drm_hash_item *hash; - struct drm_device *dev = master->minor->dev; - - mutex_lock(&dev->struct_mutex); - if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) { - pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); - retval = pt->priv; - } - mutex_unlock(&dev->struct_mutex); - return retval; -} - -/** - * Adds a magic number. - * - * \param dev DRM device. - * \param priv file private data. - * \param magic magic number. - * - * Creates a drm_magic_entry structure and appends to the linked list - * associated the magic number hash key in drm_device::magiclist, while holding - * the drm_device::struct_mutex lock. - */ -static int drm_add_magic(struct drm_master *master, struct drm_file *priv, - drm_magic_t magic) -{ - struct drm_magic_entry *entry; - struct drm_device *dev = master->minor->dev; - DRM_DEBUG("%d\n", magic); - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - entry->priv = priv; - entry->hash_item.key = (unsigned long)magic; - mutex_lock(&dev->struct_mutex); - drm_ht_insert_item(&master->magiclist, &entry->hash_item); - mutex_unlock(&dev->struct_mutex); - - return 0; -} - -/** - * Remove a magic number. - * - * \param dev DRM device. - * \param magic magic number. - * - * Searches and unlinks the entry in drm_device::magiclist with the magic - * number hash key, while holding the drm_device::struct_mutex lock. - */ -int drm_remove_magic(struct drm_master *master, drm_magic_t magic) -{ - struct drm_magic_entry *pt; - struct drm_hash_item *hash; - struct drm_device *dev = master->minor->dev; - - DRM_DEBUG("%d\n", magic); - - mutex_lock(&dev->struct_mutex); - if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) { - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); - drm_ht_remove_item(&master->magiclist, hash); - mutex_unlock(&dev->struct_mutex); - - kfree(pt); - - return 0; -} - /** - * Get a unique magic number (ioctl). + * drm_getmagic - Get unique magic of a client + * @dev: DRM device to operate on + * @data: ioctl data containing the drm_auth object + * @file_priv: DRM file that performs the operation * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a resulting drm_auth structure. - * \return zero on success, or a negative number on failure. + * This looks up the unique magic of the passed client and returns it. If the + * client did not have a magic assigned, yet, a new one is registered. The magic + * is stored in the passed drm_auth object. * - * If there is a magic number in drm_file::magic then use it, otherwise - * searches an unique non-zero magic number and add it associating it with \p - * file_priv. - * This ioctl needs protection by the drm_global_mutex, which protects - * struct drm_file::magic and struct drm_magic_entry::priv. + * Returns: 0 on success, negative error code on failure. */ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) { - static drm_magic_t sequence = 0; - static DEFINE_SPINLOCK(lock); struct drm_auth *auth = data; + int ret = 0; - /* Find unique magic */ - if (file_priv->magic) { - auth->magic = file_priv->magic; - } else { - do { - spin_lock(&lock); - if (!sequence) - ++sequence; /* reserve 0 */ - auth->magic = sequence++; - spin_unlock(&lock); - } while (drm_find_file(file_priv->master, auth->magic)); - file_priv->magic = auth->magic; - drm_add_magic(file_priv->master, file_priv, auth->magic); + mutex_lock(&dev->struct_mutex); + if (!file_priv->magic) { + ret = idr_alloc(&file_priv->master->magic_map, file_priv, + 1, 0, GFP_KERNEL); + if (ret >= 0) + file_priv->magic = ret; } + auth->magic = file_priv->magic; + mutex_unlock(&dev->struct_mutex); DRM_DEBUG("%u\n", auth->magic); - return 0; + return ret < 0 ? ret : 0; } /** - * Authenticate with a magic. + * drm_authmagic - Authenticate client with a magic + * @dev: DRM device to operate on + * @data: ioctl data containing the drm_auth object + * @file_priv: DRM file that performs the operation * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_auth structure. - * \return zero if authentication successed, or a negative number otherwise. + * This looks up a DRM client by the passed magic and authenticates it. * - * Checks if \p file_priv is associated with the magic number passed in \arg. - * This ioctl needs protection by the drm_global_mutex, which protects - * struct drm_file::magic and struct drm_magic_entry::priv. + * Returns: 0 on success, negative error code on failure. */ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -189,10 +80,14 @@ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file; DRM_DEBUG("%u\n", auth->magic); - if ((file = drm_find_file(file_priv->master, auth->magic))) { + + mutex_lock(&dev->struct_mutex); + file = idr_find(&file_priv->master->magic_map, auth->magic); + if (file) { file->authenticated = 1; - drm_remove_magic(file_priv->master, auth->magic); - return 0; + idr_replace(&file_priv->master->magic_map, NULL, auth->magic); } - return -EINVAL; + mutex_unlock(&dev->struct_mutex); + + return file ? 0 : -EINVAL; } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 26ed9feb3cfd..88b594c0593a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -92,8 +92,6 @@ void drm_ut_debug_printk(const char *function_name, const char *format, ...) } EXPORT_SYMBOL(drm_ut_debug_printk); -#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ - struct drm_master *drm_master_create(struct drm_minor *minor) { struct drm_master *master; @@ -105,10 +103,7 @@ struct drm_master *drm_master_create(struct drm_minor *minor) kref_init(&master->refcount); spin_lock_init(&master->lock.spinlock); init_waitqueue_head(&master->lock.lock_queue); - if (drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER)) { - kfree(master); - return NULL; - } + idr_init(&master->magic_map); master->minor = minor; return master; @@ -143,10 +138,9 @@ static void drm_master_destroy(struct kref *kref) master->unique = NULL; master->unique_len = 0; } - - drm_ht_remove(&master->magiclist); - mutex_unlock(&dev->struct_mutex); + + idr_destroy(&master->magic_map); kfree(master); } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 076dd606b580..0f6a5c8801e3 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -380,6 +380,8 @@ int drm_release(struct inode *inode, struct file *filp) mutex_lock(&dev->struct_mutex); list_del(&file_priv->lhead); + if (file_priv->magic) + idr_remove(&file_priv->master->magic_map, file_priv->magic); mutex_unlock(&dev->struct_mutex); if (dev->driver->preclose) @@ -394,11 +396,6 @@ int drm_release(struct inode *inode, struct file *filp) (long)old_encode_dev(file_priv->minor->kdev->devt), dev->open_count); - /* Release any auth tokens that might point to this file_priv, - (do that under the drm_global_mutex) */ - if (file_priv->magic) - (void) drm_remove_magic(file_priv->master, file_priv->magic); - /* if the master has gone away we can't do anything with the lock */ if (file_priv->minor->master) drm_master_release(dev, filp); diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 12a61d706827..059af01bd07a 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -69,7 +69,6 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_remove_magic(struct drm_master *master, drm_magic_t magic); /* drm_sysfs.c */ extern struct class *drm_class; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index e2b101b49112..df6d9970d9a4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -355,7 +355,7 @@ struct drm_lock_data { * @minor: Link back to minor char device we are master for. Immutable. * @unique: Unique identifier: e.g. busid. Protected by drm_global_mutex. * @unique_len: Length of unique field. Protected by drm_global_mutex. - * @magiclist: Hash of used authentication tokens. Protected by struct_mutex. + * @magic_map: Map of used authentication tokens. Protected by struct_mutex. * @lock: DRI lock information. * @driver_priv: Pointer to driver-private information. */ @@ -364,7 +364,7 @@ struct drm_master { struct drm_minor *minor; char *unique; int unique_len; - struct drm_open_hash magiclist; + struct idr magic_map; struct drm_lock_data lock; void *driver_priv; }; -- cgit v1.2.3 From 4a324d33bfe95a279d4c7370d84087d3e773e799 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 4 May 2015 16:05:14 +0200 Subject: drm: simplify master cleanup In drm_master_destroy() we _free_ the master object. There is no reason to hold any locks while dropping its static members, nor do we have to reset it to 0. Furthermore, kfree() already does NULL checks, so call it directly on master->unique and drop the redundant reset-code. Signed-off-by: David Herrmann Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_drv.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 88b594c0593a..3b3c4f537e95 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -132,15 +132,10 @@ static void drm_master_destroy(struct kref *kref) r_list = NULL; } } - - if (master->unique) { - kfree(master->unique); - master->unique = NULL; - master->unique_len = 0; - } mutex_unlock(&dev->struct_mutex); idr_destroy(&master->magic_map); + kfree(master->unique); kfree(master); } -- cgit v1.2.3