All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] prepare for preparing for atomic
@ 2014-05-28 23:57 Rob Clark
  2014-05-28 23:57 ` [PATCH 1/8] drm: add object property type Rob Clark
                   ` (7 more replies)
  0 siblings, 8 replies; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel

As suggested by Daniel, splitting out ww_mutex conversion and a few
other bits out.

Daniel Vetter (1):
  drm: Split connection_mutex out of mode_config.mutex (v2)

Rob Clark (6):
  drm: add object property type
  drm: add signed-range property type
  drm: helpers to find mode objects
  drm: spiff out FB refcnting traces
  drm: push locking down into restore_fbdev_mode (v2)
  drm: convert crtc and connection_mutex to ww_mutex

Ville Syrjälä (1):
  drm: Allow drm_mode_object_find() to look up an object of any type

 drivers/gpu/drm/Makefile                   |   2 +-
 drivers/gpu/drm/armada/armada_fbdev.c      |   4 +-
 drivers/gpu/drm/drm_crtc.c                 | 263 ++++++++++++++++++-----------
 drivers/gpu/drm/drm_crtc_helper.c          |   1 +
 drivers/gpu/drm/drm_edid.c                 |   2 +
 drivers/gpu/drm/drm_fb_cma_helper.c        |   9 +-
 drivers/gpu/drm/drm_fb_helper.c            |  33 +++-
 drivers/gpu/drm/drm_modeset_lock.c         | 187 ++++++++++++++++++++
 drivers/gpu/drm/drm_plane_helper.c         |   7 +
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c  |   4 +-
 drivers/gpu/drm/gma500/psb_drv.c           |   4 +-
 drivers/gpu/drm/i915/intel_crt.c           |   5 +-
 drivers/gpu/drm/i915/intel_display.c       |  55 ++++--
 drivers/gpu/drm/i915/intel_dp.c            |   1 +
 drivers/gpu/drm/i915/intel_drv.h           |   6 +-
 drivers/gpu/drm/i915/intel_fbdev.c         |   6 +-
 drivers/gpu/drm/i915/intel_sprite.c        |   2 +-
 drivers/gpu/drm/i915/intel_tv.c            |   5 +-
 drivers/gpu/drm/msm/msm_drv.c              |   7 +-
 drivers/gpu/drm/omapdrm/omap_crtc.c        |  10 +-
 drivers/gpu/drm/omapdrm/omap_drv.c         |   4 +-
 drivers/gpu/drm/omapdrm/omap_fb.c          |   1 +
 drivers/gpu/drm/radeon/radeon_connectors.c |   1 +
 drivers/gpu/drm/tegra/fb.c                 |   7 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c        |   8 +-
 include/drm/drmP.h                         |   5 -
 include/drm/drm_crtc.h                     |  87 +++++++++-
 include/drm/drm_fb_helper.h                |   1 +
 include/drm/drm_modeset_lock.h             | 139 +++++++++++++++
 include/uapi/drm/drm_mode.h                |  18 ++
 30 files changed, 708 insertions(+), 176 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_modeset_lock.c
 create mode 100644 include/drm/drm_modeset_lock.h

-- 
1.9.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 1/8] drm: add object property type
  2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
@ 2014-05-28 23:57 ` Rob Clark
  2014-05-29  8:01   ` David Herrmann
  2014-05-30  7:57   ` Thierry Reding
  2014-05-28 23:57 ` [PATCH 2/8] drm: add signed-range " Rob Clark
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel

An object property is an id (idr) for a drm mode object.  This
will allow a property to be used set/get a framebuffer, CRTC, etc.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c  | 50 +++++++++++++++++++++++++++++++++++++--------
 include/drm/drm_crtc.h      | 27 ++++++++++++++++++++++++
 include/uapi/drm/drm_mode.h | 14 +++++++++++++
 3 files changed, 82 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 37a3e07..61ddc66 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3117,6 +3117,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
 	if (!property)
 		return NULL;
 
+	property->dev = dev;
+
 	if (num_values) {
 		property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
 		if (!property->values)
@@ -3137,6 +3139,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
 	}
 
 	list_add_tail(&property->head, &dev->mode_config.property_list);
+
+	BUG_ON(!drm_property_type_valid(property));
+
 	return property;
 fail:
 	kfree(property->values);
@@ -3274,6 +3279,23 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
 }
 EXPORT_SYMBOL(drm_property_create_range);
 
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+					 int flags, const char *name, uint32_t type)
+{
+	struct drm_property *property;
+
+	flags |= DRM_MODE_PROP_OBJECT;
+
+	property = drm_property_create(dev, flags, name, 1);
+	if (!property)
+		return NULL;
+
+	property->values[0] = type;
+
+	return property;
+}
+EXPORT_SYMBOL(drm_property_create_object);
+
 /**
  * drm_property_add_enum - add a possible value to an enumeration property
  * @property: enumeration property to change
@@ -3294,14 +3316,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
 {
 	struct drm_property_enum *prop_enum;
 
-	if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
+	if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
 		return -EINVAL;
 
 	/*
 	 * Bitmask enum properties have the additional constraint of values
 	 * from 0 to 63
 	 */
-	if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
+	if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
+			(value > 63))
 		return -EINVAL;
 
 	if (!list_empty(&property->enum_blob_list)) {
@@ -3484,10 +3508,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 	}
 	property = obj_to_property(obj);
 
-	if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
 		list_for_each_entry(prop_enum, &property->enum_blob_list, head)
 			enum_count++;
-	} else if (property->flags & DRM_MODE_PROP_BLOB) {
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
 		list_for_each_entry(prop_blob, &property->enum_blob_list, head)
 			blob_count++;
 	}
@@ -3509,7 +3534,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 	}
 	out_resp->count_values = value_count;
 
-	if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
 		if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
 			copied = 0;
 			enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3531,7 +3557,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 		out_resp->count_enum_blobs = enum_count;
 	}
 
-	if (property->flags & DRM_MODE_PROP_BLOB) {
+	if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
 		if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
 			copied = 0;
 			blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3687,19 +3713,25 @@ static bool drm_property_change_is_valid(struct drm_property *property,
 {
 	if (property->flags & DRM_MODE_PROP_IMMUTABLE)
 		return false;
-	if (property->flags & DRM_MODE_PROP_RANGE) {
+
+	if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
 		if (value < property->values[0] || value > property->values[1])
 			return false;
 		return true;
-	} else if (property->flags & DRM_MODE_PROP_BITMASK) {
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
 		int i;
 		uint64_t valid_mask = 0;
 		for (i = 0; i < property->num_values; i++)
 			valid_mask |= (1ULL << property->values[i]);
 		return !(value & ~valid_mask);
-	} else if (property->flags & DRM_MODE_PROP_BLOB) {
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
 		/* Only the driver knows */
 		return true;
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
+		/* a zero value for an object property translates to null: */
+		if (value == 0)
+			return true;
+		return drm_property_get_obj(property, value) != NULL;
 	} else {
 		int i;
 		for (i = 0; i < property->num_values; i++)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 5c1c31c..c1c243f 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -190,6 +190,7 @@ struct drm_property {
 	char name[DRM_PROP_NAME_LEN];
 	uint32_t num_values;
 	uint64_t *values;
+	struct drm_device *dev;
 
 	struct list_head enum_blob_list;
 };
@@ -931,6 +932,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
 
 extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
 						struct edid *edid);
+
+static inline bool drm_property_type_is(struct drm_property *property,
+		uint32_t type)
+{
+	/* instanceof for props.. handles extended type vs original types: */
+	if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+		return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
+	return property->flags & type;
+}
+
+static inline bool drm_property_type_valid(struct drm_property *property)
+{
+	if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+		return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+	return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+}
+
 extern int drm_object_property_set_value(struct drm_mode_object *obj,
 					 struct drm_property *property,
 					 uint64_t val);
@@ -964,6 +982,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
 					 const char *name,
 					 uint64_t min, uint64_t max);
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+					 int flags, const char *name, uint32_t type);
 extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
 extern int drm_property_add_enum(struct drm_property *property, int index,
 				 uint64_t value, const char *name);
@@ -980,6 +1000,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
 					 int gamma_size);
 extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
 		uint32_t id, uint32_t type);
+
+static inline struct drm_mode_object *
+drm_property_get_obj(struct drm_property *property, uint64_t value)
+{
+	return drm_mode_object_find(property->dev, value, property->values[0]);
+}
+
 /* IOCTLs */
 extern int drm_mode_getresources(struct drm_device *dev,
 				 void *data, struct drm_file *file_priv);
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index f104c26..5b530c7 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -251,6 +251,20 @@ struct drm_mode_get_connector {
 #define DRM_MODE_PROP_BLOB	(1<<4)
 #define DRM_MODE_PROP_BITMASK	(1<<5) /* bitmask of enumerated types */
 
+/* non-extended types: legacy bitmask, one bit per type: */
+#define DRM_MODE_PROP_LEGACY_TYPE  ( \
+		DRM_MODE_PROP_RANGE | \
+		DRM_MODE_PROP_ENUM | \
+		DRM_MODE_PROP_BLOB | \
+		DRM_MODE_PROP_BITMASK)
+
+/* extended-types: rather than continue to consume a bit per type,
+ * grab a chunk of the bits to use as integer type id.
+ */
+#define DRM_MODE_PROP_EXTENDED_TYPE	0x0000ffc0
+#define DRM_MODE_PROP_TYPE(n)		((n) << 6)
+#define DRM_MODE_PROP_OBJECT		DRM_MODE_PROP_TYPE(1)
+
 struct drm_mode_property_enum {
 	__u64 value;
 	char name[DRM_PROP_NAME_LEN];
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 2/8] drm: add signed-range property type
  2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
  2014-05-28 23:57 ` [PATCH 1/8] drm: add object property type Rob Clark
@ 2014-05-28 23:57 ` Rob Clark
  2014-05-29  8:29   ` David Herrmann
  2014-05-28 23:57 ` [PATCH 3/8] drm: helpers to find mode objects Rob Clark
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel

Like range, but values are signed.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c  | 45 +++++++++++++++++++++++++++++++++------------
 include/drm/drm_crtc.h      | 12 ++++++++++++
 include/uapi/drm/drm_mode.h |  1 +
 3 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 61ddc66..88a0741 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3242,6 +3242,22 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_property_create_bitmask);
 
+static struct drm_property *property_create_range(struct drm_device *dev,
+					 int flags, const char *name,
+					 uint64_t min, uint64_t max)
+{
+	struct drm_property *property;
+
+	property = drm_property_create(dev, flags, name, 2);
+	if (!property)
+		return NULL;
+
+	property->values[0] = min;
+	property->values[1] = max;
+
+	return property;
+}
+
 /**
  * drm_property_create - create a new ranged property type
  * @dev: drm device
@@ -3264,21 +3280,20 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
 					 const char *name,
 					 uint64_t min, uint64_t max)
 {
-	struct drm_property *property;
-
-	flags |= DRM_MODE_PROP_RANGE;
-
-	property = drm_property_create(dev, flags, name, 2);
-	if (!property)
-		return NULL;
-
-	property->values[0] = min;
-	property->values[1] = max;
-
-	return property;
+	return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
+			name, min, max);
 }
 EXPORT_SYMBOL(drm_property_create_range);
 
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+					 int flags, const char *name,
+					 int64_t min, int64_t max)
+{
+	return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
+			name, I642U64(min), I642U64(max));
+}
+EXPORT_SYMBOL(drm_property_create_signed_range);
+
 struct drm_property *drm_property_create_object(struct drm_device *dev,
 					 int flags, const char *name, uint32_t type)
 {
@@ -3718,6 +3733,12 @@ static bool drm_property_change_is_valid(struct drm_property *property,
 		if (value < property->values[0] || value > property->values[1])
 			return false;
 		return true;
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
+		int64_t svalue = U642I64(value);
+		if (svalue < U642I64(property->values[0]) ||
+				svalue > U642I64(property->values[1]))
+			return false;
+		return true;
 	} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
 		int i;
 		uint64_t valid_mask = 0;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index c1c243f..9bd3551 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -64,6 +64,15 @@ struct drm_object_properties {
 	uint64_t values[DRM_OBJECT_MAX_PROPERTY];
 };
 
+static inline int64_t U642I64(uint64_t val)
+{
+	return (int64_t)*((int64_t *)&val);
+}
+static inline uint64_t I642U64(int64_t val)
+{
+	return (uint64_t)*((uint64_t *)&val);
+}
+
 enum drm_connector_force {
 	DRM_FORCE_UNSPECIFIED,
 	DRM_FORCE_OFF,
@@ -982,6 +991,9 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
 					 const char *name,
 					 uint64_t min, uint64_t max);
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+					 int flags, const char *name,
+					 int64_t min, int64_t max);
 struct drm_property *drm_property_create_object(struct drm_device *dev,
 					 int flags, const char *name, uint32_t type);
 extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 5b530c7..ded505e 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -264,6 +264,7 @@ struct drm_mode_get_connector {
 #define DRM_MODE_PROP_EXTENDED_TYPE	0x0000ffc0
 #define DRM_MODE_PROP_TYPE(n)		((n) << 6)
 #define DRM_MODE_PROP_OBJECT		DRM_MODE_PROP_TYPE(1)
+#define DRM_MODE_PROP_SIGNED_RANGE	DRM_MODE_PROP_TYPE(2)
 
 struct drm_mode_property_enum {
 	__u64 value;
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 3/8] drm: helpers to find mode objects
  2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
  2014-05-28 23:57 ` [PATCH 1/8] drm: add object property type Rob Clark
  2014-05-28 23:57 ` [PATCH 2/8] drm: add signed-range " Rob Clark
@ 2014-05-28 23:57 ` Rob Clark
  2014-05-28 23:57 ` [PATCH 4/8] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel

Add a few more useful helpers to find mode objects.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c | 90 +++++++++++++++-------------------------------
 include/drm/drm_crtc.h     | 33 +++++++++++++++++
 2 files changed, 61 insertions(+), 62 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 88a0741..c53ddc3 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1711,7 +1711,6 @@ int drm_mode_getcrtc(struct drm_device *dev,
 {
 	struct drm_mode_crtc *crtc_resp = data;
 	struct drm_crtc *crtc;
-	struct drm_mode_object *obj;
 	int ret = 0;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -1719,13 +1718,11 @@ int drm_mode_getcrtc(struct drm_device *dev,
 
 	drm_modeset_lock_all(dev);
 
-	obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
-				   DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
+	if (!crtc) {
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 
 	crtc_resp->x = crtc->x;
 	crtc_resp->y = crtc->y;
@@ -1779,7 +1776,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 			  struct drm_file *file_priv)
 {
 	struct drm_mode_get_connector *out_resp = data;
-	struct drm_mode_object *obj;
 	struct drm_connector *connector;
 	struct drm_display_mode *mode;
 	int mode_count = 0;
@@ -1803,13 +1799,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
 	mutex_lock(&dev->mode_config.mutex);
 
-	obj = drm_mode_object_find(dev, out_resp->connector_id,
-				   DRM_MODE_OBJECT_CONNECTOR);
-	if (!obj) {
+	connector = drm_connector_find(dev, out_resp->connector_id);
+	if (!connector) {
 		ret = -ENOENT;
 		goto out;
 	}
-	connector = obj_to_connector(obj);
 
 	props_count = connector->properties.count;
 
@@ -1924,7 +1918,6 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
 			struct drm_file *file_priv)
 {
 	struct drm_mode_get_encoder *enc_resp = data;
-	struct drm_mode_object *obj;
 	struct drm_encoder *encoder;
 	int ret = 0;
 
@@ -1932,13 +1925,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, enc_resp->encoder_id,
-				   DRM_MODE_OBJECT_ENCODER);
-	if (!obj) {
+	encoder = drm_encoder_find(dev, enc_resp->encoder_id);
+	if (!encoder) {
 		ret = -ENOENT;
 		goto out;
 	}
-	encoder = obj_to_encoder(obj);
 
 	if (encoder->crtc)
 		enc_resp->crtc_id = encoder->crtc->base.id;
@@ -2036,7 +2027,6 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
 		      struct drm_file *file_priv)
 {
 	struct drm_mode_get_plane *plane_resp = data;
-	struct drm_mode_object *obj;
 	struct drm_plane *plane;
 	uint32_t __user *format_ptr;
 	int ret = 0;
@@ -2045,13 +2035,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, plane_resp->plane_id,
-				   DRM_MODE_OBJECT_PLANE);
-	if (!obj) {
+	plane = drm_plane_find(dev, plane_resp->plane_id);
+	if (!plane) {
 		ret = -ENOENT;
 		goto out;
 	}
-	plane = obj_to_plane(obj);
 
 	if (plane->crtc)
 		plane_resp->crtc_id = plane->crtc->base.id;
@@ -2104,7 +2092,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 		      struct drm_file *file_priv)
 {
 	struct drm_mode_set_plane *plane_req = data;
-	struct drm_mode_object *obj;
 	struct drm_plane *plane;
 	struct drm_crtc *crtc;
 	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
@@ -2119,14 +2106,12 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 	 * First, find the plane, crtc, and fb objects.  If not available,
 	 * we don't bother to call the driver.
 	 */
-	obj = drm_mode_object_find(dev, plane_req->plane_id,
-				   DRM_MODE_OBJECT_PLANE);
-	if (!obj) {
+	plane = drm_plane_find(dev, plane_req->plane_id);
+	if (!plane) {
 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
 			      plane_req->plane_id);
 		return -ENOENT;
 	}
-	plane = obj_to_plane(obj);
 
 	/* No fb means shut it down */
 	if (!plane_req->fb_id) {
@@ -2143,15 +2128,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 		goto out;
 	}
 
-	obj = drm_mode_object_find(dev, plane_req->crtc_id,
-				   DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, plane_req->crtc_id);
+	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown crtc ID %d\n",
 			      plane_req->crtc_id);
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 
 	fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
 	if (!fb) {
@@ -2338,7 +2321,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 {
 	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_mode_crtc *crtc_req = data;
-	struct drm_mode_object *obj;
 	struct drm_crtc *crtc;
 	struct drm_connector **connector_set = NULL, *connector;
 	struct drm_framebuffer *fb = NULL;
@@ -2356,14 +2338,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 		return -ERANGE;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, crtc_req->crtc_id,
-				   DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, crtc_req->crtc_id);
+	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 	DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
 
 	if (crtc_req->mode_valid) {
@@ -2446,15 +2426,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 				goto out;
 			}
 
-			obj = drm_mode_object_find(dev, out_id,
-						   DRM_MODE_OBJECT_CONNECTOR);
-			if (!obj) {
+			connector = drm_connector_find(dev, out_id);
+			if (!connector) {
 				DRM_DEBUG_KMS("Connector id %d unknown\n",
 						out_id);
 				ret = -ENOENT;
 				goto out;
 			}
-			connector = obj_to_connector(obj);
 			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
 					connector->base.id,
 					drm_get_connector_name(connector));
@@ -2486,7 +2464,6 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 				  struct drm_mode_cursor2 *req,
 				  struct drm_file *file_priv)
 {
-	struct drm_mode_object *obj;
 	struct drm_crtc *crtc;
 	int ret = 0;
 
@@ -2496,12 +2473,11 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
 		return -EINVAL;
 
-	obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, req->crtc_id);
+	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
 		return -ENOENT;
 	}
