[RFCv4 09/14] drm: convert plane to properties/state
Sean Paul
seanpaul at chromium.org
Mon Mar 3 11:22:50 PST 2014
On Wed, Feb 26, 2014 at 7:18 PM, Rob Clark <robdclark at gmail.com> wrote:
> On Wed, Feb 26, 2014 at 4:30 PM, Sean Paul <seanpaul at chromium.org> wrote:
>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark at gmail.com> wrote:
>>> Break the mutable state of a plane out into a separate structure
>>> and use atomic properties mechanism to set plane attributes. This
>>> makes it easier to have some helpers for plane->set_property()
>>> and for checking for invalid params. The idea is that individual
>>> drivers can wrap the state struct in their own struct which adds
>>> driver specific parameters, for easy build-up of state across
>>> multiple set_property() calls and for easy atomic commit or roll-
>>> back.
>>>
>>> The same should be done for CRTC, encoder, and connector, but this
>>> patch only includes the first part (plane).
>>
>>
>> Hi Rob,
>> I've been tracking down a crash that came up on my exynos board when I
>> applied this patch. I hope you can hold my hand a little bit and give
>> some guidance.
>>
>> <snip>
>>
>>> +static int
>>> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
>>> + struct drm_plane_state *pstate)
>>> +{
>>> + struct drm_framebuffer *old_fb = NULL, *fb = NULL;
>>> + int ret = 0;
>>> +
>>> + fb = pstate->fb;
>>> +
>>> + if (pstate->crtc && fb) {
>>> + ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
>>> + pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
>>> + pstate->src_x, pstate->src_y, pstate->src_w, pstate->src_h);
>>> + if (!ret) {
>>> + /* on success, update state and fb refcnting: */
>>> + /* NOTE: if we ensure no driver sets plane->state->fb = NULL
>>> + * on disable, we can move this up a level and not duplicate
>>> + * nearly the same thing for both update_plane and disable_plane
>>> + * cases.. I leave it like this for now to be paranoid due to
>>> + * the slightly different ordering in the two cases in the
>>> + * original code.
>>> + */
>>> + old_fb = plane->state->fb;
>>
>> I think this is slightly different than what we had before in
>> setplane. In the previous code, we had:
>>
>> ret = plane->funcs->update_plane(plane, crtc, fb,
>> plane_req->crtc_x,
>> plane_req->crtc_y,
>> plane_req->crtc_w,
>> plane_req->crtc_h,
>> plane_req->src_x,
>> plane_req->src_y,
>> plane_req->src_w,
>> plane_req->src_h);
>> if (!ret) {
>> old_fb = plane->fb;
>> fb = NULL;
>> plane->crtc = crtc;
>> plane->fb = fb;
>> }
>>
>> In this case, we'd unreference old_fb which is the previous plane->fb.
>> AFAICT, update_plane did not set plane->fb to fb, so it would, in
>> fact, be the old plane.
>
> to be honest, I need to dig up that branch again and remember (and
> rebase it while I'm at it)..
>
> I don't think the driver should be changing state->fb (ie. the state
> object should in theory, once constructed, be a sort of constant
> thing). So until swap_plane_state() plane->state->fb is *supposed* to
> be the old fb.
>
Hey Rob,
Sorry for the delay getting back to you. Indeed, I was confused in my
last email, thanks for straightening me out.
I traced through things a little more carefully and it looks like my
fb is being unreferenced every time we call set_property on the plane.
On exynos, we set a private zpos property via the set_property ioctl.
In this case, drm_mode_set_property_ioctl does not take a reference on
the plane's current fb, but we put a reference in atomic_commit
(assuming it's got a valid crtc/fb). Eventually, this runs the
refcount down to 0 and we end up freeing the fb early.
I think the fix here is to only unreference the plane's current fb in
the case where new_fb is true. ie:
diff --git a/drivers/gpu/drm/drm_atomic_helper.c
b/drivers/gpu/drm/drm_atomic_helper.c
index 14e0571..ec60d4e 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -394,7 +394,8 @@ drm_atomic_helper_commit_plane_state(struct
drm_plane *plane,
* the slightly different ordering in the two
cases in the
* original code.
*/
- old_fb = plane->state->fb;
+ if (pstate->new_fb)
+ old_fb = plane->state->fb;
swap_plane_state(plane, pstate->state);
fb = NULL;
}
Seem reasonable?
Sean
> For crtc, as there were so many places in code accessing crtc->fb (and
> it was pretty intertwined in the crtc helpers), the way I left it as:
> crtc->state->fb is what is requested by userspace, and crtc->fb is
> what is currently used (which is updated to the new cstate->fb in the
> course of modeset).
>
> I don't remember if I left it the same way for plane (which isn't
> referenced in nearly as many places, and which doesn't have a big
> chunk of modeset helper to care about).
>
>> In the new code, drm_mode_setplane calls drm_mode_plane_set_obj_prop
>> on fb_id, which will update plane->state->fb. Then we come in here and
>> unreference it as old_fb. I _believe_ we're taking one more reference
>> than we need in this case.
>
> well, it should be updating 'pstate', what will become the new
> plane->state.. But not the current plane->state.
>
> BR,
> -R
>
>> What I'm seeing in exynos is that when we setplane to an fb, we leave
>> it with refcount == 1. If we disable that plane, we'll free the fb.
>> Unfortunately, this leaves the fb in the fbs list and the next time we
>> try to iterate that list Bad Things Happen.
>>
>> Does this make any sense?
>>
>> Sean
>>
>>> + swap_plane_state(plane, pstate->state);
>>> + fb = NULL;
>>> + }
>>> + } else {
>>> + old_fb = plane->state->fb;
>>> + plane->funcs->disable_plane(plane);
>>> + swap_plane_state(plane, pstate->state);
>>> + }
>>> +
>>> +
>>> + if (fb)
>>> + drm_framebuffer_unreference(fb);
>>> + if (old_fb)
>>> + drm_framebuffer_unreference(old_fb);
>>> +
>>> + return ret;
>>> +}
>>>
>>
>> <snip>
>>
>>> +int drm_plane_set_property(struct drm_plane *plane,
>>> + struct drm_plane_state *state,
>>> + struct drm_property *property,
>>> + uint64_t value, void *blob_data)
>>> +{
>>> + struct drm_device *dev = plane->dev;
>>> + struct drm_mode_config *config = &dev->mode_config;
>>> +
>>> + drm_object_property_set_value(&plane->base,
>>> + &state->propvals, property, value, blob_data);
>>> +
>>> + if (property == config->prop_fb_id) {
>>> + state->new_fb = true;
>>> + state->fb = drm_framebuffer_lookup(dev, value);
>>> + } else if (property == config->prop_crtc_id) {
>>> + struct drm_mode_object *obj = drm_property_get_obj(property, value);
>>> + struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
>>> + /* take the lock of the incoming crtc as well, moving
>>> + * plane between crtcs is synchronized on both incoming
>>> + * and outgoing crtc.
>>> + */
>>> + if (crtc) {
>>> + int ret = drm_modeset_lock(&crtc->mutex, state->state);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> + state->crtc = crtc;
>>> + } else if (property == config->prop_crtc_x) {
>>> + state->crtc_x = U642I64(value);
>>> + } else if (property == config->prop_crtc_y) {
>>> + state->crtc_y = U642I64(value);
>>> + } else if (property == config->prop_crtc_w) {
>>> + state->crtc_w = value;
>>> + } else if (property == config->prop_crtc_h) {
>>> + state->crtc_h = value;
>>> + } else if (property == config->prop_src_x) {
>>> + state->src_x = value;
>>> + } else if (property == config->prop_src_y) {
>>> + state->src_y = value;
>>> + } else if (property == config->prop_src_w) {
>>> + state->src_w = value;
>>> + } else if (property == config->prop_src_h) {
>>> + state->src_h = value;
>>> + } else {
>>> + return -EINVAL;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL(drm_plane_set_property);
>>> +
>>
>> <snip>
>>
>>> @@ -1987,20 +2192,19 @@ 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_config *config = &dev->mode_config;
>>> struct drm_plane *plane;
>>> - struct drm_crtc *crtc;
>>> - struct drm_framebuffer *fb = NULL, *old_fb = NULL;
>>> + void *state;
>>> int ret = 0;
>>> - unsigned int fb_width, fb_height;
>>> - int i;
>>>
>>> if (!drm_core_check_feature(dev, DRIVER_MODESET))
>>> return -EINVAL;
>>>
>>> - /*
>>> - * First, find the plane, crtc, and fb objects. If not available,
>>> - * we don't bother to call the driver.
>>> - */
>>> +retry:
>>> + state = dev->driver->atomic_begin(dev, 0);
>>> + if (IS_ERR(state))
>>> + return PTR_ERR(state);
>>> +
>>> plane = drm_plane_find(dev, plane_req->plane_id);
>>> if (!plane) {
>>> DRM_DEBUG_KMS("Unknown plane ID %d\n",
>>> @@ -2008,98 +2212,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>> return -ENOENT;
>>> }
>>>
>>> - /* No fb means shut it down */
>>> - if (!plane_req->fb_id) {
>>> - drm_modeset_lock_all(dev);
>>> - old_fb = plane->fb;
>>> - plane->funcs->disable_plane(plane);
>>> - plane->crtc = NULL;
>>> - plane->fb = NULL;
>>> - drm_modeset_unlock_all(dev);
>>> - goto out;
>>> - }
>>> -
>>> - 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;
>>> - }
>>> -
>>> - fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
>>> - if (!fb) {
>>> - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
>>> - plane_req->fb_id);
>>> - ret = -ENOENT;
>>> - goto out;
>>> - }
>>> -
>>> - /* Check whether this plane supports the fb pixel format. */
>>> - for (i = 0; i < plane->format_count; i++)
>>> - if (fb->pixel_format == plane->format_types[i])
>>> - break;
>>> - if (i == plane->format_count) {
>>> - DRM_DEBUG_KMS("Invalid pixel format %s\n",
>>> - drm_get_format_name(fb->pixel_format));
>>> - ret = -EINVAL;
>>> - goto out;
>>> - }
>>> -
>>> - fb_width = fb->width << 16;
>>> - fb_height = fb->height << 16;
>>> -
>>> - /* Make sure source coordinates are inside the fb. */
>>> - if (plane_req->src_w > fb_width ||
>>> - plane_req->src_x > fb_width - plane_req->src_w ||
>>> - plane_req->src_h > fb_height ||
>>> - plane_req->src_y > fb_height - plane_req->src_h) {
>>> - DRM_DEBUG_KMS("Invalid source coordinates "
>>> - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
>>> - plane_req->src_w >> 16,
>>> - ((plane_req->src_w & 0xffff) * 15625) >> 10,
>>> - plane_req->src_h >> 16,
>>> - ((plane_req->src_h & 0xffff) * 15625) >> 10,
>>> - plane_req->src_x >> 16,
>>> - ((plane_req->src_x & 0xffff) * 15625) >> 10,
>>> - plane_req->src_y >> 16,
>>> - ((plane_req->src_y & 0xffff) * 15625) >> 10);
>>> - ret = -ENOSPC;
>>> - goto out;
>>> - }
>>> -
>>> - /* Give drivers some help against integer overflows */
>>> - if (plane_req->crtc_w > INT_MAX ||
>>> - plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
>>> - plane_req->crtc_h > INT_MAX ||
>>> - plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
>>> - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
>>> - plane_req->crtc_w, plane_req->crtc_h,
>>> - plane_req->crtc_x, plane_req->crtc_y);
>>> - ret = -ERANGE;
>>> + ret =
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_crtc_id, plane_req->crtc_id, NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_fb_id, plane_req->fb_id, NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_crtc_w, plane_req->crtc_w, NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_crtc_h, plane_req->crtc_h, NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_src_w, plane_req->src_w, NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_src_h, plane_req->src_h, NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_src_x, plane_req->src_x, NULL) ||
>>> + drm_mode_plane_set_obj_prop(plane, state,
>>> + config->prop_src_y, plane_req->src_y, NULL) ||
>>> + dev->driver->atomic_check(dev, state);
>>> + if (ret)
>>> goto out;
>>> - }
>>>
>>> - drm_modeset_lock_all(dev);
>>> - ret = plane->funcs->update_plane(plane, crtc, fb,
>>> - plane_req->crtc_x, plane_req->crtc_y,
>>> - plane_req->crtc_w, plane_req->crtc_h,
>>> - plane_req->src_x, plane_req->src_y,
>>> - plane_req->src_w, plane_req->src_h);
>>> - if (!ret) {
>>> - old_fb = plane->fb;
>>> - plane->crtc = crtc;
>>> - plane->fb = fb;
>>> - fb = NULL;
>>> - }
>>> - drm_modeset_unlock_all(dev);
>>> + ret = dev->driver->atomic_commit(dev, state);
>>>
>>> out:
>>> - if (fb)
>>> - drm_framebuffer_unreference(fb);
>>> - if (old_fb)
>>> - drm_framebuffer_unreference(old_fb);
>>> -
>>> + dev->driver->atomic_end(dev, state);
>>> + if (ret == -EDEADLK)
>>> + goto retry;
>>> return ret;
>>> }
>>>
>>
>> <snip>
More information about the dri-devel
mailing list