Message ID | 20230713163213.1028952-1-javierm@redhat.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:a6b2:0:b0:3e4:2afc:c1 with SMTP id c18csp1947583vqm; Thu, 13 Jul 2023 09:38:34 -0700 (PDT) X-Google-Smtp-Source: APBJJlGNB1CrB3pVf+c33LJHr1RYIZVU8aZzAj7N3bW1smcp1KeIYV4Lat2DJO4rZ1vgj8ty6nc3 X-Received: by 2002:a17:907:2bcf:b0:98d:63c5:d135 with SMTP id gv15-20020a1709072bcf00b0098d63c5d135mr1865182ejc.54.1689266314520; Thu, 13 Jul 2023 09:38:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689266314; cv=none; d=google.com; s=arc-20160816; b=Y3o0mC71c/bcxo3YjcS+zgr17nNvBgS90N8FCaVrvyEK8hJry72fShlGZwyJLUKywx 7cqRLp4c0FgPvEgWshP+0zcAtMYszXCeRLacaWLz3y+xn9BA7sxO6/F72BBATxl2wvWg p6Mub2ooFSx+cKirTSdpivkUy6ws/93UCcQiuefPR0/wsY5W3AlaU6S/xonnMp0XoLNC ns17y/UFJTuWGanxkDJZXNW6lXTeBOmy4V68A6h2R2OeZmltGkzVBjajFjTnbX+PS0aj 58Pz1Fgs1VstBFKAuWLxyui+T05B0k2gcZY8N7Gu0CimIP8BYiHqVd/Yc8MCnXLhPwUX Z3OA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=hA4oXFz7iMz2TN7Oq1WGXGk81ddHsb/K8fUHUI+fX50=; fh=xQLzqvagBaT/JxUJ9S2F9j2c9SChg5Xo7G9xUwKva9s=; b=i7BAHRytV9aWxu/bLJ8ue8dBR2hTLVt6Eb2idE8U0MKNXpG7FUEZj7kk9Zsxu6q3XN +3/eTMc/vwnu66aSYLxSw3ok7gHVKhhiGSgSUB2p5+PKqQgm4bM26Uy0MtaR7YnoQDJQ K2oFFGWarjKppTaIdQ1NDB9RVvQ2d5/Nm793jVMDPkUo/cWJCW0I6RYSlkmIed18kykH QzdnQiiq8a5U5cY4Y30GmByvZZ3XlhHZtyDHW6v8G3EoqdwaPhVA5o3nozQ5av4uPRjQ 7YiLzASSwTiggZQn5Nvt5qiE/JKRwunMzydm40NbuBrmvsvGDMkNWFhPbW38b35BZXT8 tFww== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=OtLS2MwF; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id w20-20020a17090633d400b00982817b0858si8249861eja.364.2023.07.13.09.38.10; Thu, 13 Jul 2023 09:38:34 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=OtLS2MwF; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233870AbjGMQde (ORCPT <rfc822;ybw1215001957@gmail.com> + 99 others); Thu, 13 Jul 2023 12:33:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43498 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231559AbjGMQdT (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Thu, 13 Jul 2023 12:33:19 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D0B33273E for <linux-kernel@vger.kernel.org>; Thu, 13 Jul 2023 09:32:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1689265952; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=hA4oXFz7iMz2TN7Oq1WGXGk81ddHsb/K8fUHUI+fX50=; b=OtLS2MwFZtmhHALcbAOqfg0VM3aSQMTAgIKTdSrM3b77CGvvEQpJHjvIedSPQPRuTehfxi GiGKEQ2VUkSo2oAEQK9kNIqbeMwrmpAh7hI1X+gwODxTDPsP1rTe4zvcqD5BMccoRxW/ak gm9BJnfR2BzdEIY63e/ekEIYfEUS6ho= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-342-z80AxN_PNIqPEgABguwkCQ-1; Thu, 13 Jul 2023 12:32:24 -0400 X-MC-Unique: z80AxN_PNIqPEgABguwkCQ-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-315998d6e7fso591135f8f.3 for <linux-kernel@vger.kernel.org>; Thu, 13 Jul 2023 09:32:23 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689265942; x=1691857942; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=hA4oXFz7iMz2TN7Oq1WGXGk81ddHsb/K8fUHUI+fX50=; b=H9AUQeY03zwN0DOv7L9l9pcU6X2PUyjopt+uorXgPjnH/agQtheuTYxZ28DV6q+Z3G GBVmsa6yi/NkRc/dYMMUeHTtXWJsFIJPAVKNjbfT6OVMeGTglzprBKzVhiDQdtRR9IAm T+fx+kh9mhA135D9xZYFhTSkgFwyIfgKI280GI8bSIB+sMFqWXDKo1llSXdDfrLCoUqH 50RtqKwfxrCdkUc0pWi49s8XciBpJ/KgXYnDrb7QwK6MbXG85Rs4ByBRNmC6z0fFciD0 gJ9z+FFPpLfqBOctyTqey+46OrHUKE7qUdpzytX0WnmKbI9spOGdlCeFAX4KVO/3LA10 Ncow== X-Gm-Message-State: ABy/qLaTN1BJLNP1u6MVtdw+LksBMWxRFtpLMRbzKmjdA4fq4BJXAHPL Abof2X/jxrcX4g4to+1cd1bObOK16hV9YnqGD9ClSnE11WQDDt6YElmmNDZDDjQWJwppstJDqZ6 SeKUnu5nS8joUCAJhQkcKq68M4JBI2rYZU+y+AugHuRjT8ReQlQ2bNO498A3N1EaQKZNKy/0vIp j1MU414RM= X-Received: by 2002:a05:6000:87:b0:314:449e:8536 with SMTP id m7-20020a056000008700b00314449e8536mr2005175wrx.10.1689265941820; Thu, 13 Jul 2023 09:32:21 -0700 (PDT) X-Received: by 2002:a05:6000:87:b0:314:449e:8536 with SMTP id m7-20020a056000008700b00314449e8536mr2005152wrx.10.1689265941523; Thu, 13 Jul 2023 09:32:21 -0700 (PDT) Received: from minerva.home (205.pool92-176-231.dynamic.orange.es. [92.176.231.205]) by smtp.gmail.com with ESMTPSA id z13-20020adfe54d000000b003143ac73fd0sm8486974wrm.1.2023.07.13.09.32.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 13 Jul 2023 09:32:21 -0700 (PDT) From: Javier Martinez Canillas <javierm@redhat.com> To: linux-kernel@vger.kernel.org Cc: Javier Martinez Canillas <javierm@redhat.com>, Geert Uytterhoeven <geert@linux-m68k.org>, Daniel Vetter <daniel@ffwll.ch>, David Airlie <airlied@gmail.com>, dri-devel@lists.freedesktop.org Subject: [PATCH] drm/ssd130x: Fix an oops when attempting to update a disabled plane Date: Thu, 13 Jul 2023 18:32:06 +0200 Message-ID: <20230713163213.1028952-1-javierm@redhat.com> X-Mailer: git-send-email 2.41.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1771324114817732645 X-GMAIL-MSGID: 1771324114817732645 |
Series |
drm/ssd130x: Fix an oops when attempting to update a disabled plane
|
|
Commit Message
Javier Martinez Canillas
July 13, 2023, 4:32 p.m. UTC
Geert reports that the following NULL pointer dereference happens for him
after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each
plane update"):
[drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0
ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1)
and format(R1 little-endian (0x20203152))
Unable to handle kernel NULL pointer dereference at virtual address 00000000
Oops [#1]
CPU: 0 PID: 1 Comm: swapper Not tainted
6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565
epc : ssd130x_update_rect.isra.0+0x13c/0x340
ra : ssd130x_update_rect.isra.0+0x2bc/0x340
...
status: 00000120 badaddr: 00000000 cause: 0000000f
[<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340
[<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284
[<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c
[<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4
[<c02f94fc>] commit_tail+0x190/0x1b8
[<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0
[<c02c5d00>] drm_atomic_commit+0xa4/0xe4
[<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278
[<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc
[<c02cd064>] drm_client_modeset_commit+0x34/0x64
[<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8
[<c0303424>] drm_fb_helper_set_par+0x38/0x58
[<c027c410>] fbcon_init+0x294/0x534
...
The problem is that fbcon calls fbcon_init() which triggers a DRM modeset
and this leads to drm_atomic_helper_commit_planes() attempting to commit
the atomic state for all planes, even the ones whose CRTC is not enabled.
Since the primary plane buffer is allocated in the encoder .atomic_enable
callback, this happens after that initial modeset commit and leads to the
mentioned NULL pointer dereference.
Fix this by not using the default drm_atomic_helper_commit_tail() helper,
but instead the drm_atomic_helper_commit_tail_rpm() function that doesn't
attempt to commit the atomic state for planes related to inactive CRTCs.
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---
drivers/gpu/drm/solomon/ssd130x.c | 5 +++++
1 file changed, 5 insertions(+)
Comments
Hi Javier, On Thu, Jul 13, 2023 at 6:32 PM Javier Martinez Canillas <javierm@redhat.com> wrote: > Geert reports that the following NULL pointer dereference happens for him > after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each > plane update"): > > [drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0 > ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1) > and format(R1 little-endian (0x20203152)) > Unable to handle kernel NULL pointer dereference at virtual address 00000000 > Oops [#1] > CPU: 0 PID: 1 Comm: swapper Not tainted > 6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565 > epc : ssd130x_update_rect.isra.0+0x13c/0x340 > ra : ssd130x_update_rect.isra.0+0x2bc/0x340 > ... > status: 00000120 badaddr: 00000000 cause: 0000000f > [<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340 > [<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284 > [<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c > [<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4 > [<c02f94fc>] commit_tail+0x190/0x1b8 > [<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0 > [<c02c5d00>] drm_atomic_commit+0xa4/0xe4 > [<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278 > [<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc > [<c02cd064>] drm_client_modeset_commit+0x34/0x64 > [<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8 > [<c0303424>] drm_fb_helper_set_par+0x38/0x58 > [<c027c410>] fbcon_init+0x294/0x534 > ... > > The problem is that fbcon calls fbcon_init() which triggers a DRM modeset > and this leads to drm_atomic_helper_commit_planes() attempting to commit > the atomic state for all planes, even the ones whose CRTC is not enabled. > > Since the primary plane buffer is allocated in the encoder .atomic_enable > callback, this happens after that initial modeset commit and leads to the > mentioned NULL pointer dereference. > > Fix this by not using the default drm_atomic_helper_commit_tail() helper, > but instead the drm_atomic_helper_commit_tail_rpm() function that doesn't > attempt to commit the atomic state for planes related to inactive CRTCs. > > Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> > Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> Tested-by: Geert Uytterhoeven <geert@linux-m68k.org> > --- a/drivers/gpu/drm/solomon/ssd130x.c > +++ b/drivers/gpu/drm/solomon/ssd130x.c > @@ -795,6 +795,10 @@ static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { > .atomic_commit = drm_atomic_helper_commit, > }; > > +static const struct drm_mode_config_helper_funcs ssd130x_mode_config_helpers = { > + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, The docs say this is intended for drivers that support runtime_pm or need the CRTC to be enabled to perform a commit. Might be worthwhile to add basic Runtime PM, so the I2C controller can go to sleep when the display is not used. > +}; > + > static const uint32_t ssd130x_formats[] = { > DRM_FORMAT_XRGB8888, > }; Gr{oetje,eeting}s, Geert
Geert Uytterhoeven <geert@linux-m68k.org> writes: Hello Geert, > Hi Javier, > > On Thu, Jul 13, 2023 at 6:32 PM Javier Martinez Canillas > <javierm@redhat.com> wrote: >> Geert reports that the following NULL pointer dereference happens for him >> after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each >> plane update"): >> >> [drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0 >> ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1) >> and format(R1 little-endian (0x20203152)) >> Unable to handle kernel NULL pointer dereference at virtual address 00000000 >> Oops [#1] >> CPU: 0 PID: 1 Comm: swapper Not tainted >> 6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565 >> epc : ssd130x_update_rect.isra.0+0x13c/0x340 >> ra : ssd130x_update_rect.isra.0+0x2bc/0x340 >> ... >> status: 00000120 badaddr: 00000000 cause: 0000000f >> [<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340 >> [<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284 >> [<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c >> [<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4 >> [<c02f94fc>] commit_tail+0x190/0x1b8 >> [<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0 >> [<c02c5d00>] drm_atomic_commit+0xa4/0xe4 >> [<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278 >> [<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc >> [<c02cd064>] drm_client_modeset_commit+0x34/0x64 >> [<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8 >> [<c0303424>] drm_fb_helper_set_par+0x38/0x58 >> [<c027c410>] fbcon_init+0x294/0x534 >> ... >> >> The problem is that fbcon calls fbcon_init() which triggers a DRM modeset >> and this leads to drm_atomic_helper_commit_planes() attempting to commit >> the atomic state for all planes, even the ones whose CRTC is not enabled. >> >> Since the primary plane buffer is allocated in the encoder .atomic_enable >> callback, this happens after that initial modeset commit and leads to the >> mentioned NULL pointer dereference. >> >> Fix this by not using the default drm_atomic_helper_commit_tail() helper, >> but instead the drm_atomic_helper_commit_tail_rpm() function that doesn't >> attempt to commit the atomic state for planes related to inactive CRTCs. >> >> Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> >> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> > > Tested-by: Geert Uytterhoeven <geert@linux-m68k.org> > Thanks for reporting the issue in the first place and for the testing! >> --- a/drivers/gpu/drm/solomon/ssd130x.c >> +++ b/drivers/gpu/drm/solomon/ssd130x.c >> @@ -795,6 +795,10 @@ static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { >> .atomic_commit = drm_atomic_helper_commit, >> }; >> >> +static const struct drm_mode_config_helper_funcs ssd130x_mode_config_helpers = { >> + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, > > The docs say this is intended for drivers that support runtime_pm or > need the CRTC to be enabled to perform a commit. Might be worthwhile > to add basic Runtime PM, so the I2C controller can go to sleep when > the display is not used. > Indeed, I thought the same. But I believe we can do that as a follow-up patch.
Hi Am 13.07.23 um 18:32 schrieb Javier Martinez Canillas: > Geert reports that the following NULL pointer dereference happens for him > after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each > plane update"): > > [drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0 > ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1) > and format(R1 little-endian (0x20203152)) > Unable to handle kernel NULL pointer dereference at virtual address 00000000 > Oops [#1] > CPU: 0 PID: 1 Comm: swapper Not tainted > 6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565 > epc : ssd130x_update_rect.isra.0+0x13c/0x340 > ra : ssd130x_update_rect.isra.0+0x2bc/0x340 > ... > status: 00000120 badaddr: 00000000 cause: 0000000f > [<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340 > [<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284 > [<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c > [<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4 > [<c02f94fc>] commit_tail+0x190/0x1b8 > [<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0 > [<c02c5d00>] drm_atomic_commit+0xa4/0xe4 > [<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278 > [<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc > [<c02cd064>] drm_client_modeset_commit+0x34/0x64 > [<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8 > [<c0303424>] drm_fb_helper_set_par+0x38/0x58 > [<c027c410>] fbcon_init+0x294/0x534 > ... > > The problem is that fbcon calls fbcon_init() which triggers a DRM modeset > and this leads to drm_atomic_helper_commit_planes() attempting to commit > the atomic state for all planes, even the ones whose CRTC is not enabled. > > Since the primary plane buffer is allocated in the encoder .atomic_enable > callback, this happens after that initial modeset commit and leads to the > mentioned NULL pointer dereference. > > Fix this by not using the default drm_atomic_helper_commit_tail() helper, > but instead the drm_atomic_helper_commit_tail_rpm() function that doesn't > attempt to commit the atomic state for planes related to inactive CRTCs. > > Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> > Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> > --- > > drivers/gpu/drm/solomon/ssd130x.c | 5 +++++ > 1 file changed, 5 insertions(+) > > diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c > index afb08a8aa9fc..12820d16b15b 100644 > --- a/drivers/gpu/drm/solomon/ssd130x.c > +++ b/drivers/gpu/drm/solomon/ssd130x.c > @@ -795,6 +795,10 @@ static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { > .atomic_commit = drm_atomic_helper_commit, > }; > > +static const struct drm_mode_config_helper_funcs ssd130x_mode_config_helpers = { > + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, > +}; > + After some discussion on IRC, I'd suggest to allocate the buffer somewhere within probe. So it will always be there when the plane code runs. A full fix would be to allocate the buffer memory as part of the plane state and/or the plane's atomic_check. That's a bit more complicated if you want to shared the buffer memory across plane updates. Best regards Thomas > static const uint32_t ssd130x_formats[] = { > DRM_FORMAT_XRGB8888, > }; > @@ -923,6 +927,7 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x) > drm->mode_config.max_height = max_height; > drm->mode_config.preferred_depth = 24; > drm->mode_config.funcs = &ssd130x_mode_config_funcs; > + drm->mode_config.helper_private = &ssd130x_mode_config_helpers; > > /* Primary plane */ > -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Frankenstrasse 146, 90461 Nuernberg, Germany GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman HRB 36809 (AG Nuernberg)
Thomas Zimmermann <tzimmermann@suse.de> writes: Hello Thomas, > Hi > > Am 13.07.23 um 18:32 schrieb Javier Martinez Canillas: [...] >> >> +static const struct drm_mode_config_helper_funcs ssd130x_mode_config_helpers = { >> + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, >> +}; >> + > > After some discussion on IRC, I'd suggest to allocate the buffer > somewhere within probe. So it will always be there when the plane code runs. > Yes, that's also what Geert suggested so I'll just do that. And also make it a dev managed resource. > A full fix would be to allocate the buffer memory as part of the plane > state and/or the plane's atomic_check. That's a bit more complicated if > you want to shared the buffer memory across plane updates. > I don't think is worth the complexity, allocating it on probe and released when the device is unbound from the driver should be enough as Geert said. > Best regards > Thomas > >
Hi Thomas, On Mon, Jul 17, 2023 at 10:48 AM Thomas Zimmermann <tzimmermann@suse.de> wrote: > Am 13.07.23 um 18:32 schrieb Javier Martinez Canillas: > > Geert reports that the following NULL pointer dereference happens for him > > after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each > > plane update"): > > > > [drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0 > > ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1) > > and format(R1 little-endian (0x20203152)) > > Unable to handle kernel NULL pointer dereference at virtual address 00000000 > > Oops [#1] > > CPU: 0 PID: 1 Comm: swapper Not tainted > > 6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565 > > epc : ssd130x_update_rect.isra.0+0x13c/0x340 > > ra : ssd130x_update_rect.isra.0+0x2bc/0x340 > > ... > > status: 00000120 badaddr: 00000000 cause: 0000000f > > [<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340 > > [<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284 > > [<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c > > [<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4 > > [<c02f94fc>] commit_tail+0x190/0x1b8 > > [<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0 > > [<c02c5d00>] drm_atomic_commit+0xa4/0xe4 > > [<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278 > > [<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc > > [<c02cd064>] drm_client_modeset_commit+0x34/0x64 > > [<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8 > > [<c0303424>] drm_fb_helper_set_par+0x38/0x58 > > [<c027c410>] fbcon_init+0x294/0x534 > > ... > > > > The problem is that fbcon calls fbcon_init() which triggers a DRM modeset > > and this leads to drm_atomic_helper_commit_planes() attempting to commit > > the atomic state for all planes, even the ones whose CRTC is not enabled. > > > > Since the primary plane buffer is allocated in the encoder .atomic_enable > > callback, this happens after that initial modeset commit and leads to the > > mentioned NULL pointer dereference. > > > > Fix this by not using the default drm_atomic_helper_commit_tail() helper, > > but instead the drm_atomic_helper_commit_tail_rpm() function that doesn't > > attempt to commit the atomic state for planes related to inactive CRTCs. > > > > Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> > > Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> > > --- a/drivers/gpu/drm/solomon/ssd130x.c > > +++ b/drivers/gpu/drm/solomon/ssd130x.c > > @@ -795,6 +795,10 @@ static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { > > .atomic_commit = drm_atomic_helper_commit, > > }; > > > > +static const struct drm_mode_config_helper_funcs ssd130x_mode_config_helpers = { > > + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, > > +}; > > + > > After some discussion on IRC, I'd suggest to allocate the buffer > somewhere within probe. So it will always be there when the plane code runs. > > A full fix would be to allocate the buffer memory as part of the plane > state and/or the plane's atomic_check. That's a bit more complicated if > you want to shared the buffer memory across plane updates. Note that actually two buffers are involved: data_array (monochrome, needed for each update), and buffer (R8, only needed when converting from XR24 to R1). For the former, I agree, as it's always needed. For the latter, I'm afraid it would set a bad example: while allocating a possibly-unused buffer doesn't hurt for small displays, it would mean wasting 1 MiB in e.g. the repaper driver (once it has gained support for R1 ;^). Gr{oetje,eeting}s, Geert
Geert Uytterhoeven <geert@linux-m68k.org> writes: Hello Geert and Thomas, > Hi Thomas, > > On Mon, Jul 17, 2023 at 10:48 AM Thomas Zimmermann <tzimmermann@suse.de> wrote: [...] >> >> After some discussion on IRC, I'd suggest to allocate the buffer >> somewhere within probe. So it will always be there when the plane code runs. >> >> A full fix would be to allocate the buffer memory as part of the plane >> state and/or the plane's atomic_check. That's a bit more complicated if >> you want to shared the buffer memory across plane updates. > > Note that actually two buffers are involved: data_array (monochrome, > needed for each update), and buffer (R8, only needed when converting > from XR24 to R1). > > For the former, I agree, as it's always needed. > For the latter, I'm afraid it would set a bad example: while allocating > a possibly-unused buffer doesn't hurt for small displays, it would > mean wasting 1 MiB in e.g. the repaper driver (once it has gained > support for R1 ;^). > Maybe another option is to allocate on the struct drm_mode_config_funcs .fb_create callback? That way, we can get the mode_cmd->pixel_format and determine if only "data_array" buffer must be allocated or also "buffer". > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org > > In personal conversations with technical people, I call myself a hacker. But > when I'm talking to journalists I just say "programmer" or something like that. > -- Linus Torvalds >
Javier Martinez Canillas <javierm@redhat.com> writes: > Geert Uytterhoeven <geert@linux-m68k.org> writes: > > Hello Geert and Thomas, > >> Hi Thomas, >> >> On Mon, Jul 17, 2023 at 10:48 AM Thomas Zimmermann <tzimmermann@suse.de> wrote: > > [...] > >>> >>> After some discussion on IRC, I'd suggest to allocate the buffer >>> somewhere within probe. So it will always be there when the plane code runs. >>> >>> A full fix would be to allocate the buffer memory as part of the plane >>> state and/or the plane's atomic_check. That's a bit more complicated if >>> you want to shared the buffer memory across plane updates. >> >> Note that actually two buffers are involved: data_array (monochrome, >> needed for each update), and buffer (R8, only needed when converting >> from XR24 to R1). >> >> For the former, I agree, as it's always needed. >> For the latter, I'm afraid it would set a bad example: while allocating >> a possibly-unused buffer doesn't hurt for small displays, it would >> mean wasting 1 MiB in e.g. the repaper driver (once it has gained >> support for R1 ;^). >> > > Maybe another option is to allocate on the struct drm_mode_config_funcs > .fb_create callback? That way, we can get the mode_cmd->pixel_format and > determine if only "data_array" buffer must be allocated or also "buffer". > Something like the following patch: From 307bf062c9a999693a4a68c6740ec55b81f796b8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas <javierm@redhat.com> Date: Mon, 17 Jul 2023 12:30:35 +0200 Subject: [PATCH v2] drm/ssd130x: Fix an oops when attempting to update a disabled plane Geert reports that the following NULL pointer dereference happens for him after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each plane update"): [drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0 ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1) and format(R1 little-endian (0x20203152)) Unable to handle kernel NULL pointer dereference at virtual address 00000000 Oops [#1] CPU: 0 PID: 1 Comm: swapper Not tainted 6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565 epc : ssd130x_update_rect.isra.0+0x13c/0x340 ra : ssd130x_update_rect.isra.0+0x2bc/0x340 ... status: 00000120 badaddr: 00000000 cause: 0000000f [<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340 [<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284 [<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c [<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4 [<c02f94fc>] commit_tail+0x190/0x1b8 [<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0 [<c02c5d00>] drm_atomic_commit+0xa4/0xe4 [<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278 [<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc [<c02cd064>] drm_client_modeset_commit+0x34/0x64 [<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8 [<c0303424>] drm_fb_helper_set_par+0x38/0x58 [<c027c410>] fbcon_init+0x294/0x534 ... The problem is that fbcon calls fbcon_init() which triggers a DRM modeset and this leads to drm_atomic_helper_commit_planes() attempting to commit the atomic state for all planes, even the ones whose CRTC is not enabled. Since the primary plane buffer is allocated in the encoder .atomic_enable callback, this happens after that initial modeset commit and leads to the mentioned NULL pointer dereference. Fix this by allocating the buffers in the struct drm_mode_config_funcs .fb_create, instead of doing it when the encoder is enabled. Also, make a couple of improvements to the ssd130x_buf_alloc() function: * Make the allocation smarter to only allocate the intermediate buffer if the native R1 format is not used. Otherwise is not needed, since there is no XRGB8888 to R1 format conversion during plane updates. * Allocate the buffers as device managed resources, this is enough and simplifies the logic since there is no need to explicitly free them. Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> --- Changes in v2: - Move the buffers allocation to .fb_create instead of preventing atomic updates for disable planes. - Don't allocate the intermediate buffer when using the native R1 format. - Allocate buffers as device managed resources. drivers/gpu/drm/solomon/ssd130x.c | 54 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index b8c90d507a35..615805a066de 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -146,38 +146,35 @@ static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm) return container_of(drm, struct ssd130x_device, drm); } -static int ssd130x_buf_alloc(struct ssd130x_device *ssd130x) +static int ssd130x_buf_alloc(struct ssd130x_device *ssd130x, __u32 pixel_format) { unsigned int page_height = ssd130x->device_info->page_height; unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height); const struct drm_format_info *fi; unsigned int pitch; - fi = drm_format_info(DRM_FORMAT_R1); - if (!fi) - return -EINVAL; + /* If the native format is not used an intermediate buffer is needed */ + if (pixel_format != DRM_FORMAT_R1) { + fi = drm_format_info(DRM_FORMAT_R1); + if (!fi) + return -EINVAL; - pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); + pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); - ssd130x->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL); - if (!ssd130x->buffer) - return -ENOMEM; + ssd130x->buffer = devm_kcalloc(ssd130x->dev, pitch, ssd130x->height, + GFP_KERNEL); + if (!ssd130x->buffer) + return -ENOMEM; + } - ssd130x->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL); - if (!ssd130x->data_array) { - kfree(ssd130x->buffer); + ssd130x->data_array = devm_kcalloc(ssd130x->dev, ssd130x->width, pages, + GFP_KERNEL); + if (!ssd130x->data_array) return -ENOMEM; - } return 0; } -static void ssd130x_buf_free(struct ssd130x_device *ssd130x) -{ - kfree(ssd130x->data_array); - kfree(ssd130x->buffer); -} - /* * Helper to write data (SSD130X_DATA) to the device. */ @@ -741,10 +738,6 @@ static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder, if (ret) goto power_off; - ret = ssd130x_buf_alloc(ssd130x); - if (ret) - goto power_off; - ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON); backlight_enable(ssd130x->bl_dev); @@ -766,8 +759,6 @@ static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder, ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF); - ssd130x_buf_free(ssd130x); - ssd130x_power_off(ssd130x); } @@ -811,8 +802,21 @@ static const struct drm_connector_funcs ssd130x_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; +static struct drm_framebuffer *ssd130x_fb_create(struct drm_device *dev, struct drm_file *file, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(dev); + int ret; + + ret = ssd130x_buf_alloc(ssd130x, mode_cmd->pixel_format); + if (ret) + return ERR_PTR(ret); + + return drm_gem_fb_create_with_dirty(dev, file, mode_cmd); +} + static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { - .fb_create = drm_gem_fb_create_with_dirty, + .fb_create = ssd130x_fb_create, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, };
Hi Am 17.07.23 um 11:04 schrieb Geert Uytterhoeven: > Hi Thomas, > > On Mon, Jul 17, 2023 at 10:48 AM Thomas Zimmermann <tzimmermann@suse.de> wrote: >> Am 13.07.23 um 18:32 schrieb Javier Martinez Canillas: >>> Geert reports that the following NULL pointer dereference happens for him >>> after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each >>> plane update"): >>> >>> [drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0 >>> ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1) >>> and format(R1 little-endian (0x20203152)) >>> Unable to handle kernel NULL pointer dereference at virtual address 00000000 >>> Oops [#1] >>> CPU: 0 PID: 1 Comm: swapper Not tainted >>> 6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565 >>> epc : ssd130x_update_rect.isra.0+0x13c/0x340 >>> ra : ssd130x_update_rect.isra.0+0x2bc/0x340 >>> ... >>> status: 00000120 badaddr: 00000000 cause: 0000000f >>> [<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340 >>> [<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284 >>> [<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c >>> [<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4 >>> [<c02f94fc>] commit_tail+0x190/0x1b8 >>> [<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0 >>> [<c02c5d00>] drm_atomic_commit+0xa4/0xe4 >>> [<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278 >>> [<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc >>> [<c02cd064>] drm_client_modeset_commit+0x34/0x64 >>> [<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8 >>> [<c0303424>] drm_fb_helper_set_par+0x38/0x58 >>> [<c027c410>] fbcon_init+0x294/0x534 >>> ... >>> >>> The problem is that fbcon calls fbcon_init() which triggers a DRM modeset >>> and this leads to drm_atomic_helper_commit_planes() attempting to commit >>> the atomic state for all planes, even the ones whose CRTC is not enabled. >>> >>> Since the primary plane buffer is allocated in the encoder .atomic_enable >>> callback, this happens after that initial modeset commit and leads to the >>> mentioned NULL pointer dereference. >>> >>> Fix this by not using the default drm_atomic_helper_commit_tail() helper, >>> but instead the drm_atomic_helper_commit_tail_rpm() function that doesn't >>> attempt to commit the atomic state for planes related to inactive CRTCs. >>> >>> Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> >>> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> > >>> --- a/drivers/gpu/drm/solomon/ssd130x.c >>> +++ b/drivers/gpu/drm/solomon/ssd130x.c >>> @@ -795,6 +795,10 @@ static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { >>> .atomic_commit = drm_atomic_helper_commit, >>> }; >>> >>> +static const struct drm_mode_config_helper_funcs ssd130x_mode_config_helpers = { >>> + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, >>> +}; >>> + >> >> After some discussion on IRC, I'd suggest to allocate the buffer >> somewhere within probe. So it will always be there when the plane code runs. >> >> A full fix would be to allocate the buffer memory as part of the plane >> state and/or the plane's atomic_check. That's a bit more complicated if >> you want to shared the buffer memory across plane updates. > > Note that actually two buffers are involved: data_array (monochrome, > needed for each update), and buffer (R8, only needed when converting > from XR24 to R1). > > For the former, I agree, as it's always needed. > For the latter, I'm afraid it would set a bad example: while allocating > a possibly-unused buffer doesn't hurt for small displays, it would > mean wasting 1 MiB in e.g. the repaper driver (once it has gained > support for R1 ;^). Let me explain: a real DRM-ideomatic solution would allocate the required buffers at the correct size in the plane's atomic check. The pointer would be stored in the plane state and later be free'd as part of releasing that plane_state. But as this is temporary memory for the plane update, so it can be shared among plane states. Keeping track of the references requires a bit of work though. Repaper appears to allocate buffer memory on each update, so anything is an improvement there. Best regards Thomsa > > Gr{oetje,eeting}s, > > Geert > -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Frankenstrasse 146, 90461 Nuernberg, Germany GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman HRB 36809 (AG Nuernberg)
Hi Javier, On Mon, Jul 17, 2023 at 12:37 PM Javier Martinez Canillas <javierm@redhat.com> wrote: > Javier Martinez Canillas <javierm@redhat.com> writes: > >> On Mon, Jul 17, 2023 at 10:48 AM Thomas Zimmermann <tzimmermann@suse.de> wrote: > >>> After some discussion on IRC, I'd suggest to allocate the buffer > >>> somewhere within probe. So it will always be there when the plane code runs. > >>> > >>> A full fix would be to allocate the buffer memory as part of the plane > >>> state and/or the plane's atomic_check. That's a bit more complicated if > >>> you want to shared the buffer memory across plane updates. > >> > >> Note that actually two buffers are involved: data_array (monochrome, > >> needed for each update), and buffer (R8, only needed when converting > >> from XR24 to R1). > >> > >> For the former, I agree, as it's always needed. > >> For the latter, I'm afraid it would set a bad example: while allocating > >> a possibly-unused buffer doesn't hurt for small displays, it would > >> mean wasting 1 MiB in e.g. the repaper driver (once it has gained > >> support for R1 ;^). > >> > > > > Maybe another option is to allocate on the struct drm_mode_config_funcs > > .fb_create callback? That way, we can get the mode_cmd->pixel_format and > > determine if only "data_array" buffer must be allocated or also "buffer". > > > > Something like the following patch: > > From 307bf062c9a999693a4a68c6740ec55b81f796b8 Mon Sep 17 00:00:00 2001 > From: Javier Martinez Canillas <javierm@redhat.com> > Date: Mon, 17 Jul 2023 12:30:35 +0200 > Subject: [PATCH v2] drm/ssd130x: Fix an oops when attempting to update a > disabled plane > > Geert reports that the following NULL pointer dereference happens for him > after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each > plane update"): > > [drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0 > ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1) > and format(R1 little-endian (0x20203152)) > Unable to handle kernel NULL pointer dereference at virtual address 00000000 > Oops [#1] > CPU: 0 PID: 1 Comm: swapper Not tainted > 6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565 > epc : ssd130x_update_rect.isra.0+0x13c/0x340 > ra : ssd130x_update_rect.isra.0+0x2bc/0x340 > ... > status: 00000120 badaddr: 00000000 cause: 0000000f > [<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340 > [<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284 > [<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c > [<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4 > [<c02f94fc>] commit_tail+0x190/0x1b8 > [<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0 > [<c02c5d00>] drm_atomic_commit+0xa4/0xe4 > [<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278 > [<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc > [<c02cd064>] drm_client_modeset_commit+0x34/0x64 > [<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8 > [<c0303424>] drm_fb_helper_set_par+0x38/0x58 > [<c027c410>] fbcon_init+0x294/0x534 > ... > > The problem is that fbcon calls fbcon_init() which triggers a DRM modeset > and this leads to drm_atomic_helper_commit_planes() attempting to commit > the atomic state for all planes, even the ones whose CRTC is not enabled. > > Since the primary plane buffer is allocated in the encoder .atomic_enable > callback, this happens after that initial modeset commit and leads to the > mentioned NULL pointer dereference. > > Fix this by allocating the buffers in the struct drm_mode_config_funcs > .fb_create, instead of doing it when the encoder is enabled. > > Also, make a couple of improvements to the ssd130x_buf_alloc() function: > > * Make the allocation smarter to only allocate the intermediate buffer > if the native R1 format is not used. Otherwise is not needed, since > there is no XRGB8888 to R1 format conversion during plane updates. > > * Allocate the buffers as device managed resources, this is enough and > simplifies the logic since there is no need to explicitly free them. > > Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> > Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> > --- > > Changes in v2: > - Move the buffers allocation to .fb_create instead of preventing atomic > updates for disable planes. > - Don't allocate the intermediate buffer when using the native R1 format. > - Allocate buffers as device managed resources. Thanks for the update! > --- a/drivers/gpu/drm/solomon/ssd130x.c > +++ b/drivers/gpu/drm/solomon/ssd130x.c > @@ -146,38 +146,35 @@ static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm) > return container_of(drm, struct ssd130x_device, drm); > } > > -static int ssd130x_buf_alloc(struct ssd130x_device *ssd130x) > +static int ssd130x_buf_alloc(struct ssd130x_device *ssd130x, __u32 pixel_format) > { > unsigned int page_height = ssd130x->device_info->page_height; > unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height); > const struct drm_format_info *fi; > unsigned int pitch; > > - fi = drm_format_info(DRM_FORMAT_R1); > - if (!fi) > - return -EINVAL; > + /* If the native format is not used an intermediate buffer is needed */ > + if (pixel_format != DRM_FORMAT_R1) { > + fi = drm_format_info(DRM_FORMAT_R1); > + if (!fi) > + return -EINVAL; > > - pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); > + pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); > > - ssd130x->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL); > - if (!ssd130x->buffer) > - return -ENOMEM; > + ssd130x->buffer = devm_kcalloc(ssd130x->dev, pitch, ssd130x->height, > + GFP_KERNEL); This should check if the buffer was allocated before. Else an application creating two or more frame buffers will keep on allocating new buffers. The same is true for fbdev emulation vs. a userspace application. > + if (!ssd130x->buffer) > + return -ENOMEM; > + } > > - ssd130x->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL); > - if (!ssd130x->data_array) { > - kfree(ssd130x->buffer); > + ssd130x->data_array = devm_kcalloc(ssd130x->dev, ssd130x->width, pages, > + GFP_KERNEL); Same here. > + if (!ssd130x->data_array) > return -ENOMEM; > - } And you still need the data_array buffer for clearing the screen, which is not tied to the creation of a frame buffer, I think? > > return 0; > } > > -static void ssd130x_buf_free(struct ssd130x_device *ssd130x) > -{ > - kfree(ssd130x->data_array); > - kfree(ssd130x->buffer); > -} > - > /* > * Helper to write data (SSD130X_DATA) to the device. > */ > @@ -741,10 +738,6 @@ static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder, > if (ret) > goto power_off; > > - ret = ssd130x_buf_alloc(ssd130x); > - if (ret) > - goto power_off; > - > ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON); > > backlight_enable(ssd130x->bl_dev); > @@ -766,8 +759,6 @@ static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder, > > ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF); > > - ssd130x_buf_free(ssd130x); > - > ssd130x_power_off(ssd130x); > } > > @@ -811,8 +802,21 @@ static const struct drm_connector_funcs ssd130x_connector_funcs = { > .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > }; > > +static struct drm_framebuffer *ssd130x_fb_create(struct drm_device *dev, struct drm_file *file, > + const struct drm_mode_fb_cmd2 *mode_cmd) > +{ > + struct ssd130x_device *ssd130x = drm_to_ssd130x(dev); > + int ret; > + > + ret = ssd130x_buf_alloc(ssd130x, mode_cmd->pixel_format); > + if (ret) > + return ERR_PTR(ret); > + > + return drm_gem_fb_create_with_dirty(dev, file, mode_cmd); > +} > + > static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { > - .fb_create = drm_gem_fb_create_with_dirty, > + .fb_create = ssd130x_fb_create, > .atomic_check = drm_atomic_helper_check, > .atomic_commit = drm_atomic_helper_commit, > }; Gr{oetje,eeting}s, Geert
Geert Uytterhoeven <geert@linux-m68k.org> writes: Hello Geert, Thanks for your review! > Hi Javier, [...] >> - ssd130x->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL); >> - if (!ssd130x->buffer) >> - return -ENOMEM; >> + ssd130x->buffer = devm_kcalloc(ssd130x->dev, pitch, ssd130x->height, >> + GFP_KERNEL); > > This should check if the buffer was allocated before. > Else an application creating two or more frame buffers will keep > on allocating new buffers. The same is true for fbdev emulation vs. > a userspace application. > Yes, you are correct. >> + if (!ssd130x->buffer) >> + return -ENOMEM; >> + } >> >> - ssd130x->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL); >> - if (!ssd130x->data_array) { >> - kfree(ssd130x->buffer); >> + ssd130x->data_array = devm_kcalloc(ssd130x->dev, ssd130x->width, pages, >> + GFP_KERNEL); > > Same here. > >> + if (!ssd130x->data_array) >> return -ENOMEM; >> - } > > And you still need the data_array buffer for clearing the screen, > which is not tied to the creation of a frame buffer, I think? > It is indeed. I forgot about that. So what I would do for now is just to allocate the two unconditionally and then we can optimize as a follow-up. But also, this v2 is not correct because as I mentioned the device and drm_device lifecycles are not the same and the kernel crashes on poweroff: [ 568.351783] ssd130x_update_rect.isra.0+0xe0/0x308 [ 568.356881] ssd130x_primary_plane_helper_atomic_disable+0x54/0x78 [ 568.363475] drm_atomic_helper_commit_planes+0x1ec/0x288 [ 568.369128] drm_atomic_helper_commit_tail+0x5c/0xb0 [ 568.374422] commit_tail+0x168/0x1a0 [ 568.378240] drm_atomic_helper_commit+0x1c8/0x1e8 [ 568.383265] drm_atomic_commit+0xa0/0xc8 [ 568.387439] drm_atomic_helper_disable_all+0x204/0x220 [ 568.392914] drm_atomic_helper_shutdown+0x98/0x138 [ 568.398033] ssd130x_shutdown+0x18/0x30 [ 568.402117] ssd130x_i2c_shutdown+0x1c/0x30 [ 568.406558] i2c_device_shutdown+0x48/0x78 [ 568.410913] device_shutdown+0x130/0x238 [ 568.415087] kernel_restart+0x48/0xd0 [ 568.418996] __do_sys_reboot+0x14c/0x230 [ 568.423167] __arm64_sys_reboot+0x2c/0x40 [ 568.427426] invoke_syscall+0x78/0x100 [ 568.431420] el0_svc_common.constprop.0+0x68/0x130 [ 568.436531] do_el0_svc+0x34/0x50 [ 568.440080] el0_svc+0x48/0x150 [ 568.443455] el0t_64_sync_handler+0x114/0x120 [ 568.448072] el0t_64_sync+0x194/0x198 [ 568.451985] Code: 12000949 52800001 52800002 0b830f63 (38634b20) [ 568.458502] ---[ end trace 0000000000000000 ]--- Sima also suggested to make the allocation in the plane .prepare_fb, I think that the better place is .begin_fb_access for allocation and the .end_fb_access for free. Updated patch below and this is the one that I'm more comfortable now as a solution: From b4d078dd65a04ab61dd9264e982b37321f7a75d9 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas <javierm@redhat.com> Date: Mon, 17 Jul 2023 12:30:35 +0200 Subject: [PATCH v3] drm/ssd130x: Fix an oops when attempting to update a disabled plane Geert reports that the following NULL pointer dereference happens for him after commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each plane update"): [drm] Initialized ssd130x 1.0.0 20220131 for 0-003c on minor 0 ssd130x-i2c 0-003c: [drm] surface width(128), height(32), bpp(1) and format(R1 little-endian (0x20203152)) Unable to handle kernel NULL pointer dereference at virtual address 00000000 Oops [#1] CPU: 0 PID: 1 Comm: swapper Not tainted 6.5.0-rc1-orangecrab-02219-g0a529a1e4bf4 #565 epc : ssd130x_update_rect.isra.0+0x13c/0x340 ra : ssd130x_update_rect.isra.0+0x2bc/0x340 ... status: 00000120 badaddr: 00000000 cause: 0000000f [<c0303d90>] ssd130x_update_rect.isra.0+0x13c/0x340 [<c0304200>] ssd130x_primary_plane_helper_atomic_update+0x26c/0x284 [<c02f8d54>] drm_atomic_helper_commit_planes+0xfc/0x27c [<c02f9314>] drm_atomic_helper_commit_tail+0x5c/0xb4 [<c02f94fc>] commit_tail+0x190/0x1b8 [<c02f99fc>] drm_atomic_helper_commit+0x194/0x1c0 [<c02c5d00>] drm_atomic_commit+0xa4/0xe4 [<c02cce40>] drm_client_modeset_commit_atomic+0x244/0x278 [<c02ccef0>] drm_client_modeset_commit_locked+0x7c/0x1bc [<c02cd064>] drm_client_modeset_commit+0x34/0x64 [<c0301a78>] __drm_fb_helper_restore_fbdev_mode_unlocked+0xc4/0xe8 [<c0303424>] drm_fb_helper_set_par+0x38/0x58 [<c027c410>] fbcon_init+0x294/0x534 ... The problem is that fbcon calls fbcon_init() which triggers a DRM modeset and this leads to drm_atomic_helper_commit_planes() attempting to commit the atomic state for all planes, even the ones whose CRTC is not enabled. Since the primary plane buffer is allocated in the encoder .atomic_enable callback, this happens after that initial modeset commit and leads to the mentioned NULL pointer dereference. Fix this by allocating the buffers in the struct drm_plane_helper_funcs .begin_fb_access callback and free them in to .end_fb_access callback, instead of doing it when the encoder is enabled. Fixes: commit 49d7d581ceaf ("drm/ssd130x: Don't allocate buffers on each plane update") Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> --- Changes in v3: - Move the buffers allocation to the plane helper funcs .begin_fb_access and the free to .end_fb_access callbacks. - Always allocate intermediate buffer because is use in ssd130x_clear_screen(). - Don't allocate the buffers as device managed resources. Changes in v2: - Move the buffers allocation to .fb_create instead of preventing atomic updates for disable planes. - Don't allocate the intermediate buffer when using the native R1 format. - Allocate buffers as device managed resources. drivers/gpu/drm/solomon/ssd130x.c | 33 ++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index b8c90d507a35..549d78e9985d 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -634,6 +634,30 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_m return ret; } +static int ssd130x_primary_plane_helper_begin_fb_access(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_device *drm = plane->dev; + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); + int ret = ssd130x_buf_alloc(ssd130x); + + if (ret) + return ret; + + return drm_gem_begin_shadow_fb_access(plane, state); +} + +static void ssd130x_primary_plane_helper_end_fb_access(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_device *drm = plane->dev; + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); + + ssd130x_buf_free(ssd130x); + + return drm_gem_end_shadow_fb_access(plane, state); +} + static void ssd130x_primary_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -678,7 +702,8 @@ static void ssd130x_primary_plane_helper_atomic_disable(struct drm_plane *plane, } static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs = { - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .begin_fb_access = ssd130x_primary_plane_helper_begin_fb_access, + .end_fb_access = ssd130x_primary_plane_helper_end_fb_access, .atomic_check = drm_plane_helper_atomic_check, .atomic_update = ssd130x_primary_plane_helper_atomic_update, .atomic_disable = ssd130x_primary_plane_helper_atomic_disable, @@ -741,10 +766,6 @@ static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder, if (ret) goto power_off; - ret = ssd130x_buf_alloc(ssd130x); - if (ret) - goto power_off; - ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON); backlight_enable(ssd130x->bl_dev); @@ -766,8 +787,6 @@ static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder, ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF); - ssd130x_buf_free(ssd130x); - ssd130x_power_off(ssd130x); }
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index afb08a8aa9fc..12820d16b15b 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -795,6 +795,10 @@ static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; +static const struct drm_mode_config_helper_funcs ssd130x_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + static const uint32_t ssd130x_formats[] = { DRM_FORMAT_XRGB8888, }; @@ -923,6 +927,7 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x) drm->mode_config.max_height = max_height; drm->mode_config.preferred_depth = 24; drm->mode_config.funcs = &ssd130x_mode_config_funcs; + drm->mode_config.helper_private = &ssd130x_mode_config_helpers; /* Primary plane */