-	crtc = obj_to_crtc(obj);
 
 	mutex_lock(&crtc->mutex);
 	if (req->flags & DRM_MODE_CURSOR_BO) {
@@ -3497,7 +3473,6 @@ EXPORT_SYMBOL(drm_object_property_get_value);
 int drm_mode_getproperty_ioctl(struct drm_device *dev,
 			       void *data, struct drm_file *file_priv)
 {
-	struct drm_mode_object *obj;
 	struct drm_mode_get_property *out_resp = data;
 	struct drm_property *property;
 	int enum_count = 0;
@@ -3516,12 +3491,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
-	if (!obj) {
+	property = drm_property_find(dev, out_resp->prop_id);
+	if (!property) {
 		ret = -ENOENT;
 		goto done;
 	}
-	property = obj_to_property(obj);
 
 	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
 			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
@@ -3651,7 +3625,6 @@ static void drm_property_destroy_blob(struct drm_device *dev,
 int drm_mode_getblob_ioctl(struct drm_device *dev,
 			   void *data, struct drm_file *file_priv)
 {
-	struct drm_mode_object *obj;
 	struct drm_mode_get_blob *out_resp = data;
 	struct drm_property_blob *blob;
 	int ret = 0;
@@ -3661,12 +3634,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
-	if (!obj) {
+	blob = drm_property_blob_find(dev, out_resp->blob_id);
+	if (!blob) {
 		ret = -ENOENT;
 		goto done;
 	}
-	blob = obj_to_blob(obj);
 
 	if (out_resp->length == blob->length) {
 		blob_ptr = (void __user *)(unsigned long)out_resp->data;
@@ -4060,7 +4032,6 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 			     void *data, struct drm_file *file_priv)
 {
 	struct drm_mode_crtc_lut *crtc_lut = data;
-	struct drm_mode_object *obj;
 	struct drm_crtc *crtc;
 	void *r_base, *g_base, *b_base;
 	int size;
@@ -4070,12 +4041,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+	if (!crtc) {
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 
 	if (crtc->funcs->gamma_set == NULL) {
 		ret = -ENOSYS;
@@ -4134,7 +4104,6 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 			     void *data, struct drm_file *file_priv)
 {
 	struct drm_mode_crtc_lut *crtc_lut = data;
-	struct drm_mode_object *obj;
 	struct drm_crtc *crtc;
 	void *r_base, *g_base, *b_base;
 	int size;
@@ -4144,12 +4113,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+	if (!crtc) {
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 
 	/* memcpy into gamma store */
 	if (crtc_lut->gamma_size != crtc->gamma_size) {
@@ -4202,7 +4170,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 			     void *data, struct drm_file *file_priv)
 {
 	struct drm_mode_crtc_page_flip *page_flip = data;
-	struct drm_mode_object *obj;
 	struct drm_crtc *crtc;
 	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
 	struct drm_pending_vblank_event *e = NULL;
@@ -4216,10 +4183,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
 		return -EINVAL;
 
-	obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
-	if (!obj)
+	crtc = drm_crtc_find(dev, page_flip->crtc_id);
+	if (!crtc)
 		return -ENOENT;
-	crtc = obj_to_crtc(obj);
 
 	mutex_lock(&crtc->mutex);
 	if (crtc->primary->fb == NULL) {
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 9bd3551..dbd7954 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1102,6 +1102,15 @@ extern int drm_format_vert_chroma_subsampling(uint32_t format);
 extern const char *drm_get_format_name(uint32_t format);
 
 /* Helpers */
+
+static inline struct drm_plane *drm_plane_find(struct drm_device *dev,
+		uint32_t id)
+{
+	struct drm_mode_object *mo;
+	mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE);
+	return mo ? obj_to_plane(mo) : NULL;
+}
+
 static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev,
 	uint32_t id)
 {
@@ -1118,6 +1127,30 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
 	return mo ? obj_to_encoder(mo) : NULL;
 }
 
+static inline struct drm_connector *drm_connector_find(struct drm_device *dev,
+		uint32_t id)
+{
+	struct drm_mode_object *mo;
+	mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR);
+	return mo ? obj_to_connector(mo) : NULL;
+}
+
+static inline struct drm_property *drm_property_find(struct drm_device *dev,
+		uint32_t id)
+{
+	struct drm_mode_object *mo;
+	mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY);
+	return mo ? obj_to_property(mo) : NULL;
+}
+
+static inline struct drm_property_blob *
+drm_property_blob_find(struct drm_device *dev, uint32_t id)
+{
+	struct drm_mode_object *mo;
+	mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
+	return mo ? obj_to_blob(mo) : NULL;
+}
+
 /* Plane list iterator for legacy (overlay only) planes. */
 #define drm_for_each_legacy_plane(plane, planelist) \
 	list_for_each_entry(plane, planelist, head) \
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 4/8] drm: Allow drm_mode_object_find() to look up an object of any type
  2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
                   ` (2 preceding siblings ...)
  2014-05-28 23:57 ` [PATCH 3/8] drm: helpers to find mode objects Rob Clark
@ 2014-05-28 23:57 ` Rob Clark
  2014-05-29 11:18   ` Daniel Vetter
  2014-05-28 23:57 ` [PATCH 5/8] drm: spiff out FB refcnting traces Rob Clark
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel

From: Ville Syrjälä <ville.syrjala@linux.intel.com>

To avoid having to pass object types from userspace for atomic mode
setting ioctl, allow drm_mode_object_find() to look up an object of any
type. This will only work as long as the all object types share the ID
space.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c | 3 ++-
 include/drm/drm_crtc.h     | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index c53ddc3..b975575 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -409,7 +409,8 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
 
 	mutex_lock(&dev->mode_config.idr_mutex);
 	obj = idr_find(&dev->mode_config.crtc_idr, id);
-	if (!obj || (obj->type != type) || (obj->id != id))
+	if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
+	    (obj->id != id))
 		obj = NULL;
 	mutex_unlock(&dev->mode_config.idr_mutex);
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index dbd7954..44d7964 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -50,6 +50,7 @@ struct drm_clip_rect;
 #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
 #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
 #define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd
+#define DRM_MODE_OBJECT_ANY 0
 
 struct drm_mode_object {
 	uint32_t id;
-- 
1.9.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 5/8] drm: spiff out FB refcnting traces
  2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
                   ` (3 preceding siblings ...)
  2014-05-28 23:57 ` [PATCH 4/8] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
@ 2014-05-28 23:57 ` Rob Clark
  2014-05-29  8:36   ` David Herrmann
  2014-05-28 23:57 ` [PATCH 6/8] drm: push locking down into restore_fbdev_mode (v2) Rob Clark
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel

I find myself making this change locally whenever debugging FB reference
counting.  Which seems a bit silly.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b975575..4d243fd 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -519,7 +519,7 @@ EXPORT_SYMBOL(drm_framebuffer_lookup);
  */
 void drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-	DRM_DEBUG("FB ID: %d\n", fb->base.id);
+	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);
 	kref_put(&fb->refcount, drm_framebuffer_free);
 }
 EXPORT_SYMBOL(drm_framebuffer_unreference);
@@ -532,7 +532,7 @@ EXPORT_SYMBOL(drm_framebuffer_unreference);
  */
 void drm_framebuffer_reference(struct drm_framebuffer *fb)
 {
-	DRM_DEBUG("FB ID: %d\n", fb->base.id);
+	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);
 	kref_get(&fb->refcount);
 }
 EXPORT_SYMBOL(drm_framebuffer_reference);
@@ -544,7 +544,7 @@ static void drm_framebuffer_free_bug(struct kref *kref)
 
 static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-	DRM_DEBUG("FB ID: %d\n", fb->base.id);
+	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);
 	kref_put(&fb->refcount, drm_framebuffer_free_bug);
 }
 
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 6/8] drm: push locking down into restore_fbdev_mode (v2)
  2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
                   ` (4 preceding siblings ...)
  2014-05-28 23:57 ` [PATCH 5/8] drm: spiff out FB refcnting traces Rob Clark
@ 2014-05-28 23:57 ` Rob Clark
  2014-05-28 23:57 ` [PATCH 7/8] drm: Split connection_mutex out of mode_config.mutex (v2) Rob Clark
  2014-05-28 23:57 ` [PATCH 8/8] drm: convert crtc and connection_mutex to ww_mutex Rob Clark
  7 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel

All the call-sites save one need locking.  Add an _unlocked() variant
which handles the locking for you.  This simplifies the call-sites,
and will make it easier for atomic and ww_mutex conversion.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/armada/armada_fbdev.c     |  4 +---
 drivers/gpu/drm/drm_fb_cma_helper.c       |  9 ++-------
 drivers/gpu/drm/drm_fb_helper.c           | 29 +++++++++++++++++++++++++----
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  4 +---
 drivers/gpu/drm/gma500/psb_drv.c          |  4 +---
 drivers/gpu/drm/i915/intel_fbdev.c        |  6 +-----
 drivers/gpu/drm/msm/msm_drv.c             |  7 ++-----
 drivers/gpu/drm/omapdrm/omap_drv.c        |  4 +---
 drivers/gpu/drm/tegra/fb.c                |  7 ++-----
 include/drm/drm_fb_helper.h               |  1 +
 10 files changed, 37 insertions(+), 38 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 948cb14..fd166f5 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev)
 {
 	struct armada_private *priv = dev->dev_private;
 
-	drm_modeset_lock_all(dev);
 	if (priv->fbdev)
-		drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-	drm_modeset_unlock_all(dev);
+		drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
 }
 
 void armada_fbdev_fini(struct drm_device *dev)
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 61b5a47..f27c883 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -429,13 +429,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
  */
 void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
 {
-	if (fbdev_cma) {
-		struct drm_device *dev = fbdev_cma->fb_helper.dev;
-
-		drm_modeset_lock_all(dev);
-		drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper);
-		drm_modeset_unlock_all(dev);
-	}
+	if (fbdev_cma)
+		drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper);
 }
 EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index e95ed58..1e28ba6 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -280,6 +280,9 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave);
  * This should be called from driver's drm ->lastclose callback
  * when implementing an fbcon on top of kms using this helper. This ensures that
  * the user isn't greeted with a black screen when e.g. X dies.
+ *
+ * Use this variant if you need to bypass locking (panic), or already
+ * hold all modeset locks.  Otherwise use drm_fb_helper_restore_fbdev_mode_unlocked()
  */
 bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
@@ -313,6 +316,27 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 }
 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
 
+/**
+ * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
+ * @fb_helper: fbcon to restore
+ *
+ * This should be called from driver's drm ->lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ */
+bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+{
+	struct drm_device *dev = fb_helper->dev;
+	bool ret;
+
+	drm_modeset_lock_all(dev);
+	ret = drm_fb_helper_restore_fbdev_mode(fb_helper);
+	drm_modeset_unlock_all(dev);
+
+	return ret;
+}
+EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
+
 /*
  * restore fbcon display for all kms driver's using this helper, used for sysrq
  * and panic handling.
@@ -820,7 +844,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var);
 int drm_fb_helper_set_par(struct fb_info *info)
 {
 	struct drm_fb_helper *fb_helper = info->par;
-	struct drm_device *dev = fb_helper->dev;
 	struct fb_var_screeninfo *var = &info->var;
 
 	if (var->pixclock != 0) {
@@ -828,9 +851,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
 		return -EINVAL;
 	}
 
-	drm_modeset_lock_all(dev);
-	drm_fb_helper_restore_fbdev_mode(fb_helper);
-	drm_modeset_unlock_all(dev);
+	drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
 
 	if (fb_helper->delayed_hotplug) {
 		fb_helper->delayed_hotplug = false;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index addbf75..93e7ba6 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -375,7 +375,5 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
 	if (!private || !private->fb_helper)
 		return;
 
-	drm_modeset_lock_all(dev);
-	drm_fb_helper_restore_fbdev_mode(private->fb_helper);
-	drm_modeset_unlock_all(dev);
+	drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper);
 }
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 0a3101a..59ea45e 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -112,11 +112,9 @@ static void psb_driver_lastclose(struct drm_device *dev)
 	struct drm_psb_private *dev_priv = dev->dev_private;
 	struct psb_fbdev *fbdev = dev_priv->fbdev;
 
-	drm_modeset_lock_all(dev);
-	ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);
+	ret = drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->psb_fb_helper);
 	if (ret)
 		DRM_DEBUG("failed to restore crtc mode\n");
-	drm_modeset_unlock_all(dev);
 
 	return;
 }
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index fce4a0d..f8258ab 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -683,11 +683,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
 	if (!dev_priv->fbdev)
 		return;
 
-	drm_modeset_lock_all(dev);
-
-	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
+	ret = drm_fb_helper_restore_fbdev_mode_unlocked(&dev_priv->fbdev->helper);
 	if (ret)
 		DRM_DEBUG("failed to restore crtc mode\n");
-
-	drm_modeset_unlock_all(dev);
 }
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 50ec1be..afedd8b 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -382,11 +382,8 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file)
 static void msm_lastclose(struct drm_device *dev)
 {
 	struct msm_drm_private *priv = dev->dev_private;
-	if (priv->fbdev) {
-		drm_modeset_lock_all(dev);
-		drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-		drm_modeset_unlock_all(dev);
-	}
+	if (priv->fbdev)
+		drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
 }
 
 static irqreturn_t msm_irq(int irq, void *arg)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index c8270e4..002b972 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -588,9 +588,7 @@ static void dev_lastclose(struct drm_device *dev)
 		}
 	}
 
-	drm_modeset_lock_all(dev);
-	ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-	drm_modeset_unlock_all(dev);
+	ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
 	if (ret)
 		DBG("failed to restore crtc mode");
 }
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index f7fca09..9798a70 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
 
 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
 {
-	if (fbdev) {
-		drm_modeset_lock_all(fbdev->base.dev);
-		drm_fb_helper_restore_fbdev_mode(&fbdev->base);
-		drm_modeset_unlock_all(fbdev->base.dev);
-	}
+	if (fbdev)
+		drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base);
 }
 
 static void tegra_fb_output_poll_changed(struct drm_device *drm)
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 6e622f7..0630b51 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -109,6 +109,7 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
 			    struct fb_info *info);
 
 bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper);
+bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper);
 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
 			    uint32_t fb_width, uint32_t fb_height);
 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 7/8] drm: Split connection_mutex out of mode_config.mutex (v2)
  2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
                   ` (5 preceding siblings ...)
  2014-05-28 23:57 ` [PATCH 6/8] drm: push locking down into restore_fbdev_mode (v2) Rob Clark
@ 2014-05-28 23:57 ` Rob Clark
  2014-05-28 23:57 ` [PATCH 8/8] drm: convert crtc and connection_mutex to ww_mutex Rob Clark
  7 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter

From: Daniel Vetter <daniel.vetter@ffwll.ch>

After the split-out of crtc locks from the big mode_config.mutex
there's still two major areas it protects:
- Various connector probe states, like connector->status, EDID
  properties, probed mode lists and similar information.
- The links from connector->encoder and encoder->crtc and other
  modeset-relevant connector state (e.g. properties which control the
  panel fitter).

The later is used by modeset operations. But they don't really care
about the former since it's allowed to e.g. enable a disconnected VGA
output or with a mode not in the probed list.

Thus far this hasn't been a problem, but for the atomic modeset
conversion Rob Clark needs to convert all modeset relevant locks into
w/w locks. This is required because the order of acquisition is
determined by how userspace supplies the atomic modeset data. This has
run into troubles in the detect path since the i915 load detect code
needs _both_ protections offered by the mode_config.mutex: It updates
probe state and it needs to change the modeset configuration to enable
the temporary load detect pipe.

The big deal here is that for the probe/detect users of this lock a
plain mutex fits best, but for atomic modesets we really want a w/w
mutex. To fix this lets split out a new connection_mutex lock for the
modeset relevant parts.

For simplicity I've decided to only add one additional lock for all
connector/encoder links and modeset configuration states. We have
piles of different modeset objects in addition to those (like bridges
or panels), so adding per-object locks would be much more effort.

Also, we're guaranteed (at least for now) to do a full modeset if we
need to acquire this lock. Which means that fine-grained locking is
fairly irrelevant compared to the amount of time the full modeset will
take.

I've done a full audit, and there's just a few things that justify
special focus:
- Locking in drm_sysfs.c is almost completely absent. We should
  sprinkle mode_config.connection_mutex over this file a bit, but
  since it already lacks mode_config.mutex this patch wont make the
  situation any worse. This is material for a follow-up patch.

- omap has a omap_framebuffer_flush function which walks the
  connector->encoder->crtc links and is called from many contexts.
  Some look like they don't acquire mode_config.mutex, so this is
  already racy. Again fixing this is material for a separate patch.

- The radeon hot_plug function to retrain DP links looks at
  connector->dpms. Currently this happens without any locking, so is
  already racy. I think radeon_hotplug_work_func should gain
  mutex_lock/unlock calls for the mode_config.connection_mutex.

- Same applies to i915's intel_dp_hot_plug. But again, this is already
  racy.

- i915 load_detect code needs to acquire this lock. Which means the
  w/w dance due to Rob's work will be nicely contained to _just_ this
  function.

I've added fixme comments everywhere where it looks suspicious but in
the sysfs code. After a quick irc discussion with Dave Airlie it
sounds like the lack of locking in there is due to sysfs cleanup fun
at module unload.

v1: original (only compile tested)
v2: missing mutex_init(), etc

Cc: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c                 | 8 ++++++++
 drivers/gpu/drm/drm_crtc_helper.c          | 2 ++
 drivers/gpu/drm/drm_edid.c                 | 2 ++
 drivers/gpu/drm/drm_plane_helper.c         | 7 +++++++
 drivers/gpu/drm/i915/intel_display.c       | 9 ++++++++-
 drivers/gpu/drm/i915/intel_dp.c            | 1 +
 drivers/gpu/drm/omapdrm/omap_fb.c          | 1 +
 drivers/gpu/drm/radeon/radeon_connectors.c | 1 +
 include/drm/drm_crtc.h                     | 1 +
 9 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 4d243fd..de1520c 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -54,6 +54,8 @@ void drm_modeset_lock_all(struct drm_device *dev)
 
 	mutex_lock(&dev->mode_config.mutex);
 
+	mutex_lock(&dev->mode_config.connection_mutex);
+
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
 		mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
 }
@@ -72,6 +74,8 @@ void drm_modeset_unlock_all(struct drm_device *dev)
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
 		mutex_unlock(&crtc->mutex);
 
+	mutex_unlock(&dev->mode_config.connection_mutex);
+
 	mutex_unlock(&dev->mode_config.mutex);
 }
 EXPORT_SYMBOL(drm_modeset_unlock_all);
@@ -93,6 +97,7 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
 		WARN_ON(!mutex_is_locked(&crtc->mutex));
 
+	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 }
 EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
@@ -1799,6 +1804,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 	DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
 
 	mutex_lock(&dev->mode_config.mutex);
+	mutex_lock(&dev->mode_config.connection_mutex);
 
 	connector = drm_connector_find(dev, out_resp->connector_id);
 	if (!connector) {
@@ -1897,6 +1903,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 	out_resp->count_encoders = encoders_count;
 
 out:
+	mutex_unlock(&dev->mode_config.connection_mutex);
 	mutex_unlock(&dev->mode_config.mutex);
 
 	return ret;
@@ -4637,6 +4644,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
 void drm_mode_config_init(struct drm_device *dev)
 {
 	mutex_init(&dev->mode_config.mutex);
+	mutex_init(&dev->mode_config.connection_mutex);
 	mutex_init(&dev->mode_config.idr_mutex);
 	mutex_init(&dev->mode_config.fb_lock);
 	INIT_LIST_HEAD(&dev->mode_config.fb_list);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 54e8fdb..5b93caf 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -89,6 +89,8 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
 	struct drm_device *dev = encoder->dev;
 
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 		if (connector->encoder == encoder)
 			return true;
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index d74239f..e3bc051 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -3299,6 +3299,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
 	struct drm_connector *connector;
 	struct drm_device *dev = encoder->dev;
 
+	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 		if (connector->encoder == encoder && connector->eld[0])
 			return connector;
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index d966afa..458d9bf 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -54,6 +54,13 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
 	struct drm_connector *connector;
 	int count = 0;
 
+	/*
+	 * Note: Once we change the plane hooks to more fine-grained locking we
+	 * need to grab the connection_mutex here to be able to make these
+	 * checks.
+	 */
+	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 		if (connector->encoder && connector->encoder->crtc == crtc) {
 			if (connector_list != NULL && count < num_connectors)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 72b5c34..5844f07 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -7988,6 +7988,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 		      connector->base.id, drm_get_connector_name(connector),
 		      encoder->base.id, drm_get_encoder_name(encoder));
 
+	mutex_lock(&dev->mode_config.connection_mutex);
+
 	/*
 	 * Algorithm gets a little messy:
 	 *
@@ -8030,7 +8032,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 	 */
 	if (!crtc) {
 		DRM_DEBUG_KMS("no pipe available for load-detect\n");
-		return false;
+		goto fail_unlock_connector;
 	}
 
 	mutex_lock(&crtc->mutex);
@@ -8084,6 +8086,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 	else
 		intel_crtc->new_config = NULL;
 	mutex_unlock(&crtc->mutex);
+fail_unlock_connector:
+	mutex_unlock(&dev->mode_config.connection_mutex);
+
 	return false;
 }
 
@@ -8113,6 +8118,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
 		}
 
 		mutex_unlock(&crtc->mutex);
+		mutex_unlock(&connector->dev->mode_config.connection_mutex);
 		return;
 	}
 
@@ -8121,6 +8127,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
 		connector->funcs->dpms(connector, old->dpms_mode);
 
 	mutex_unlock(&crtc->mutex);
+	mutex_unlock(&connector->dev->mode_config.connection_mutex);
 }
 
 static int i9xx_pll_refclk(struct drm_device *dev,
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 34ed143..ce52045 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -2953,6 +2953,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
 	u8 sink_irq_vector;
 	u8 link_status[DP_LINK_STATUS_SIZE];
 
+	/* FIXME: This access isn't protected by any locks. */
 	if (!intel_encoder->connectors_active)
 		return;
 
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
index 8b01960..2a5cacd 100644
--- a/drivers/gpu/drm/omapdrm/omap_fb.c
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -346,6 +346,7 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb,
 
 	VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
 
+	/* FIXME: This is racy - no protection against modeset config changes. */
 	while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
 		/* only consider connectors that are part of a chain */
 		if (connector->encoder && connector->encoder->crtc) {
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index ea50e0a..bcfe5d5 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -48,6 +48,7 @@ void radeon_connector_hotplug(struct drm_connector *connector)
 	radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
 
 	/* if the connector is already off, don't turn it back on */
+	/* FIXME: This access isn't protected by any locks. */
 	if (connector->dpms != DRM_MODE_DPMS_ON)
 		return;
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 44d7964..1bd6708 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -738,6 +738,7 @@ struct drm_mode_group {
  */
 struct drm_mode_config {
 	struct mutex mutex; /* protects configuration (mode lists etc.) */
+	struct mutex connection_mutex; /* protects connector->encoder and encoder->crtc links */
 	struct mutex idr_mutex; /* for IDR management */
 	struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
 	/* this is limited to one for now */
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 8/8] drm: convert crtc and connection_mutex to ww_mutex
  2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
                   ` (6 preceding siblings ...)
  2014-05-28 23:57 ` [PATCH 7/8] drm: Split connection_mutex out of mode_config.mutex (v2) Rob Clark
@ 2014-05-28 23:57 ` Rob Clark
  2014-05-29 11:22   ` Daniel Vetter
  7 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2014-05-28 23:57 UTC (permalink / raw)
  To: dri-devel

For atomic, it will be quite necessary to not need to care so much
about locking order.  And 'state' gives us a convenient place to stash a
ww_ctx for any sort of update that needs to grab multiple crtc locks.

Because we will want to eventually make locking even more fine grained
(giving locks to planes, connectors, etc), split out drm_modeset_lock
and drm_modeset_acquire_ctx to track acquired locks.

Atomic will use this to keep track of which locks have been acquired
in a transaction.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/Makefile             |   2 +-
 drivers/gpu/drm/drm_crtc.c           |  87 +++++++++++-----
 drivers/gpu/drm/drm_crtc_helper.c    |   3 +-
 drivers/gpu/drm/drm_fb_helper.c      |   4 +
 drivers/gpu/drm/drm_modeset_lock.c   | 187 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_plane_helper.c   |   2 +-
 drivers/gpu/drm/i915/intel_crt.c     |   5 +-
 drivers/gpu/drm/i915/intel_display.c |  58 +++++++----
 drivers/gpu/drm/i915/intel_drv.h     |   6 +-
 drivers/gpu/drm/i915/intel_sprite.c  |   2 +-
 drivers/gpu/drm/i915/intel_tv.c      |   5 +-
 drivers/gpu/drm/omapdrm/omap_crtc.c  |  10 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c  |   8 +-
 include/drm/drmP.h                   |   5 -
 include/drm/drm_crtc.h               |  15 +--
 include/drm/drm_modeset_lock.h       | 139 ++++++++++++++++++++++++++
 include/uapi/drm/drm_mode.h          |   3 +
 17 files changed, 468 insertions(+), 73 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_modeset_lock.c
 create mode 100644 include/drm/drm_modeset_lock.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 48e38ba..bf4c12d 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -14,7 +14,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_info.o drm_debugfs.o drm_encoder_slave.o \
 		drm_trace_points.o drm_global.o drm_prime.o \
 		drm_rect.o drm_vma_manager.o drm_flip_work.o \
-		drm_plane_helper.o
+		drm_plane_helper.o drm_modeset_lock.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index de1520c..cf622f6 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -37,8 +37,8 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_modeset_lock.h>
 
-#include "drm_crtc_internal.h"
 
 /**
  * drm_modeset_lock_all - take all modeset locks
@@ -50,14 +50,42 @@
  */
 void drm_modeset_lock_all(struct drm_device *dev)
 {
-	struct drm_crtc *crtc;
+	struct drm_mode_config *config = &dev->mode_config;
+	struct drm_modeset_acquire_ctx *ctx;
+	int ret;
 
-	mutex_lock(&dev->mode_config.mutex);
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (WARN_ON(!ctx))
+		return;
 
-	mutex_lock(&dev->mode_config.connection_mutex);
+	mutex_lock(&config->mutex);
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+	drm_modeset_acquire_init(ctx, false, false);
+
+retry:
+	ret = drm_modeset_lock(&config->connection_mutex, ctx);
+	if (ret)
+		goto fail;
+	ret = drm_modeset_lock_all_crtcs(dev, ctx);
+	if (ret)
+		goto fail;
+
+	WARN_ON(config->acquire_ctx);
+
+	/* now we hold the locks, so now that it is safe, stash the
+	 * ctx for drm_modeset_unlock_all():
+	 */
+	config->acquire_ctx = ctx;
+
+	drm_warn_on_modeset_not_all_locked(dev);
+
+	return;
+
+fail:
+	if (ret == -EDEADLK) {
+		drm_modeset_backoff(ctx);
+		goto retry;
+	}
 }
 EXPORT_SYMBOL(drm_modeset_lock_all);
 
@@ -69,12 +97,18 @@ EXPORT_SYMBOL(drm_modeset_lock_all);
  */
 void drm_modeset_unlock_all(struct drm_device *dev)
 {
-	struct drm_crtc *crtc;
+	struct drm_mode_config *config = &dev->mode_config;
+	struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		mutex_unlock(&crtc->mutex);
+	if (WARN_ON(!ctx))
+		return;
+
+	config->acquire_ctx = NULL;
+	drm_modeset_drop_locks(ctx);
+	ww_acquire_fini(&ctx->ww_ctx);
+	drm_modeset_acquire_fini(ctx);
 
-	mutex_unlock(&dev->mode_config.connection_mutex);
+	kfree(ctx);
 
 	mutex_unlock(&dev->mode_config.mutex);
 }
@@ -95,9 +129,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
 		return;
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		WARN_ON(!mutex_is_locked(&crtc->mutex));
+		WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
-	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 }
 EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
@@ -677,6 +711,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);
 
+DEFINE_WW_CLASS(crtc_ww_class);
+
 /**
  * drm_crtc_init_with_planes - Initialise a new CRTC object with
  *    specified primary and cursor planes.
@@ -696,6 +732,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 			      void *cursor,
 			      const struct drm_crtc_funcs *funcs)
 {
+	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
 	crtc->dev = dev;
@@ -703,8 +740,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 	crtc->invert_dimensions = false;
 
 	drm_modeset_lock_all(dev);
-	mutex_init(&crtc->mutex);
-	mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+	drm_modeset_lock_init(&crtc->mutex);
+	/* dropped by _unlock_all(): */
+	drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
 
 	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
 	if (ret)
@@ -712,8 +750,8 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 
 	crtc->base.properties = &crtc->properties;
 
-	list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
-	dev->mode_config.num_crtc++;
+	list_add_tail(&crtc->head, &config->crtc_list);
+	config->num_crtc++;
 
 	crtc->primary = primary;
 	if (primary)
@@ -741,6 +779,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
 	kfree(crtc->gamma_store);
 	crtc->gamma_store = NULL;
 
+	drm_modeset_lock_fini(&crtc->mutex);
+
 	drm_mode_object_put(dev, &crtc->base);
 	list_del(&crtc->head);
 	dev->mode_config.num_crtc--;
@@ -1804,7 +1844,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 	DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
 
 	mutex_lock(&dev->mode_config.mutex);
-	mutex_lock(&dev->mode_config.connection_mutex);
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
 	connector = drm_connector_find(dev, out_resp->connector_id);
 	if (!connector) {
@@ -1903,7 +1943,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 	out_resp->count_encoders = encoders_count;
 
 out:
-	mutex_unlock(&dev->mode_config.connection_mutex);
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
 	mutex_unlock(&dev->mode_config.mutex);
 
 	return ret;
@@ -2487,7 +2527,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 		return -ENOENT;
 	}
 
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	if (req->flags & DRM_MODE_CURSOR_BO) {
 		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
 			ret = -ENXIO;
@@ -2511,7 +2551,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 		}
 	}
 out:
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 
 	return ret;
 
@@ -4195,7 +4235,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 	if (!crtc)
 		return -ENOENT;
 
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	if (crtc->primary->fb == NULL) {
 		/* The framebuffer is currently unbound, presumably
 		 * due to a hotplug event, that userspace has not
@@ -4279,7 +4319,7 @@ out:
 		drm_framebuffer_unreference(fb);
 	if (old_fb)
 		drm_framebuffer_unreference(old_fb);
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 
 	return ret;
 }
@@ -4644,7 +4684,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
 void drm_mode_config_init(struct drm_device *dev)
 {
 	mutex_init(&dev->mode_config.mutex);
-	mutex_init(&dev->mode_config.connection_mutex);
+	drm_modeset_lock_init(&dev->mode_config.connection_mutex);
 	mutex_init(&dev->mode_config.idr_mutex);
 	mutex_init(&dev->mode_config.fb_lock);
 	INIT_LIST_HEAD(&dev->mode_config.fb_list);
@@ -4744,5 +4784,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
 	}
 
 	idr_destroy(&dev->mode_config.crtc_idr);
+	drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 5b93caf..4eaded8 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -89,8 +89,7 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
 	struct drm_device *dev = encoder->dev;
 
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
-	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
-
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 		if (connector->encoder == encoder)
 			return true;
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 1e28ba6..25f687d 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -355,6 +355,10 @@ static bool drm_fb_helper_force_kernel_mode(void)
 		if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
 			continue;
 
+		/* NOTE: we use lockless flag below to avoid grabbing other
+		 * modeset locks.  So just trylock the underlying mutex
+		 * directly:
+		 */
 		if (!mutex_trylock(&dev->mode_config.mutex)) {
 			error = true;
 			continue;
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
new file mode 100644
index 0000000..6580d6c
--- /dev/null
+++ b/drivers/gpu/drm/drm_modeset_lock.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * 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
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_modeset_lock.h>
+
+
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+		bool nolock, bool nonblock)
+{
+	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
+	INIT_LIST_HEAD(&ctx->locked);
+	mutex_init(&ctx->mutex);
+	ctx->nolock = nolock;
+	ctx->nonblock = nonblock;
+}
+EXPORT_SYMBOL(drm_modeset_acquire_init);
+
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
+{
+	WARN_ON(ctx->contended);
+	/*
+	 * NOTE: it is intentional that ww_acquire_fini() is not called
+	 * here.. due to the way lock handover works in drm_atomic
+	 */
+	mutex_destroy(&ctx->mutex);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_fini);
+
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
+{
+	WARN_ON(ctx->contended);
+	mutex_lock(&ctx->mutex);
+	while (!list_empty(&ctx->locked)) {
+		struct drm_modeset_lock *lock;
+
+		lock = list_first_entry(&ctx->locked,
+				struct drm_modeset_lock, head);
+
+		drm_modeset_unlock(lock);
+	}
+	mutex_unlock(&ctx->mutex);
+}
+EXPORT_SYMBOL(drm_modeset_drop_locks);
+
+static int modeset_lock(struct drm_modeset_lock *lock,
+		struct drm_modeset_acquire_ctx *ctx,
+		bool interruptible, bool slow)
+{
+	int ret;
+
+	if (ctx->nolock)
+		return 0;
+
+	WARN_ON(ctx->frozen);    /* all locks should be held by now! */
+	WARN_ON(ctx->contended);
+
+retry:
+	if (interruptible) {
+		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
+	} else if (slow) {
+		ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
+		ret = 0;
+	} else {
+		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
+	}
+	if (!ret) {
+		if (lock->atomic_pending) {
+			/* some other pending update with dropped locks */
+			ww_mutex_unlock(&lock->mutex);
+			if (ctx->nonblock)
+				return -EBUSY;
+			wait_event(lock->event, !lock->atomic_pending);
+			goto retry;
+		}
+		lock->atomic_pending = true;
+		WARN_ON(!list_empty(&lock->head));
+		list_add(&lock->head, &ctx->locked);
+	} else if (ret == -EALREADY) {
+		/* we already hold the lock.. this is fine */
+		ret = 0;
+	} else if (ret == -EDEADLK) {
+		ctx->contended = lock;
+	}
+
+	return ret;
+}
+
+void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
+{
+	struct drm_modeset_lock *contended = ctx->contended;
+
+	ctx->contended = NULL;
+
+	if (WARN_ON(!contended))
+		return;
+
+	drm_modeset_drop_locks(ctx);
+
+	modeset_lock(contended, ctx, false, true);
+}
+EXPORT_SYMBOL(drm_modeset_backoff);
+
+/**
+ * drm_modeset_lock - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * If ctx is not NULL, then then it's acquire context is used
+ * and the lock does not need to be explicitly unlocked, it
+ * will be automatically unlocked when the atomic update is
+ * complete
+ */
+int drm_modeset_lock(struct drm_modeset_lock *lock,
+		struct drm_modeset_acquire_ctx *ctx)
+{
+	if (ctx)
+		return modeset_lock(lock, ctx, false, false);
+
+	ww_mutex_lock(&lock->mutex, NULL);
+	return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock);
+
+int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
+		struct drm_modeset_acquire_ctx *ctx)
+{
+	if (ctx)
+		return modeset_lock(lock, ctx, true, false);
+
+	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
+}
+EXPORT_SYMBOL(drm_modeset_lock_interruptible);
+
+/**
+ * drm_modeset_unlock - drop modeset lock
+ * @lock: lock to release
+ */
+void drm_modeset_unlock(struct drm_modeset_lock *lock)
+{
+	list_del_init(&lock->head);
+	lock->atomic_pending = false;
+	ww_mutex_unlock(&lock->mutex);
+	wake_up_all(&lock->event);
+}
+EXPORT_SYMBOL(drm_modeset_unlock);
+
+/**
+ * drm_modeset_lock_all_crtcs - helper to drm_modeset_lock() all CRTCs
+ */
+int drm_modeset_lock_all_crtcs(struct drm_device *dev,
+		struct drm_modeset_acquire_ctx *ctx)
+{
+	struct drm_mode_config *config = &dev->mode_config;
+	struct drm_crtc *crtc;
+	int ret = 0;
+
+	list_for_each_entry(crtc, &config->crtc_list, head) {
+		ret = drm_modeset_lock(&crtc->mutex, ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index 458d9bf..6301ec6 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -59,7 +59,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
 	 * need to grab the connection_mutex here to be able to make these
 	 * checks.
 	 */
-	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 		if (connector->encoder && connector->encoder->crtc == crtc) {
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 22d8347..33328fc 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -630,6 +630,7 @@ intel_crt_detect(struct drm_connector *connector, bool force)
 	enum intel_display_power_domain power_domain;
 	enum drm_connector_status status;
 	struct intel_load_detect_pipe tmp;
+	struct drm_modeset_acquire_ctx ctx;
 
 	intel_runtime_pm_get(dev_priv);
 
@@ -673,12 +674,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
 	}
 
 	/* for pre-945g platforms use load detect */
-	if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
+	if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) {
 		if (intel_crt_detect_ddc(connector))
 			status = connector_status_connected;
 		else
 			status = intel_crt_load_detect(crt);
-		intel_release_load_detect_pipe(connector, &tmp);
+		intel_release_load_detect_pipe(connector, &tmp, &ctx);
 	} else
 		status = connector_status_unknown;
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 5844f07..ba6cbbb 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2363,7 +2363,7 @@ void intel_display_handle_reset(struct drm_device *dev)
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-		mutex_lock(&crtc->mutex);
+		drm_modeset_lock(&crtc->mutex, NULL);
 		/*
 		 * FIXME: Once we have proper support for primary planes (and
 		 * disabling them without disabling the entire crtc) allow again
@@ -2374,7 +2374,7 @@ void intel_display_handle_reset(struct drm_device *dev)
 							       crtc->primary->fb,
 							       crtc->x,
 							       crtc->y);
-		mutex_unlock(&crtc->mutex);
+		drm_modeset_unlock(&crtc->mutex);
 	}
 }
 
@@ -7972,7 +7972,8 @@ mode_fits_in_fbdev(struct drm_device *dev,
 
 bool intel_get_load_detect_pipe(struct drm_connector *connector,
 				struct drm_display_mode *mode,
-				struct intel_load_detect_pipe *old)
+				struct intel_load_detect_pipe *old,
+				struct drm_modeset_acquire_ctx *ctx)
 {
 	struct intel_crtc *intel_crtc;
 	struct intel_encoder *intel_encoder =
@@ -7982,13 +7983,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 	struct drm_crtc *crtc = NULL;
 	struct drm_device *dev = encoder->dev;
 	struct drm_framebuffer *fb;
-	int i = -1;
+	struct drm_mode_config *config = &dev->mode_config;
+	int ret, i = -1;
 
 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
 		      connector->base.id, drm_get_connector_name(connector),
 		      encoder->base.id, drm_get_encoder_name(encoder));
 
-	mutex_lock(&dev->mode_config.connection_mutex);
+	drm_modeset_acquire_init(ctx, false, false);
+
+retry:
+	ret = drm_modeset_lock(&config->connection_mutex, ctx);
+	if (ret)
+		goto fail_unlock;
 
 	/*
 	 * Algorithm gets a little messy:
@@ -8004,7 +8011,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 	if (encoder->crtc) {
 		crtc = encoder->crtc;
 
-		mutex_lock(&crtc->mutex);
+		ret = drm_modeset_lock(&crtc->mutex, ctx);
+		if (ret)
+			goto fail_unlock;
 
 		old->dpms_mode = connector->dpms;
 		old->load_detect_temp = false;
@@ -8017,7 +8026,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 	}
 
 	/* Find an unused one (if possible) */
-	list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
+	list_for_each_entry(possible_crtc, &config->crtc_list, head) {
 		i++;
 		if (!(encoder->possible_crtcs & (1 << i)))
 			continue;
@@ -8032,10 +8041,12 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 	 */
 	if (!crtc) {
 		DRM_DEBUG_KMS("no pipe available for load-detect\n");
-		goto fail_unlock_connector;
+		goto fail_unlock;
 	}
 
-	mutex_lock(&crtc->mutex);
+	ret = drm_modeset_lock(&crtc->mutex, ctx);
+	if (ret)
+		goto fail_unlock;
 	intel_encoder->new_crtc = to_intel_crtc(crtc);
 	to_intel_connector(connector)->new_encoder = intel_encoder;
 
@@ -8085,15 +8096,22 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 		intel_crtc->new_config = &intel_crtc->config;
 	else
 		intel_crtc->new_config = NULL;
-	mutex_unlock(&crtc->mutex);
-fail_unlock_connector:
-	mutex_unlock(&dev->mode_config.connection_mutex);
+fail_unlock:
+	if (ret == -EDEADLK) {
+		drm_modeset_backoff(ctx);
+		goto retry;
+	}
+
+	drm_modeset_drop_locks(ctx);
+	ww_acquire_fini(&ctx->ww_ctx);
+	drm_modeset_acquire_fini(ctx);
 
 	return false;
 }
 
 void intel_release_load_detect_pipe(struct drm_connector *connector,
-				    struct intel_load_detect_pipe *old)
+				    struct intel_load_detect_pipe *old,
+				    struct drm_modeset_acquire_ctx *ctx)
 {
 	struct intel_encoder *intel_encoder =
 		intel_attached_encoder(connector);
@@ -8117,8 +8135,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
 			drm_framebuffer_unreference(old->release_fb);
 		}
 
-		mutex_unlock(&crtc->mutex);
-		mutex_unlock(&connector->dev->mode_config.connection_mutex);
+		goto unlock;
 		return;
 	}
 
@@ -8126,8 +8143,10 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
 	if (old->dpms_mode != DRM_MODE_DPMS_ON)
 		connector->funcs->dpms(connector, old->dpms_mode);
 
-	mutex_unlock(&crtc->mutex);
-	mutex_unlock(&connector->dev->mode_config.connection_mutex);
+unlock:
+	drm_modeset_drop_locks(ctx);
+	ww_acquire_fini(&ctx->ww_ctx);
+	drm_modeset_acquire_fini(ctx);
 }
 
 static int i9xx_pll_refclk(struct drm_device *dev,
@@ -11385,6 +11404,7 @@ static void intel_enable_pipe_a(struct drm_device *dev)
 	struct intel_connector *connector;
 	struct drm_connector *crt = NULL;
 	struct intel_load_detect_pipe load_detect_temp;
+	struct drm_modeset_acquire_ctx ctx;
 
 	/* We can't just switch on the pipe A, we need to set things up with a
 	 * proper mode and output configuration. As a gross hack, enable pipe A
@@ -11401,8 +11421,8 @@ static void intel_enable_pipe_a(struct drm_device *dev)
 	if (!crt)
 		return;
 
-	if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp))
-		intel_release_load_detect_pipe(crt, &load_detect_temp);
+	if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx))
+		intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx);
 
 
 }
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d8b540b..824f8ae 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -721,9 +721,11 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
 			 struct intel_digital_port *dport);
 bool intel_get_load_detect_pipe(struct drm_connector *connector,
 				struct drm_display_mode *mode,
-				struct intel_load_detect_pipe *old);
+				struct intel_load_detect_pipe *old,
+				struct drm_modeset_acquire_ctx *ctx);
 void intel_release_load_detect_pipe(struct drm_connector *connector,
-				    struct intel_load_detect_pipe *old);
+				    struct intel_load_detect_pipe *old,
+				    struct drm_modeset_acquire_ctx *ctx);
 int intel_pin_and_fence_fb_obj(struct drm_device *dev,
 			       struct drm_i915_gem_object *obj,
 			       struct intel_ring_buffer *pipelined);
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 213cd58..c235546 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -55,7 +55,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl
 	int scanline, min, max, vblank_start;
 	DEFINE_WAIT(wait);
 
-	WARN_ON(!mutex_is_locked(&crtc->base.mutex));
+	WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
 
 	vblank_start = mode->crtc_vblank_start;
 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index e0193e8..97d9fc9 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1321,10 +1321,11 @@ intel_tv_detect(struct drm_connector *connector, bool force)
 
 	if (force) {
 		struct intel_load_detect_pipe tmp;
+		struct drm_modeset_acquire_ctx ctx;
 
-		if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
+		if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) {
 			type = intel_tv_detect_type(intel_tv, connector);
-			intel_release_load_detect_pipe(connector, &tmp);
+			intel_release_load_detect_pipe(connector, &tmp, &ctx);
 		} else
 			return connector_status_unknown;
 	} else
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index e3c47a8..2d28dc3 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -319,13 +319,13 @@ static void page_flip_worker(struct work_struct *work)
 	struct drm_display_mode *mode = &crtc->mode;
 	struct drm_gem_object *bo;
 
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
 			0, 0, mode->hdisplay, mode->vdisplay,
 			crtc->x << 16, crtc->y << 16,
 			mode->hdisplay << 16, mode->vdisplay << 16,
 			vblank_cb, crtc);
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 
 	bo = omap_framebuffer_bo(crtc->primary->fb, 0);
 	drm_gem_object_unreference_unlocked(bo);
@@ -465,7 +465,7 @@ static void apply_worker(struct work_struct *work)
 	 * the callbacks and list modification all serialized
 	 * with respect to modesetting ioctls from userspace.
 	 */
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	dispc_runtime_get();
 
 	/*
@@ -510,7 +510,7 @@ static void apply_worker(struct work_struct *work)
 
 out:
 	dispc_runtime_put();
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 }
 
 int omap_crtc_apply(struct drm_crtc *crtc,
@@ -518,7 +518,7 @@ int omap_crtc_apply(struct drm_crtc *crtc,
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 
-	WARN_ON(!mutex_is_locked(&crtc->mutex));
+	WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
 	/* no need to queue it again if it is already queued: */
 	if (apply->queued)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index e7199b4..8f3edc4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -187,7 +187,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
 	 * can do this since the caller in the drm core doesn't check anything
 	 * which is protected by any looks.
 	 */
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 	drm_modeset_lock_all(dev_priv->dev);
 
 	/* A lot of the code assumes this */
@@ -252,7 +252,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
 	ret = 0;
 out:
 	drm_modeset_unlock_all(dev_priv->dev);
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 
 	return ret;
 }
@@ -273,7 +273,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 	 * can do this since the caller in the drm core doesn't check anything
 	 * which is protected by any looks.
 	 */
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 	drm_modeset_lock_all(dev_priv->dev);
 
 	vmw_cursor_update_position(dev_priv, shown,
@@ -281,7 +281,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 				   du->cursor_y + du->hotspot_y);
 
 	drm_modeset_unlock_all(dev_priv->dev);
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 
 	return 0;
 }
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 12f10bc..a9b8a5d 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1184,11 +1184,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev)
 	return ret;
 }
 
-static inline bool drm_modeset_is_locked(struct drm_device *dev)
-{
-	return mutex_is_locked(&dev->mode_config.mutex);
-}
-
 static inline bool drm_is_render_client(const struct drm_file *file_priv)
 {
 	return file_priv->minor->type == DRM_MINOR_RENDER;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 1bd6708..deadb32 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -33,6 +33,7 @@
 #include <linux/hdmi.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_modeset_lock.h>
 
 struct drm_device;
 struct drm_mode_set;
@@ -205,6 +206,10 @@ struct drm_property {
 	struct list_head enum_blob_list;
 };
 
+void drm_modeset_lock_all(struct drm_device *dev);
+void drm_modeset_unlock_all(struct drm_device *dev);
+void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
+
 struct drm_crtc;
 struct drm_connector;
 struct drm_encoder;
@@ -280,6 +285,7 @@ struct drm_crtc_funcs {
  * drm_crtc - central CRTC control structure
  * @dev: parent DRM device
  * @head: list management
+ * @mutex: per-CRTC locking
  * @base: base KMS object for ID tracking etc.
  * @primary: primary plane for this CRTC
  * @cursor: cursor plane for this CRTC
@@ -314,7 +320,7 @@ struct drm_crtc {
 	 * state, ...) and a write lock for everything which can be update
 	 * without a full modeset (fb, cursor data, ...)
 	 */
-	struct mutex mutex;
+	struct drm_modeset_lock mutex;
 
 	struct drm_mode_object base;
 
@@ -738,7 +744,8 @@ struct drm_mode_group {
  */
 struct drm_mode_config {
 	struct mutex mutex; /* protects configuration (mode lists etc.) */
-	struct mutex connection_mutex; /* protects connector->encoder and encoder->crtc links */
+	struct drm_modeset_lock connection_mutex; /* protects connector->encoder and encoder->crtc links */
+	struct drm_modeset_acquire_ctx *acquire_ctx; /* for legacy _lock_all() / _unlock_all() */
 	struct mutex idr_mutex; /* for IDR management */
 	struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
 	/* this is limited to one for now */
@@ -839,10 +846,6 @@ struct drm_prop_enum_list {
 	char *name;
 };
 
-extern void drm_modeset_lock_all(struct drm_device *dev);
-extern void drm_modeset_unlock_all(struct drm_device *dev);
-extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
-
 extern int drm_crtc_init_with_planes(struct drm_device *dev,
 				     struct drm_crtc *crtc,
 				     struct drm_plane *primary,
diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
new file mode 100644
index 0000000..2630da3
--- /dev/null
+++ b/include/drm/drm_modeset_lock.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * 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
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DRM_MODESET_LOCK_H_
+#define DRM_MODESET_LOCK_H_
+
+#include <linux/ww_mutex.h>
+
+struct drm_modeset_lock;
+
+struct drm_modeset_acquire_ctx {
+
+	struct ww_acquire_ctx ww_ctx;
+
+	bool nolock : 1;
+	bool nonblock : 1;
+
+	/* just for debugging, the context is 'frozen' in drm_atomic_check()
+	 * to catch anyone who might be trying to acquire a lock after it is
+	 * too late.
+	 */
+	bool frozen : 1;
+
+	/* contended lock: if a lock is contended you should only call
+	 * drm_modeset_backoff() which drops locks and slow-locks the
+	 * contended lock.
+	 */
+	struct drm_modeset_lock *contended;
+
+	/* list of 'struct drm_modeset_lock': */
+	struct list_head locked;
+
+	/* currently simply for protecting against 'locked' list manipulation
+	 * between original thread calling atomic->end() and driver thread
+	 * calling back drm_atomic_commit_unlocked().
+	 *
+	 * Other spots are sufficiently synchronized by virtue of holding
+	 * the lock's ww_mutex.  But during the lock/resource hand-over to the
+	 * driver thread (drop_locks()/grab_locks()), we cannot rely on this.
+	 */
+	struct mutex mutex;
+};
+
+/**
+ * drm_modeset_lock - used for locking modeset resources.
+ * @mutex: resource locking
+ * @atomic_pending: is this resource part of a still-pending
+ *    atomic update
+ * @head: used to hold it's place on state->locked list when
+ *    part of an atomic update
+ *
+ * Used for locking CRTCs and other modeset resources.
+ */
+struct drm_modeset_lock {
+	/**
+	 * modeset lock
+	 */
+	struct ww_mutex mutex;
+
+	/**
+	 * Are we busy (pending asynchronous/NONBLOCK update)?  Any further
+	 * asynchronous update will return -EBUSY if it also needs to acquire
+	 * this lock.  While a synchronous update will block until the pending
+	 * async update completes.
+	 *
+	 * Drivers must ensure the update is completed before sending vblank
+	 * event to userspace.  Typically this just means don't send event
+	 * before drm_atomic_commit_unlocked() returns.
+	 */
+	bool atomic_pending;
+
+	/**
+	 * Resources that are locked as part of an atomic update are added
+	 * to a list (so we know what to unlock at the end).
+	 */
+	struct list_head head;
+
+	/**
+	 * For waiting on atomic_pending locks, if not a NONBLOCK operation.
+	 */
+	wait_queue_head_t event;
+};
+
+extern struct ww_class crtc_ww_class;
+
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+		bool nolock, bool nonblock);
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
+
+static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock)
+{
+	ww_mutex_init(&lock->mutex, &crtc_ww_class);
+	INIT_LIST_HEAD(&lock->head);
+	init_waitqueue_head(&lock->event);
+}
+
+static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
+{
+	WARN_ON(!list_empty(&lock->head));
+}
+
+static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock)
+{
+	return ww_mutex_is_locked(&lock->mutex);
+}
+
+int drm_modeset_lock(struct drm_modeset_lock *lock,
+		struct drm_modeset_acquire_ctx *ctx);
+int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
+		struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_unlock(struct drm_modeset_lock *lock);
+
+struct drm_device;
+int drm_modeset_lock_all_crtcs(struct drm_device *dev,
+		struct drm_modeset_acquire_ctx *ctx);
+
+#endif /* DRM_MODESET_LOCK_H_ */
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index ded505e..6421edc 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -511,4 +511,7 @@ struct drm_mode_destroy_dumb {
 	uint32_t handle;
 };
 
+#define DRM_MODE_ATOMIC_NONBLOCK  0x0200
+#define DRM_MODE_ATOMIC_NOLOCK    0x8000  /* only used internally */
+
 #endif
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/8] drm: add object property type
  2014-05-28 23:57 ` [PATCH 1/8] drm: add object property type Rob Clark
@ 2014-05-29  8:01   ` David Herrmann
  2014-05-29 11:45     ` Rob Clark
  2014-05-30  7:57   ` Thierry Reding
  1 sibling, 1 reply; 23+ messages in thread
From: David Herrmann @ 2014-05-29  8:01 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

Hi

On Thu, May 29, 2014 at 1:57 AM, Rob Clark <robdclark@gmail.com> wrote:
> An object property is an id (idr) for a drm mode object.  This
> will allow a property to be used set/get a framebuffer, CRTC, etc.
>
> Signed-off-by: Rob Clark <robdclark@gmail.com>
> ---
>  drivers/gpu/drm/drm_crtc.c  | 50 +++++++++++++++++++++++++++++++++++++--------
>  include/drm/drm_crtc.h      | 27 ++++++++++++++++++++++++
>  include/uapi/drm/drm_mode.h | 14 +++++++++++++
>  3 files changed, 82 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 37a3e07..61ddc66 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -3117,6 +3117,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
>         if (!property)
>                 return NULL;
>
> +       property->dev = dev;
> +

Ugh.. embarrassing.

>         if (num_values) {
>                 property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
>                 if (!property->values)
> @@ -3137,6 +3139,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
>         }
>
>         list_add_tail(&property->head, &dev->mode_config.property_list);
> +
> +       BUG_ON(!drm_property_type_valid(property));

WARN_ON(). There is no reason to panic.. I mean, it's obviously a bug,
but we can continue just normally.

> +
>         return property;
>  fail:
>         kfree(property->values);
> @@ -3274,6 +3279,23 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
>  }
>  EXPORT_SYMBOL(drm_property_create_range);
>
> +struct drm_property *drm_property_create_object(struct drm_device *dev,
> +                                        int flags, const char *name, uint32_t type)
> +{
> +       struct drm_property *property;
> +
> +       flags |= DRM_MODE_PROP_OBJECT;
> +
> +       property = drm_property_create(dev, flags, name, 1);
> +       if (!property)
> +               return NULL;
> +
> +       property->values[0] = type;
> +
> +       return property;
> +}
> +EXPORT_SYMBOL(drm_property_create_object);
> +
>  /**
>   * drm_property_add_enum - add a possible value to an enumeration property
>   * @property: enumeration property to change
> @@ -3294,14 +3316,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
>  {
>         struct drm_property_enum *prop_enum;
>
> -       if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
> +       if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
> +                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
>                 return -EINVAL;
>
>         /*
>          * Bitmask enum properties have the additional constraint of values
>          * from 0 to 63
>          */
> -       if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
> +       if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
> +                       (value > 63))
>                 return -EINVAL;
>
>         if (!list_empty(&property->enum_blob_list)) {
> @@ -3484,10 +3508,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
>         }
>         property = obj_to_property(obj);
>
> -       if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
> +       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
> +                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
>                 list_for_each_entry(prop_enum, &property->enum_blob_list, head)
>                         enum_count++;
> -       } else if (property->flags & DRM_MODE_PROP_BLOB) {
> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
>                 list_for_each_entry(prop_blob, &property->enum_blob_list, head)
>                         blob_count++;
>         }
> @@ -3509,7 +3534,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
>         }
>         out_resp->count_values = value_count;
>
> -       if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
> +       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
> +                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
>                 if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
>                         copied = 0;
>                         enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
> @@ -3531,7 +3557,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
>                 out_resp->count_enum_blobs = enum_count;
>         }
>
> -       if (property->flags & DRM_MODE_PROP_BLOB) {
> +       if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
>                 if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
>                         copied = 0;
>                         blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
> @@ -3687,19 +3713,25 @@ static bool drm_property_change_is_valid(struct drm_property *property,
>  {
>         if (property->flags & DRM_MODE_PROP_IMMUTABLE)
>                 return false;
> -       if (property->flags & DRM_MODE_PROP_RANGE) {
> +
> +       if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
>                 if (value < property->values[0] || value > property->values[1])
>                         return false;
>                 return true;
> -       } else if (property->flags & DRM_MODE_PROP_BITMASK) {
> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
>                 int i;
>                 uint64_t valid_mask = 0;
>                 for (i = 0; i < property->num_values; i++)
>                         valid_mask |= (1ULL << property->values[i]);
>                 return !(value & ~valid_mask);
> -       } else if (property->flags & DRM_MODE_PROP_BLOB) {
> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
>                 /* Only the driver knows */
>                 return true;
> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
> +               /* a zero value for an object property translates to null: */
> +               if (value == 0)
> +                       return true;
> +               return drm_property_get_obj(property, value) != NULL;
>         } else {
>                 int i;
>                 for (i = 0; i < property->num_values; i++)
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 5c1c31c..c1c243f 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -190,6 +190,7 @@ struct drm_property {
>         char name[DRM_PROP_NAME_LEN];
>         uint32_t num_values;
>         uint64_t *values;
> +       struct drm_device *dev;
>
>         struct list_head enum_blob_list;
>  };
> @@ -931,6 +932,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
>
>  extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
>                                                 struct edid *edid);
> +
> +static inline bool drm_property_type_is(struct drm_property *property,
> +               uint32_t type)
> +{
> +       /* instanceof for props.. handles extended type vs original types: */
> +       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
> +               return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
> +       return property->flags & type;
> +}
> +
> +static inline bool drm_property_type_valid(struct drm_property *property)
> +{
> +       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
> +               return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
> +       return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
> +}

I really don't get your naming scheme. I mean, both objects operate on
"drm_property" objects, so the drm_property_* prefixes looks good. But
as suffix, I'd expect a properly named function, so something like
"drm_property_is_type()" or "*_is_of_type()" and
"drm_property_is_valid_type()". Your naming-scheme looks more like
these functions work on "drm_property_type" objects (which they
don't).

Just saying.. no reason to argue about taste here.

> +
>  extern int drm_object_property_set_value(struct drm_mode_object *obj,
>                                          struct drm_property *property,
>                                          uint64_t val);
> @@ -964,6 +982,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
>  struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
>                                          const char *name,
>                                          uint64_t min, uint64_t max);
> +struct drm_property *drm_property_create_object(struct drm_device *dev,
> +                                        int flags, const char *name, uint32_t type);
>  extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
>  extern int drm_property_add_enum(struct drm_property *property, int index,
>                                  uint64_t value, const char *name);
> @@ -980,6 +1000,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
>                                          int gamma_size);
>  extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
>                 uint32_t id, uint32_t type);
> +
> +static inline struct drm_mode_object *
> +drm_property_get_obj(struct drm_property *property, uint64_t value)
> +{
> +       return drm_mode_object_find(property->dev, value, property->values[0]);
> +}
> +
>  /* IOCTLs */
>  extern int drm_mode_getresources(struct drm_device *dev,
>                                  void *data, struct drm_file *file_priv);
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index f104c26..5b530c7 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -251,6 +251,20 @@ struct drm_mode_get_connector {
>  #define DRM_MODE_PROP_BLOB     (1<<4)
>  #define DRM_MODE_PROP_BITMASK  (1<<5) /* bitmask of enumerated types */
>
> +/* non-extended types: legacy bitmask, one bit per type: */
> +#define DRM_MODE_PROP_LEGACY_TYPE  ( \
> +               DRM_MODE_PROP_RANGE | \
> +               DRM_MODE_PROP_ENUM | \
> +               DRM_MODE_PROP_BLOB | \
> +               DRM_MODE_PROP_BITMASK)
> +
> +/* extended-types: rather than continue to consume a bit per type,
> + * grab a chunk of the bits to use as integer type id.
> + */
> +#define DRM_MODE_PROP_EXTENDED_TYPE    0x0000ffc0
> +#define DRM_MODE_PROP_TYPE(n)          ((n) << 6)
> +#define DRM_MODE_PROP_OBJECT           DRM_MODE_PROP_TYPE(1)
> +

Oh god, this is ugly. But I get your intention. Using bit-masks for
types wasn't smart at all, and mixing it with flags even worse. But
ok.

With or without my nitpicks fixed, this is:

Reviewed-by: David Herrmann <dh.herrmann@gmail.com>

Thanks
David

>  struct drm_mode_property_enum {
>         __u64 value;
>         char name[DRM_PROP_NAME_LEN];
> --
> 1.9.3
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/8] drm: add signed-range property type
  2014-05-28 23:57 ` [PATCH 2/8] drm: add signed-range " Rob Clark
@ 2014-05-29  8:29   ` David Herrmann
  2014-05-29 11:51     ` Rob Clark
  0 siblings, 1 reply; 23+ messages in thread
From: David Herrmann @ 2014-05-29  8:29 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

Hi

On Thu, May 29, 2014 at 1:57 AM, Rob Clark <robdclark@gmail.com> wrote:
> Like range, but values are signed.
>
> Signed-off-by: Rob Clark <robdclark@gmail.com>
> ---
>  drivers/gpu/drm/drm_crtc.c  | 45 +++++++++++++++++++++++++++++++++------------
>  include/drm/drm_crtc.h      | 12 ++++++++++++
>  include/uapi/drm/drm_mode.h |  1 +
>  3 files changed, 46 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 61ddc66..88a0741 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -3242,6 +3242,22 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
>  }
>  EXPORT_SYMBOL(drm_property_create_bitmask);
>
> +static struct drm_property *property_create_range(struct drm_device *dev,
> +                                        int flags, const char *name,
> +                                        uint64_t min, uint64_t max)
> +{
> +       struct drm_property *property;
> +
> +       property = drm_property_create(dev, flags, name, 2);
> +       if (!property)
> +               return NULL;
> +
> +       property->values[0] = min;
> +       property->values[1] = max;
> +
> +       return property;
> +}
> +
>  /**
>   * drm_property_create - create a new ranged property type
>   * @dev: drm device
> @@ -3264,21 +3280,20 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
>                                          const char *name,
>                                          uint64_t min, uint64_t max)
>  {
> -       struct drm_property *property;
> -
> -       flags |= DRM_MODE_PROP_RANGE;
> -
> -       property = drm_property_create(dev, flags, name, 2);
> -       if (!property)
> -               return NULL;
> -
> -       property->values[0] = min;
> -       property->values[1] = max;
> -
> -       return property;
> +       return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
> +                       name, min, max);
>  }
>  EXPORT_SYMBOL(drm_property_create_range);
>
> +struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
> +                                        int flags, const char *name,
> +                                        int64_t min, int64_t max)
> +{
> +       return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
> +                       name, I642U64(min), I642U64(max));
> +}
> +EXPORT_SYMBOL(drm_property_create_signed_range);
> +
>  struct drm_property *drm_property_create_object(struct drm_device *dev,
>                                          int flags, const char *name, uint32_t type)
>  {
> @@ -3718,6 +3733,12 @@ static bool drm_property_change_is_valid(struct drm_property *property,
>                 if (value < property->values[0] || value > property->values[1])
>                         return false;
>                 return true;
> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
> +               int64_t svalue = U642I64(value);
> +               if (svalue < U642I64(property->values[0]) ||
> +                               svalue > U642I64(property->values[1]))
> +                       return false;
> +               return true;
>         } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
>                 int i;
>                 uint64_t valid_mask = 0;
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index c1c243f..9bd3551 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -64,6 +64,15 @@ struct drm_object_properties {
>         uint64_t values[DRM_OBJECT_MAX_PROPERTY];
>  };
>
> +static inline int64_t U642I64(uint64_t val)
> +{
> +       return (int64_t)*((int64_t *)&val);

The cast to "(int64_t)" is unneeded. Dereferencing (int64_t*) will
always yield (int64_t).

Same below.

Btw., why are these macros needed? unsigned->signed conversion is
well-defined. signed->unsigned is undefined, but I thought we rely on
static reinterpretation in the kernel. Dunno.. maybe I'm wrong.

Anyhow, this is:

Reviewed-by: David Herrmann <dh.herrmann@gmail.com>

Thanks
David

> +}
> +static inline uint64_t I642U64(int64_t val)
> +{
> +       return (uint64_t)*((uint64_t *)&val);
> +}
> +
>  enum drm_connector_force {
>         DRM_FORCE_UNSPECIFIED,
>         DRM_FORCE_OFF,
> @@ -982,6 +991,9 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
>  struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
>                                          const char *name,
>                                          uint64_t min, uint64_t max);
> +struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
> +                                        int flags, const char *name,
> +                                        int64_t min, int64_t max);
>  struct drm_property *drm_property_create_object(struct drm_device *dev,
>                                          int flags, const char *name, uint32_t type);
>  extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index 5b530c7..ded505e 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -264,6 +264,7 @@ struct drm_mode_get_connector {
>  #define DRM_MODE_PROP_EXTENDED_TYPE    0x0000ffc0
>  #define DRM_MODE_PROP_TYPE(n)          ((n) << 6)
>  #define DRM_MODE_PROP_OBJECT           DRM_MODE_PROP_TYPE(1)
> +#define DRM_MODE_PROP_SIGNED_RANGE     DRM_MODE_PROP_TYPE(2)
>
>  struct drm_mode_property_enum {
>         __u64 value;
> --
> 1.9.3
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 5/8] drm: spiff out FB refcnting traces
  2014-05-28 23:57 ` [PATCH 5/8] drm: spiff out FB refcnting traces Rob Clark
@ 2014-05-29  8:36   ` David Herrmann
  0 siblings, 0 replies; 23+ messages in thread
From: David Herrmann @ 2014-05-29  8:36 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

Hi

On Thu, May 29, 2014 at 1:57 AM, Rob Clark <robdclark@gmail.com> wrote:
> I find myself making this change locally whenever debugging FB reference
> counting.  Which seems a bit silly.
>
> Signed-off-by: Rob Clark <robdclark@gmail.com>
> ---
>  drivers/gpu/drm/drm_crtc.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index b975575..4d243fd 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -519,7 +519,7 @@ EXPORT_SYMBOL(drm_framebuffer_lookup);
>   */
>  void drm_framebuffer_unreference(struct drm_framebuffer *fb)
>  {
> -       DRM_DEBUG("FB ID: %d\n", fb->base.id);
> +       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);

