drm/modes: add connector reference counting. (v2)
authorDave Airlie <airlied@redhat.com>
Wed, 27 Apr 2016 01:10:09 +0000 (11:10 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 5 May 2016 02:51:53 +0000 (12:51 +1000)
This uses the previous changes to add reference counts
to drm connector objects.

v2: move fbdev changes to their own patch.
add some kerneldoc

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Daniel Stone <daniels@collabora.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_crtc.c
include/drm/drm_crtc.h

index 69adcf3944cc96a075ec02686fa675fa7061c0ab..e4879d34c2800206c31ab87b55b5ffa0991e9fc5 100644 (file)
@@ -156,6 +156,7 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
                                                       state->connector_states[i]);
                state->connectors[i] = NULL;
                state->connector_states[i] = NULL;
+               drm_connector_unreference(connector);
        }
 
        for (i = 0; i < config->num_crtc; i++) {
@@ -932,6 +933,7 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
        if (!connector_state)
                return ERR_PTR(-ENOMEM);
 
+       drm_connector_reference(connector);
        state->connector_states[index] = connector_state;
        state->connectors[index] = connector;
        connector_state->state = state;
@@ -1622,12 +1624,19 @@ retry:
                }
 
                obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
-               if (!obj || !obj->properties) {
+               if (!obj) {
+                       ret = -ENOENT;
+                       goto out;
+               }
+
+               if (!obj->properties) {
+                       drm_mode_object_unreference(obj);
                        ret = -ENOENT;
                        goto out;
                }
 
                if (get_user(count_props, count_props_ptr + copied_objs)) {
+                       drm_mode_object_unreference(obj);
                        ret = -EFAULT;
                        goto out;
                }
@@ -1640,12 +1649,14 @@ retry:
                        struct drm_property *prop;
 
                        if (get_user(prop_id, props_ptr + copied_props)) {
+                               drm_mode_object_unreference(obj);
                                ret = -EFAULT;
                                goto out;
                        }
 
                        prop = drm_property_find(dev, prop_id);
                        if (!prop) {
+                               drm_mode_object_unreference(obj);
                                ret = -ENOENT;
                                goto out;
                        }
@@ -1653,13 +1664,16 @@ retry:
                        if (copy_from_user(&prop_value,
                                           prop_values_ptr + copied_props,
                                           sizeof(prop_value))) {
+                               drm_mode_object_unreference(obj);
                                ret = -EFAULT;
                                goto out;
                        }
 
                        ret = atomic_set_prop(state, obj, prop, prop_value);
-                       if (ret)
+                       if (ret) {
+                               drm_mode_object_unreference(obj);
                                goto out;
+                       }
 
                        copied_props++;
                }
@@ -1670,6 +1684,7 @@ retry:
                        plane_mask |= (1 << drm_plane_index(plane));
                        plane->old_fb = plane->fb;
                }
+               drm_mode_object_unreference(obj);
        }
 
        if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
index c8253ebad680bcd741edb5904bafb7cac4162c0d..a9c0a43483226f8ffcc0cd24ca4bc7546acca0a9 100644 (file)
@@ -864,6 +864,16 @@ static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
                      mode->interlace ?  " interlaced" : "");
 }
 
+static void drm_connector_free(struct kref *kref)
+{
+       struct drm_connector *connector =
+               container_of(kref, struct drm_connector, base.refcount);
+       struct drm_device *dev = connector->dev;
+
+       drm_mode_object_unregister(dev, &connector->base);
+       connector->funcs->destroy(connector);
+}
+
 /**
  * drm_connector_init - Init a preallocated connector
  * @dev: DRM device
@@ -889,7 +899,9 @@ int drm_connector_init(struct drm_device *dev,
 
        drm_modeset_lock_all(dev);
 
-       ret = drm_mode_object_get_reg(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR, false, NULL);
+       ret = drm_mode_object_get_reg(dev, &connector->base,
+                                     DRM_MODE_OBJECT_CONNECTOR,
+                                     false, drm_connector_free);
        if (ret)
                goto out_unlock;
 
@@ -2137,7 +2149,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
        mutex_lock(&dev->mode_config.mutex);
 
-       connector = drm_connector_find(dev, out_resp->connector_id);
+       connector = drm_connector_lookup(dev, out_resp->connector_id);
        if (!connector) {
                ret = -ENOENT;
                goto out_unlock;
@@ -2221,6 +2233,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 out:
        drm_modeset_unlock(&dev->mode_config.connection_mutex);
 
+       drm_connector_unreference(connector);
 out_unlock:
        mutex_unlock(&dev->mode_config.mutex);
 
@@ -2865,13 +2878,14 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                }
 
                for (i = 0; i < crtc_req->count_connectors; i++) {
+                       connector_set[i] = NULL;
                        set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
                        if (get_user(out_id, &set_connectors_ptr[i])) {
                                ret = -EFAULT;
                                goto out;
                        }
 
-                       connector = drm_connector_find(dev, out_id);
+                       connector = drm_connector_lookup(dev, out_id);
                        if (!connector) {
                                DRM_DEBUG_KMS("Connector id %d unknown\n",
                                                out_id);
@@ -2899,6 +2913,12 @@ out:
        if (fb)
                drm_framebuffer_unreference(fb);
 
+       if (connector_set) {
+               for (i = 0; i < crtc_req->count_connectors; i++) {
+                       if (connector_set[i])
+                               drm_connector_unreference(connector_set[i]);
+               }
+       }
        kfree(connector_set);
        drm_mode_destroy(dev, mode);
        drm_modeset_unlock_all(dev);
@@ -4989,7 +5009,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
        property = obj_to_property(prop_obj);
 
        if (!drm_property_change_valid_get(property, arg->value, &ref))
-               goto out;
+               goto out_unref;
 
        switch (arg_obj->type) {
        case DRM_MODE_OBJECT_CONNECTOR:
index 0cc1eb7b4fd2b255db194c34ffd8dba8345e30d1..4acdaf5e283d7d8314bdf92fe580efda0ceb153a 100644 (file)
@@ -2571,7 +2571,15 @@ 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,
+/**
+ * drm_connector_lookup - lookup connector object
+ * @dev: DRM device
+ * @id: connector object id
+ *
+ * This function looks up the connector object specified by id
+ * add takes a reference to it.
+ */
+static inline struct drm_connector *drm_connector_lookup(struct drm_device *dev,
                uint32_t id)
 {
        struct drm_mode_object *mo;
@@ -2639,6 +2647,28 @@ static inline uint32_t drm_framebuffer_read_refcount(struct drm_framebuffer *fb)
        return atomic_read(&fb->base.refcount.refcount);
 }
 
+/**
+ * drm_connector_reference - incr the connector refcnt
+ * @connector: connector
+ *
+ * This function increments the connector's refcount.
+ */
+static inline void drm_connector_reference(struct drm_connector *connector)
+{
+       drm_mode_object_reference(&connector->base);
+}
+
+/**
+ * drm_connector_unreference - unref a connector
+ * @connector: connector to unref
+ *
+ * This function decrements the connector's refcount and frees it if it drops to zero.
+ */
+static inline void drm_connector_unreference(struct drm_connector *connector)
+{
+       drm_mode_object_unreference(&connector->base);
+}
+
 /* Plane list iterator for legacy (overlay only) planes. */
 #define drm_for_each_legacy_plane(plane, dev) \
        list_for_each_entry(plane, &(dev)->mode_config.plane_list, head) \