diff mbox

[5/7] drm: Don't compute obj counts expensively in get_resources

Message ID 20161209141944.22121-5-daniel.vetter@ffwll.ch (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel Vetter Dec. 9, 2016, 2:19 p.m. UTC
Looping when we keep track of this is silly. Only thing we have to
be careful is with sampling the connector count. To avoid inconsisten
results due to gcc re-computing this, use READ_ONCE.

And to avoid surprising userspace, make sure we don't copy more
connectors than planned, and report the actual number of connectors
copied. That way any racing hot-add/remove will be handled.

v2: Actually try to not blow up, somehow I lost the hunk that checks
we don't copy too much. Noticed by Chris.

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/drm_mode_config.c | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

Comments

Chris Wilson Dec. 9, 2016, 9:47 p.m. UTC | #1
On Fri, Dec 09, 2016 at 03:19:42PM +0100, Daniel Vetter wrote:
> Looping when we keep track of this is silly. Only thing we have to
> be careful is with sampling the connector count. To avoid inconsisten
> results due to gcc re-computing this, use READ_ONCE.

Later on in the function we take the mutex that should prevent the
values from changing.

If each block was like

mutex_lock(&mode_config->lockc);

count = dev->mode_config.num_crtc;
if (card_res->count_crtcs >= count) {
	u32 __user id = u64_to_user_ptr(card_res->crtc_id_ptr);
	unsigned int copied = 0;

	drm_for_each_crtc(crtc, dev) {
		if (copied >= count)
			break;

		if (put_user(crtc->base.id, id + copied)) {
			ret = -EFAULT;
			goto out;
		}

		copied++;
	}

	count = copied;
}
card_res->count_crtcs = count;

...

mutex_unlock(&mode_config->lockc);

it would look a bit neater.
-Chris
Daniel Vetter Dec. 9, 2016, 10:25 p.m. UTC | #2
On Fri, Dec 09, 2016 at 09:47:56PM +0000, Chris Wilson wrote:
> On Fri, Dec 09, 2016 at 03:19:42PM +0100, Daniel Vetter wrote:
> > Looping when we keep track of this is silly. Only thing we have to
> > be careful is with sampling the connector count. To avoid inconsisten
> > results due to gcc re-computing this, use READ_ONCE.
> 
> Later on in the function we take the mutex that should prevent the
> values from changing.
> 
> If each block was like
> 
> mutex_lock(&mode_config->lockc);

Well that mutex_lock is fiction too. It's not needed for any of the lists,
and it definitely doesn't protect connector_list. Step-by-step I'd say ...
-Daniel

> 
> count = dev->mode_config.num_crtc;
> if (card_res->count_crtcs >= count) {
> 	u32 __user id = u64_to_user_ptr(card_res->crtc_id_ptr);
> 	unsigned int copied = 0;
> 
> 	drm_for_each_crtc(crtc, dev) {
> 		if (copied >= count)
> 			break;
> 
> 		if (put_user(crtc->base.id, id + copied)) {
> 			ret = -EFAULT;
> 			goto out;
> 		}
> 
> 		copied++;
> 	}
> 
> 	count = copied;
> }
> card_res->count_crtcs = count;
> 
> ...
> 
> mutex_unlock(&mode_config->lockc);
> 
> it would look a bit neater.
> -Chris
> 
> -- 
> Chris Wilson, Intel Open Source Technology Centre
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index 2735a5847ffa..64c2971478db 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -90,10 +90,10 @@  int drm_mode_getresources(struct drm_device *dev, void *data,
 	struct drm_crtc *crtc;
 	struct drm_encoder *encoder;
 	int ret = 0;
-	int connector_count = 0;
-	int crtc_count = 0;
+	int connector_count = READ_ONCE(dev->mode_config.num_connector);
+	int crtc_count = dev->mode_config.num_crtc;
 	int fb_count = 0;
-	int encoder_count = 0;
+	int encoder_count = dev->mode_config.num_encoder;
 	int copied = 0;
 	uint32_t __user *fb_id;
 	uint32_t __user *crtc_id;
@@ -131,15 +131,6 @@  int drm_mode_getresources(struct drm_device *dev, void *data,
 	/* mode_config.mutex protects the connector list against e.g. DP MST
 	 * connector hot-adding. CRTC/Plane lists are invariant. */
 	mutex_lock(&dev->mode_config.mutex);
-	drm_for_each_crtc(crtc, dev)
-		crtc_count++;
-
-	drm_for_each_connector(connector, dev)
-		connector_count++;
-
-	drm_for_each_encoder(encoder, dev)
-		encoder_count++;
-
 	card_res->max_height = dev->mode_config.max_height;
 	card_res->min_height = dev->mode_config.min_height;
 	card_res->max_width = dev->mode_config.max_width;
@@ -179,6 +170,9 @@  int drm_mode_getresources(struct drm_device *dev, void *data,
 		copied = 0;
 		connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
 		drm_for_each_connector(connector, dev) {
+			if (copied >= connector_count)
+				break;
+
 			if (put_user(connector->base.id,
 				     connector_id + copied)) {
 				ret = -EFAULT;
@@ -186,6 +180,7 @@  int drm_mode_getresources(struct drm_device *dev, void *data,
 			}
 			copied++;
 		}
+		connector_count = copied;
 	}
 	card_res->count_connectors = connector_count;