atomic_read()!!!

With that fixed, this is:

Reviewed-by: David Herrmann <dh.herrmann@gmail.com>

Thanks
David

>         kref_put(&fb->refcount, drm_framebuffer_free);
>  }
>  EXPORT_SYMBOL(drm_framebuffer_unreference);
> @@ -532,7 +532,7 @@ EXPORT_SYMBOL(drm_framebuffer_unreference);
>   */
>  void drm_framebuffer_reference(struct drm_framebuffer *fb)
>  {
> -       DRM_DEBUG("FB ID: %d\n", fb->base.id);
> +       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);
>         kref_get(&fb->refcount);
>  }
>  EXPORT_SYMBOL(drm_framebuffer_reference);
> @@ -544,7 +544,7 @@ static void drm_framebuffer_free_bug(struct kref *kref)
>
>  static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
>  {
> -       DRM_DEBUG("FB ID: %d\n", fb->base.id);
> +       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);
>         kref_put(&fb->refcount, drm_framebuffer_free_bug);
>  }
>
> --
> 1.9.3
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/8] drm: Allow drm_mode_object_find() to look up an object of any type
  2014-05-28 23:57 ` [PATCH 4/8] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
@ 2014-05-29 11:18   ` Daniel Vetter
  2014-05-29 12:01     ` Rob Clark
  0 siblings, 1 reply; 23+ messages in thread
From: Daniel Vetter @ 2014-05-29 11:18 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Wed, May 28, 2014 at 07:57:21PM -0400, Rob Clark wrote:
> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> To avoid having to pass object types from userspace for atomic mode
> setting ioctl, allow drm_mode_object_find() to look up an object of any
> type. This will only work as long as the all object types share the ID
> space.
> 
> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Rob Clark <robdclark@gmail.com>

Still NACK. Iirc you only want a boolean "does this exist" so please add a
new function for this. Returning a full pointer for framebuffer is simply
unsafe, allowing that a call for trouble. Yes, I know your current code is
safe but I don't want to freak out and do an ad-hoc audit every time I
stumble over this again.
-Daniel

> ---
>  drivers/gpu/drm/drm_crtc.c | 3 ++-
>  include/drm/drm_crtc.h     | 1 +
>  2 files changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index c53ddc3..b975575 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -409,7 +409,8 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
>  
>  	mutex_lock(&dev->mode_config.idr_mutex);
>  	obj = idr_find(&dev->mode_config.crtc_idr, id);
> -	if (!obj || (obj->type != type) || (obj->id != id))
> +	if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
> +	    (obj->id != id))
>  		obj = NULL;
>  	mutex_unlock(&dev->mode_config.idr_mutex);
>  
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index dbd7954..44d7964 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -50,6 +50,7 @@ struct drm_clip_rect;
>  #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
>  #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
>  #define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd
> +#define DRM_MODE_OBJECT_ANY 0
>  
>  struct drm_mode_object {
>  	uint32_t id;
> -- 
> 1.9.3
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 8/8] drm: convert crtc and connection_mutex to ww_mutex
  2014-05-28 23:57 ` [PATCH 8/8] drm: convert crtc and connection_mutex to ww_mutex Rob Clark
