[PATCH v2 1/2] drm/crtc: fix connector reference counting mismatch in drm_crtc_helper_set_config
Daniel Vetter
daniel at ffwll.ch
Tue Jun 14 17:40:27 UTC 2016
On Tue, Jun 14, 2016 at 06:08:35PM +0200, Daniel Vetter wrote:
> On Thu, Jun 02, 2016 at 07:27:51PM +0200, Philipp Zabel wrote:
> > Since commit 0955c1250e96 ("drm/crtc: take references to connectors used
> > in a modeset. (v2)"), the reference counts of all connectors in the
> > drm_mode_set given to drm_crtc_helper_set_config are incremented, and then
> > the reference counts of all connectors are decremented on success, but in a
> > temporary copy of the connector structure. This leads to the following
> > error after the first modeset on imx-drm:
> >
> > Unable to handle kernel NULL pointer dereference at virtual address 00000004
> > pgd = ad8c4000
> > [00000004] *pgd=3d9c5831, *pte=00000000, *ppte=00000000
> > Internal error: Oops: 817 [#1] PREEMPT SMP ARM
> > Modules linked in:
> > CPU: 1 PID: 190 Comm: kmsfb-manage Not tainted 4.7.0-rc1+ #657
> > Hardware name: Freescale i.MX6 Quad/DualLit: [<80506098>] lr : [<80252e94>] psr: 200c0013
> > sp : adca7ca8 ip : adca7b90 fp : adca7cd4
> > r10: 00000000 r9 : 00000100 r8 : 00000200
> > r7 : af3c9800 r6 : aded7848 r5 : aded7800 r4 : 00000000
> > r3 : af3ca058 r2 : 00000200 r1 : af3ca058 r0 : 00000000
> > Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none
> > Control: 10c5387d Table: 3d8c404a DAC: 00000051
> > Process kmsfb-manage (pid: 190, stack limit = 0xadca6210)
> > Stack: (0xadca7ca8 to 0xadca8000)
> > 7ca0: 805190e0 aded7800 aded7820 80501a88 8155a290 af3c9c6c
> > 7cc0: adca7ddc 0000000f adca7cec adca7cd8 80519104 80506044 805190e0 aded7800
> > 7ce0: adca7d04 adca7cf0 80501ac0 805190ec aded7820 aded7814 adca7d24 adca7d08
> > 7d00: 804fdb80 80501a94 aded7800 af3ca010 aded7afc af3c9c60 adca7d94 adca7d28
> > 7d20: 804e3518 804fdb20 00000000 af3c9b1c adca7d50 81506f44 00000000 8093c500
> > 7d40: af3c9c6c ae4f2ca8 ae4f2c18 00000000 00000000 ae637f00 00000000 aded7800
> > 7d60: 00000001 af3c9800 af23c300 ae77fcc0 ae4f2c18 00000001 af3c9800 8155a290
> > 7d80: af1af700 adca6000 adca7db4 adca7d98 804fea6c 804e2de4 adca7e50 adb3d940
> > 7da0: 00000001 af3c9800 adca7e24 adca7db8 8050440c 804fea0c ae77fcc0 00000003
> > 7dc0: adca7e24 adb3d940 af1af700 ae77fcc0 ae77fccc ae4f2c18 8083d44c ae77fcc0
> > 7de0: ae4002 80d03040 adca7e64 adca7e40 adca7e50 80503f08
> > 7e40: 7ebd5630 adca7e50 00000068 c06864a2 7ebd5be8 00000000 00000001 00000018
> > 7e60: 00000026 00000000 00000000 00000000 00000001 000115bc 05010500 05a0059f
> > 7e80: 03200000 03360321 00000337 0000003c 00000000 00000040 30383231 30303878
> > 7ea0: 00000000 00000000 00000000 00000000 00000000 00000000 80173058 80172e30
> > 7ec0: 80d77d32 00004000 adf7d900 00000003 00000000 7ebd5630 af342bb0 adfe3b80
> > 7ee0: 80272f50 00000003 adca6000 00000000 adca7f7c adca7f00 802725ec 804f52cc
> > 7f00: 802809cc 80178450 00000000 00000000 80280880 80145904 adb3d8c0 adf7d990
> > 7f20: ffffffff 00000003 00004000 01614c10 c06864a2 00000003 adca6000 00000000
> > 7f40: adca7f6c adca7f50 80280b04 8028088c 000115bc adfe3b81 7ebd5630 adfe3b80
> > 7f60: c06864a2 00000003 adca6000 00000000 adca7fa4 adca7f80 80272f50 80272548
> > 7f80: 000115bc 00017050 00000001 01614c10 00000036 801089e4 00000000 adca7fa8
> > 7fa0: 80108840 80272f18 00017050 00000001 00000003 c06864a2 7ebd5630 000115bc
> > 7fc0: 00017050 00000001 01614c10 00000036 00000003 00000000 00000026 00000018
> > 7fe0: 00016f38 7ebd562c 0000b5e9 76ef31e6 400c0030 00000003 ff5f37db bfe7dd4d
> > Backtrace:
> > [<80506038>] (drm_connector_cleanup) from [<80519104>] (dw_hdmi_connector_destroy+0x24/0x28)
> > r10:0000000f r9:adca7ddc r8:af3c9c6c r7:8155a290 r6:80501a88 r5:aded7820
> > r4:aded7800 r3:805190e0
> > [<805190e0>] (dw_hdmi_connector_destroy) from [<80501ac0>] (drm_connector_free+0x38/0x3c)
> > r4:aded7800 nreference) from [<804e3518>] (drm_crtc_helper_set_config+0x740/0xbf4)
> > r6:af3c9c60 r5:aded7afc r4:af3ca010 r3:aded7800
> > [<804e2dd8>] (drm_crtc_helper_set_config) from [<804fea6c>] (drm_mode_set_config_internal+0x6c/0xf4)
> > r10:adca6000 r9:af1af700 r8:8155a290 r7:af3c9800 r6:00000001 r5:ae4f2c18
> > r4:ae77fcc0
> > [<804fea00>] (drm_mode_set_config_internal) from [<8050440c>] (drm_mode_setcrtc+0x504/0x57c)
> > r7:af3c9800 r6:00000001 r5:adb3d940 r4:adca7e50
> > [<80503f08>] (drm_mode_setcrtc) from [<804f5404>] (drm_ioctl+0x144/0x4dc)
> > r10:ada2e000 r9:000000a2 r8:af3c9800 r7:8155a290 r6:809320b4 r5:00000051
> > r4:adca7e50
> > [<804f52c0>] (drm_ioctl) from [<802725ec>] (do_vfs_ioctl+0xb0/0x9d0)
> > r10:00000000 r9:adca6000 r8:00000003 r7:80272f50 r6:adfe3b80 r5:af342bb0
> > r4:7ebd5630
> > [<8027253c>] (do_vfs_ioctl) from [<80272f50>] (SyS_ioctl+0x44/0x6c)
> > r10:00000000 r9:adca6000 r8:00000003 r7:c06864a2 r6:adfe3b80 r5:7ebd5630
> > r4:adfe3b81
> > [<80272f0c>] (SyS_ioctl) from [<80108840>] (ret_fast_syscall+0x0/0x1c)
> > r8:801089e4 r7:00000036 r6:01614c10 r5:00000001 r4:00017050 r3:000115bc
> > Code: 0a00000c e5932004 e1a01003 e1a0a004 (e5842004)
> > ---[ end trace 9a7257572ccacb16 ]---
> >
> > Only the reference count of connectors that weren't previously bound to
> > an encoder should be incremented after a call to drm_crtc_helper_set_config.
> > And only the reference count of connectors that were previously bound to
> > an encoder and are unbound afterwards should ever be decremented.
> > The reference counts of the temporary copies in the save_connectors
> > should not be touched at all.
> >
> > This patch fixes the above error by only incrementing the reference count
> > of those connectors in the set that are initially not bound to any encoder,
> > and also by restoring the reference count of only those connectors in the
> > set in the failure case.
> >
> > Fixes: 0955c1250e96 ("drm/crtc: take references to connectors used in a modeset. (v2)")
> > Signed-off-by: Philipp Zabel <p.zabel at pengutronix.de>
>
> Commit message needs to be augmented:
>
> "Note that this can only be hit when fbdev emulation is disabled, since
> then the refcount drops from 1 to 0 and we call the connector destroy
> functions on the backup copy, which eventually results in tears. With
> fbdev emulation the refcount only goes down from 2 to 1 ever. And since we
> unconditionally increment the refcount on the real object, the refcount of
> that will slowly increase. The backup connector's refcount doesn't matter,
> since we kfree() that either way in the end of
> drm_crtc_helper_set_config()."
> > ---
> > drivers/gpu/drm/drm_crtc_helper.c | 18 ++++++++++--------
> > 1 file changed, 10 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
> > index a6e4243..1c4d674 100644
> > --- a/drivers/gpu/drm/drm_crtc_helper.c
> > +++ b/drivers/gpu/drm/drm_crtc_helper.c
> > @@ -631,8 +631,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
> > mode_changed = true;
> > }
> >
> > - /* take a reference on all connectors in set */
> > + /* take a reference on all unbound connectors in set, reuse the
> > + * already taken reference for bound connectors
> > + */
> > for (ro = 0; ro < set->num_connectors; ro++) {
> > + if (set->connectors[ro]->encoder)
> > + continue;
>
> The real bugfix is that you're switching from save_connectors to
> set->connectors. This filtering here should be irrelevant.
>
> > drm_connector_reference(set->connectors[ro]);
> > }
> >
> > @@ -754,12 +758,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
> > }
> > }
> >
> > - /* after fail drop reference on all connectors in save set */
> > - count = 0;
> > - drm_for_each_connector(connector, dev) {
> > - drm_connector_unreference(&save_connectors[count++]);
>
> And we need to keep the unreference here (but on set->connectors instead),
> otherwise this will leak badly. In short this is the only place you need
> to fix up, the other hunks shouldn't be needed.
>
> > - }
> > -
> > kfree(save_connectors);
> > kfree(save_encoders);
> > return 0;
> > @@ -776,8 +774,12 @@ fail:
> > *connector = save_connectors[count++];
> > }
> >
> > - /* after fail drop reference on all connectors in set */
> > + /* after fail drop reference on all unbound connectors in set, let
> > + * bound connectors keep their reference
> > + */
> > for (ro = 0; ro < set->num_connectors; ro++) {
> > + if (set->connectors[ro]->encoder)
> > + continue;
>
> Again, filtering on ->encoder is superflous.
>
> > drm_connector_unreference(set->connectors[ro]);
> > }
>
> With the bugs in the code fixed and the commit message augmented:
Ok, I thought about this some more while eating, and I was totally off the
tracks. The patch is sound, but still needs the commit message ammended.
With that:
Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
More information about the dri-devel
mailing list