@ 2014-05-29 11:22   ` Daniel Vetter
  2014-05-29 12:07     ` Rob Clark
  0 siblings, 1 reply; 23+ messages in thread
From: Daniel Vetter @ 2014-05-29 11:22 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Wed, May 28, 2014 at 07:57:25PM -0400, Rob Clark wrote:
> For atomic, it will be quite necessary to not need to care so much
> about locking order.  And 'state' gives us a convenient place to stash a
> ww_ctx for any sort of update that needs to grab multiple crtc locks.
> 
> Because we will want to eventually make locking even more fine grained
> (giving locks to planes, connectors, etc), split out drm_modeset_lock
> and drm_modeset_acquire_ctx to track acquired locks.
> 
> Atomic will use this to keep track of which locks have been acquired
> in a transaction.
> 
> Signed-off-by: Rob Clark <robdclark@gmail.com>

I still would like to see all the additional magic for atomic updates
removed from the drm_mode_lock functions. Getting the w/w dance right
isn't that simple and all these complications confuse.

Also I simply can't review whether these added features make sense without
their users ;-)
-Daniel

> ---
>  drivers/gpu/drm/Makefile             |   2 +-
>  drivers/gpu/drm/drm_crtc.c           |  87 +++++++++++-----
>  drivers/gpu/drm/drm_crtc_helper.c    |   3 +-
>  drivers/gpu/drm/drm_fb_helper.c      |   4 +
>  drivers/gpu/drm/drm_modeset_lock.c   | 187 +++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_plane_helper.c   |   2 +-
>  drivers/gpu/drm/i915/intel_crt.c     |   5 +-
>  drivers/gpu/drm/i915/intel_display.c |  58 +++++++----
>  drivers/gpu/drm/i915/intel_drv.h     |   6 +-
>  drivers/gpu/drm/i915/intel_sprite.c  |   2 +-
>  drivers/gpu/drm/i915/intel_tv.c      |   5 +-
>  drivers/gpu/drm/omapdrm/omap_crtc.c  |  10 +-
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c  |   8 +-
>  include/drm/drmP.h                   |   5 -
>  include/drm/drm_crtc.h               |  15 +--
>  include/drm/drm_modeset_lock.h       | 139 ++++++++++++++++++++++++++
>  include/uapi/drm/drm_mode.h          |   3 +
>  17 files changed, 468 insertions(+), 73 deletions(-)
>  create mode 100644 drivers/gpu/drm/drm_modeset_lock.c
>  create mode 100644 include/drm/drm_modeset_lock.h
> 
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 48e38ba..bf4c12d 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -14,7 +14,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
>  		drm_info.o drm_debugfs.o drm_encoder_slave.o \
>  		drm_trace_points.o drm_global.o drm_prime.o \
>  		drm_rect.o drm_vma_manager.o drm_flip_work.o \
> -		drm_plane_helper.o
> +		drm_plane_helper.o drm_modeset_lock.o
>  
>  drm-$(CONFIG_COMPAT) += drm_ioc32.o
>  drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index de1520c..cf622f6 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -37,8 +37,8 @@
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_edid.h>
>  #include <drm/drm_fourcc.h>
> +#include <drm/drm_modeset_lock.h>
>  
> -#include "drm_crtc_internal.h"
>  
>  /**
>   * drm_modeset_lock_all - take all modeset locks
> @@ -50,14 +50,42 @@
>   */
>  void drm_modeset_lock_all(struct drm_device *dev)
>  {
> -	struct drm_crtc *crtc;
> +	struct drm_mode_config *config = &dev->mode_config;
> +	struct drm_modeset_acquire_ctx *ctx;
> +	int ret;
>  
> -	mutex_lock(&dev->mode_config.mutex);
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (WARN_ON(!ctx))
> +		return;
>  
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	mutex_lock(&config->mutex);
>  
> -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> -		mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
> +	drm_modeset_acquire_init(ctx, false, false);
> +
> +retry:
> +	ret = drm_modeset_lock(&config->connection_mutex, ctx);
> +	if (ret)
> +		goto fail;
> +	ret = drm_modeset_lock_all_crtcs(dev, ctx);
> +	if (ret)
> +		goto fail;
> +
> +	WARN_ON(config->acquire_ctx);
> +
> +	/* now we hold the locks, so now that it is safe, stash the
> +	 * ctx for drm_modeset_unlock_all():
> +	 */
> +	config->acquire_ctx = ctx;
> +
> +	drm_warn_on_modeset_not_all_locked(dev);
> +
> +	return;
> +
> +fail:
> +	if (ret == -EDEADLK) {
> +		drm_modeset_backoff(ctx);
> +		goto retry;
> +	}
>  }
>  EXPORT_SYMBOL(drm_modeset_lock_all);
>  
> @@ -69,12 +97,18 @@ EXPORT_SYMBOL(drm_modeset_lock_all);
>   */
>  void drm_modeset_unlock_all(struct drm_device *dev)
>  {
> -	struct drm_crtc *crtc;
> +	struct drm_mode_config *config = &dev->mode_config;
> +	struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
>  
> -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> -		mutex_unlock(&crtc->mutex);
> +	if (WARN_ON(!ctx))
> +		return;
> +
> +	config->acquire_ctx = NULL;
> +	drm_modeset_drop_locks(ctx);
> +	ww_acquire_fini(&ctx->ww_ctx);
> +	drm_modeset_acquire_fini(ctx);
>  
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +	kfree(ctx);
>  
>  	mutex_unlock(&dev->mode_config.mutex);
>  }
> @@ -95,9 +129,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
>  		return;
>  
>  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> -		WARN_ON(!mutex_is_locked(&crtc->mutex));
> +		WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
>  
> -	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
>  }
>  EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
> @@ -677,6 +711,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  }
>  EXPORT_SYMBOL(drm_framebuffer_remove);
>  
> +DEFINE_WW_CLASS(crtc_ww_class);
> +
>  /**
>   * drm_crtc_init_with_planes - Initialise a new CRTC object with
>   *    specified primary and cursor planes.
> @@ -696,6 +732,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>  			      void *cursor,
>  			      const struct drm_crtc_funcs *funcs)
>  {
> +	struct drm_mode_config *config = &dev->mode_config;
>  	int ret;
>  
>  	crtc->dev = dev;
> @@ -703,8 +740,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>  	crtc->invert_dimensions = false;
>  
>  	drm_modeset_lock_all(dev);
> -	mutex_init(&crtc->mutex);
> -	mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
> +	drm_modeset_lock_init(&crtc->mutex);
> +	/* dropped by _unlock_all(): */
> +	drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
>  
>  	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
>  	if (ret)
> @@ -712,8 +750,8 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>  
>  	crtc->base.properties = &crtc->properties;
>  
> -	list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
> -	dev->mode_config.num_crtc++;
> +	list_add_tail(&crtc->head, &config->crtc_list);
> +	config->num_crtc++;
>  
>  	crtc->primary = primary;
>  	if (primary)
> @@ -741,6 +779,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
>  	kfree(crtc->gamma_store);
>  	crtc->gamma_store = NULL;
>  
> +	drm_modeset_lock_fini(&crtc->mutex);
> +
>  	drm_mode_object_put(dev, &crtc->base);
>  	list_del(&crtc->head);
>  	dev->mode_config.num_crtc--;
> @@ -1804,7 +1844,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>  	DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
>  
>  	mutex_lock(&dev->mode_config.mutex);
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  
>  	connector = drm_connector_find(dev, out_resp->connector_id);
>  	if (!connector) {
> @@ -1903,7 +1943,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>  	out_resp->count_encoders = encoders_count;
>  
>  out:
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  	mutex_unlock(&dev->mode_config.mutex);
>  
>  	return ret;
> @@ -2487,7 +2527,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
>  		return -ENOENT;
>  	}
>  
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  	if (req->flags & DRM_MODE_CURSOR_BO) {
>  		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
>  			ret = -ENXIO;
> @@ -2511,7 +2551,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
>  		}
>  	}
>  out:
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  
>  	return ret;
>  
> @@ -4195,7 +4235,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>  	if (!crtc)
>  		return -ENOENT;
>  
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  	if (crtc->primary->fb == NULL) {
>  		/* The framebuffer is currently unbound, presumably
>  		 * due to a hotplug event, that userspace has not
> @@ -4279,7 +4319,7 @@ out:
>  		drm_framebuffer_unreference(fb);
>  	if (old_fb)
>  		drm_framebuffer_unreference(old_fb);
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  
>  	return ret;
>  }
> @@ -4644,7 +4684,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
>  void drm_mode_config_init(struct drm_device *dev)
>  {
>  	mutex_init(&dev->mode_config.mutex);
> -	mutex_init(&dev->mode_config.connection_mutex);
> +	drm_modeset_lock_init(&dev->mode_config.connection_mutex);
>  	mutex_init(&dev->mode_config.idr_mutex);
>  	mutex_init(&dev->mode_config.fb_lock);
>  	INIT_LIST_HEAD(&dev->mode_config.fb_list);
> @@ -4744,5 +4784,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
>  	}
>  
>  	idr_destroy(&dev->mode_config.crtc_idr);
> +	drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
>  }
>  EXPORT_SYMBOL(drm_mode_config_cleanup);
> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
> index 5b93caf..4eaded8 100644
> --- a/drivers/gpu/drm/drm_crtc_helper.c
> +++ b/drivers/gpu/drm/drm_crtc_helper.c
> @@ -89,8 +89,7 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
>  	struct drm_device *dev = encoder->dev;
>  
>  	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
> -	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> -
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>  		if (connector->encoder == encoder)
>  			return true;
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 1e28ba6..25f687d 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -355,6 +355,10 @@ static bool drm_fb_helper_force_kernel_mode(void)
>  		if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
>  			continue;
>  
> +		/* NOTE: we use lockless flag below to avoid grabbing other
> +		 * modeset locks.  So just trylock the underlying mutex
> +		 * directly:
> +		 */
>  		if (!mutex_trylock(&dev->mode_config.mutex)) {
>  			error = true;
>  			continue;
> diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
> new file mode 100644
> index 0000000..6580d6c
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_modeset_lock.c
> @@ -0,0 +1,187 @@
> +/*
> + * Copyright (C) 2014 Red Hat
> + * Author: Rob Clark <robdclark@gmail.com>
> + *
> + * 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
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_modeset_lock.h>
> +
> +
> +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
> +		bool nolock, bool nonblock)
> +{
> +	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
> +	INIT_LIST_HEAD(&ctx->locked);
> +	mutex_init(&ctx->mutex);
> +	ctx->nolock = nolock;
> +	ctx->nonblock = nonblock;
> +}
> +EXPORT_SYMBOL(drm_modeset_acquire_init);
> +
> +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
> +{
> +	WARN_ON(ctx->contended);
> +	/*
> +	 * NOTE: it is intentional that ww_acquire_fini() is not called
> +	 * here.. due to the way lock handover works in drm_atomic
> +	 */
> +	mutex_destroy(&ctx->mutex);
> +}
> +EXPORT_SYMBOL(drm_modeset_acquire_fini);
> +
> +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
> +{
> +	WARN_ON(ctx->contended);
> +	mutex_lock(&ctx->mutex);
> +	while (!list_empty(&ctx->locked)) {
> +		struct drm_modeset_lock *lock;
> +
> +		lock = list_first_entry(&ctx->locked,
> +				struct drm_modeset_lock, head);
> +
> +		drm_modeset_unlock(lock);
> +	}
> +	mutex_unlock(&ctx->mutex);
> +}
> +EXPORT_SYMBOL(drm_modeset_drop_locks);
> +
> +static int modeset_lock(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx,
> +		bool interruptible, bool slow)
> +{
> +	int ret;
> +
> +	if (ctx->nolock)
> +		return 0;
> +
> +	WARN_ON(ctx->frozen);    /* all locks should be held by now! */
> +	WARN_ON(ctx->contended);
> +
> +retry:
> +	if (interruptible) {
> +		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
> +	} else if (slow) {
> +		ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
> +		ret = 0;
> +	} else {
> +		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
> +	}
> +	if (!ret) {
> +		if (lock->atomic_pending) {
> +			/* some other pending update with dropped locks */
> +			ww_mutex_unlock(&lock->mutex);
> +			if (ctx->nonblock)
> +				return -EBUSY;
> +			wait_event(lock->event, !lock->atomic_pending);
> +			goto retry;
> +		}
> +		lock->atomic_pending = true;
> +		WARN_ON(!list_empty(&lock->head));
> +		list_add(&lock->head, &ctx->locked);
> +	} else if (ret == -EALREADY) {
> +		/* we already hold the lock.. this is fine */
> +		ret = 0;
> +	} else if (ret == -EDEADLK) {
> +		ctx->contended = lock;
> +	}
> +
> +	return ret;
> +}
> +
> +void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
> +{
> +	struct drm_modeset_lock *contended = ctx->contended;
> +
> +	ctx->contended = NULL;
> +
> +	if (WARN_ON(!contended))
> +		return;
> +
> +	drm_modeset_drop_locks(ctx);
> +
> +	modeset_lock(contended, ctx, false, true);
> +}
> +EXPORT_SYMBOL(drm_modeset_backoff);
> +
> +/**
> + * drm_modeset_lock - take modeset lock
> + * @lock: lock to take
> + * @ctx: acquire ctx
> + *
> + * If ctx is not NULL, then then it's acquire context is used
> + * and the lock does not need to be explicitly unlocked, it
> + * will be automatically unlocked when the atomic update is
> + * complete
> + */
> +int drm_modeset_lock(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx)
> +{
> +	if (ctx)
> +		return modeset_lock(lock, ctx, false, false);
> +
> +	ww_mutex_lock(&lock->mutex, NULL);
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_modeset_lock);
> +
> +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx)
> +{
> +	if (ctx)
> +		return modeset_lock(lock, ctx, true, false);
> +
> +	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
> +}
> +EXPORT_SYMBOL(drm_modeset_lock_interruptible);
> +
> +/**
> + * drm_modeset_unlock - drop modeset lock
> + * @lock: lock to release
> + */
> +void drm_modeset_unlock(struct drm_modeset_lock *lock)
> +{
> +	list_del_init(&lock->head);
> +	lock->atomic_pending = false;
> +	ww_mutex_unlock(&lock->mutex);
> +	wake_up_all(&lock->event);
> +}
> +EXPORT_SYMBOL(drm_modeset_unlock);
> +
> +/**
> + * drm_modeset_lock_all_crtcs - helper to drm_modeset_lock() all CRTCs
> + */
> +int drm_modeset_lock_all_crtcs(struct drm_device *dev,
> +		struct drm_modeset_acquire_ctx *ctx)
> +{
> +	struct drm_mode_config *config = &dev->mode_config;
> +	struct drm_crtc *crtc;
> +	int ret = 0;
> +
> +	list_for_each_entry(crtc, &config->crtc_list, head) {
> +		ret = drm_modeset_lock(&crtc->mutex, ctx);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
> index 458d9bf..6301ec6 100644
> --- a/drivers/gpu/drm/drm_plane_helper.c
> +++ b/drivers/gpu/drm/drm_plane_helper.c
> @@ -59,7 +59,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
>  	 * need to grab the connection_mutex here to be able to make these
>  	 * checks.
>  	 */
> -	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  
>  	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>  		if (connector->encoder && connector->encoder->crtc == crtc) {
> diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
> index 22d8347..33328fc 100644
> --- a/drivers/gpu/drm/i915/intel_crt.c
> +++ b/drivers/gpu/drm/i915/intel_crt.c
> @@ -630,6 +630,7 @@ intel_crt_detect(struct drm_connector *connector, bool force)
>  	enum intel_display_power_domain power_domain;
>  	enum drm_connector_status status;
>  	struct intel_load_detect_pipe tmp;
> +	struct drm_modeset_acquire_ctx ctx;
>  
>  	intel_runtime_pm_get(dev_priv);
>  
> @@ -673,12 +674,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
>  	}
>  
>  	/* for pre-945g platforms use load detect */
> -	if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
> +	if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) {
>  		if (intel_crt_detect_ddc(connector))
>  			status = connector_status_connected;
>  		else
>  			status = intel_crt_load_detect(crt);
> -		intel_release_load_detect_pipe(connector, &tmp);
> +		intel_release_load_detect_pipe(connector, &tmp, &ctx);
>  	} else
>  		status = connector_status_unknown;
>  
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 5844f07..ba6cbbb 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -2363,7 +2363,7 @@ void intel_display_handle_reset(struct drm_device *dev)
>  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
>  		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>  
> -		mutex_lock(&crtc->mutex);
> +		drm_modeset_lock(&crtc->mutex, NULL);
>  		/*
>  		 * FIXME: Once we have proper support for primary planes (and
>  		 * disabling them without disabling the entire crtc) allow again
> @@ -2374,7 +2374,7 @@ void intel_display_handle_reset(struct drm_device *dev)
>  							       crtc->primary->fb,
>  							       crtc->x,
>  							       crtc->y);
> -		mutex_unlock(&crtc->mutex);
> +		drm_modeset_unlock(&crtc->mutex);
>  	}
>  }
>  
> @@ -7972,7 +7972,8 @@ mode_fits_in_fbdev(struct drm_device *dev,
>  
>  bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  				struct drm_display_mode *mode,
> -				struct intel_load_detect_pipe *old)
> +				struct intel_load_detect_pipe *old,
> +				struct drm_modeset_acquire_ctx *ctx)
>  {
>  	struct intel_crtc *intel_crtc;
>  	struct intel_encoder *intel_encoder =
> @@ -7982,13 +7983,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  	struct drm_crtc *crtc = NULL;
>  	struct drm_device *dev = encoder->dev;
>  	struct drm_framebuffer *fb;
> -	int i = -1;
> +	struct drm_mode_config *config = &dev->mode_config;
> +	int ret, i = -1;
>  
>  	DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
>  		      connector->base.id, drm_get_connector_name(connector),
>  		      encoder->base.id, drm_get_encoder_name(encoder));
>  
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	drm_modeset_acquire_init(ctx, false, false);
> +
> +retry:
> +	ret = drm_modeset_lock(&config->connection_mutex, ctx);
> +	if (ret)
> +		goto fail_unlock;
>  
>  	/*
>  	 * Algorithm gets a little messy:
> @@ -8004,7 +8011,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  	if (encoder->crtc) {
>  		crtc = encoder->crtc;
>  
> -		mutex_lock(&crtc->mutex);
> +		ret = drm_modeset_lock(&crtc->mutex, ctx);
> +		if (ret)
> +			goto fail_unlock;
>  
>  		old->dpms_mode = connector->dpms;
>  		old->load_detect_temp = false;
> @@ -8017,7 +8026,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  	}
>  
>  	/* Find an unused one (if possible) */
> -	list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
> +	list_for_each_entry(possible_crtc, &config->crtc_list, head) {
>  		i++;
>  		if (!(encoder->possible_crtcs & (1 << i)))
>  			continue;
> @@ -8032,10 +8041,12 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  	 */
>  	if (!crtc) {
>  		DRM_DEBUG_KMS("no pipe available for load-detect\n");
> -		goto fail_unlock_connector;
> +		goto fail_unlock;
>  	}
>  
> -	mutex_lock(&crtc->mutex);
> +	ret = drm_modeset_lock(&crtc->mutex, ctx);
> +	if (ret)
> +		goto fail_unlock;
>  	intel_encoder->new_crtc = to_intel_crtc(crtc);
>  	to_intel_connector(connector)->new_encoder = intel_encoder;
>  
> @@ -8085,15 +8096,22 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  		intel_crtc->new_config = &intel_crtc->config;
>  	else
>  		intel_crtc->new_config = NULL;
> -	mutex_unlock(&crtc->mutex);
> -fail_unlock_connector:
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +fail_unlock:
> +	if (ret == -EDEADLK) {
> +		drm_modeset_backoff(ctx);
> +		goto retry;
> +	}
> +
> +	drm_modeset_drop_locks(ctx);
> +	ww_acquire_fini(&ctx->ww_ctx);
> +	drm_modeset_acquire_fini(ctx);
>  
>  	return false;
>  }
>  
>  void intel_release_load_detect_pipe(struct drm_connector *connector,
> -				    struct intel_load_detect_pipe *old)
> +				    struct intel_load_detect_pipe *old,
> +				    struct drm_modeset_acquire_ctx *ctx)
>  {
>  	struct intel_encoder *intel_encoder =
>  		intel_attached_encoder(connector);
> @@ -8117,8 +8135,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
>  			drm_framebuffer_unreference(old->release_fb);
>  		}
>  
> -		mutex_unlock(&crtc->mutex);
> -		mutex_unlock(&connector->dev->mode_config.connection_mutex);
> +		goto unlock;
>  		return;
>  	}
>  
> @@ -8126,8 +8143,10 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
>  	if (old->dpms_mode != DRM_MODE_DPMS_ON)
>  		connector->funcs->dpms(connector, old->dpms_mode);
>  
> -	mutex_unlock(&crtc->mutex);
> -	mutex_unlock(&connector->dev->mode_config.connection_mutex);
> +unlock:
> +	drm_modeset_drop_locks(ctx);
> +	ww_acquire_fini(&ctx->ww_ctx);
> +	drm_modeset_acquire_fini(ctx);
>  }
>  
>  static int i9xx_pll_refclk(struct drm_device *dev,
> @@ -11385,6 +11404,7 @@ static void intel_enable_pipe_a(struct drm_device *dev)
>  	struct intel_connector *connector;
>  	struct drm_connector *crt = NULL;
>  	struct intel_load_detect_pipe load_detect_temp;
> +	struct drm_modeset_acquire_ctx ctx;
>  
>  	/* We can't just switch on the pipe A, we need to set things up with a
>  	 * proper mode and output configuration. As a gross hack, enable pipe A
> @@ -11401,8 +11421,8 @@ static void intel_enable_pipe_a(struct drm_device *dev)
>  	if (!crt)
>  		return;
>  
> -	if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp))
> -		intel_release_load_detect_pipe(crt, &load_detect_temp);
> +	if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx))
> +		intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx);
>  
>  
>  }
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index d8b540b..824f8ae 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -721,9 +721,11 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
>  			 struct intel_digital_port *dport);
>  bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  				struct drm_display_mode *mode,
> -				struct intel_load_detect_pipe *old);
> +				struct intel_load_detect_pipe *old,
> +				struct drm_modeset_acquire_ctx *ctx);
>  void intel_release_load_detect_pipe(struct drm_connector *connector,
> -				    struct intel_load_detect_pipe *old);
> +				    struct intel_load_detect_pipe *old,
> +				    struct drm_modeset_acquire_ctx *ctx);
>  int intel_pin_and_fence_fb_obj(struct drm_device *dev,
>  			       struct drm_i915_gem_object *obj,
>  			       struct intel_ring_buffer *pipelined);
> diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
> index 213cd58..c235546 100644
> --- a/drivers/gpu/drm/i915/intel_sprite.c
> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> @@ -55,7 +55,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl
>  	int scanline, min, max, vblank_start;
>  	DEFINE_WAIT(wait);
>  
> -	WARN_ON(!mutex_is_locked(&crtc->base.mutex));
> +	WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
>  
>  	vblank_start = mode->crtc_vblank_start;
>  	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
> index e0193e8..97d9fc9 100644
> --- a/drivers/gpu/drm/i915/intel_tv.c
> +++ b/drivers/gpu/drm/i915/intel_tv.c
> @@ -1321,10 +1321,11 @@ intel_tv_detect(struct drm_connector *connector, bool force)
>  
>  	if (force) {
>  		struct intel_load_detect_pipe tmp;
> +		struct drm_modeset_acquire_ctx ctx;
>  
> -		if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
> +		if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) {
>  			type = intel_tv_detect_type(intel_tv, connector);
> -			intel_release_load_detect_pipe(connector, &tmp);
> +			intel_release_load_detect_pipe(connector, &tmp, &ctx);
>  		} else
>  			return connector_status_unknown;
>  	} else
> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
> index e3c47a8..2d28dc3 100644
> --- a/drivers/gpu/drm/omapdrm/omap_crtc.c
> +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
> @@ -319,13 +319,13 @@ static void page_flip_worker(struct work_struct *work)
>  	struct drm_display_mode *mode = &crtc->mode;
>  	struct drm_gem_object *bo;
>  
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  	omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
>  			0, 0, mode->hdisplay, mode->vdisplay,
>  			crtc->x << 16, crtc->y << 16,
>  			mode->hdisplay << 16, mode->vdisplay << 16,
>  			vblank_cb, crtc);
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  
>  	bo = omap_framebuffer_bo(crtc->primary->fb, 0);
>  	drm_gem_object_unreference_unlocked(bo);
> @@ -465,7 +465,7 @@ static void apply_worker(struct work_struct *work)
>  	 * the callbacks and list modification all serialized
>  	 * with respect to modesetting ioctls from userspace.
>  	 */
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  	dispc_runtime_get();
>  
>  	/*
> @@ -510,7 +510,7 @@ static void apply_worker(struct work_struct *work)
>  
>  out:
>  	dispc_runtime_put();
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  }
>  
>  int omap_crtc_apply(struct drm_crtc *crtc,
> @@ -518,7 +518,7 @@ int omap_crtc_apply(struct drm_crtc *crtc,
>  {
>  	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>  
> -	WARN_ON(!mutex_is_locked(&crtc->mutex));
> +	WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
>  
>  	/* no need to queue it again if it is already queued: */
>  	if (apply->queued)
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> index e7199b4..8f3edc4 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> @@ -187,7 +187,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
>  	 * can do this since the caller in the drm core doesn't check anything
>  	 * which is protected by any looks.
>  	 */
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  	drm_modeset_lock_all(dev_priv->dev);
>  
>  	/* A lot of the code assumes this */
> @@ -252,7 +252,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
>  	ret = 0;
>  out:
>  	drm_modeset_unlock_all(dev_priv->dev);
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  
>  	return ret;
>  }
> @@ -273,7 +273,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
>  	 * can do this since the caller in the drm core doesn't check anything
>  	 * which is protected by any looks.
>  	 */
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  	drm_modeset_lock_all(dev_priv->dev);
>  
>  	vmw_cursor_update_position(dev_priv, shown,
> @@ -281,7 +281,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
>  				   du->cursor_y + du->hotspot_y);
>  
>  	drm_modeset_unlock_all(dev_priv->dev);
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  
>  	return 0;
>  }
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 12f10bc..a9b8a5d 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -1184,11 +1184,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev)
>  	return ret;
>  }
>  
> -static inline bool drm_modeset_is_locked(struct drm_device *dev)
> -{
> -	return mutex_is_locked(&dev->mode_config.mutex);
> -}
> -
>  static inline bool drm_is_render_client(const struct drm_file *file_priv)
>  {
>  	return file_priv->minor->type == DRM_MINOR_RENDER;
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 1bd6708..deadb32 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -33,6 +33,7 @@
>  #include <linux/hdmi.h>
>  #include <drm/drm_mode.h>
>  #include <drm/drm_fourcc.h>
> +#include <drm/drm_modeset_lock.h>
>  
>  struct drm_device;
>  struct drm_mode_set;
> @@ -205,6 +206,10 @@ struct drm_property {
>  	struct list_head enum_blob_list;
>  };
>  
> +void drm_modeset_lock_all(struct drm_device *dev);
> +void drm_modeset_unlock_all(struct drm_device *dev);
> +void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
> +
>  struct drm_crtc;
>  struct drm_connector;
>  struct drm_encoder;
> @@ -280,6 +285,7 @@ struct drm_crtc_funcs {
>   * drm_crtc - central CRTC control structure
>   * @dev: parent DRM device
>   * @head: list management
> + * @mutex: per-CRTC locking
>   * @base: base KMS object for ID tracking etc.
>   * @primary: primary plane for this CRTC
>   * @cursor: cursor plane for this CRTC
> @@ -314,7 +320,7 @@ struct drm_crtc {
>  	 * state, ...) and a write lock for everything which can be update
>  	 * without a full modeset (fb, cursor data, ...)
>  	 */
> -	struct mutex mutex;
> +	struct drm_modeset_lock mutex;
>  
>  	struct drm_mode_object base;
>  
> @@ -738,7 +744,8 @@ struct drm_mode_group {
>   */
>  struct drm_mode_config {
>  	struct mutex mutex; /* protects configuration (mode lists etc.) */
> -	struct mutex connection_mutex; /* protects connector->encoder and encoder->crtc links */
> +	struct drm_modeset_lock connection_mutex; /* protects connector->encoder and encoder->crtc links */
> +	struct drm_modeset_acquire_ctx *acquire_ctx; /* for legacy _lock_all() / _unlock_all() */
>  	struct mutex idr_mutex; /* for IDR management */
>  	struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
>  	/* this is limited to one for now */
> @@ -839,10 +846,6 @@ struct drm_prop_enum_list {
>  	char *name;
>  };
>  
> -extern void drm_modeset_lock_all(struct drm_device *dev);
> -extern void drm_modeset_unlock_all(struct drm_device *dev);
> -extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
> -
>  extern int drm_crtc_init_with_planes(struct drm_device *dev,
>  				     struct drm_crtc *crtc,
>  				     struct drm_plane *primary,
> diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
> new file mode 100644
> index 0000000..2630da3
> --- /dev/null
> +++ b/include/drm/drm_modeset_lock.h
> @@ -0,0 +1,139 @@
> +/*
> + * Copyright (C) 2014 Red Hat
> + * Author: Rob Clark <robdclark@gmail.com>
> + *
> + * 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
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#ifndef DRM_MODESET_LOCK_H_
> +#define DRM_MODESET_LOCK_H_
> +
> +#include <linux/ww_mutex.h>
> +
> +struct drm_modeset_lock;
> +
> +struct drm_modeset_acquire_ctx {
> +
> +	struct ww_acquire_ctx ww_ctx;
> +
> +	bool nolock : 1;
> +	bool nonblock : 1;
> +
> +	/* just for debugging, the context is 'frozen' in drm_atomic_check()
> +	 * to catch anyone who might be trying to acquire a lock after it is
> +	 * too late.
> +	 */
> +	bool frozen : 1;
> +
> +	/* contended lock: if a lock is contended you should only call
> +	 * drm_modeset_backoff() which drops locks and slow-locks the
> +	 * contended lock.
> +	 */
> +	struct drm_modeset_lock *contended;
> +
> +	/* list of 'struct drm_modeset_lock': */
> +	struct list_head locked;
> +
> +	/* currently simply for protecting against 'locked' list manipulation
> +	 * between original thread calling atomic->end() and driver thread
> +	 * calling back drm_atomic_commit_unlocked().
> +	 *
> +	 * Other spots are sufficiently synchronized by virtue of holding
> +	 * the lock's ww_mutex.  But during the lock/resource hand-over to the
> +	 * driver thread (drop_locks()/grab_locks()), we cannot rely on this.
> +	 */
> +	struct mutex mutex;
> +};
> +
> +/**
> + * drm_modeset_lock - used for locking modeset resources.
> + * @mutex: resource locking
> + * @atomic_pending: is this resource part of a still-pending
> + *    atomic update
> + * @head: used to hold it's place on state->locked list when
> + *    part of an atomic update
> + *
> + * Used for locking CRTCs and other modeset resources.
> + */
> +struct drm_modeset_lock {
> +	/**
> +	 * modeset lock
> +	 */
> +	struct ww_mutex mutex;
> +
> +	/**
> +	 * Are we busy (pending asynchronous/NONBLOCK update)?  Any further
> +	 * asynchronous update will return -EBUSY if it also needs to acquire
> +	 * this lock.  While a synchronous update will block until the pending
> +	 * async update completes.
> +	 *
> +	 * Drivers must ensure the update is completed before sending vblank
> +	 * event to userspace.  Typically this just means don't send event
> +	 * before drm_atomic_commit_unlocked() returns.
> +	 */
> +	bool atomic_pending;
> +
> +	/**
> +	 * Resources that are locked as part of an atomic update are added
> +	 * to a list (so we know what to unlock at the end).
> +	 */
> +	struct list_head head;
> +
> +	/**
> +	 * For waiting on atomic_pending locks, if not a NONBLOCK operation.
> +	 */
> +	wait_queue_head_t event;
> +};
> +
> +extern struct ww_class crtc_ww_class;
> +
> +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
> +		bool nolock, bool nonblock);
> +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
> +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
> +void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
> +
> +static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock)
> +{
> +	ww_mutex_init(&lock->mutex, &crtc_ww_class);
> +	INIT_LIST_HEAD(&lock->head);
> +	init_waitqueue_head(&lock->event);
> +}
> +
> +static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
> +{
> +	WARN_ON(!list_empty(&lock->head));
> +}
> +
> +static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock)
> +{
> +	return ww_mutex_is_locked(&lock->mutex);
> +}
> +
> +int drm_modeset_lock(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx);
> +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx);
> +void drm_modeset_unlock(struct drm_modeset_lock *lock);
> +
> +struct drm_device;
> +int drm_modeset_lock_all_crtcs(struct drm_device *dev,
> +		struct drm_modeset_acquire_ctx *ctx);
> +
> +#endif /* DRM_MODESET_LOCK_H_ */
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index ded505e..6421edc 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -511,4 +511,7 @@ struct drm_mode_destroy_dumb {
>  	uint32_t handle;
>  };
>  
> +#define DRM_MODE_ATOMIC_NONBLOCK  0x0200
> +#define DRM_MODE_ATOMIC_NOLOCK    0x8000  /* only used internally */
> +
>  #endif
> -- 
> 1.9.3
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/8] drm: add object property type
  2014-05-29  8:01   ` David Herrmann
@ 2014-05-29 11:45     ` Rob Clark
  0 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2014-05-29 11:45 UTC (permalink / raw)
  To: David Herrmann; +Cc: dri-devel

On Thu, May 29, 2014 at 4:01 AM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> On Thu, May 29, 2014 at 1:57 AM, Rob Clark <robdclark@gmail.com> wrote:
>> An object property is an id (idr) for a drm mode object.  This
>> will allow a property to be used set/get a framebuffer, CRTC, etc.
>>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>> ---
>>  drivers/gpu/drm/drm_crtc.c  | 50 +++++++++++++++++++++++++++++++++++++--------
>>  include/drm/drm_crtc.h      | 27 ++++++++++++++++++++++++
>>  include/uapi/drm/drm_mode.h | 14 +++++++++++++
>>  3 files changed, 82 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index 37a3e07..61ddc66 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -3117,6 +3117,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
>>         if (!property)
>>                 return NULL;
>>
>> +       property->dev = dev;
>> +
>
> Ugh.. embarrassing.

??

>>         if (num_values) {
>>                 property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
>>                 if (!property->values)
>> @@ -3137,6 +3139,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
>>         }
>>
>>         list_add_tail(&property->head, &dev->mode_config.property_list);
>> +
>> +       BUG_ON(!drm_property_type_valid(property));
>
> WARN_ON(). There is no reason to panic.. I mean, it's obviously a bug,
> but we can continue just normally.

hmm, yeah, I keep forgetting not everyone has debug uart :-P

>> +
>>         return property;
>>  fail:
>>         kfree(property->values);
>> @@ -3274,6 +3279,23 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
>>  }
>>  EXPORT_SYMBOL(drm_property_create_range);
>>
>> +struct drm_property *drm_property_create_object(struct drm_device *dev,
>> +                                        int flags, const char *name, uint32_t type)
>> +{
>> +       struct drm_property *property;
>> +
>> +       flags |= DRM_MODE_PROP_OBJECT;
>> +
>> +       property = drm_property_create(dev, flags, name, 1);
>> +       if (!property)
>> +               return NULL;
>> +
>> +       property->values[0] = type;
>> +
>> +       return property;
>> +}
>> +EXPORT_SYMBOL(drm_property_create_object);
>> +
>>  /**
>>   * drm_property_add_enum - add a possible value to an enumeration property
>>   * @property: enumeration property to change
>> @@ -3294,14 +3316,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
>>  {
>>         struct drm_property_enum *prop_enum;
>>
>> -       if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
>> +       if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
>> +                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
>>                 return -EINVAL;
>>
>>         /*
>>          * Bitmask enum properties have the additional constraint of values
>>          * from 0 to 63
>>          */
>> -       if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
>> +       if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
>> +                       (value > 63))
>>                 return -EINVAL;
>>
>>         if (!list_empty(&property->enum_blob_list)) {
>> @@ -3484,10 +3508,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
>>         }
>>         property = obj_to_property(obj);
>>
>> -       if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
>> +       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
>> +                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
>>                 list_for_each_entry(prop_enum, &property->enum_blob_list, head)
>>                         enum_count++;
>> -       } else if (property->flags & DRM_MODE_PROP_BLOB) {
>> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
>>                 list_for_each_entry(prop_blob, &property->enum_blob_list, head)
>>                         blob_count++;
>>         }
>> @@ -3509,7 +3534,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
>>         }
>>         out_resp->count_values = value_count;
>>
>> -       if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
>> +       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
>> +                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
>>                 if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
>>                         copied = 0;
>>                         enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
>> @@ -3531,7 +3557,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
>>                 out_resp->count_enum_blobs = enum_count;
>>         }
>>
>> -       if (property->flags & DRM_MODE_PROP_BLOB) {
>> +       if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
>>                 if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
>>                         copied = 0;
>>                         blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
>> @@ -3687,19 +3713,25 @@ static bool drm_property_change_is_valid(struct drm_property *property,
>>  {
>>         if (property->flags & DRM_MODE_PROP_IMMUTABLE)
>>                 return false;
>> -       if (property->flags & DRM_MODE_PROP_RANGE) {
>> +
>> +       if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
>>                 if (value < property->values[0] || value > property->values[1])
>>                         return false;
>>                 return true;
>> -       } else if (property->flags & DRM_MODE_PROP_BITMASK) {
>> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
>>                 int i;
>>                 uint64_t valid_mask = 0;
>>                 for (i = 0; i < property->num_values; i++)
>>                         valid_mask |= (1ULL << property->values[i]);
>>                 return !(value & ~valid_mask);
>> -       } else if (property->flags & DRM_MODE_PROP_BLOB) {
>> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
>>                 /* Only the driver knows */
>>                 return true;
>> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
>> +               /* a zero value for an object property translates to null: */
>> +               if (value == 0)
>> +                       return true;
>> +               return drm_property_get_obj(property, value) != NULL;
>>         } else {
>>                 int i;
>>                 for (i = 0; i < property->num_values; i++)
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index 5c1c31c..c1c243f 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -190,6 +190,7 @@ struct drm_property {
>>         char name[DRM_PROP_NAME_LEN];
>>         uint32_t num_values;
>>         uint64_t *values;
>> +       struct drm_device *dev;
>>
>>         struct list_head enum_blob_list;
>>  };
>> @@ -931,6 +932,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
>>
>>  extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
>>                                                 struct edid *edid);
>> +
>> +static inline bool drm_property_type_is(struct drm_property *property,
>> +               uint32_t type)
>> +{
>> +       /* instanceof for props.. handles extended type vs original types: */
>> +       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
>> +               return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
>> +       return property->flags & type;
>> +}
>> +
>> +static inline bool drm_property_type_valid(struct drm_property *property)
>> +{
>> +       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
>> +               return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
>> +       return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
>> +}
>
> I really don't get your naming scheme. I mean, both objects operate on
> "drm_property" objects, so the drm_property_* prefixes looks good. But
> as suffix, I'd expect a properly named function, so something like
> "drm_property_is_type()" or "*_is_of_type()" and
> "drm_property_is_valid_type()". Your naming-scheme looks more like
> these functions work on "drm_property_type" objects (which they
> don't).

_is_type() would have been kinda funny looking sitting next to _type_is() ;-)

basically the name is _type_is_valid() with the _is drop to shorten it
because my fingers already have too much to type

> Just saying.. no reason to argue about taste here.
>
>> +
>>  extern int drm_object_property_set_value(struct drm_mode_object *obj,
>>                                          struct drm_property *property,
>>                                          uint64_t val);
>> @@ -964,6 +982,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
>>  struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
>>                                          const char *name,
>>                                          uint64_t min, uint64_t max);
>> +struct drm_property *drm_property_create_object(struct drm_device *dev,
>> +                                        int flags, const char *name, uint32_t type);
>>  extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
>>  extern int drm_property_add_enum(struct drm_property *property, int index,
>>                                  uint64_t value, const char *name);
>> @@ -980,6 +1000,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
>>                                          int gamma_size);
>>  extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
>>                 uint32_t id, uint32_t type);
>> +
>> +static inline struct drm_mode_object *
>> +drm_property_get_obj(struct drm_property *property, uint64_t value)
>> +{
>> +       return drm_mode_object_find(property->dev, value, property->values[0]);
>> +}
>> +
>>  /* IOCTLs */
>>  extern int drm_mode_getresources(struct drm_device *dev,
>>                                  void *data, struct drm_file *file_priv);
>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>> index f104c26..5b530c7 100644
>> --- a/include/uapi/drm/drm_mode.h
>> +++ b/include/uapi/drm/drm_mode.h
>> @@ -251,6 +251,20 @@ struct drm_mode_get_connector {
>>  #define DRM_MODE_PROP_BLOB     (1<<4)
>>  #define DRM_MODE_PROP_BITMASK  (1<<5) /* bitmask of enumerated types */
>>
>> +/* non-extended types: legacy bitmask, one bit per type: */
>> +#define DRM_MODE_PROP_LEGACY_TYPE  ( \
>> +               DRM_MODE_PROP_RANGE | \
>> +               DRM_MODE_PROP_ENUM | \
>> +               DRM_MODE_PROP_BLOB | \
>> +               DRM_MODE_PROP_BITMASK)
>> +
>> +/* extended-types: rather than continue to consume a bit per type,
>> + * grab a chunk of the bits to use as integer type id.
>> + */
>> +#define DRM_MODE_PROP_EXTENDED_TYPE    0x0000ffc0
>> +#define DRM_MODE_PROP_TYPE(n)          ((n) << 6)
>> +#define DRM_MODE_PROP_OBJECT           DRM_MODE_PROP_TYPE(1)
>> +
>
> Oh god, this is ugly. But I get your intention. Using bit-masks for
> types wasn't smart at all, and mixing it with flags even worse. But
> ok.

hurray of keeping backwards compat w/ userspace!  If it weren't part
of the ABI we could do something much cleaner.  I tried to wrap the
uglyness up into those inline helpers as much as possible.  But we
kinda painted ourselves into a corner on that one with the existing
ABI.

BR,
-R

>
> With or without my nitpicks fixed, this is:
>
> Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
>
> Thanks
> David
>
>>  struct drm_mode_property_enum {
>>         __u64 value;
>>         char name[DRM_PROP_NAME_LEN];
>> --
>> 1.9.3
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/8] drm: add signed-range property type
  2014-05-29  8:29   ` David Herrmann
@ 2014-05-29 11:51     ` Rob Clark
  0 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2014-05-29 11:51 UTC (permalink / raw)
  To: David Herrmann; +Cc: dri-devel

On Thu, May 29, 2014 at 4:29 AM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> On Thu, May 29, 2014 at 1:57 AM, Rob Clark <robdclark@gmail.com> wrote:
>> Like range, but values are signed.
>>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>> ---
>>  drivers/gpu/drm/drm_crtc.c  | 45 +++++++++++++++++++++++++++++++++------------
>>  include/drm/drm_crtc.h      | 12 ++++++++++++
>>  include/uapi/drm/drm_mode.h |  1 +
>>  3 files changed, 46 insertions(+), 12 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index 61ddc66..88a0741 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -3242,6 +3242,22 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
>>  }
>>  EXPORT_SYMBOL(drm_property_create_bitmask);
>>
>> +static struct drm_property *property_create_range(struct drm_device *dev,
>> +                                        int flags, const char *name,
>> +                                        uint64_t min, uint64_t max)
>> +{
>> +       struct drm_property *property;
>> +
>> +       property = drm_property_create(dev, flags, name, 2);
>> +       if (!property)
>> +               return NULL;
>> +
>> +       property->values[0] = min;
>> +       property->values[1] = max;
>> +
>> +       return property;
>> +}
>> +
>>  /**
>>   * drm_property_create - create a new ranged property type
>>   * @dev: drm device
>> @@ -3264,21 +3280,20 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
>>                                          const char *name,
>>                                          uint64_t min, uint64_t max)
>>  {
>> -       struct drm_property *property;
>> -
>> -       flags |= DRM_MODE_PROP_RANGE;
>> -
>> -       property = drm_property_create(dev, flags, name, 2);
>> -       if (!property)
>> -               return NULL;
>> -
>> -       property->values[0] = min;
>> -       property->values[1] = max;
>> -
>> -       return property;
>> +       return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
>> +                       name, min, max);
>>  }
>>  EXPORT_SYMBOL(drm_property_create_range);
>>
>> +struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
>> +                                        int flags, const char *name,
>> +                                        int64_t min, int64_t max)
>> +{
>> +       return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
>> +                       name, I642U64(min), I642U64(max));
>> +}
>> +EXPORT_SYMBOL(drm_property_create_signed_range);
>> +
>>  struct drm_property *drm_property_create_object(struct drm_device *dev,
>>                                          int flags, const char *name, uint32_t type)
>>  {
>> @@ -3718,6 +3733,12 @@ static bool drm_property_change_is_valid(struct drm_property *property,
>>                 if (value < property->values[0] || value > property->values[1])
>>                         return false;
>>                 return true;
>> +       } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
>> +               int64_t svalue = U642I64(value);
>> +               if (svalue < U642I64(property->values[0]) ||
>> +                               svalue > U642I64(property->values[1]))
>> +                       return false;
>> +               return true;
>>         } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
>>                 int i;
>>                 uint64_t valid_mask = 0;
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index c1c243f..9bd3551 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -64,6 +64,15 @@ struct drm_object_properties {
>>         uint64_t values[DRM_OBJECT_MAX_PROPERTY];
>>  };
>>
>> +static inline int64_t U642I64(uint64_t val)
>> +{
>> +       return (int64_t)*((int64_t *)&val);
>
> The cast to "(int64_t)" is unneeded. Dereferencing (int64_t*) will
> always yield (int64_t).
>
> Same below.
>
> Btw., why are these macros needed? unsigned->signed conversion is
> well-defined. signed->unsigned is undefined, but I thought we rely on
> static reinterpretation in the kernel. Dunno.. maybe I'm wrong.

iirc, I added these in userspace similar to U642VOID()/VOID2U64() and
copied to kernel when I needed them there ;-)

BR,
-R

> Anyhow, this is:
>
> Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
>
> Thanks
> David
>
>> +}
>> +static inline uint64_t I642U64(int64_t val)
>> +{
>> +       return (uint64_t)*((uint64_t *)&val);
>> +}
>> +
>>  enum drm_connector_force {
>>         DRM_FORCE_UNSPECIFIED,
>>         DRM_FORCE_OFF,
>> @@ -982,6 +991,9 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
>>  struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
>>                                          const char *name,
>>                                          uint64_t min, uint64_t max);
>> +struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
>> +                                        int flags, const char *name,
>> +                                        int64_t min, int64_t max);
>>  struct drm_property *drm_property_create_object(struct drm_device *dev,
>>                                          int flags, const char *name, uint32_t type);
>>  extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>> index 5b530c7..ded505e 100644
>> --- a/include/uapi/drm/drm_mode.h
>> +++ b/include/uapi/drm/drm_mode.h
>> @@ -264,6 +264,7 @@ struct drm_mode_get_connector {
>>  #define DRM_MODE_PROP_EXTENDED_TYPE    0x0000ffc0
>>  #define DRM_MODE_PROP_TYPE(n)          ((n) << 6)
>>  #define DRM_MODE_PROP_OBJECT           DRM_MODE_PROP_TYPE(1)
>> +#define DRM_MODE_PROP_SIGNED_RANGE     DRM_MODE_PROP_TYPE(2)
>>
>>  struct drm_mode_property_enum {
>>         __u64 value;
>> --
>> 1.9.3
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/8] drm: Allow drm_mode_object_find() to look up an object of any type
  2014-05-29 11:18   ` Daniel Vetter
@ 2014-05-29 12:01     ` Rob Clark
  2014-05-29 12:17       ` Daniel Vetter
  0 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2014-05-29 12:01 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Thu, May 29, 2014 at 7:18 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Wed, May 28, 2014 at 07:57:21PM -0400, Rob Clark wrote:
>> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
>>
>> To avoid having to pass object types from userspace for atomic mode
>> setting ioctl, allow drm_mode_object_find() to look up an object of any
>> type. This will only work as long as the all object types share the ID
>> space.
>>
>> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>
> Still NACK. Iirc you only want a boolean "does this exist" so please add a
> new function for this. Returning a full pointer for framebuffer is simply
> unsafe, allowing that a call for trouble. Yes, I know your current code is
> safe but I don't want to freak out and do an ad-hoc audit every time I
> stumble over this again.

this one isn't removing the (type != FB_ID) check.. if you looked more
carefully at the other patches you'd realized I'd already changed this
;-)

I suppose for completeness we should add an obj->type != FB_ID check
too, although it should *not* be a WARN_ON().  The one place it is
used is for atomic ioctl:

+ obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
+ if (!obj || !obj->properties) {
+ ret = -ENOENT;
+ goto out;
+ }

we don't want userspace to be able to request a WARN_ON().

BR,
-R


> -Daniel
>
>> ---
>>  drivers/gpu/drm/drm_crtc.c | 3 ++-
>>  include/drm/drm_crtc.h     | 1 +
>>  2 files changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index c53ddc3..b975575 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -409,7 +409,8 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
>>
>>       mutex_lock(&dev->mode_config.idr_mutex);
>>       obj = idr_find(&dev->mode_config.crtc_idr, id);
>> -     if (!obj || (obj->type != type) || (obj->id != id))
>> +     if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
>> +         (obj->id != id))
>>               obj = NULL;
>>       mutex_unlock(&dev->mode_config.idr_mutex);
>>
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index dbd7954..44d7964 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -50,6 +50,7 @@ struct drm_clip_rect;
>>  #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
>>  #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
>>  #define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd
>> +#define DRM_MODE_OBJECT_ANY 0
>>
>>  struct drm_mode_object {
>>       uint32_t id;
>> --
>> 1.9.3
>>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 8/8] drm: convert crtc and connection_mutex to ww_mutex
  2014-05-29 11:22   ` Daniel Vetter
@ 2014-05-29 12:07     ` Rob Clark
  2014-05-29 12:59       ` [PATCH 1/2] v2 (Daniel): - Readd lost include. - Drop all the additional complexity which we only need later: The basic ww dance only requires the lock list and the contended pointer, nothing more. Some of the stuff also looks very suspicious. - Improve the contended checks a bit to make sure we only get an -EALREADY when we expect it. - Add an interruptible backoff function just to be complete (since otherwise the interruptible locking function makes 0 sense at all). - Inline the slowpath (due to the above). - Move ww_acquire_fini into drm_modeset_acquire_fini. I suspect this has been due to the confusion about the lifetime of the acquire ctx wrt the locking retry loop. - Drop uapi changes, they really shouldn't be in this patch. - s/!ret/ret == 0/ because I got confused - I tend to use !ret only for booleans or pointers, not for errno integer return values Daniel Vetter
  0 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2014-05-29 12:07 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Thu, May 29, 2014 at 7:22 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Wed, May 28, 2014 at 07:57:25PM -0400, Rob Clark wrote:
>> For atomic, it will be quite necessary to not need to care so much
>> about locking order.  And 'state' gives us a convenient place to stash a
>> ww_ctx for any sort of update that needs to grab multiple crtc locks.
>>
>> Because we will want to eventually make locking even more fine grained
>> (giving locks to planes, connectors, etc), split out drm_modeset_lock
>> and drm_modeset_acquire_ctx to track acquired locks.
>>
>> Atomic will use this to keep track of which locks have been acquired
>> in a transaction.
>>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>
> I still would like to see all the additional magic for atomic updates
> removed from the drm_mode_lock functions. Getting the w/w dance right
> isn't that simple and all these complications confuse.

there are something like 10 lines of additional magic in this patch
which is not used yet.  I managed to move most of it to the now-later
atomic patch (which was previously earlier in the series).  But
running out of time/energy to care about juggling things around.  So,
meh.

BR,
-R

> Also I simply can't review whether these added features make sense without
> their users ;-)
> -Daniel
>
>> ---
>>  drivers/gpu/drm/Makefile             |   2 +-
>>  drivers/gpu/drm/drm_crtc.c           |  87 +++++++++++-----
>>  drivers/gpu/drm/drm_crtc_helper.c    |   3 +-
>>  drivers/gpu/drm/drm_fb_helper.c      |   4 +
>>  drivers/gpu/drm/drm_modeset_lock.c   | 187 +++++++++++++++++++++++++++++++++++
>>  drivers/gpu/drm/drm_plane_helper.c   |   2 +-
>>  drivers/gpu/drm/i915/intel_crt.c     |   5 +-
>>  drivers/gpu/drm/i915/intel_display.c |  58 +++++++----
>>  drivers/gpu/drm/i915/intel_drv.h     |   6 +-
>>  drivers/gpu/drm/i915/intel_sprite.c  |   2 +-
>>  drivers/gpu/drm/i915/intel_tv.c      |   5 +-
>>  drivers/gpu/drm/omapdrm/omap_crtc.c  |  10 +-
>>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c  |   8 +-
>>  include/drm/drmP.h                   |   5 -
>>  include/drm/drm_crtc.h               |  15 +--
>>  include/drm/drm_modeset_lock.h       | 139 ++++++++++++++++++++++++++
>>  include/uapi/drm/drm_mode.h          |   3 +
>>  17 files changed, 468 insertions(+), 73 deletions(-)
>>  create mode 100644 drivers/gpu/drm/drm_modeset_lock.c
>>  create mode 100644 include/drm/drm_modeset_lock.h
>>
>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>> index 48e38ba..bf4c12d 100644
>> --- a/drivers/gpu/drm/Makefile
>> +++ b/drivers/gpu/drm/Makefile
>> @@ -14,7 +14,7 @@ drm-y       :=      drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
>>               drm_info.o drm_debugfs.o drm_encoder_slave.o \
>>               drm_trace_points.o drm_global.o drm_prime.o \
>>               drm_rect.o drm_vma_manager.o drm_flip_work.o \
>> -             drm_plane_helper.o
>> +             drm_plane_helper.o drm_modeset_lock.o
>>
>>  drm-$(CONFIG_COMPAT) += drm_ioc32.o
>>  drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index de1520c..cf622f6 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -37,8 +37,8 @@
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_edid.h>
>>  #include <drm/drm_fourcc.h>
>> +#include <drm/drm_modeset_lock.h>
>>
>> -#include "drm_crtc_internal.h"
>>
>>  /**
>>   * drm_modeset_lock_all - take all modeset locks
>> @@ -50,14 +50,42 @@
>>   */
>>  void drm_modeset_lock_all(struct drm_device *dev)
>>  {
>> -     struct drm_crtc *crtc;
>> +     struct drm_mode_config *config = &dev->mode_config;
>> +     struct drm_modeset_acquire_ctx *ctx;
>> +     int ret;
>>
>> -     mutex_lock(&dev->mode_config.mutex);
>> +     ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
>> +     if (WARN_ON(!ctx))
>> +             return;
>>
>> -     mutex_lock(&dev->mode_config.connection_mutex);
>> +     mutex_lock(&config->mutex);
>>
>> -     list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
>> -             mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
>> +     drm_modeset_acquire_init(ctx, false, false);
>> +
>> +retry:
>> +     ret = drm_modeset_lock(&config->connection_mutex, ctx);
>> +     if (ret)
>> +             goto fail;
>> +     ret = drm_modeset_lock_all_crtcs(dev, ctx);
>> +     if (ret)
>> +             goto fail;
>> +
>> +     WARN_ON(config->acquire_ctx);
>> +
>> +     /* now we hold the locks, so now that it is safe, stash the
>> +      * ctx for drm_modeset_unlock_all():
>> +      */
>> +     config->acquire_ctx = ctx;
>> +
>> +     drm_warn_on_modeset_not_all_locked(dev);
>> +
>> +     return;
>> +
>> +fail:
>> +     if (ret == -EDEADLK) {
>> +             drm_modeset_backoff(ctx);
>> +             goto retry;
>> +     }
>>  }
>>  EXPORT_SYMBOL(drm_modeset_lock_all);
>>
>> @@ -69,12 +97,18 @@ EXPORT_SYMBOL(drm_modeset_lock_all);
>>   */
>>  void drm_modeset_unlock_all(struct drm_device *dev)
>>  {
>> -     struct drm_crtc *crtc;
>> +     struct drm_mode_config *config = &dev->mode_config;
>> +     struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
>>
>> -     list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
>> -             mutex_unlock(&crtc->mutex);
>> +     if (WARN_ON(!ctx))
>> +             return;
>> +
>> +     config->acquire_ctx = NULL;
>> +     drm_modeset_drop_locks(ctx);
>> +     ww_acquire_fini(&ctx->ww_ctx);
>> +     drm_modeset_acquire_fini(ctx);
>>
>> -     mutex_unlock(&dev->mode_config.connection_mutex);
>> +     kfree(ctx);
>>
>>       mutex_unlock(&dev->mode_config.mutex);
>>  }
>> @@ -95,9 +129,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
>>               return;
>>
>>       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
>> -             WARN_ON(!mutex_is_locked(&crtc->mutex));
>> +             WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
>>
>> -     WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
>> +     WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
>>  }
>>  EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
>> @@ -677,6 +711,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>>  }
>>  EXPORT_SYMBOL(drm_framebuffer_remove);
>>
>> +DEFINE_WW_CLASS(crtc_ww_class);
>> +
>>  /**
>>   * drm_crtc_init_with_planes - Initialise a new CRTC object with
>>   *    specified primary and cursor planes.
>> @@ -696,6 +732,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>>                             void *cursor,
>>                             const struct drm_crtc_funcs *funcs)
>>  {
>> +     struct drm_mode_config *config = &dev->mode_config;
>>       int ret;
>>
>>       crtc->dev = dev;
>> @@ -703,8 +740,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>>       crtc->invert_dimensions = false;
>>
>>       drm_modeset_lock_all(dev);
>> -     mutex_init(&crtc->mutex);
>> -     mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
>> +     drm_modeset_lock_init(&crtc->mutex);
>> +     /* dropped by _unlock_all(): */
>> +     drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
>>
>>       ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
>>       if (ret)
>> @@ -712,8 +750,8 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>>
>>       crtc->base.properties = &crtc->properties;
>>
>> -     list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
>> -     dev->mode_config.num_crtc++;
>> +     list_add_tail(&crtc->head, &config->crtc_list);
>> +     config->num_crtc++;
>>
>>       crtc->primary = primary;
>>       if (primary)
>> @@ -741,6 +779,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
>>       kfree(crtc->gamma_store);
>>       crtc->gamma_store = NULL;
>>
>> +     drm_modeset_lock_fini(&crtc->mutex);
>> +
>>       drm_mode_object_put(dev, &crtc->base);
>>       list_del(&crtc->head);
>>       dev->mode_config.num_crtc--;
>> @@ -1804,7 +1844,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>>       DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
>>
>>       mutex_lock(&dev->mode_config.mutex);
>> -     mutex_lock(&dev->mode_config.connection_mutex);
>> +     drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>>
>>       connector = drm_connector_find(dev, out_resp->connector_id);
>>       if (!connector) {
>> @@ -1903,7 +1943,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>>       out_resp->count_encoders = encoders_count;
>>
>>  out:
>> -     mutex_unlock(&dev->mode_config.connection_mutex);
>> +     drm_modeset_unlock(&dev->mode_config.connection_mutex);
>>       mutex_unlock(&dev->mode_config.mutex);
>>
>>       return ret;
>> @@ -2487,7 +2527,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
>>               return -ENOENT;
>>       }
>>
>> -     mutex_lock(&crtc->mutex);
>> +     drm_modeset_lock(&crtc->mutex, NULL);
>>       if (req->flags & DRM_MODE_CURSOR_BO) {
>>               if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
>>                       ret = -ENXIO;
>> @@ -2511,7 +2551,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
>>               }
>>       }
>>  out:
>> -     mutex_unlock(&crtc->mutex);
>> +     drm_modeset_unlock(&crtc->mutex);
>>
>>       return ret;
>>
>> @@ -4195,7 +4235,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>>       if (!crtc)
>>               return -ENOENT;
>>
>> -     mutex_lock(&crtc->mutex);
>> +     drm_modeset_lock(&crtc->mutex, NULL);
>>       if (crtc->primary->fb == NULL) {
>>               /* The framebuffer is currently unbound, presumably
>>                * due to a hotplug event, that userspace has not
>> @@ -4279,7 +4319,7 @@ out:
>>               drm_framebuffer_unreference(fb);
>>       if (old_fb)
>>               drm_framebuffer_unreference(old_fb);
>> -     mutex_unlock(&crtc->mutex);
>> +     drm_modeset_unlock(&crtc->mutex);
>>
>>       return ret;
>>  }
>> @@ -4644,7 +4684,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
>>  void drm_mode_config_init(struct drm_device *dev)
>>  {
>>       mutex_init(&dev->mode_config.mutex);
>> -     mutex_init(&dev->mode_config.connection_mutex);
>> +     drm_modeset_lock_init(&dev->mode_config.connection_mutex);
>>       mutex_init(&dev->mode_config.idr_mutex);
>>       mutex_init(&dev->mode_config.fb_lock);
>>       INIT_LIST_HEAD(&dev->mode_config.fb_list);
>> @@ -4744,5 +4784,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
>>       }
>>
>>       idr_destroy(&dev->mode_config.crtc_idr);
>> +     drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
>>  }
>>  EXPORT_SYMBOL(drm_mode_config_cleanup);
>> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
>> index 5b93caf..4eaded8 100644
>> --- a/drivers/gpu/drm/drm_crtc_helper.c
>> +++ b/drivers/gpu/drm/drm_crtc_helper.c
>> @@ -89,8 +89,7 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
>>       struct drm_device *dev = encoder->dev;
>>
>>       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
>> -     WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
>> -
>> +     WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>>               if (connector->encoder == encoder)
>>                       return true;
>> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
>> index 1e28ba6..25f687d 100644
>> --- a/drivers/gpu/drm/drm_fb_helper.c
>> +++ b/drivers/gpu/drm/drm_fb_helper.c
>> @@ -355,6 +355,10 @@ static bool drm_fb_helper_force_kernel_mode(void)
>>               if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
>>                       continue;
>>
>> +             /* NOTE: we use lockless flag below to avoid grabbing other
>> +              * modeset locks.  So just trylock the underlying mutex
>> +              * directly:
>> +              */
>>               if (!mutex_trylock(&dev->mode_config.mutex)) {
>>                       error = true;
>>                       continue;
>> diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
>> new file mode 100644
>> index 0000000..6580d6c
>> --- /dev/null
>> +++ b/drivers/gpu/drm/drm_modeset_lock.c
>> @@ -0,0 +1,187 @@
>> +/*
>> + * Copyright (C) 2014 Red Hat
>> + * Author: Rob Clark <robdclark@gmail.com>
>> + *
>> + * 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
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
>> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
>> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>> + * OTHER DEALINGS IN THE SOFTWARE.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_modeset_lock.h>
>> +
>> +
>> +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
>> +             bool nolock, bool nonblock)
>> +{
>> +     ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
>> +     INIT_LIST_HEAD(&ctx->locked);
>> +     mutex_init(&ctx->mutex);
>> +     ctx->nolock = nolock;
>> +     ctx->nonblock = nonblock;
>> +}
>> +EXPORT_SYMBOL(drm_modeset_acquire_init);
>> +
>> +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
>> +{
>> +     WARN_ON(ctx->contended);
>> +     /*
>> +      * NOTE: it is intentional that ww_acquire_fini() is not called
>> +      * here.. due to the way lock handover works in drm_atomic
>> +      */
>> +     mutex_destroy(&ctx->mutex);
>> +}
>> +EXPORT_SYMBOL(drm_modeset_acquire_fini);
>> +
>> +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
>> +{
>> +     WARN_ON(ctx->contended);
>> +     mutex_lock(&ctx->mutex);
>> +     while (!list_empty(&ctx->locked)) {
>> +             struct drm_modeset_lock *lock;
>> +
>> +             lock = list_first_entry(&ctx->locked,
>> +                             struct drm_modeset_lock, head);
>> +
>> +             drm_modeset_unlock(lock);
>> +     }
>> +     mutex_unlock(&ctx->mutex);
>> +}
>> +EXPORT_SYMBOL(drm_modeset_drop_locks);
>> +
>> +static int modeset_lock(struct drm_modeset_lock *lock,
>> +             struct drm_modeset_acquire_ctx *ctx,
>> +             bool interruptible, bool slow)
>> +{
>> +     int ret;
>> +
>> +     if (ctx->nolock)
>> +             return 0;
>> +
>> +     WARN_ON(ctx->frozen);    /* all locks should be held by now! */
>> +     WARN_ON(ctx->contended);
>> +
>> +retry:
>> +     if (interruptible) {
>> +             ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
>> +     } else if (slow) {
>> +             ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
>> +             ret = 0;
>> +     } else {
>> +             ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
>> +     }
>> +     if (!ret) {
>> +             if (lock->atomic_pending) {
>> +                     /* some other pending update with dropped locks */
>> +                     ww_mutex_unlock(&lock->mutex);
>> +                     if (ctx->nonblock)
>> +                             return -EBUSY;
>> +                     wait_event(lock->event, !lock->atomic_pending);
>> +                     goto retry;
>> +             }
>> +             lock->atomic_pending = true;
>> +             WARN_ON(!list_empty(&lock->head));
>> +             list_add(&lock->head, &ctx->locked);
>> +     } else if (ret == -EALREADY) {
>> +             /* we already hold the lock.. this is fine */
>> +             ret = 0;
>> +     } else if (ret == -EDEADLK) {
>> +             ctx->contended = lock;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
>> +{
>> +     struct drm_modeset_lock *contended = ctx->contended;
>> +
>> +     ctx->contended = NULL;
>> +
>> +     if (WARN_ON(!contended))
>> +             return;
>> +
>> +     drm_modeset_drop_locks(ctx);
>> +
>> +     modeset_lock(contended, ctx, false, true);
>> +}
>> +EXPORT_SYMBOL(drm_modeset_backoff);
>> +
>> +/**
>> + * drm_modeset_lock - take modeset lock
>> + * @lock: lock to take
>> + * @ctx: acquire ctx
>> + *
>> + * If ctx is not NULL, then then it's acquire context is used
>> + * and the lock does not need to be explicitly unlocked, it
>> + * will be automatically unlocked when the atomic update is
>> + * complete
>> + */
>> +int drm_modeset_lock(struct drm_modeset_lock *lock,
>> +             struct drm_modeset_acquire_ctx *ctx)
>> +{
>> +     if (ctx)
>> +             return modeset_lock(lock, ctx, false, false);
>> +
>> +     ww_mutex_lock(&lock->mutex, NULL);
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(drm_modeset_lock);
>> +
>> +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
>> +             struct drm_modeset_acquire_ctx *ctx)
>> +{
>> +     if (ctx)
>> +             return modeset_lock(lock, ctx, true, false);
>> +
>> +     return ww_mutex_lock_interruptible(&lock->mutex, NULL);
>> +}
>> +EXPORT_SYMBOL(drm_modeset_lock_interruptible);
>> +
>> +/**
>> + * drm_modeset_unlock - drop modeset lock
>> + * @lock: lock to release
>> + */
>> +void drm_modeset_unlock(struct drm_modeset_lock *lock)
>> +{
>> +     list_del_init(&lock->head);
>> +     lock->atomic_pending = false;
>> +     ww_mutex_unlock(&lock->mutex);
>> +     wake_up_all(&lock->event);
>> +}
>> +EXPORT_SYMBOL(drm_modeset_unlock);
>> +
>> +/**
>> + * drm_modeset_lock_all_crtcs - helper to drm_modeset_lock() all CRTCs
>> + */
>> +int drm_modeset_lock_all_crtcs(struct drm_device *dev,
>> +             struct drm_modeset_acquire_ctx *ctx)
>> +{
>> +     struct drm_mode_config *config = &dev->mode_config;
>> +     struct drm_crtc *crtc;
>> +     int ret = 0;
>> +
>> +     list_for_each_entry(crtc, &config->crtc_list, head) {
>> +             ret = drm_modeset_lock(&crtc->mutex, ctx);
>> +             if (ret)
>> +                     return ret;
>> +     }
>> +
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
>> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
>> index 458d9bf..6301ec6 100644
>> --- a/drivers/gpu/drm/drm_plane_helper.c
>> +++ b/drivers/gpu/drm/drm_plane_helper.c
>> @@ -59,7 +59,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
>>        * need to grab the connection_mutex here to be able to make these
>>        * checks.
>>        */
>> -     WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
>> +     WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>
>>       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>>               if (connector->encoder && connector->encoder->crtc == crtc) {
>> diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
>> index 22d8347..33328fc 100644
>> --- a/drivers/gpu/drm/i915/intel_crt.c
>> +++ b/drivers/gpu/drm/i915/intel_crt.c
>> @@ -630,6 +630,7 @@ intel_crt_detect(struct drm_connector *connector, bool force)
>>       enum intel_display_power_domain power_domain;
>>       enum drm_connector_status status;
>>       struct intel_load_detect_pipe tmp;
>> +     struct drm_modeset_acquire_ctx ctx;
>>
>>       intel_runtime_pm_get(dev_priv);
>>
>> @@ -673,12 +674,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
>>       }
>>
>>       /* for pre-945g platforms use load detect */
>> -     if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
>> +     if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) {
>>               if (intel_crt_detect_ddc(connector))
>>                       status = connector_status_connected;
>>               else
>>                       status = intel_crt_load_detect(crt);
>> -             intel_release_load_detect_pipe(connector, &tmp);
>> +             intel_release_load_detect_pipe(connector, &tmp, &ctx);
>>       } else
>>               status = connector_status_unknown;
>>
>> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
>> index 5844f07..ba6cbbb 100644
>> --- a/drivers/gpu/drm/i915/intel_display.c
>> +++ b/drivers/gpu/drm/i915/intel_display.c
>> @@ -2363,7 +2363,7 @@ void intel_display_handle_reset(struct drm_device *dev)
>>       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
>>               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>>
>> -             mutex_lock(&crtc->mutex);
>> +             drm_modeset_lock(&crtc->mutex, NULL);
>>               /*
>>                * FIXME: Once we have proper support for primary planes (and
>>                * disabling them without disabling the entire crtc) allow again
>> @@ -2374,7 +2374,7 @@ void intel_display_handle_reset(struct drm_device *dev)
>>                                                              crtc->primary->fb,
>>                                                              crtc->x,
>>                                                              crtc->y);
>> -             mutex_unlock(&crtc->mutex);
>> +             drm_modeset_unlock(&crtc->mutex);
>>       }
>>  }
>>
>> @@ -7972,7 +7972,8 @@ mode_fits_in_fbdev(struct drm_device *dev,
>>
>>  bool intel_get_load_detect_pipe(struct drm_connector *connector,
>>                               struct drm_display_mode *mode,
>> -                             struct intel_load_detect_pipe *old)
>> +                             struct intel_load_detect_pipe *old,
>> +                             struct drm_modeset_acquire_ctx *ctx)
>>  {
>>       struct intel_crtc *intel_crtc;
>>       struct intel_encoder *intel_encoder =
>> @@ -7982,13 +7983,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>>       struct drm_crtc *crtc = NULL;
>>       struct drm_device *dev = encoder->dev;
>>       struct drm_framebuffer *fb;
>> -     int i = -1;
>> +     struct drm_mode_config *config = &dev->mode_config;
>> +     int ret, i = -1;
>>
>>       DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
>>                     connector->base.id, drm_get_connector_name(connector),
>>                     encoder->base.id, drm_get_encoder_name(encoder));
>>
>> -     mutex_lock(&dev->mode_config.connection_mutex);
>> +     drm_modeset_acquire_init(ctx, false, false);
>> +
>> +retry:
>> +     ret = drm_modeset_lock(&config->connection_mutex, ctx);
>> +     if (ret)
>> +             goto fail_unlock;
>>
>>       /*
>>        * Algorithm gets a little messy:
>> @@ -8004,7 +8011,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>>       if (encoder->crtc) {
>>               crtc = encoder->crtc;
>>
>> -             mutex_lock(&crtc->mutex);
>> +             ret = drm_modeset_lock(&crtc->mutex, ctx);
>> +             if (ret)
>> +                     goto fail_unlock;
>>
>>               old->dpms_mode = connector->dpms;
>>               old->load_detect_temp = false;
>> @@ -8017,7 +8026,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>>       }
>>
>>       /* Find an unused one (if possible) */
>> -     list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
>> +     list_for_each_entry(possible_crtc, &config->crtc_list, head) {
>>               i++;
>>               if (!(encoder->possible_crtcs & (1 << i)))
>>                       continue;
>> @@ -8032,10 +8041,12 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>>        */
>>       if (!crtc) {
>>               DRM_DEBUG_KMS("no pipe available for load-detect\n");
>> -             goto fail_unlock_connector;
>> +             goto fail_unlock;
>>       }
>>
>> -     mutex_lock(&crtc->mutex);
>> +     ret = drm_modeset_lock(&crtc->mutex, ctx);
>> +     if (ret)
>> +             goto fail_unlock;
>>       intel_encoder->new_crtc = to_intel_crtc(crtc);
>>       to_intel_connector(connector)->new_encoder = intel_encoder;
>>
>> @@ -8085,15 +8096,22 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>>               intel_crtc->new_config = &intel_crtc->config;
>>       else
>>               intel_crtc->new_config = NULL;
>> -     mutex_unlock(&crtc->mutex);
>> -fail_unlock_connector:
>> -     mutex_unlock(&dev->mode_config.connection_mutex);
>> +fail_unlock:
>> +     if (ret == -EDEADLK) {
>> +             drm_modeset_backoff(ctx);
>> +             goto retry;
>> +     }
>> +
>> +     drm_modeset_drop_locks(ctx);
>> +     ww_acquire_fini(&ctx->ww_ctx);
>> +     drm_modeset_acquire_fini(ctx);
>>
>>       return false;
>>  }
>>
>>  void intel_release_load_detect_pipe(struct drm_connector *connector,
>> -                                 struct intel_load_detect_pipe *old)
>> +                                 struct intel_load_detect_pipe *old,
>> +                                 struct drm_modeset_acquire_ctx *ctx)
>>  {
>>       struct intel_encoder *intel_encoder =
>>               intel_attached_encoder(connector);
>> @@ -8117,8 +8135,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
>>                       drm_framebuffer_unreference(old->release_fb);
>>               }
>>
>> -             mutex_unlock(&crtc->mutex);
>> -             mutex_unlock(&connector->dev->mode_config.connection_mutex);
>> +             goto unlock;
>>               return;
>>       }
>>
>> @@ -8126,8 +8143,10 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
>>       if (old->dpms_mode != DRM_MODE_DPMS_ON)
>>               connector->funcs->dpms(connector, old->dpms_mode);
>>
>> -     mutex_unlock(&crtc->mutex);
>> -     mutex_unlock(&connector->dev->mode_config.connection_mutex);
>> +unlock:
>> +     drm_modeset_drop_locks(ctx);
>> +     ww_acquire_fini(&ctx->ww_ctx);
>> +     drm_modeset_acquire_fini(ctx);
>>  }
>>
>>  static int i9xx_pll_refclk(struct drm_device *dev,
>> @@ -11385,6 +11404,7 @@ static void intel_enable_pipe_a(struct drm_device *dev)
>>       struct intel_connector *connector;
>>       struct drm_connector *crt = NULL;
>>       struct intel_load_detect_pipe load_detect_temp;
>> +     struct drm_modeset_acquire_ctx ctx;
>>
>>       /* We can't just switch on the pipe A, we need to set things up with a
>>        * proper mode and output configuration. As a gross hack, enable pipe A
>> @@ -11401,8 +11421,8 @@ static void intel_enable_pipe_a(struct drm_device *dev)
>>       if (!crt)
>>               return;
>>
>> -     if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp))
>> -             intel_release_load_detect_pipe(crt, &load_detect_temp);
>> +     if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx))
>> +             intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx);
>>
>>
>>  }
>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>> index d8b540b..824f8ae 100644
>> --- a/drivers/gpu/drm/i915/intel_drv.h
>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>> @@ -721,9 +721,11 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
>>                        struct intel_digital_port *dport);
>>  bool intel_get_load_detect_pipe(struct drm_connector *connector,
>>                               struct drm_display_mode *mode,
>> -                             struct intel_load_detect_pipe *old);
>> +                             struct intel_load_detect_pipe *old,
>> +                             struct drm_modeset_acquire_ctx *ctx);
>>  void intel_release_load_detect_pipe(struct drm_connector *connector,
>> -                                 struct intel_load_detect_pipe *old);
>> +                                 struct intel_load_detect_pipe *old,
>> +                                 struct drm_modeset_acquire_ctx *ctx);
>>  int intel_pin_and_fence_fb_obj(struct drm_device *dev,
>>                              struct drm_i915_gem_object *obj,
>>                              struct intel_ring_buffer *pipelined);
>> diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
>> index 213cd58..c235546 100644
>> --- a/drivers/gpu/drm/i915/intel_sprite.c
>> +++ b/drivers/gpu/drm/i915/intel_sprite.c
>> @@ -55,7 +55,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl
>>       int scanline, min, max, vblank_start;
>>       DEFINE_WAIT(wait);
>>
>> -     WARN_ON(!mutex_is_locked(&crtc->base.mutex));
>> +     WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
>>
>>       vblank_start = mode->crtc_vblank_start;
>>       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
>> diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
>> index e0193e8..97d9fc9 100644
>> --- a/drivers/gpu/drm/i915/intel_tv.c
>> +++ b/drivers/gpu/drm/i915/intel_tv.c
>> @@ -1321,10 +1321,11 @@ intel_tv_detect(struct drm_connector *connector, bool force)
>>
>>       if (force) {
>>               struct intel_load_detect_pipe tmp;
>> +             struct drm_modeset_acquire_ctx ctx;
>>
>> -             if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
>> +             if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) {
>>                       type = intel_tv_detect_type(intel_tv, connector);
>> -                     intel_release_load_detect_pipe(connector, &tmp);
>> +                     intel_release_load_detect_pipe(connector, &tmp, &ctx);
>>               } else
>>                       return connector_status_unknown;
>>       } else
>> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
>> index e3c47a8..2d28dc3 100644
>> --- a/drivers/gpu/drm/omapdrm/omap_crtc.c
>> +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
>> @@ -319,13 +319,13 @@ static void page_flip_worker(struct work_struct *work)
>>       struct drm_display_mode *mode = &crtc->mode;
>>       struct drm_gem_object *bo;
>>
>> -     mutex_lock(&crtc->mutex);
>> +     drm_modeset_lock(&crtc->mutex, NULL);
>>       omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
>>                       0, 0, mode->hdisplay, mode->vdisplay,
>>                       crtc->x << 16, crtc->y << 16,
>>                       mode->hdisplay << 16, mode->vdisplay << 16,
>>                       vblank_cb, crtc);
>> -     mutex_unlock(&crtc->mutex);
>> +     drm_modeset_unlock(&crtc->mutex);
>>
>>       bo = omap_framebuffer_bo(crtc->primary->fb, 0);
>>       drm_gem_object_unreference_unlocked(bo);
>> @@ -465,7 +465,7 @@ static void apply_worker(struct work_struct *work)
>>        * the callbacks and list modification all serialized
>>        * with respect to modesetting ioctls from userspace.
>>        */
>> -     mutex_lock(&crtc->mutex);
>> +     drm_modeset_lock(&crtc->mutex, NULL);
>>       dispc_runtime_get();
>>
>>       /*
>> @@ -510,7 +510,7 @@ static void apply_worker(struct work_struct *work)
>>
>>  out:
>>       dispc_runtime_put();
>> -     mutex_unlock(&crtc->mutex);
>> +     drm_modeset_unlock(&crtc->mutex);
>>  }
>>
>>  int omap_crtc_apply(struct drm_crtc *crtc,
>> @@ -518,7 +518,7 @@ int omap_crtc_apply(struct drm_crtc *crtc,
>>  {
>>       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>>
>> -     WARN_ON(!mutex_is_locked(&crtc->mutex));
>> +     WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
>>
>>       /* no need to queue it again if it is already queued: */
>>       if (apply->queued)
>> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
>> index e7199b4..8f3edc4 100644
>> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
>> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
>> @@ -187,7 +187,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
>>        * can do this since the caller in the drm core doesn't check anything
>>        * which is protected by any looks.
>>        */
>> -     mutex_unlock(&crtc->mutex);
>> +     drm_modeset_unlock(&crtc->mutex);
>>       drm_modeset_lock_all(dev_priv->dev);
>>
>>       /* A lot of the code assumes this */
>> @@ -252,7 +252,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
>>       ret = 0;
>>  out:
>>       drm_modeset_unlock_all(dev_priv->dev);
>> -     mutex_lock(&crtc->mutex);
>> +     drm_modeset_lock(&crtc->mutex, NULL);
>>
>>       return ret;
>>  }
>> @@ -273,7 +273,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
>>        * can do this since the caller in the drm core doesn't check anything
>>        * which is protected by any looks.
>>        */
>> -     mutex_unlock(&crtc->mutex);
>> +     drm_modeset_unlock(&crtc->mutex);
>>       drm_modeset_lock_all(dev_priv->dev);
>>
>>       vmw_cursor_update_position(dev_priv, shown,
>> @@ -281,7 +281,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
>>                                  du->cursor_y + du->hotspot_y);
>>
>>       drm_modeset_unlock_all(dev_priv->dev);
>> -     mutex_lock(&crtc->mutex);
>> +     drm_modeset_lock(&crtc->mutex, NULL);
>>
>>       return 0;
>>  }
>> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
>> index 12f10bc..a9b8a5d 100644
>> --- a/include/drm/drmP.h
>> +++ b/include/drm/drmP.h
>> @@ -1184,11 +1184,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev)
>>       return ret;
>>  }
>>
>> -static inline bool drm_modeset_is_locked(struct drm_device *dev)
>> -{
>> -     return mutex_is_locked(&dev->mode_config.mutex);
>> -}
>> -
>>  static inline bool drm_is_render_client(const struct drm_file *file_priv)
>>  {
>>       return file_priv->minor->type == DRM_MINOR_RENDER;
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index 1bd6708..deadb32 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -33,6 +33,7 @@
>>  #include <linux/hdmi.h>
>>  #include <drm/drm_mode.h>
>>  #include <drm/drm_fourcc.h>
>> +#include <drm/drm_modeset_lock.h>
>>
>>  struct drm_device;
>>  struct drm_mode_set;
>> @@ -205,6 +206,10 @@ struct drm_property {
>>       struct list_head enum_blob_list;
>>  };
>>
>> +void drm_modeset_lock_all(struct drm_device *dev);
>> +void drm_modeset_unlock_all(struct drm_device *dev);
>> +void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
>> +
>>  struct drm_crtc;
>>  struct drm_connector;
>>  struct drm_encoder;
>> @@ -280,6 +285,7 @@ struct drm_crtc_funcs {
>>   * drm_crtc - central CRTC control structure
>>   * @dev: parent DRM device
>>   * @head: list management
>> + * @mutex: per-CRTC locking
>>   * @base: base KMS object for ID tracking etc.
>>   * @primary: primary plane for this CRTC
>>   * @cursor: cursor plane for this CRTC
>> @@ -314,7 +320,7 @@ struct drm_crtc {
>>        * state, ...) and a write lock for everything which can be update
>>        * without a full modeset (fb, cursor data, ...)
>>        */
>> -     struct mutex mutex;
>> +     struct drm_modeset_lock mutex;
>>
>>       struct drm_mode_object base;
>>
>> @@ -738,7 +744,8 @@ struct drm_mode_group {
>>   */
>>  struct drm_mode_config {
>>       struct mutex mutex; /* protects configuration (mode lists etc.) */
>> -     struct mutex connection_mutex; /* protects connector->encoder and encoder->crtc links */
>> +     struct drm_modeset_lock connection_mutex; /* protects connector->encoder and encoder->crtc links */
>> +     struct drm_modeset_acquire_ctx *acquire_ctx; /* for legacy _lock_all() / _unlock_all() */
>>       struct mutex idr_mutex; /* for IDR management */
>>       struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
>>       /* this is limited to one for now */
>> @@ -839,10 +846,6 @@ struct drm_prop_enum_list {
>>       char *name;
>>  };
>>
>> -extern void drm_modeset_lock_all(struct drm_device *dev);
>> -extern void drm_modeset_unlock_all(struct drm_device *dev);
>> -extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
>> -
>>  extern int drm_crtc_init_with_planes(struct drm_device *dev,
>>                                    struct drm_crtc *crtc,
>>                                    struct drm_plane *primary,
>> diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
>> new file mode 100644
>> index 0000000..2630da3
>> --- /dev/null
>> +++ b/include/drm/drm_modeset_lock.h
>> @@ -0,0 +1,139 @@
>> +/*
>> + * Copyright (C) 2014 Red Hat
>> + * Author: Rob Clark <robdclark@gmail.com>
>> + *
>> + * 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
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
>> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
>> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>> + * OTHER DEALINGS IN THE SOFTWARE.
>> + */
>> +
>> +#ifndef DRM_MODESET_LOCK_H_
>> +#define DRM_MODESET_LOCK_H_
>> +
>> +#include <linux/ww_mutex.h>
>> +
>> +struct drm_modeset_lock;
>> +
>> +struct drm_modeset_acquire_ctx {
>> +
>> +     struct ww_acquire_ctx ww_ctx;
>> +
>> +     bool nolock : 1;
>> +     bool nonblock : 1;
>> +
>> +     /* just for debugging, the context is 'frozen' in drm_atomic_check()
>> +      * to catch anyone who might be trying to acquire a lock after it is
>> +      * too late.
>> +      */
>> +     bool frozen : 1;
>> +
>> +     /* contended lock: if a lock is contended you should only call
>> +      * drm_modeset_backoff() which drops locks and slow-locks the
>> +      * contended lock.
>> +      */
>> +     struct drm_modeset_lock *contended;
>> +
>> +     /* list of 'struct drm_modeset_lock': */
>> +     struct list_head locked;
>> +
>> +     /* currently simply for protecting against 'locked' list manipulation
>> +      * between original thread calling atomic->end() and driver thread
>> +      * calling back drm_atomic_commit_unlocked().
>> +      *
>> +      * Other spots are sufficiently synchronized by virtue of holding
>> +      * the lock's ww_mutex.  But during the lock/resource hand-over to the
>> +      * driver thread (drop_locks()/grab_locks()), we cannot rely on this.
>> +      */
>> +     struct mutex mutex;
>> +};
>> +
>> +/**
>> + * drm_modeset_lock - used for locking modeset resources.
>> + * @mutex: resource locking
>> + * @atomic_pending: is this resource part of a still-pending
>> + *    atomic update
>> + * @head: used to hold it's place on state->locked list when
>> + *    part of an atomic update
>> + *
>> + * Used for locking CRTCs and other modeset resources.
>> + */
>> +struct drm_modeset_lock {
>> +     /**
>> +      * modeset lock
>> +      */
>> +     struct ww_mutex mutex;
>> +
>> +     /**
>> +      * Are we busy (pending asynchronous/NONBLOCK update)?  Any further
>> +      * asynchronous update will return -EBUSY if it also needs to acquire
>> +      * this lock.  While a synchronous update will block until the pending
>> +      * async update completes.
>> +      *
>> +      * Drivers must ensure the update is completed before sending vblank
>> +      * event to userspace.  Typically this just means don't send event
>> +      * before drm_atomic_commit_unlocked() returns.
>> +      */
>> +     bool atomic_pending;
>> +
>> +     /**
>> +      * Resources that are locked as part of an atomic update are added
>> +      * to a list (so we know what to unlock at the end).
>> +      */
>> +     struct list_head head;
>> +
>> +     /**
>> +      * For waiting on atomic_pending locks, if not a NONBLOCK operation.
>> +      */
>> +     wait_queue_head_t event;
>> +};
>> +
>> +extern struct ww_class crtc_ww_class;
>> +
>> +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
>> +             bool nolock, bool nonblock);
>> +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
>> +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
>> +void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
>> +
>> +static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock)
>> +{
>> +     ww_mutex_init(&lock->mutex, &crtc_ww_class);
>> +     INIT_LIST_HEAD(&lock->head);
>> +     init_waitqueue_head(&lock->event);
>> +}
>> +
>> +static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
>> +{
>> +     WARN_ON(!list_empty(&lock->head));
>> +}
>> +
>> +static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock)
>> +{
>> +     return ww_mutex_is_locked(&lock->mutex);
>> +}
>> +
>> +int drm_modeset_lock(struct drm_modeset_lock *lock,
>> +             struct drm_modeset_acquire_ctx *ctx);
>> +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
>> +             struct drm_modeset_acquire_ctx *ctx);
>> +void drm_modeset_unlock(struct drm_modeset_lock *lock);
>> +
>> +struct drm_device;
>> +int drm_modeset_lock_all_crtcs(struct drm_device *dev,
>> +             struct drm_modeset_acquire_ctx *ctx);
>> +
>> +#endif /* DRM_MODESET_LOCK_H_ */
>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>> index ded505e..6421edc 100644
>> --- a/include/uapi/drm/drm_mode.h
>> +++ b/include/uapi/drm/drm_mode.h
>> @@ -511,4 +511,7 @@ struct drm_mode_destroy_dumb {
>>       uint32_t handle;
>>  };
>>
>> +#define DRM_MODE_ATOMIC_NONBLOCK  0x0200
>> +#define DRM_MODE_ATOMIC_NOLOCK    0x8000  /* only used internally */
>> +
>>  #endif
>> --
>> 1.9.3
>>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/8] drm: Allow drm_mode_object_find() to look up an object of any type
  2014-05-29 12:01     ` Rob Clark
@ 2014-05-29 12:17       ` Daniel Vetter
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel Vetter @ 2014-05-29 12:17 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Thu, May 29, 2014 at 08:01:48AM -0400, Rob Clark wrote:
> On Thu, May 29, 2014 at 7:18 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Wed, May 28, 2014 at 07:57:21PM -0400, Rob Clark wrote:
> >> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> >>
> >> To avoid having to pass object types from userspace for atomic mode
> >> setting ioctl, allow drm_mode_object_find() to look up an object of any
> >> type. This will only work as long as the all object types share the ID
> >> space.
> >>
> >> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> >> Signed-off-by: Rob Clark <robdclark@gmail.com>
> >
> > Still NACK. Iirc you only want a boolean "does this exist" so please add a
> > new function for this. Returning a full pointer for framebuffer is simply
> > unsafe, allowing that a call for trouble. Yes, I know your current code is
> > safe but I don't want to freak out and do an ad-hoc audit every time I
> > stumble over this again.
> 
> this one isn't removing the (type != FB_ID) check.. if you looked more
> carefully at the other patches you'd realized I'd already changed this
> ;-)

Argh, today is an embarrassing day indeed.

> I suppose for completeness we should add an obj->type != FB_ID check
> too, although it should *not* be a WARN_ON().  The one place it is
> used is for atomic ioctl:
> 
> + obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
> + if (!obj || !obj->properties) {
> + ret = -ENOENT;
> + goto out;
> + }

If you want to use this I think we need a new function
drm_mode_object_has_properties or something, which checks whether the
passed-in object_id is a framebuffer or not under the protection of the
idr_mutex.

Because the moment you drop that look obj can be freed if it's indeed an
fb, and so all your obj->properties check walks into lalala-land. Or at
least can.

fb objects really have completely different lifetime rules so we need to
be extremely careful with handling them ;-)
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 1/2] v2 (Daniel): - Readd lost include. - Drop all the additional complexity which we only need later: The basic ww dance only requires the lock list and the contended pointer, nothing more. Some of the stuff also looks very suspicious. - Improve the contended checks a bit to make sure we only get an -EALREADY when we expect it. - Add an interruptible backoff function just to be complete (since otherwise the interruptible locking function makes 0 sense at all). - Inline the slowpath (due to the above). - Move ww_acquire_fini into drm_modeset_acquire_fini. I suspect this has been due to the confusion about the lifetime of the acquire ctx wrt the locking retry loop. - Drop uapi changes, they really shouldn't be in this patch. - s/!ret/ret == 0/ because I got confused - I tend to use !ret only for booleans or pointers, not for errno integer return values.
  2014-05-29 12:07     ` Rob Clark
@ 2014-05-29 12:59       ` Daniel Vetter
  2014-05-29 12:59         ` [PATCH 2/2] Revert "v2 (Daniel):" Daniel Vetter
  0 siblings, 1 reply; 23+ messages in thread
From: Daniel Vetter @ 2014-05-29 12:59 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter

Still missing: Updated kerneldoc and integration into the docbook.
Also, this isn't a full review yet.

Should be squashed into Rob's patch.

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/drm_crtc.c           |  4 +-
 drivers/gpu/drm/drm_modeset_lock.c   | 72 ++++++++++++------------------------
 drivers/gpu/drm/i915/intel_display.c |  3 +-
 include/drm/drm_modeset_lock.h       | 41 +-------------------
 include/uapi/drm/drm_mode.h          |  3 --
 5 files changed, 28 insertions(+), 95 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index cf622f6de07c..9e826aa5fc5c 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -39,6 +39,7 @@
 #include <drm/drm_fourcc.h>
 #include <drm/drm_modeset_lock.h>
 
+#include "drm_crtc_internal.h"
 
 /**
  * drm_modeset_lock_all - take all modeset locks
@@ -60,7 +61,7 @@ void drm_modeset_lock_all(struct drm_device *dev)
 
 	mutex_lock(&config->mutex);
 
-	drm_modeset_acquire_init(ctx, false, false);
+	drm_modeset_acquire_init(ctx);
 
 retry:
 	ret = drm_modeset_lock(&config->connection_mutex, ctx);
@@ -105,7 +106,6 @@ void drm_modeset_unlock_all(struct drm_device *dev)
 
 	config->acquire_ctx = NULL;
 	drm_modeset_drop_locks(ctx);
-	ww_acquire_fini(&ctx->ww_ctx);
 	drm_modeset_acquire_fini(ctx);
 
 	kfree(ctx);
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
index 6580d6ce8f63..81607ccebd89 100644
--- a/drivers/gpu/drm/drm_modeset_lock.c
+++ b/drivers/gpu/drm/drm_modeset_lock.c
@@ -26,32 +26,21 @@
 #include <drm/drm_modeset_lock.h>
 
 
-void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
-		bool nolock, bool nonblock)
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx)
 {
 	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
 	INIT_LIST_HEAD(&ctx->locked);
-	mutex_init(&ctx->mutex);
-	ctx->nolock = nolock;
-	ctx->nonblock = nonblock;
 }
 EXPORT_SYMBOL(drm_modeset_acquire_init);
 
 void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
 {
-	WARN_ON(ctx->contended);
-	/*
-	 * NOTE: it is intentional that ww_acquire_fini() is not called
-	 * here.. due to the way lock handover works in drm_atomic
-	 */
-	mutex_destroy(&ctx->mutex);
+	ww_acquire_fini(&ctx->ww_ctx);
 }
 EXPORT_SYMBOL(drm_modeset_acquire_fini);
 
 void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
 {
-	WARN_ON(ctx->contended);
-	mutex_lock(&ctx->mutex);
 	while (!list_empty(&ctx->locked)) {
 		struct drm_modeset_lock *lock;
 
@@ -60,45 +49,31 @@ void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
 
 		drm_modeset_unlock(lock);
 	}
-	mutex_unlock(&ctx->mutex);
 }
 EXPORT_SYMBOL(drm_modeset_drop_locks);
 
 static int modeset_lock(struct drm_modeset_lock *lock,
-		struct drm_modeset_acquire_ctx *ctx,
-		bool interruptible, bool slow)
+			struct drm_modeset_acquire_ctx *ctx,
+			bool interruptible)
 {
 	int ret;
 
-	if (ctx->nolock)
-		return 0;
-
-	WARN_ON(ctx->frozen);    /* all locks should be held by now! */
-	WARN_ON(ctx->contended);
-
-retry:
 	if (interruptible) {
 		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
-	} else if (slow) {
-		ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
-		ret = 0;
 	} else {
 		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
 	}
-	if (!ret) {
-		if (lock->atomic_pending) {
-			/* some other pending update with dropped locks */
-			ww_mutex_unlock(&lock->mutex);
-			if (ctx->nonblock)
-				return -EBUSY;
-			wait_event(lock->event, !lock->atomic_pending);
-			goto retry;
-		}
-		lock->atomic_pending = true;
-		WARN_ON(!list_empty(&lock->head));
+
+	if (ret == 0) {
 		list_add(&lock->head, &ctx->locked);
 	} else if (ret == -EALREADY) {
-		/* we already hold the lock.. this is fine */
+		/*
+		 * We already hold the lock. This is only fine if it's the lock
+		 * we've contended on.
+		 */
+		WARN_ON(ctx->contended != lock);
+		ctx->contended = NULL;
+
 		ret = 0;
 	} else if (ret == -EDEADLK) {
 		ctx->contended = lock;
@@ -109,18 +84,19 @@ retry:
 
 void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
 {
-	struct drm_modeset_lock *contended = ctx->contended;
-
-	ctx->contended = NULL;
+	drm_modeset_drop_locks(ctx);
 
-	if (WARN_ON(!contended))
-		return;
+	ww_mutex_lock_slow(&ctx->contended->mutex, ctx);
+}
+EXPORT_SYMBOL(drm_modeset_backoff);
 
+void drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
+{
 	drm_modeset_drop_locks(ctx);
 
-	modeset_lock(contended, ctx, false, true);
+	ww_mutex_lock_slow(&ctx->contended->mutex, ctx);
 }
-EXPORT_SYMBOL(drm_modeset_backoff);
+EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
 
 /**
  * drm_modeset_lock - take modeset lock
@@ -136,7 +112,7 @@ int drm_modeset_lock(struct drm_modeset_lock *lock,
 		struct drm_modeset_acquire_ctx *ctx)
 {
 	if (ctx)
-		return modeset_lock(lock, ctx, false, false);
+		return modeset_lock(lock, ctx, false);
 
 	ww_mutex_lock(&lock->mutex, NULL);
 	return 0;
@@ -147,7 +123,7 @@ int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
 		struct drm_modeset_acquire_ctx *ctx)
 {
 	if (ctx)
-		return modeset_lock(lock, ctx, true, false);
+		return modeset_lock(lock, ctx, true);
 
 	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
 }
@@ -160,9 +136,7 @@ EXPORT_SYMBOL(drm_modeset_lock_interruptible);
 void drm_modeset_unlock(struct drm_modeset_lock *lock)
 {
 	list_del_init(&lock->head);
-	lock->atomic_pending = false;
 	ww_mutex_unlock(&lock->mutex);
-	wake_up_all(&lock->event);
 }
 EXPORT_SYMBOL(drm_modeset_unlock);
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index ba6cbbbfe596..9d12e3e15dfa 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -7990,7 +7990,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 		      connector->base.id, drm_get_connector_name(connector),
 		      encoder->base.id, drm_get_encoder_name(encoder));
 
-	drm_modeset_acquire_init(ctx, false, false);
+	drm_modeset_acquire_init(ctx);
 
 retry:
 	ret = drm_modeset_lock(&config->connection_mutex, ctx);
@@ -8103,7 +8103,6 @@ fail_unlock:
 	}
 
 	drm_modeset_drop_locks(ctx);
-	ww_acquire_fini(&ctx->ww_ctx);
 	drm_modeset_acquire_fini(ctx);
 
 	return false;
diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
index 2630da3ef1b4..e67fb815b610 100644
--- a/include/drm/drm_modeset_lock.h
+++ b/include/drm/drm_modeset_lock.h
@@ -32,15 +32,6 @@ struct drm_modeset_acquire_ctx {
 
 	struct ww_acquire_ctx ww_ctx;
 
-	bool nolock : 1;
-	bool nonblock : 1;
-
-	/* just for debugging, the context is 'frozen' in drm_atomic_check()
-	 * to catch anyone who might be trying to acquire a lock after it is
-	 * too late.
-	 */
-	bool frozen : 1;
-
 	/* contended lock: if a lock is contended you should only call
 	 * drm_modeset_backoff() which drops locks and slow-locks the
 	 * contended lock.
@@ -49,16 +40,6 @@ struct drm_modeset_acquire_ctx {
 
 	/* list of 'struct drm_modeset_lock': */
 	struct list_head locked;
-
-	/* currently simply for protecting against 'locked' list manipulation
-	 * between original thread calling atomic->end() and driver thread
-	 * calling back drm_atomic_commit_unlocked().
-	 *
-	 * Other spots are sufficiently synchronized by virtue of holding
-	 * the lock's ww_mutex.  But during the lock/resource hand-over to the
-	 * driver thread (drop_locks()/grab_locks()), we cannot rely on this.
-	 */
-	struct mutex mutex;
 };
 
 /**
@@ -78,42 +59,24 @@ struct drm_modeset_lock {
 	struct ww_mutex mutex;
 
 	/**
-	 * Are we busy (pending asynchronous/NONBLOCK update)?  Any further
-	 * asynchronous update will return -EBUSY if it also needs to acquire
-	 * this lock.  While a synchronous update will block until the pending
-	 * async update completes.
-	 *
-	 * Drivers must ensure the update is completed before sending vblank
-	 * event to userspace.  Typically this just means don't send event
-	 * before drm_atomic_commit_unlocked() returns.
-	 */
-	bool atomic_pending;
-
-	/**
 	 * Resources that are locked as part of an atomic update are added
 	 * to a list (so we know what to unlock at the end).
 	 */
 	struct list_head head;
-
-	/**
-	 * For waiting on atomic_pending locks, if not a NONBLOCK operation.
-	 */
-	wait_queue_head_t event;
 };
 
 extern struct ww_class crtc_ww_class;
 
-void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
-		bool nolock, bool nonblock);
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx);
 void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
 void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
 void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx);
 
 static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock)
 {
 	ww_mutex_init(&lock->mutex, &crtc_ww_class);
 	INIT_LIST_HEAD(&lock->head);
-	init_waitqueue_head(&lock->event);
 }
 
 static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 6421edcd27a8..ded505ede145 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -511,7 +511,4 @@ struct drm_mode_destroy_dumb {
 	uint32_t handle;
 };
 
-#define DRM_MODE_ATOMIC_NONBLOCK  0x0200
-#define DRM_MODE_ATOMIC_NOLOCK    0x8000  /* only used internally */
-
 #endif
-- 
1.9.2

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 2/2] Revert "v2 (Daniel):"
  2014-05-29 12:59       ` [PATCH 1/2] v2 (Daniel): - Readd lost include. - Drop all the additional complexity which we only need later: The basic ww dance only requires the lock list and the contended pointer, nothing more. Some of the stuff also looks very suspicious. - Improve the contended checks a bit to make sure we only get an -EALREADY when we expect it. - Add an interruptible backoff function just to be complete (since otherwise the interruptible locking function makes 0 sense at all). - Inline the slowpath (due to the above). - Move ww_acquire_fini into drm_modeset_acquire_fini. I suspect this has been due to the confusion about the lifetime of the acquire ctx wrt the locking retry loop. - Drop uapi changes, they really shouldn't be in this patch. - s/!ret/ret == 0/ because I got confused - I tend to use !ret only for booleans or pointers, not for errno integer return values Daniel Vetter
@ 2014-05-29 12:59         ` Daniel Vetter
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel Vetter @ 2014-05-29 12:59 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter

This should be squashed into the relevant later patches that start to
use this.

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/drm_crtc.c           |  4 +-
 drivers/gpu/drm/drm_modeset_lock.c   | 72 ++++++++++++++++++++++++------------
 drivers/gpu/drm/i915/intel_display.c |  3 +-
 include/drm/drm_modeset_lock.h       | 41 +++++++++++++++++++-
 include/uapi/drm/drm_mode.h          |  3 ++
 5 files changed, 95 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 9e826aa5fc5c..cf622f6de07c 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -39,7 +39,6 @@
 #include <drm/drm_fourcc.h>
 #include <drm/drm_modeset_lock.h>
 
-#include "drm_crtc_internal.h"
 
 /**
  * drm_modeset_lock_all - take all modeset locks
@@ -61,7 +60,7 @@ void drm_modeset_lock_all(struct drm_device *dev)
 
 	mutex_lock(&config->mutex);
 
-	drm_modeset_acquire_init(ctx);
+	drm_modeset_acquire_init(ctx, false, false);
 
 retry:
 	ret = drm_modeset_lock(&config->connection_mutex, ctx);
@@ -106,6 +105,7 @@ void drm_modeset_unlock_all(struct drm_device *dev)
 
 	config->acquire_ctx = NULL;
 	drm_modeset_drop_locks(ctx);
+	ww_acquire_fini(&ctx->ww_ctx);
 	drm_modeset_acquire_fini(ctx);
 
 	kfree(ctx);
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
index 81607ccebd89..6580d6ce8f63 100644
--- a/drivers/gpu/drm/drm_modeset_lock.c
+++ b/drivers/gpu/drm/drm_modeset_lock.c
@@ -26,21 +26,32 @@
 #include <drm/drm_modeset_lock.h>
 
 
-void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx)
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+		bool nolock, bool nonblock)
 {
 	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
 	INIT_LIST_HEAD(&ctx->locked);
+	mutex_init(&ctx->mutex);
+	ctx->nolock = nolock;
+	ctx->nonblock = nonblock;
 }
 EXPORT_SYMBOL(drm_modeset_acquire_init);
 
 void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
 {
-	ww_acquire_fini(&ctx->ww_ctx);
+	WARN_ON(ctx->contended);
+	/*
+	 * NOTE: it is intentional that ww_acquire_fini() is not called
+	 * here.. due to the way lock handover works in drm_atomic
+	 */
+	mutex_destroy(&ctx->mutex);
 }
 EXPORT_SYMBOL(drm_modeset_acquire_fini);
 
 void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
 {
+	WARN_ON(ctx->contended);
+	mutex_lock(&ctx->mutex);
 	while (!list_empty(&ctx->locked)) {
 		struct drm_modeset_lock *lock;
 
@@ -49,31 +60,45 @@ void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
 
 		drm_modeset_unlock(lock);
 	}
+	mutex_unlock(&ctx->mutex);
 }
 EXPORT_SYMBOL(drm_modeset_drop_locks);
 
 static int modeset_lock(struct drm_modeset_lock *lock,
-			struct drm_modeset_acquire_ctx *ctx,
-			bool interruptible)
+		struct drm_modeset_acquire_ctx *ctx,
+		bool interruptible, bool slow)
 {
 	int ret;
 
+	if (ctx->nolock)
+		return 0;
+
+	WARN_ON(ctx->frozen);    /* all locks should be held by now! */
+	WARN_ON(ctx->contended);
+
+retry:
 	if (interruptible) {
 		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
+	} else if (slow) {
+		ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
+		ret = 0;
 	} else {
 		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
 	}
-
-	if (ret == 0) {
+	if (!ret) {
+		if (lock->atomic_pending) {
+			/* some other pending update with dropped locks */
+			ww_mutex_unlock(&lock->mutex);
+			if (ctx->nonblock)
+				return -EBUSY;
+			wait_event(lock->event, !lock->atomic_pending);
+			goto retry;
+		}
+		lock->atomic_pending = true;
+		WARN_ON(!list_empty(&lock->head));
 		list_add(&lock->head, &ctx->locked);
 	} else if (ret == -EALREADY) {
-		/*
-		 * We already hold the lock. This is only fine if it's the lock
-		 * we've contended on.
-		 */
-		WARN_ON(ctx->contended != lock);
-		ctx->contended = NULL;
-
+		/* we already hold the lock.. this is fine */
 		ret = 0;
 	} else if (ret == -EDEADLK) {
 		ctx->contended = lock;
@@ -84,19 +109,18 @@ static int modeset_lock(struct drm_modeset_lock *lock,
 
 void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
 {
-	drm_modeset_drop_locks(ctx);
+	struct drm_modeset_lock *contended = ctx->contended;
 
-	ww_mutex_lock_slow(&ctx->contended->mutex, ctx);
-}
-EXPORT_SYMBOL(drm_modeset_backoff);
+	ctx->contended = NULL;
+
+	if (WARN_ON(!contended))
+		return;
 
-void drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
-{
 	drm_modeset_drop_locks(ctx);
 
-	ww_mutex_lock_slow(&ctx->contended->mutex, ctx);
+	modeset_lock(contended, ctx, false, true);
 }
-EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
+EXPORT_SYMBOL(drm_modeset_backoff);
 
 /**
  * drm_modeset_lock - take modeset lock
@@ -112,7 +136,7 @@ int drm_modeset_lock(struct drm_modeset_lock *lock,
 		struct drm_modeset_acquire_ctx *ctx)
 {
 	if (ctx)
-		return modeset_lock(lock, ctx, false);
+		return modeset_lock(lock, ctx, false, false);
 
 	ww_mutex_lock(&lock->mutex, NULL);
 	return 0;
@@ -123,7 +147,7 @@ int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
 		struct drm_modeset_acquire_ctx *ctx)
 {
 	if (ctx)
-		return modeset_lock(lock, ctx, true);
+		return modeset_lock(lock, ctx, true, false);
 
 	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
 }
@@ -136,7 +160,9 @@ EXPORT_SYMBOL(drm_modeset_lock_interruptible);
 void drm_modeset_unlock(struct drm_modeset_lock *lock)
 {
 	list_del_init(&lock->head);
+	lock->atomic_pending = false;
 	ww_mutex_unlock(&lock->mutex);
+	wake_up_all(&lock->event);
 }
 EXPORT_SYMBOL(drm_modeset_unlock);
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 9d12e3e15dfa..ba6cbbbfe596 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -7990,7 +7990,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 		      connector->base.id, drm_get_connector_name(connector),
 		      encoder->base.id, drm_get_encoder_name(encoder));
 
-	drm_modeset_acquire_init(ctx);
+	drm_modeset_acquire_init(ctx, false, false);
 
 retry:
 	ret = drm_modeset_lock(&config->connection_mutex, ctx);
@@ -8103,6 +8103,7 @@ fail_unlock:
 	}
 
 	drm_modeset_drop_locks(ctx);
+	ww_acquire_fini(&ctx->ww_ctx);
 	drm_modeset_acquire_fini(ctx);
 
 	return false;
diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
index e67fb815b610..2630da3ef1b4 100644
--- a/include/drm/drm_modeset_lock.h
+++ b/include/drm/drm_modeset_lock.h
@@ -32,6 +32,15 @@ struct drm_modeset_acquire_ctx {
 
 	struct ww_acquire_ctx ww_ctx;
 
+	bool nolock : 1;
+	bool nonblock : 1;
+
+	/* just for debugging, the context is 'frozen' in drm_atomic_check()
+	 * to catch anyone who might be trying to acquire a lock after it is
+	 * too late.
+	 */
+	bool frozen : 1;
+
 	/* contended lock: if a lock is contended you should only call
 	 * drm_modeset_backoff() which drops locks and slow-locks the
 	 * contended lock.
@@ -40,6 +49,16 @@ struct drm_modeset_acquire_ctx {
 
 	/* list of 'struct drm_modeset_lock': */
 	struct list_head locked;
+
+	/* currently simply for protecting against 'locked' list manipulation
+	 * between original thread calling atomic->end() and driver thread
+	 * calling back drm_atomic_commit_unlocked().
+	 *
+	 * Other spots are sufficiently synchronized by virtue of holding
+	 * the lock's ww_mutex.  But during the lock/resource hand-over to the
+	 * driver thread (drop_locks()/grab_locks()), we cannot rely on this.
+	 */
+	struct mutex mutex;
 };
 
 /**
@@ -59,24 +78,42 @@ struct drm_modeset_lock {
 	struct ww_mutex mutex;
 
 	/**
+	 * Are we busy (pending asynchronous/NONBLOCK update)?  Any further
+	 * asynchronous update will return -EBUSY if it also needs to acquire
+	 * this lock.  While a synchronous update will block until the pending
+	 * async update completes.
+	 *
+	 * Drivers must ensure the update is completed before sending vblank
+	 * event to userspace.  Typically this just means don't send event
+	 * before drm_atomic_commit_unlocked() returns.
+	 */
+	bool atomic_pending;
+
+	/**
 	 * Resources that are locked as part of an atomic update are added
 	 * to a list (so we know what to unlock at the end).
 	 */
 	struct list_head head;
+
+	/**
+	 * For waiting on atomic_pending locks, if not a NONBLOCK operation.
+	 */
+	wait_queue_head_t event;
 };
 
 extern struct ww_class crtc_ww_class;
 
-void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+		bool nolock, bool nonblock);
 void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
 void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
 void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
-void drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx);
 
 static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock)
 {
 	ww_mutex_init(&lock->mutex, &crtc_ww_class);
 	INIT_LIST_HEAD(&lock->head);
+	init_waitqueue_head(&lock->event);
 }
 
 static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index ded505ede145..6421edcd27a8 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -511,4 +511,7 @@ struct drm_mode_destroy_dumb {
 	uint32_t handle;
 };
 
+#define DRM_MODE_ATOMIC_NONBLOCK  0x0200
+#define DRM_MODE_ATOMIC_NOLOCK    0x8000  /* only used internally */
+
 #endif
-- 
1.9.2

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/8] drm: add object property type
  2014-05-28 23:57 ` [PATCH 1/8] drm: add object property type Rob Clark
  2014-05-29  8:01   ` David Herrmann
@ 2014-05-30  7:57   ` Thierry Reding
  2014-05-30 12:00     ` Rob Clark
  1 sibling, 1 reply; 23+ messages in thread
From: Thierry Reding @ 2014-05-30  7:57 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 3630 bytes --]

On Wed, May 28, 2014 at 07:57:18PM -0400, Rob Clark wrote:
[...]
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
[...]
> +struct drm_property *drm_property_create_object(struct drm_device *dev,
> +					 int flags, const char *name, uint32_t type)

Indentation here is inconsistent with other functions in this file.

> @@ -3294,14 +3316,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
>  {
>  	struct drm_property_enum *prop_enum;
>  
> -	if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
> +	if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
> +			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
>  		return -EINVAL;

The bulk of this patch seems to be this pattern of substituting explicit
flag checks with drm_property_type_is() and that kind of hides the real
purpose of this patch. Splitting that into a separate patch would make
the addition of drm_property_create_object() more concise.

> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
[...]
> @@ -964,6 +982,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
>  struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
>  					 const char *name,
>  					 uint64_t min, uint64_t max);
> +struct drm_property *drm_property_create_object(struct drm_device *dev,
> +					 int flags, const char *name, uint32_t type);
>  extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
>  extern int drm_property_add_enum(struct drm_property *property, int index,
>  				 uint64_t value, const char *name);
> @@ -980,6 +1000,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
>  					 int gamma_size);
>  extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
>  		uint32_t id, uint32_t type);
> +
> +static inline struct drm_mode_object *
> +drm_property_get_obj(struct drm_property *property, uint64_t value)

Perhaps for consistent naming with drm_property_create_object() this
would be better called drm_property_get_object()?

> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
[...]
> +/* non-extended types: legacy bitmask, one bit per type: */
> +#define DRM_MODE_PROP_LEGACY_TYPE  ( \
> +		DRM_MODE_PROP_RANGE | \
> +		DRM_MODE_PROP_ENUM | \
> +		DRM_MODE_PROP_BLOB | \
> +		DRM_MODE_PROP_BITMASK)
> +
> +/* extended-types: rather than continue to consume a bit per type,
> + * grab a chunk of the bits to use as integer type id.
> + */
> +#define DRM_MODE_PROP_EXTENDED_TYPE	0x0000ffc0
> +#define DRM_MODE_PROP_TYPE(n)		((n) << 6)
> +#define DRM_MODE_PROP_OBJECT		DRM_MODE_PROP_TYPE(1)

Ugh, that's an unfortunate bit of userspace ABI...

Could this perhaps be done in a more unified, yet backwards-compatible
way? For example if we define legacy properties like this:

#define DRM_MODE_PROP_TYPE(n)	((n) << 0)
#define DRM_MODE_PROP_RANGE	DRM_MODE_PROP_TYPE( 2)
#define DRM_MODE_PROP_ENUM	DRM_MODE_PROP_TYPE( 8)
#define DRM_MODE_PROP_RANGE	DRM_MODE_PROP_TYPE(16)
#define DRM_MODE_PROP_RANGE	DRM_MODE_PROP_TYPE(32)

And define the type mask to be:

#define DRM_MODE_PROP_TYPE 0x0000fffa

Leaving out only the two real flags (PENDING and IMMUTABLE). This should
allow things to be done without constantly having to special case legacy
vs. extended types. It leaves a bunch of wholes in the number space and
we need to be careful to introduce new types only in the upper range,
but it has the advantage of not requiring special handling.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/8] drm: add object property type
  2014-05-30  7:57   ` Thierry Reding
@ 2014-05-30 12:00     ` Rob Clark
  0 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2014-05-30 12:00 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

On Fri, May 30, 2014 at 3:57 AM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Wed, May 28, 2014 at 07:57:18PM -0400, Rob Clark wrote:
> [...]
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> [...]
>> +struct drm_property *drm_property_create_object(struct drm_device *dev,
>> +                                      int flags, const char *name, uint32_t type)
>
> Indentation here is inconsistent with other functions in this file.
>
>> @@ -3294,14 +3316,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
>>  {
>>       struct drm_property_enum *prop_enum;
>>
>> -     if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
>> +     if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
>> +                     drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
>>               return -EINVAL;
>
> The bulk of this patch seems to be this pattern of substituting explicit
> flag checks with drm_property_type_is() and that kind of hides the real
> purpose of this patch. Splitting that into a separate patch would make
> the addition of drm_property_create_object() more concise.
>
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> [...]
>> @@ -964,6 +982,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
>>  struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
>>                                        const char *name,
>>                                        uint64_t min, uint64_t max);
>> +struct drm_property *drm_property_create_object(struct drm_device *dev,
>> +                                      int flags, const char *name, uint32_t type);
>>  extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
>>  extern int drm_property_add_enum(struct drm_property *property, int index,
>>                                uint64_t value, const char *name);
>> @@ -980,6 +1000,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
>>                                        int gamma_size);
>>  extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
>>               uint32_t id, uint32_t type);
>> +
>> +static inline struct drm_mode_object *
>> +drm_property_get_obj(struct drm_property *property, uint64_t value)
>
> Perhaps for consistent naming with drm_property_create_object() this
> would be better called drm_property_get_object()?

hmm, I thought I'd dropped the drm_property_get_obj() fxn and used
_object_find() directly in the one call site.  We don't actually want
drm_property_get_obj() because of fb's.  What that is supposed to be
is _object_find() directly in drm_property_change_is_valid() and
drm_mode_object_find() elsewhere.

so apparently I lost some patch which was supposed to be squashed back
into this one. :-/

>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> [...]
>> +/* non-extended types: legacy bitmask, one bit per type: */
>> +#define DRM_MODE_PROP_LEGACY_TYPE  ( \
>> +             DRM_MODE_PROP_RANGE | \
>> +             DRM_MODE_PROP_ENUM | \
>> +             DRM_MODE_PROP_BLOB | \
>> +             DRM_MODE_PROP_BITMASK)
>> +
>> +/* extended-types: rather than continue to consume a bit per type,
>> + * grab a chunk of the bits to use as integer type id.
>> + */
>> +#define DRM_MODE_PROP_EXTENDED_TYPE  0x0000ffc0
>> +#define DRM_MODE_PROP_TYPE(n)                ((n) << 6)
>> +#define DRM_MODE_PROP_OBJECT         DRM_MODE_PROP_TYPE(1)
>
> Ugh, that's an unfortunate bit of userspace ABI...
>
> Could this perhaps be done in a more unified, yet backwards-compatible
> way? For example if we define legacy properties like this:

I mostly went for the keep-bitmask-comparision-for-old-types out of
backwards compat paranoia..  at least if someone somehow managed to
create an enum-blob-range property, it would follow the same code
paths it did before ;-)

quite possibly overkill.  But it's hidden behind helper fxns/macros
(and I was planning on doing the same on the userspace side).  So meh.

BR,
-R

> #define DRM_MODE_PROP_TYPE(n)   ((n) << 0)
> #define DRM_MODE_PROP_RANGE     DRM_MODE_PROP_TYPE( 2)
> #define DRM_MODE_PROP_ENUM      DRM_MODE_PROP_TYPE( 8)
> #define DRM_MODE_PROP_RANGE     DRM_MODE_PROP_TYPE(16)
> #define DRM_MODE_PROP_RANGE     DRM_MODE_PROP_TYPE(32)
>
> And define the type mask to be:
>
> #define DRM_MODE_PROP_TYPE 0x0000fffa
>
> Leaving out only the two real flags (PENDING and IMMUTABLE). This should
> allow things to be done without constantly having to special case legacy
> vs. extended types. It leaves a bunch of wholes in the number space and
> we need to be careful to introduce new types only in the upper range,
> but it has the advantage of not requiring special handling.
>
> Thierry

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2014-05-30 12:00 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-05-28 23:57 [PATCH 0/8] prepare for preparing for atomic Rob Clark
2014-05-28 23:57 ` [PATCH 1/8] drm: add object property type Rob Clark
2014-05-29  8:01   ` David Herrmann
2014-05-29 11:45     ` Rob Clark
2014-05-30  7:57   ` Thierry Reding
2014-05-30 12:00     ` Rob Clark
2014-05-28 23:57 ` [PATCH 2/8] drm: add signed-range " Rob Clark
2014-05-29  8:29   ` David Herrmann
2014-05-29 11:51     ` Rob Clark
2014-05-28 23:57 ` [PATCH 3/8] drm: helpers to find mode objects Rob Clark
2014-05-28 23:57 ` [PATCH 4/8] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
2014-05-29 11:18   ` Daniel Vetter
2014-05-29 12:01     ` Rob Clark
2014-05-29 12:17       ` Daniel Vetter
2014-05-28 23:57 ` [PATCH 5/8] drm: spiff out FB refcnting traces Rob Clark
2014-05-29  8:36   ` David Herrmann
2014-05-28 23:57 ` [PATCH 6/8] drm: push locking down into restore_fbdev_mode (v2) Rob Clark
2014-05-28 23:57 ` [PATCH 7/8] drm: Split connection_mutex out of mode_config.mutex (v2) Rob Clark
2014-05-28 23:57 ` [PATCH 8/8] drm: convert crtc and connection_mutex to ww_mutex Rob Clark
2014-05-29 11:22   ` Daniel Vetter
2014-05-29 12:07     ` Rob Clark
2014-05-29 12:59       ` [PATCH 1/2] v2 (Daniel): - Readd lost include. - Drop all the additional complexity which we only need later: The basic ww dance only requires the lock list and the contended pointer, nothing more. Some of the stuff also looks very suspicious. - Improve the contended checks a bit to make sure we only get an -EALREADY when we expect it. - Add an interruptible backoff function just to be complete (since otherwise the interruptible locking function makes 0 sense at all). - Inline the slowpath (due to the above). - Move ww_acquire_fini into drm_modeset_acquire_fini. I suspect this has been due to the confusion about the lifetime of the acquire ctx wrt the locking retry loop. - Drop uapi changes, they really shouldn't be in this patch. - s/!ret/ret == 0/ because I got confused - I tend to use !ret only for booleans or pointers, not for errno integer return values Daniel Vetter
2014-05-29 12:59         ` [PATCH 2/2] Revert "v2 (Daniel):" Daniel Vetter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.