* [PATCH] staging: sm750fb: add const to g_fbmode array
From: Chaitanya Sabnis @ 2026-05-06 3:56 UTC (permalink / raw)
To: sudipm.mukherjee, teddy.wang, gregkh
Cc: linux-fbdev, linux-staging, linux-kernel, Chaitanya Sabnis
Fix a checkpatch warning by changing the `g_fbmode` array of
pointers to be `static const char * const`. This ensures that both
the strings and the array of pointers are placed in read-only memory,
preventing unintended modifications and improving security.
Signed-off-by: Chaitanya Sabnis <chaitanya.msabnis@gmail.com>
---
drivers/staging/sm750fb/sm750.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index dec1f6b88a7d..08d4979c6755 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -33,7 +33,7 @@
static int g_hwcursor = 1;
static int g_noaccel;
static int g_nomtrr;
-static const char *g_fbmode[] = {NULL, NULL};
+static const char * const g_fbmode[] = {NULL, NULL};
static const char *g_def_fbmode = "1024x768-32@60";
static char *g_settings;
static int g_dualview;
--
2.43.0
^ permalink raw reply related
* Re: [GIT PULL] fbdev fixes and updates for v7.1-rc3
From: pr-tracker-bot @ 2026-05-05 23:55 UTC (permalink / raw)
To: Helge Deller; +Cc: Linus Torvalds, linux-kernel, linux-fbdev, dri-devel
In-Reply-To: <afpYkddCLxIsicwb@carbonx1>
The pull request you sent on Tue, 5 May 2026 22:52:33 +0200:
> http://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev.git tags/fbdev-for-7.1-rc3
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/13ad98eaabef611f042d49a9077be060ad03284d
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* [GIT PULL] fbdev fixes and updates for v7.1-rc3
From: Helge Deller @ 2026-05-05 20:52 UTC (permalink / raw)
To: Linus Torvalds, linux-kernel, linux-fbdev, dri-devel
Hi Linus,
please pull four small patches for fbdev, of which two are important:
One fixes the bitmap font generation and the other prevents a possible
use-after-free in udlfb.
Thanks!
Helge
----------------------------------------------------------------
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
http://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev.git tags/fbdev-for-7.1-rc3
for you to fetch changes up to 8de779dc40d35d39fa07387b6f921eb11df0f511:
fbdev: udlfb: add vm_ops to dlfb_ops_mmap to prevent use-after-free (2026-05-04 10:35:55 +0200)
----------------------------------------------------------------
fbdev fixes & cleanups for 7.1-rc3:
- Fix rotating fonts by 180 degrees [Thomas Zimmermann]
- Drop duplicate include of linux/module.h in fb_defio [Chen Ni]
- Add vm_ops in udlfb to prevent use-after-free [Rajat Gupta]
- ipu-v3: clean up kernel-doc warnings [Randy Dunlap]
----------------------------------------------------------------
Chen Ni (1):
fbdev: defio: Remove duplicate include of linux/module.h
Rajat Gupta (1):
fbdev: udlfb: add vm_ops to dlfb_ops_mmap to prevent use-after-free
Randy Dunlap (1):
fbdev: ipu-v3: clean up kernel-doc warnings
Thomas Zimmermann (1):
lib/fonts: Fix bit position when rotating by 180 degrees
drivers/video/fbdev/core/fb_defio.c | 1 -
drivers/video/fbdev/udlfb.c | 31 ++++++++++++++++++++++++++++++-
include/video/imx-ipu-image-convert.h | 16 +++++++++++-----
include/video/udlfb.h | 1 +
lib/fonts/font_rotate.c | 2 +-
5 files changed, 43 insertions(+), 8 deletions(-)
^ permalink raw reply
* Re: Plan to fix nvidia_wmi_ec backlight issues, help wanted
From: Daniel Dadap @ 2026-05-05 15:51 UTC (permalink / raw)
To: Hans de Goede
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Helge Deller,
Rafael J. Wysocki, dri-devel@lists.freedesktop.org, linux-fbdev,
linux-acpi, platform-driver-x86@vger.kernel.org
In-Reply-To: <f10c039b-e9ef-4aae-8291-8f1e71074586@oss.qualcomm.com>
Thanks, Hans -
On Tue, May 05, 2026 at 11:52:55AM +0200, Hans de Goede wrote:
> <resend adding pdx86 list, sorry>
>
> Hi All,
>
> A while ago Nvidia introduced a new Nvidia specific firmware method
> for controlling the backlight on laptops with runtime switchable
> Nvidia hybrid graphics.
>
> This is supported through the nvidia-wmi-ec-backlight driver, but has
> never worked 100%.
>
> The new WMI firmware interface has a method to ask the firmware if
> the backlight is controlled though the EC and this the new WMI interface
> should be used; or if it is directly controlled by the GPU and the GPU
> driver's native backlight support should be used.
>
> There are several bugs in the implementation of this on various laptops:
>
> 1. The method to get the backlight control source sometimes does not
> report the correct value.
>
> 2. On some laptop models which method (native-gpu vs nvidia-wmi) works
> changes at runtime, e.g. when plugging in a charger.
>
> Known affected laptop models/bug reports about this:
> - Acer Predator PH315-55: needs acpi_backlight=native
> - Dell G15 5515 with RTX 3050: *needs* acpi_backlight=native
> - Dell G15 5515 with RTX 3060: *breaks* with acpi_backlight=native
> - Lenovo Legion Slim 7 2021
> - https://bugzilla.kernel.org/show_bug.cgi?id=217026
> - https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/work_items/784
> - https://gitlab.freedesktop.org/drm/amd/-/issues/4512
> - https://bugzilla.suse.com/show_bug.cgi?id=1217750
I believe there were also a couple of cases where different builds of the
same notebook (as far as DMI quirks tables could detect) would be driven
by different methods, exacerbating problem (1) because adding a quirtk to
fix one notebook would break other versions of the "same" notebook. (Oh,
maybe that's the two G15 5515 bugs linked above.)
>
> It turns out that under Windows both a WMI backlight driver and a GPU
> native backlight driver get installed and Windows simply always calls
> both when the backlight changes working around these kind of firmware
> bugs.
>
Yes, it's frustrating that so many notebooks have implementations of this
interface that have bugs that get masked by Windows always using both of
the interfaces. I wonder whether Windows behaves this way because bugs of
this type are so prevalent, or if the bugs are prevalent because Windows
always behaved that way so nobody noticed.
> When this first came up, about 2 years ago, I came up with the below
> plan to fix this. Nvidia was supposed to work in implementing this,
> but so far an implementation of this plan has not happened.
>
> Note I do not have time to work on this myself. I'm posting this here
> in the hope that either Nvidia will pick this up after all; or that
> someone else can make this happen.
>
> I'm more then happy to help answering any questions which may come up
> while implementing this; and to review the resulting patches.
>
Sorry about that - I totally forgot about this proposal. I'm still happy
to help here, although if someone else is especially eager to implement
these changes, I'm also happy to answer questions, test, review, and help
in other ways as bandwidth allows. I have some clarifying questions about
the plan - it's been some time since we talked about this, so I apologize
if I've already asked these questions and you've already answered them.
>
> The plan
> ========
>
> 1. Modify the core backlight code to allow registering a backlight-device
> for in kernel use only, disabling the registering of a class device under
> /sys/class/backlight .
>
> 2. Add a new backlight-aggregate.c backlight driver, which exports a
> devm_backlight_aggregate_register_or_add() function. Drivers can call
> this passing in an existing backlight-device (with its sysfs interface
> disabled). This either creates a singleton /sys/class/backlight/aggregate
> device, or adds the passed in backlight to the existing singleton instance
> if it already exists.
>
> This new driver always exposes a range of 0-255 to userspace. This acts
> as a proxie for any backlight-devices registered to be part of
> the aggregate, forwarding any brightness set calls to all backlights,
> scaled to their actual range. For reads this will report the last set
> brightness value (starting with the value of the first registered
> backlight-device).
>
> 3. Add a new nvidia_wmi_ec_and_native type to drivers/acpi/video_detect.c
> for both DMI quirks and commandline handling.
>
So by default both the native and GPU drivers would register a "normal"
backlight handler, and if the "both" quirk is set (or if requested via
kernel command line) they'll register with the aggregate device?
Would it make sense to make this more generic, with a parameter which
can be set via command line or quirks for which backlight devices to
aggregate? And then maybe instead of a new "nvidia_wmi_ec_and_native"
backlight type in video_detect.c, it could be called "aggregate" or
something else along those lines, used in combination with the list of
backlight types to aggregate.
Have we seen systems where both the iGPU and dGPU register their own
native handlers? I vaguely recall seeing such a system, but I do not
keep careful notes about this sort of thing, so I'm not certain. If
there are indeed such systems, then "native" might not be specific
enough, and it may be necessary to explicitly name backlight drivers
individually in order to aggregate them.
>
> 4. Modify acpi_video_backlight_use_native() to also return true if
> the __acpi_video_get_backlight_type() call there returns the new
> acpi_backlight_nvidia_wmi_ec_and_native.
> 5. Add a new devm_backlight_device_register_native() helper which
> calls __acpi_video_get_backlight_type(true, NULL) and if that returns
> the new nvidia_wmi_ec_and_native type then registers the passed in
> backlight with the flag to not register a sysfs class interface and
> then on success calls the new devm_backlight_aggregate_register_or_add()
> with the just registered backlight device; and if the returned type
> instead is acpi_backlight_native register the passed in backlight
> normally through devm_backlight_device_register()
>
> 5. Modify the i915 and amdgpu drivers to use the new
> devm_backlight_device_register_native() helper. Since this new helper
> checks the backlight-type itself, drop acpi_video_backlight_use_native()
> checks *if* the registration is the only thing guarded by that check.
>
> 6. drivers/platform/x86/nvidia-wmi-ec-backlight.c to not return
> early from probe when acpi_video_get_backlight_type() returns
> the new nvidia_wmi_ec_and_native type and for that type make it
> registers its backlight with the flag to not register a sysfs class
> interface and then on success calls the new
> devm_backlight_aggregate_register_or_add().
>
There still isn't anything tying a particular sysfs backlight interface
node to a specific display panel, right? Is there a plan to eventually
support this? How would this interact with that plan? I'm just a little
worried about designing ourselves into a corner that makes things harder
to untangle individual devices later if the need arises.
> Regards,
>
> Hans
>
^ permalink raw reply
* [PATCH 6.6.y] firmware: google: framebuffer: Do not unregister platform device
From: Sasha Levin @ 2026-05-05 12:01 UTC (permalink / raw)
To: stable
Cc: Thomas Zimmermann, Tzung-Bi Shih, Julius Werner,
Javier Martinez Canillas, Hans de Goede, linux-fbdev, Sasha Levin
In-Reply-To: <2026050336-tainted-hundredth-63d4@gregkh>
From: Thomas Zimmermann <tzimmermann@suse.de>
[ Upstream commit 5cd28bd28c8ce426b56ce4230dbd17537181d5ad ]
The native driver takes over the framebuffer aperture by removing the
system- framebuffer platform device. Afterwards the pointer in drvdata
is dangling. Remove the entire logic around drvdata and let the kernel's
aperture helpers handle this. The platform device depends on the native
hardware device instead of the coreboot device anyway.
When commit 851b4c14532d ("firmware: coreboot: Add coreboot framebuffer
driver") added the coreboot framebuffer code, the kernel did not support
device-based aperture management. Instead native driviers only removed
the conflicting fbdev device. At that point, unregistering the framebuffer
device most likely worked correctly. It was definitely broken after
commit d9702b2a2171 ("fbdev/simplefb: Do not use struct
fb_info.apertures"). So take this commit for the Fixes tag. Earlier
releases might work depending on the native hardware driver.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: d9702b2a2171 ("fbdev/simplefb: Do not use struct fb_info.apertures")
Acked-by: Tzung-Bi Shih <tzungbi@kernel.org>
Acked-by: Julius Werner <jwerner@chromium.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Hans de Goede <hansg@kernel.org>
Cc: linux-fbdev@vger.kernel.org
Cc: <stable@vger.kernel.org> # v6.3+
Link: https://patch.msgid.link/20260217155836.96267-2-tzimmermann@suse.de
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/firmware/google/framebuffer-coreboot.c | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c
index c323a818805cc..a83ef081efe02 100644
--- a/drivers/firmware/google/framebuffer-coreboot.c
+++ b/drivers/firmware/google/framebuffer-coreboot.c
@@ -64,22 +64,12 @@ static int framebuffer_probe(struct coreboot_device *dev)
sizeof(pdata));
if (IS_ERR(pdev))
pr_warn("coreboot: could not register framebuffer\n");
- else
- dev_set_drvdata(&dev->dev, pdev);
return PTR_ERR_OR_ZERO(pdev);
}
-static void framebuffer_remove(struct coreboot_device *dev)
-{
- struct platform_device *pdev = dev_get_drvdata(&dev->dev);
-
- platform_device_unregister(pdev);
-}
-
static struct coreboot_driver framebuffer_driver = {
.probe = framebuffer_probe,
- .remove = framebuffer_remove,
.drv = {
.name = "framebuffer",
},
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v1] fbdev/offb: fix PCI device reference leak on probe failure
From: 최유호 @ 2026-05-05 11:22 UTC (permalink / raw)
To: Helge Deller
Cc: Jason, linux-fbdev, dri-devel, linux-kernel, mhun512, ae878000,
tmk5904
In-Reply-To: <2bf485cd-0c6d-4f0e-99d6-7b592b949a1f@gmx.de>
Dear Helge,
Thanks for letting me know that the patches were merged.
Best regards,
Yuho Choi
On Tue, 5 May 2026 at 06:10, Helge Deller <deller@gmx.de> wrote:
>
> On 5/5/26 03:39, dbgh9129@gmail.com wrote:
> > I am sending a gentle ping on this patch. Please let me know if you
> > have any feedback or if any changes are required.
>
> Both of your patches (offb and savagefb) were merged upstream 13 days ago:
> https://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev.git/log/?h=fbdev-7.1-2
>
> Usually I send "patch applied" messages, but in this case I probably forgot.
>
> Helge
^ permalink raw reply
* Re: [PATCH v1] fbdev/offb: fix PCI device reference leak on probe failure
From: Helge Deller @ 2026-05-05 10:10 UTC (permalink / raw)
To: dbgh9129, Jason, linux-fbdev
Cc: dri-devel, linux-kernel, mhun512, ae878000, tmk5904
In-Reply-To: <CACrCO_VZg_2PXG011qQ7NWo3iz-u2HE6DdHZWQdQ_UnRk4A0Eg@mail.gmail.com>
On 5/5/26 03:39, dbgh9129@gmail.com wrote:
> I am sending a gentle ping on this patch. Please let me know if you
> have any feedback or if any changes are required.
Both of your patches (offb and savagefb) were merged upstream 13 days ago:
https://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev.git/log/?h=fbdev-7.1-2
Usually I send "patch applied" messages, but in this case I probably forgot.
Helge
^ permalink raw reply
* Plan to fix nvidia_wmi_ec backlight issues, help wanted
From: Hans de Goede @ 2026-05-05 9:52 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Helge Deller,
Daniel Dadap, Rafael J. Wysocki
Cc: dri-devel@lists.freedesktop.org, linux-fbdev, linux-acpi,
platform-driver-x86@vger.kernel.org
<resend adding pdx86 list, sorry>
Hi All,
A while ago Nvidia introduced a new Nvidia specific firmware method
for controlling the backlight on laptops with runtime switchable
Nvidia hybrid graphics.
This is supported through the nvidia-wmi-ec-backlight driver, but has
never worked 100%.
The new WMI firmware interface has a method to ask the firmware if
the backlight is controlled though the EC and this the new WMI interface
should be used; or if it is directly controlled by the GPU and the GPU
driver's native backlight support should be used.
There are several bugs in the implementation of this on various laptops:
1. The method to get the backlight control source sometimes does not
report the correct value.
2. On some laptop models which method (native-gpu vs nvidia-wmi) works
changes at runtime, e.g. when plugging in a charger.
Known affected laptop models/bug reports about this:
- Acer Predator PH315-55: needs acpi_backlight=native
- Dell G15 5515 with RTX 3050: *needs* acpi_backlight=native
- Dell G15 5515 with RTX 3060: *breaks* with acpi_backlight=native
- Lenovo Legion Slim 7 2021
- https://bugzilla.kernel.org/show_bug.cgi?id=217026
- https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/work_items/784
- https://gitlab.freedesktop.org/drm/amd/-/issues/4512
- https://bugzilla.suse.com/show_bug.cgi?id=1217750
It turns out that under Windows both a WMI backlight driver and a GPU
native backlight driver get installed and Windows simply always calls
both when the backlight changes working around these kind of firmware
bugs.
When this first came up, about 2 years ago, I came up with the below
plan to fix this. Nvidia was supposed to work in implementing this,
but so far an implementation of this plan has not happened.
Note I do not have time to work on this myself. I'm posting this here
in the hope that either Nvidia will pick this up after all; or that
someone else can make this happen.
I'm more then happy to help answering any questions which may come up
while implementing this; and to review the resulting patches.
The plan
========
1. Modify the core backlight code to allow registering a backlight-device
for in kernel use only, disabling the registering of a class device under
/sys/class/backlight .
2. Add a new backlight-aggregate.c backlight driver, which exports a
devm_backlight_aggregate_register_or_add() function. Drivers can call
this passing in an existing backlight-device (with its sysfs interface
disabled). This either creates a singleton /sys/class/backlight/aggregate
device, or adds the passed in backlight to the existing singleton instance
if it already exists.
This new driver always exposes a range of 0-255 to userspace. This acts
as a proxie for any backlight-devices registered to be part of
the aggregate, forwarding any brightness set calls to all backlights,
scaled to their actual range. For reads this will report the last set
brightness value (starting with the value of the first registered
backlight-device).
3. Add a new nvidia_wmi_ec_and_native type to drivers/acpi/video_detect.c
for both DMI quirks and commandline handling.
4. Modify acpi_video_backlight_use_native() to also return true if
the __acpi_video_get_backlight_type() call there returns the new
acpi_backlight_nvidia_wmi_ec_and_native.
5. Add a new devm_backlight_device_register_native() helper which
calls __acpi_video_get_backlight_type(true, NULL) and if that returns
the new nvidia_wmi_ec_and_native type then registers the passed in
backlight with the flag to not register a sysfs class interface and
then on success calls the new devm_backlight_aggregate_register_or_add()
with the just registered backlight device; and if the returned type
instead is acpi_backlight_native register the passed in backlight
normally through devm_backlight_device_register()
5. Modify the i915 and amdgpu drivers to use the new
devm_backlight_device_register_native() helper. Since this new helper
checks the backlight-type itself, drop acpi_video_backlight_use_native()
checks *if* the registration is the only thing guarded by that check.
6. drivers/platform/x86/nvidia-wmi-ec-backlight.c to not return
early from probe when acpi_video_get_backlight_type() returns
the new nvidia_wmi_ec_and_native type and for that type make it
registers its backlight with the flag to not register a sysfs class
interface and then on success calls the new
devm_backlight_aggregate_register_or_add().
Regards,
Hans
^ permalink raw reply
* Plan to fix nvidia_wmi_ec backlight issues, help wanted
From: Hans de Goede @ 2026-05-05 9:50 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Helge Deller,
Daniel Dadap, Rafael J. Wysocki
Cc: dri-devel@lists.freedesktop.org, linux-fbdev, linux-acpi
Hi All,
A while ago Nvidia introduced a new Nvidia specific firmware method
for controlling the backlight on laptops with runtime switchable
Nvidia hybrid graphics.
This is supported through the nvidia-wmi-ec-backlight driver, but has
never worked 100%.
The new WMI firmware interface has a method to ask the firmware if
the backlight is controlled though the EC and this the new WMI interface
should be used; or if it is directly controlled by the GPU and the GPU
driver's native backlight support should be used.
There are several bugs in the implementation of this on various laptops:
1. The method to get the backlight control source sometimes does not
report the correct value.
2. On some laptop models which method (native-gpu vs nvidia-wmi) works
changes at runtime, e.g. when plugging in a charger.
Known affected laptop models/bug reports about this:
- Acer Predator PH315-55: needs acpi_backlight=native
- Dell G15 5515 with RTX 3050: *needs* acpi_backlight=native
- Dell G15 5515 with RTX 3060: *breaks* with acpi_backlight=native
- Lenovo Legion Slim 7 2021
- https://bugzilla.kernel.org/show_bug.cgi?id=217026
- https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/work_items/784
- https://gitlab.freedesktop.org/drm/amd/-/issues/4512
- https://bugzilla.suse.com/show_bug.cgi?id=1217750
It turns out that under Windows both a WMI backlight driver and a GPU
native backlight driver get installed and Windows simply always calls
both when the backlight changes working around these kind of firmware
bugs.
When this first came up, about 2 years ago, I came up with the below
plan to fix this. Nvidia was supposed to work in implementing this,
but so far an implementation of this plan has not happened.
Note I do not have time to work on this myself. I'm posting this here
in the hope that either Nvidia will pick this up after all; or that
someone else can make this happen.
I'm more then happy to help answering any questions which may come up
while implementing this; and to review the resulting patches.
The plan
========
1. Modify the core backlight code to allow registering a backlight-device
for in kernel use only, disabling the registering of a class device under
/sys/class/backlight .
2. Add a new backlight-aggregate.c backlight driver, which exports a
devm_backlight_aggregate_register_or_add() function. Drivers can call
this passing in an existing backlight-device (with its sysfs interface
disabled). This either creates a singleton /sys/class/backlight/aggregate
device, or adds the passed in backlight to the existing singleton instance
if it already exists.
This new driver always exposes a range of 0-255 to userspace. This acts
as a proxie for any backlight-devices registered to be part of
the aggregate, forwarding any brightness set calls to all backlights,
scaled to their actual range. For reads this will report the last set
brightness value (starting with the value of the first registered
backlight-device).
3. Add a new nvidia_wmi_ec_and_native type to drivers/acpi/video_detect.c
for both DMI quirks and commandline handling.
4. Modify acpi_video_backlight_use_native() to also return true if
the __acpi_video_get_backlight_type() call there returns the new
acpi_backlight_nvidia_wmi_ec_and_native.
5. Add a new devm_backlight_device_register_native() helper which
calls __acpi_video_get_backlight_type(true, NULL) and if that returns
the new nvidia_wmi_ec_and_native type then registers the passed in
backlight with the flag to not register a sysfs class interface and
then on success calls the new devm_backlight_aggregate_register_or_add()
with the just registered backlight device; and if the returned type
instead is acpi_backlight_native register the passed in backlight
normally through devm_backlight_device_register()
5. Modify the i915 and amdgpu drivers to use the new
devm_backlight_device_register_native() helper. Since this new helper
checks the backlight-type itself, drop acpi_video_backlight_use_native()
checks *if* the registration is the only thing guarded by that check.
6. drivers/platform/x86/nvidia-wmi-ec-backlight.c to not return
early from probe when acpi_video_get_backlight_type() returns
the new nvidia_wmi_ec_and_native type and for that type make it
registers its backlight with the flag to not register a sysfs class
interface and then on success calls the new
devm_backlight_aggregate_register_or_add().
Regards,
Hans
^ permalink raw reply
* [PATCH 5.15.y] fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
From: Sasha Levin @ 2026-05-05 9:49 UTC (permalink / raw)
To: stable; +Cc: Thomas Zimmermann, Helge Deller, linux-fbdev, dri-devel,
Sasha Levin
In-Reply-To: <2026050117-upper-unhelpful-d8a9@gregkh>
From: Thomas Zimmermann <tzimmermann@suse.de>
[ Upstream commit 9ded47ad003f09a94b6a710b5c47f4aa5ceb7429 ]
Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an
instance as part of initializing deferred I/O and remove it only after
the final mapping has been closed. If the fb_info and the contained
deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info
to invalidate the mapping. Any access will then result in a SIGBUS
signal.
Fixes a long-standing problem, where a device hot-unplug happens while
user space still has an active mapping of the graphics memory. The hot-
unplug frees the instance of struct fb_info. Accessing the memory will
operate on undefined state.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support")
Cc: Helge Deller <deller@gmx.de>
Cc: linux-fbdev@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: stable@vger.kernel.org # v2.6.22+
Signed-off-by: Helge Deller <deller@gmx.de>
[ replaced `kzalloc_obj()` with `kzalloc(sizeof(*fbdefio_state), GFP_KERNEL)` ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/video/fbdev/core/fb_defio.c | 152 ++++++++++++++++++++++++----
include/linux/fb.h | 4 +-
2 files changed, 138 insertions(+), 18 deletions(-)
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index c2a0a936d5fb6..e348f4c15d81a 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -23,6 +23,75 @@
#include <linux/rmap.h>
#include <linux/pagemap.h>
+/*
+ * struct fb_deferred_io_state
+ */
+
+struct fb_deferred_io_state {
+ struct kref ref;
+
+ struct mutex lock; /* mutex that protects the pageref list */
+ /* fields protected by lock */
+ struct fb_info *info;
+};
+
+static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void)
+{
+ struct fb_deferred_io_state *fbdefio_state;
+
+ fbdefio_state = kzalloc(sizeof(*fbdefio_state), GFP_KERNEL);
+ if (!fbdefio_state)
+ return NULL;
+
+ kref_init(&fbdefio_state->ref);
+ mutex_init(&fbdefio_state->lock);
+
+ return fbdefio_state;
+}
+
+static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state)
+{
+ mutex_destroy(&fbdefio_state->lock);
+
+ kfree(fbdefio_state);
+}
+
+static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_get(&fbdefio_state->ref);
+}
+
+static void __fb_deferred_io_state_release(struct kref *ref)
+{
+ struct fb_deferred_io_state *fbdefio_state =
+ container_of(ref, struct fb_deferred_io_state, ref);
+
+ fb_deferred_io_state_release(fbdefio_state);
+}
+
+static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release);
+}
+
+/*
+ * struct vm_operations_struct
+ */
+
+static void fb_deferred_io_vm_open(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_get(fbdefio_state);
+}
+
+static void fb_deferred_io_vm_close(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_put(fbdefio_state);
+}
+
static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
{
void *screen_base = (void __force *) info->screen_base;
@@ -93,17 +162,31 @@ static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
/* this is to find and return the vmalloc-ed fb pages */
static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
{
+ struct fb_info *info;
unsigned long offset;
struct page *page;
- struct fb_info *info = vmf->vma->vm_private_data;
+ vm_fault_t ret;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
+
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= info->fix.smem_len)
- return VM_FAULT_SIGBUS;
+ if (offset >= info->fix.smem_len) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
page = fb_deferred_io_page(info, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
+ if (!page) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
get_page(page);
@@ -115,8 +198,14 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
BUG_ON(!page->mapping);
page->index = vmf->pgoff; /* for page_mkclean() */
+ mutex_unlock(&fbdefio_state->lock);
+
vmf->page = page;
return 0;
+
+err_mutex_unlock:
+ mutex_unlock(&fbdefio_state->lock);
+ return ret;
}
int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
@@ -143,8 +232,9 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
{
struct page *page = vmf->page;
- struct fb_info *info = vmf->vma->vm_private_data;
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
+ struct fb_info *info;
+ struct fb_deferred_io *fbdefio;
struct fb_deferred_io_pageref *pageref;
unsigned long offset;
vm_fault_t ret;
@@ -160,7 +250,15 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
file_update_time(vmf->vma->vm_file);
/* protect against the workqueue changing the page list */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
+
+ fbdefio = info->fbdefio;
/* first write in this cycle, notify the driver */
if (fbdefio->first_io && list_empty(&fbdefio->pagereflist))
@@ -182,18 +280,20 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
*/
lock_page(pageref->page);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
/* come back after delay to process the deferred IO */
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
return VM_FAULT_LOCKED;
err_mutex_unlock:
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
return ret;
}
static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+ .open = fb_deferred_io_vm_open,
+ .close = fb_deferred_io_vm_close,
.fault = fb_deferred_io_fault,
.page_mkwrite = fb_deferred_io_mkwrite,
};
@@ -215,7 +315,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
if (!(info->flags & FBINFO_VIRTFB))
vma->vm_flags |= VM_IO;
- vma->vm_private_data = info;
+ vma->vm_private_data = info->fbdefio_state;
+
+ fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */
+
return 0;
}
@@ -225,9 +328,10 @@ static void fb_deferred_io_work(struct work_struct *work)
struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
struct fb_deferred_io_pageref *pageref, *next;
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
/* here we mkclean the pages, then do all deferred IO */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
struct page *cur = pageref->page;
lock_page(cur);
@@ -242,12 +346,13 @@ static void fb_deferred_io_work(struct work_struct *work)
list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
fb_deferred_io_pageref_put(pageref, info);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
}
int fb_deferred_io_init(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
struct fb_deferred_io_pageref *pagerefs;
unsigned long npagerefs, i;
int ret;
@@ -257,7 +362,11 @@ int fb_deferred_io_init(struct fb_info *info)
if (WARN_ON(!info->fix.smem_len))
return -EINVAL;
- mutex_init(&fbdefio->lock);
+ fbdefio_state = fb_deferred_io_state_alloc();
+ if (!fbdefio_state)
+ return -ENOMEM;
+ fbdefio_state->info = info;
+
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
INIT_LIST_HEAD(&fbdefio->pagereflist);
if (fbdefio->delay == 0) /* set a default of 1 s */
@@ -276,10 +385,12 @@ int fb_deferred_io_init(struct fb_info *info)
info->npagerefs = npagerefs;
info->pagerefs = pagerefs;
+ info->fbdefio_state = fbdefio_state;
+
return 0;
err:
- mutex_destroy(&fbdefio->lock);
+ fb_deferred_io_state_release(fbdefio_state);
return ret;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
@@ -320,11 +431,18 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release);
void fb_deferred_io_cleanup(struct fb_info *info)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
fb_deferred_io_lastclose(info);
+ info->fbdefio_state = NULL;
+
+ mutex_lock(&fbdefio_state->lock);
+ fbdefio_state->info = NULL;
+ mutex_unlock(&fbdefio_state->lock);
+
+ fb_deferred_io_state_put(fbdefio_state);
+
kvfree(info->pagerefs);
- mutex_destroy(&fbdefio->lock);
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index b79a833524efc..9b3c198b06826 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -213,12 +213,13 @@ struct fb_deferred_io {
unsigned long delay;
bool sort_pagereflist; /* sort pagelist by offset */
int open_count; /* number of opened files; protected by fb_info lock */
- struct mutex lock; /* mutex that protects the pageref list */
struct list_head pagereflist; /* list of pagerefs for touched pages */
/* callback */
void (*first_io)(struct fb_info *info);
void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
};
+
+struct fb_deferred_io_state;
#endif
/*
@@ -480,6 +481,7 @@ struct fb_info {
unsigned long npagerefs;
struct fb_deferred_io_pageref *pagerefs;
struct fb_deferred_io *fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
#endif
const struct fb_ops *fbops;
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2] staging: sm750fb: add const qualifier to string pointer arrays
From: kernel test robot @ 2026-05-05 8:11 UTC (permalink / raw)
To: Francisco Maestre, sudipm.mukherjee, teddy.wang, gregkh
Cc: llvm, oe-kbuild-all, linux-fbdev, linux-staging, linux-kernel,
Francisco Maestre
In-Reply-To: <20260503005744.68974-1-francisco@maestretorreblanca.com>
Hi Francisco,
kernel test robot noticed the following build errors:
[auto build test ERROR on staging/staging-testing]
url: https://github.com/intel-lab-lkp/linux/commits/Francisco-Maestre/staging-sm750fb-add-const-qualifier-to-string-pointer-arrays/20260503-215917
base: staging/staging-testing
patch link: https://lore.kernel.org/r/20260503005744.68974-1-francisco%40maestretorreblanca.com
patch subject: [PATCH v2] staging: sm750fb: add const qualifier to string pointer arrays
config: loongarch-allmodconfig (https://download.01.org/0day-ci/archive/20260505/202605051602.dWbzp24c-lkp@intel.com/config)
compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260505/202605051602.dWbzp24c-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202605051602.dWbzp24c-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/staging/sm750fb/sm750.c:785:19: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
785 | g_fbmode[index] = g_def_fbmode;
| ~~~~~~~~~~~~~~~ ^
drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
36 | static const char * const g_fbmode[] = {NULL, NULL};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/staging/sm750fb/sm750.c:787:20: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
787 | g_fbmode[index] = g_fbmode[0];
| ~~~~~~~~~~~~~~~ ^
drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
36 | static const char * const g_fbmode[] = {NULL, NULL};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/staging/sm750fb/sm750.c:896:17: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
896 | g_fbmode[0] = opt;
| ~~~~~~~~~~~ ^
drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
36 | static const char * const g_fbmode[] = {NULL, NULL};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/staging/sm750fb/sm750.c:900:17: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
900 | g_fbmode[1] = opt;
| ~~~~~~~~~~~ ^
drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
36 | static const char * const g_fbmode[] = {NULL, NULL};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
4 errors generated.
vim +785 drivers/staging/sm750fb/sm750.c
81dee67e215b23 Sudip Mukherjee 2015-03-03 719
81dee67e215b23 Sudip Mukherjee 2015-03-03 720 static int lynxfb_set_fbinfo(struct fb_info *info, int index)
81dee67e215b23 Sudip Mukherjee 2015-03-03 721 {
81dee67e215b23 Sudip Mukherjee 2015-03-03 722 int i;
81dee67e215b23 Sudip Mukherjee 2015-03-03 723 struct lynxfb_par *par;
e359b6a863e19f Mike Rapoport 2015-10-26 724 struct sm750_dev *sm750_dev;
81dee67e215b23 Sudip Mukherjee 2015-03-03 725 struct lynxfb_crtc *crtc;
81dee67e215b23 Sudip Mukherjee 2015-03-03 726 struct lynxfb_output *output;
81dee67e215b23 Sudip Mukherjee 2015-03-03 727 struct fb_var_screeninfo *var;
81dee67e215b23 Sudip Mukherjee 2015-03-03 728 struct fb_fix_screeninfo *fix;
81dee67e215b23 Sudip Mukherjee 2015-03-03 729
81dee67e215b23 Sudip Mukherjee 2015-03-03 730 const struct fb_videomode *pdb[] = {
81dee67e215b23 Sudip Mukherjee 2015-03-03 731 lynx750_ext, NULL, vesa_modes,
81dee67e215b23 Sudip Mukherjee 2015-03-03 732 };
81dee67e215b23 Sudip Mukherjee 2015-03-03 733 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
d0856045f0e9fc Hungyu Lin 2026-04-01 734 static const char * const fix_id[2] = {
81dee67e215b23 Sudip Mukherjee 2015-03-03 735 "sm750_fb1", "sm750_fb2",
81dee67e215b23 Sudip Mukherjee 2015-03-03 736 };
81dee67e215b23 Sudip Mukherjee 2015-03-03 737
81dee67e215b23 Sudip Mukherjee 2015-03-03 738 int ret, line_length;
81dee67e215b23 Sudip Mukherjee 2015-03-03 739
81dee67e215b23 Sudip Mukherjee 2015-03-03 740 ret = 0;
81dee67e215b23 Sudip Mukherjee 2015-03-03 741 par = (struct lynxfb_par *)info->par;
e359b6a863e19f Mike Rapoport 2015-10-26 742 sm750_dev = par->dev;
81dee67e215b23 Sudip Mukherjee 2015-03-03 743 crtc = &par->crtc;
81dee67e215b23 Sudip Mukherjee 2015-03-03 744 output = &par->output;
81dee67e215b23 Sudip Mukherjee 2015-03-03 745 var = &info->var;
81dee67e215b23 Sudip Mukherjee 2015-03-03 746 fix = &info->fix;
81dee67e215b23 Sudip Mukherjee 2015-03-03 747
81dee67e215b23 Sudip Mukherjee 2015-03-03 748 /* set index */
81dee67e215b23 Sudip Mukherjee 2015-03-03 749 par->index = index;
81dee67e215b23 Sudip Mukherjee 2015-03-03 750 output->channel = &crtc->channel;
81dee67e215b23 Sudip Mukherjee 2015-03-03 751 sm750fb_set_drv(par);
81dee67e215b23 Sudip Mukherjee 2015-03-03 752
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 753 /*
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 754 * set current cursor variable and proc pointer,
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 755 * must be set after crtc member initialized
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 756 */
fdc234d85210d9 Benjamin Philip 2021-07-28 757 crtc->cursor.offset = crtc->o_screen + crtc->vidmem_size - 1024;
e359b6a863e19f Mike Rapoport 2015-10-26 758 crtc->cursor.mmio = sm750_dev->pvReg +
e359b6a863e19f Mike Rapoport 2015-10-26 759 0x800f0 + (int)crtc->channel * 0x140;
81dee67e215b23 Sudip Mukherjee 2015-03-03 760
cd33da26036ea5 Christopher Carbone 2022-08-23 761 crtc->cursor.max_h = 64;
cd33da26036ea5 Christopher Carbone 2022-08-23 762 crtc->cursor.max_w = 64;
39f9137268ee3d Benjamin Philip 2021-07-26 763 crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8;
e359b6a863e19f Mike Rapoport 2015-10-26 764 crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;
81dee67e215b23 Sudip Mukherjee 2015-03-03 765
3de08a2d14ff8c Lorenzo Stoakes 2015-03-20 766 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
f7c8a046577e09 Thomas Zimmermann 2023-11-27 767 if (!g_hwcursor)
52d0744d751d8f Arnd Bergmann 2016-11-09 768 sm750_hw_cursor_disable(&crtc->cursor);
81dee67e215b23 Sudip Mukherjee 2015-03-03 769
81dee67e215b23 Sudip Mukherjee 2015-03-03 770 /* set info->fbops, must be set before fb_find_mode */
e359b6a863e19f Mike Rapoport 2015-10-26 771 if (!sm750_dev->accel_off) {
81dee67e215b23 Sudip Mukherjee 2015-03-03 772 /* use 2d acceleration */
f7c8a046577e09 Thomas Zimmermann 2023-11-27 773 if (!g_hwcursor)
f7c8a046577e09 Thomas Zimmermann 2023-11-27 774 info->fbops = &lynxfb_ops_accel;
f7c8a046577e09 Thomas Zimmermann 2023-11-27 775 else
f7c8a046577e09 Thomas Zimmermann 2023-11-27 776 info->fbops = &lynxfb_ops_accel_with_cursor;
f7c8a046577e09 Thomas Zimmermann 2023-11-27 777 } else {
f7c8a046577e09 Thomas Zimmermann 2023-11-27 778 if (!g_hwcursor)
81dee67e215b23 Sudip Mukherjee 2015-03-03 779 info->fbops = &lynxfb_ops;
f7c8a046577e09 Thomas Zimmermann 2023-11-27 780 else
f7c8a046577e09 Thomas Zimmermann 2023-11-27 781 info->fbops = &lynxfb_ops_with_cursor;
f7c8a046577e09 Thomas Zimmermann 2023-11-27 782 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 783
81dee67e215b23 Sudip Mukherjee 2015-03-03 784 if (!g_fbmode[index]) {
81dee67e215b23 Sudip Mukherjee 2015-03-03 @785 g_fbmode[index] = g_def_fbmode;
81dee67e215b23 Sudip Mukherjee 2015-03-03 786 if (index)
81dee67e215b23 Sudip Mukherjee 2015-03-03 787 g_fbmode[index] = g_fbmode[0];
81dee67e215b23 Sudip Mukherjee 2015-03-03 788 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 789
81dee67e215b23 Sudip Mukherjee 2015-03-03 790 for (i = 0; i < 3; i++) {
81dee67e215b23 Sudip Mukherjee 2015-03-03 791 ret = fb_find_mode(var, info, g_fbmode[index],
81dee67e215b23 Sudip Mukherjee 2015-03-03 792 pdb[i], cdb[i], NULL, 8);
81dee67e215b23 Sudip Mukherjee 2015-03-03 793
db7fb3588ab492 Artem Lytkin 2026-02-23 794 if (ret == 1 || ret == 2)
81dee67e215b23 Sudip Mukherjee 2015-03-03 795 break;
81dee67e215b23 Sudip Mukherjee 2015-03-03 796 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 797
81dee67e215b23 Sudip Mukherjee 2015-03-03 798 /* set par */
81dee67e215b23 Sudip Mukherjee 2015-03-03 799 par->info = info;
81dee67e215b23 Sudip Mukherjee 2015-03-03 800
81dee67e215b23 Sudip Mukherjee 2015-03-03 801 /* set info */
e3a3f9f5123683 Mike Rapoport 2015-10-26 802 line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8),
e3a3f9f5123683 Mike Rapoport 2015-10-26 803 crtc->line_pad);
81dee67e215b23 Sudip Mukherjee 2015-03-03 804
81dee67e215b23 Sudip Mukherjee 2015-03-03 805 info->pseudo_palette = &par->pseudo_palette[0];
cc59bde1c920ab Benjamin Philip 2021-07-28 806 info->screen_base = crtc->v_screen;
81dee67e215b23 Sudip Mukherjee 2015-03-03 807 info->screen_size = line_length * var->yres_virtual;
81dee67e215b23 Sudip Mukherjee 2015-03-03 808
81dee67e215b23 Sudip Mukherjee 2015-03-03 809 /* set info->fix */
81dee67e215b23 Sudip Mukherjee 2015-03-03 810 fix->type = FB_TYPE_PACKED_PIXELS;
81dee67e215b23 Sudip Mukherjee 2015-03-03 811 fix->type_aux = 0;
81dee67e215b23 Sudip Mukherjee 2015-03-03 812 fix->xpanstep = crtc->xpanstep;
81dee67e215b23 Sudip Mukherjee 2015-03-03 813 fix->ypanstep = crtc->ypanstep;
81dee67e215b23 Sudip Mukherjee 2015-03-03 814 fix->ywrapstep = crtc->ywrapstep;
81dee67e215b23 Sudip Mukherjee 2015-03-03 815 fix->accel = FB_ACCEL_SMI;
81dee67e215b23 Sudip Mukherjee 2015-03-03 816
8c475735085a7d Tim Wassink 2025-12-21 817 strscpy(fix->id, fix_id[index], sizeof(fix->id));
81dee67e215b23 Sudip Mukherjee 2015-03-03 818
fdc234d85210d9 Benjamin Philip 2021-07-28 819 fix->smem_start = crtc->o_screen + sm750_dev->vidmem_start;
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 820 /*
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 821 * according to mmap experiment from user space application,
81dee67e215b23 Sudip Mukherjee 2015-03-03 822 * fix->mmio_len should not larger than virtual size
81dee67e215b23 Sudip Mukherjee 2015-03-03 823 * (xres_virtual x yres_virtual x ByPP)
81dee67e215b23 Sudip Mukherjee 2015-03-03 824 * Below line maybe buggy when user mmap fb dev node and write
81dee67e215b23 Sudip Mukherjee 2015-03-03 825 * data into the bound over virtual size
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 826 */
81dee67e215b23 Sudip Mukherjee 2015-03-03 827 fix->smem_len = crtc->vidmem_size;
81dee67e215b23 Sudip Mukherjee 2015-03-03 828 info->screen_size = fix->smem_len;
81dee67e215b23 Sudip Mukherjee 2015-03-03 829 fix->line_length = line_length;
e359b6a863e19f Mike Rapoport 2015-10-26 830 fix->mmio_start = sm750_dev->vidreg_start;
e359b6a863e19f Mike Rapoport 2015-10-26 831 fix->mmio_len = sm750_dev->vidreg_size;
b610e1193a917f Matej Dujava 2020-04-30 832
b610e1193a917f Matej Dujava 2020-04-30 833 lynxfb_set_visual_mode(info);
81dee67e215b23 Sudip Mukherjee 2015-03-03 834
81dee67e215b23 Sudip Mukherjee 2015-03-03 835 /* set var */
81dee67e215b23 Sudip Mukherjee 2015-03-03 836 var->activate = FB_ACTIVATE_NOW;
81dee67e215b23 Sudip Mukherjee 2015-03-03 837 var->accel_flags = 0;
81dee67e215b23 Sudip Mukherjee 2015-03-03 838 var->vmode = FB_VMODE_NONINTERLACED;
81dee67e215b23 Sudip Mukherjee 2015-03-03 839
61c507cf652da1 Michel von Czettritz 2015-03-26 840 ret = fb_alloc_cmap(&info->cmap, 256, 0);
61c507cf652da1 Michel von Czettritz 2015-03-26 841 if (ret < 0) {
fbab250eb51d6d Artem Lytkin 2026-02-07 842 dev_err(info->device, "Could not allocate memory for cmap.\n");
81dee67e215b23 Sudip Mukherjee 2015-03-03 843 goto exit;
81dee67e215b23 Sudip Mukherjee 2015-03-03 844 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 845
81dee67e215b23 Sudip Mukherjee 2015-03-03 846 exit:
81dee67e215b23 Sudip Mukherjee 2015-03-03 847 lynxfb_ops_check_var(var, info);
81dee67e215b23 Sudip Mukherjee 2015-03-03 848 return ret;
81dee67e215b23 Sudip Mukherjee 2015-03-03 849 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 850
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH 6.1.y] fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
From: Sasha Levin @ 2026-05-05 7:31 UTC (permalink / raw)
To: stable; +Cc: Thomas Zimmermann, Helge Deller, linux-fbdev, dri-devel,
Sasha Levin
In-Reply-To: <2026050117-caravan-atrocious-ee79@gregkh>
From: Thomas Zimmermann <tzimmermann@suse.de>
[ Upstream commit 9ded47ad003f09a94b6a710b5c47f4aa5ceb7429 ]
Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an
instance as part of initializing deferred I/O and remove it only after
the final mapping has been closed. If the fb_info and the contained
deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info
to invalidate the mapping. Any access will then result in a SIGBUS
signal.
Fixes a long-standing problem, where a device hot-unplug happens while
user space still has an active mapping of the graphics memory. The hot-
unplug frees the instance of struct fb_info. Accessing the memory will
operate on undefined state.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support")
Cc: Helge Deller <deller@gmx.de>
Cc: linux-fbdev@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: stable@vger.kernel.org # v2.6.22+
Signed-off-by: Helge Deller <deller@gmx.de>
[ context + _obj() conversion ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/video/fbdev/core/fb_defio.c | 164 ++++++++++++++++++++++++----
include/linux/fb.h | 4 +-
2 files changed, 145 insertions(+), 23 deletions(-)
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 3b376345d4d47..7ac0bf4766b34 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -23,6 +23,75 @@
#include <linux/rmap.h>
#include <linux/pagemap.h>
+/*
+ * struct fb_deferred_io_state
+ */
+
+struct fb_deferred_io_state {
+ struct kref ref;
+
+ struct mutex lock; /* mutex that protects the pageref list */
+ /* fields protected by lock */
+ struct fb_info *info;
+};
+
+static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void)
+{
+ struct fb_deferred_io_state *fbdefio_state;
+
+ fbdefio_state = kzalloc(sizeof(*fbdefio_state), GFP_KERNEL);
+ if (!fbdefio_state)
+ return NULL;
+
+ kref_init(&fbdefio_state->ref);
+ mutex_init(&fbdefio_state->lock);
+
+ return fbdefio_state;
+}
+
+static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state)
+{
+ mutex_destroy(&fbdefio_state->lock);
+
+ kfree(fbdefio_state);
+}
+
+static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_get(&fbdefio_state->ref);
+}
+
+static void __fb_deferred_io_state_release(struct kref *ref)
+{
+ struct fb_deferred_io_state *fbdefio_state =
+ container_of(ref, struct fb_deferred_io_state, ref);
+
+ fb_deferred_io_state_release(fbdefio_state);
+}
+
+static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release);
+}
+
+/*
+ * struct vm_operations_struct
+ */
+
+static void fb_deferred_io_vm_open(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_get(fbdefio_state);
+}
+
+static void fb_deferred_io_vm_close(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_put(fbdefio_state);
+}
+
static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
{
void *screen_base = (void __force *) info->screen_base;
@@ -93,17 +162,31 @@ static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
/* this is to find and return the vmalloc-ed fb pages */
static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
{
+ struct fb_info *info;
unsigned long offset;
struct page *page;
- struct fb_info *info = vmf->vma->vm_private_data;
+ vm_fault_t ret;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
+
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= info->fix.smem_len)
- return VM_FAULT_SIGBUS;
+ if (offset >= info->fix.smem_len) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
page = fb_deferred_io_page(info, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
+ if (!page) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
get_page(page);
@@ -115,8 +198,15 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
BUG_ON(!page->mapping);
page->index = vmf->pgoff; /* for page_mkclean() */
+ mutex_unlock(&fbdefio_state->lock);
+
vmf->page = page;
+
return 0;
+
+err_mutex_unlock:
+ mutex_unlock(&fbdefio_state->lock);
+ return ret;
}
int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
@@ -143,15 +233,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
* Adds a page to the dirty list. Call this from struct
* vm_operations_struct.page_mkwrite.
*/
-static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
- struct page *page)
+static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state,
+ unsigned long offset, struct page *page)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_info *info;
+ struct fb_deferred_io *fbdefio;
struct fb_deferred_io_pageref *pageref;
vm_fault_t ret;
/* protect against the workqueue changing the page list */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
+
+ fbdefio = info->fbdefio;
/* first write in this cycle, notify the driver */
if (fbdefio->first_io && list_empty(&fbdefio->pagereflist))
@@ -173,14 +272,14 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long
*/
lock_page(pageref->page);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
/* come back after delay to process the deferred IO */
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
return VM_FAULT_LOCKED;
err_mutex_unlock:
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
return ret;
}
@@ -198,25 +297,28 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long
* Returns:
* VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
*/
-static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
+static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state,
+ struct vm_fault *vmf)
{
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
struct page *page = vmf->page;
file_update_time(vmf->vma->vm_file);
- return fb_deferred_io_track_page(info, offset, page);
+ return fb_deferred_io_track_page(fbdefio_state, offset, page);
}
/* vm_ops->page_mkwrite handler */
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
{
- struct fb_info *info = vmf->vma->vm_private_data;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
- return fb_deferred_io_page_mkwrite(info, vmf);
+ return fb_deferred_io_page_mkwrite(fbdefio_state, vmf);
}
static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+ .open = fb_deferred_io_vm_open,
+ .close = fb_deferred_io_vm_close,
.fault = fb_deferred_io_fault,
.page_mkwrite = fb_deferred_io_mkwrite,
};
@@ -231,7 +333,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
if (!(info->flags & FBINFO_VIRTFB))
vma->vm_flags |= VM_IO;
- vma->vm_private_data = info;
+ vma->vm_private_data = info->fbdefio_state;
+
+ fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */
+
return 0;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
@@ -242,9 +347,10 @@ static void fb_deferred_io_work(struct work_struct *work)
struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
struct fb_deferred_io_pageref *pageref, *next;
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
/* here we mkclean the pages, then do all deferred IO */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
struct page *cur = pageref->page;
lock_page(cur);
@@ -259,12 +365,13 @@ static void fb_deferred_io_work(struct work_struct *work)
list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
fb_deferred_io_pageref_put(pageref, info);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
}
int fb_deferred_io_init(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
struct fb_deferred_io_pageref *pagerefs;
unsigned long npagerefs, i;
int ret;
@@ -274,7 +381,11 @@ int fb_deferred_io_init(struct fb_info *info)
if (WARN_ON(!info->fix.smem_len))
return -EINVAL;
- mutex_init(&fbdefio->lock);
+ fbdefio_state = fb_deferred_io_state_alloc();
+ if (!fbdefio_state)
+ return -ENOMEM;
+ fbdefio_state->info = info;
+
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
INIT_LIST_HEAD(&fbdefio->pagereflist);
if (fbdefio->delay == 0) /* set a default of 1 s */
@@ -293,10 +404,12 @@ int fb_deferred_io_init(struct fb_info *info)
info->npagerefs = npagerefs;
info->pagerefs = pagerefs;
+ info->fbdefio_state = fbdefio_state;
+
return 0;
err:
- mutex_destroy(&fbdefio->lock);
+ fb_deferred_io_state_release(fbdefio_state);
return ret;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
@@ -337,11 +450,18 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release);
void fb_deferred_io_cleanup(struct fb_info *info)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
fb_deferred_io_lastclose(info);
+ info->fbdefio_state = NULL;
+
+ mutex_lock(&fbdefio_state->lock);
+ fbdefio_state->info = NULL;
+ mutex_unlock(&fbdefio_state->lock);
+
+ fb_deferred_io_state_put(fbdefio_state);
+
kvfree(info->pagerefs);
- mutex_destroy(&fbdefio->lock);
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index c7f0f14e1f74b..5f94c58c4672e 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -213,12 +213,13 @@ struct fb_deferred_io {
unsigned long delay;
bool sort_pagereflist; /* sort pagelist by offset */
int open_count; /* number of opened files; protected by fb_info lock */
- struct mutex lock; /* mutex that protects the pageref list */
struct list_head pagereflist; /* list of pagerefs for touched pages */
/* callback */
void (*first_io)(struct fb_info *info);
void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
};
+
+struct fb_deferred_io_state;
#endif
/*
@@ -479,6 +480,7 @@ struct fb_info {
unsigned long npagerefs;
struct fb_deferred_io_pageref *pagerefs;
struct fb_deferred_io *fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
#endif
const struct fb_ops *fbops;
--
2.53.0
^ permalink raw reply related
* [PATCH 6.6.y] fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
From: Sasha Levin @ 2026-05-05 6:00 UTC (permalink / raw)
To: stable; +Cc: Thomas Zimmermann, Helge Deller, linux-fbdev, dri-devel,
Sasha Levin
In-Reply-To: <2026050116-clean-cure-c52e@gregkh>
From: Thomas Zimmermann <tzimmermann@suse.de>
[ Upstream commit 9ded47ad003f09a94b6a710b5c47f4aa5ceb7429 ]
Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an
instance as part of initializing deferred I/O and remove it only after
the final mapping has been closed. If the fb_info and the contained
deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info
to invalidate the mapping. Any access will then result in a SIGBUS
signal.
Fixes a long-standing problem, where a device hot-unplug happens while
user space still has an active mapping of the graphics memory. The hot-
unplug frees the instance of struct fb_info. Accessing the memory will
operate on undefined state.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support")
Cc: Helge Deller <deller@gmx.de>
Cc: linux-fbdev@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: stable@vger.kernel.org # v2.6.22+
Signed-off-by: Helge Deller <deller@gmx.de>
[ replaced `kzalloc_obj` with `kzalloc`, and dropped `mutex_destroy(&fbdefio->lock)` ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/video/fbdev/core/fb_defio.c | 179 ++++++++++++++++++++++------
include/linux/fb.h | 4 +-
2 files changed, 145 insertions(+), 38 deletions(-)
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index b9607d5a370d4..f4812a76c3cc0 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -23,6 +23,75 @@
#include <linux/rmap.h>
#include <linux/pagemap.h>
+/*
+ * struct fb_deferred_io_state
+ */
+
+struct fb_deferred_io_state {
+ struct kref ref;
+
+ struct mutex lock; /* mutex that protects the pageref list */
+ /* fields protected by lock */
+ struct fb_info *info;
+};
+
+static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void)
+{
+ struct fb_deferred_io_state *fbdefio_state;
+
+ fbdefio_state = kzalloc(sizeof(*fbdefio_state), GFP_KERNEL);
+ if (!fbdefio_state)
+ return NULL;
+
+ kref_init(&fbdefio_state->ref);
+ mutex_init(&fbdefio_state->lock);
+
+ return fbdefio_state;
+}
+
+static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state)
+{
+ mutex_destroy(&fbdefio_state->lock);
+
+ kfree(fbdefio_state);
+}
+
+static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_get(&fbdefio_state->ref);
+}
+
+static void __fb_deferred_io_state_release(struct kref *ref)
+{
+ struct fb_deferred_io_state *fbdefio_state =
+ container_of(ref, struct fb_deferred_io_state, ref);
+
+ fb_deferred_io_state_release(fbdefio_state);
+}
+
+static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release);
+}
+
+/*
+ * struct vm_operations_struct
+ */
+
+static void fb_deferred_io_vm_open(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_get(fbdefio_state);
+}
+
+static void fb_deferred_io_vm_close(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_put(fbdefio_state);
+}
+
static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
{
void *screen_base = (void __force *) info->screen_base;
@@ -93,17 +162,31 @@ static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
/* this is to find and return the vmalloc-ed fb pages */
static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
{
+ struct fb_info *info;
unsigned long offset;
struct page *page;
- struct fb_info *info = vmf->vma->vm_private_data;
+ vm_fault_t ret;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
+
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= info->fix.smem_len)
- return VM_FAULT_SIGBUS;
+ if (offset >= info->fix.smem_len) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
page = fb_deferred_io_page(info, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
+ if (!page) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
get_page(page);
@@ -115,8 +198,15 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
BUG_ON(!page->mapping);
page->index = vmf->pgoff; /* for page_mkclean() */
+ mutex_unlock(&fbdefio_state->lock);
+
vmf->page = page;
+
return 0;
+
+err_mutex_unlock:
+ mutex_unlock(&fbdefio_state->lock);
+ return ret;
}
int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
@@ -143,15 +233,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
* Adds a page to the dirty list. Call this from struct
* vm_operations_struct.page_mkwrite.
*/
-static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
- struct page *page)
+static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state,
+ unsigned long offset, struct page *page)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_info *info;
+ struct fb_deferred_io *fbdefio;
struct fb_deferred_io_pageref *pageref;
vm_fault_t ret;
/* protect against the workqueue changing the page list */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
+
+ fbdefio = info->fbdefio;
pageref = fb_deferred_io_pageref_get(info, offset, page);
if (WARN_ON_ONCE(!pageref)) {
@@ -169,50 +268,38 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long
*/
lock_page(pageref->page);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
/* come back after delay to process the deferred IO */
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
return VM_FAULT_LOCKED;
err_mutex_unlock:
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
return ret;
}
-/*
- * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O
- * @fb_info: The fbdev info structure
- * @vmf: The VM fault
- *
- * This is a callback we get when userspace first tries to
- * write to the page. We schedule a workqueue. That workqueue
- * will eventually mkclean the touched pages and execute the
- * deferred framebuffer IO. Then if userspace touches a page
- * again, we repeat the same scheme.
- *
- * Returns:
- * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
- */
-static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
+static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state,
+ struct vm_fault *vmf)
{
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
struct page *page = vmf->page;
file_update_time(vmf->vma->vm_file);
- return fb_deferred_io_track_page(info, offset, page);
+ return fb_deferred_io_track_page(fbdefio_state, offset, page);
}
-/* vm_ops->page_mkwrite handler */
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
{
- struct fb_info *info = vmf->vma->vm_private_data;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
- return fb_deferred_io_page_mkwrite(info, vmf);
+ return fb_deferred_io_page_mkwrite(fbdefio_state, vmf);
}
static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+ .open = fb_deferred_io_vm_open,
+ .close = fb_deferred_io_vm_close,
.fault = fb_deferred_io_fault,
.page_mkwrite = fb_deferred_io_mkwrite,
};
@@ -227,7 +314,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
if (!(info->flags & FBINFO_VIRTFB))
vm_flags_set(vma, VM_IO);
- vma->vm_private_data = info;
+ vma->vm_private_data = info->fbdefio_state;
+
+ fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */
+
return 0;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
@@ -238,9 +328,10 @@ static void fb_deferred_io_work(struct work_struct *work)
struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
struct fb_deferred_io_pageref *pageref, *next;
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
/* here we mkclean the pages, then do all deferred IO */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
struct page *cur = pageref->page;
lock_page(cur);
@@ -255,12 +346,13 @@ static void fb_deferred_io_work(struct work_struct *work)
list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
fb_deferred_io_pageref_put(pageref, info);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
}
int fb_deferred_io_init(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
struct fb_deferred_io_pageref *pagerefs;
unsigned long npagerefs, i;
int ret;
@@ -270,7 +362,11 @@ int fb_deferred_io_init(struct fb_info *info)
if (WARN_ON(!info->fix.smem_len))
return -EINVAL;
- mutex_init(&fbdefio->lock);
+ fbdefio_state = fb_deferred_io_state_alloc();
+ if (!fbdefio_state)
+ return -ENOMEM;
+ fbdefio_state->info = info;
+
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
INIT_LIST_HEAD(&fbdefio->pagereflist);
if (fbdefio->delay == 0) /* set a default of 1 s */
@@ -289,10 +385,12 @@ int fb_deferred_io_init(struct fb_info *info)
info->npagerefs = npagerefs;
info->pagerefs = pagerefs;
+ info->fbdefio_state = fbdefio_state;
+
return 0;
err:
- mutex_destroy(&fbdefio->lock);
+ fb_deferred_io_state_release(fbdefio_state);
return ret;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
@@ -333,11 +431,18 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release);
void fb_deferred_io_cleanup(struct fb_info *info)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
fb_deferred_io_lastclose(info);
+ info->fbdefio_state = NULL;
+
+ mutex_lock(&fbdefio_state->lock);
+ fbdefio_state->info = NULL;
+ mutex_unlock(&fbdefio_state->lock);
+
+ fb_deferred_io_state_put(fbdefio_state);
+
kvfree(info->pagerefs);
- mutex_destroy(&fbdefio->lock);
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 322b4d20afa55..8a9d949cc7e2d 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -214,11 +214,12 @@ struct fb_deferred_io {
unsigned long delay;
bool sort_pagereflist; /* sort pagelist by offset */
int open_count; /* number of opened files; protected by fb_info lock */
- struct mutex lock; /* mutex that protects the pageref list */
struct list_head pagereflist; /* list of pagerefs for touched pages */
/* callback */
void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
};
+
+struct fb_deferred_io_state;
#endif
/*
@@ -476,6 +477,7 @@ struct fb_info {
unsigned long npagerefs;
struct fb_deferred_io_pageref *pagerefs;
struct fb_deferred_io *fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
#endif
const struct fb_ops *fbops;
--
2.53.0
^ permalink raw reply related
* [PATCH v1] fbdev/offb: fix PCI device reference leak on probe failure
From: dbgh9129 @ 2026-05-05 1:39 UTC (permalink / raw)
To: deller, Jason, linux-fbdev
Cc: dri-devel, linux-kernel, mhun512, ae878000, tmk5904, dbgh9129
In-Reply-To: <20260420010118.17960-1-dbgh9129@gmail.com>
Dear Helge and Jason,
I am sending a gentle ping on this patch. Please let me know if you
have any feedback or if any changes are required.
Best regards,
Yuho Choi
^ permalink raw reply
* [PATCH v1] fbdev: savage: fix probe-path EDID cleanup leaks
From: dbgh9129 @ 2026-05-05 1:35 UTC (permalink / raw)
To: adaplas, deller, linux-fbdev
Cc: dri-devel, linux-kernel, mhun512, ae878000, tmk5904, dbgh9129
In-Reply-To: <20260420051926.28276-1-dbgh9129@gmail.com>
Hi Antonino and Helge,
I am sending a gentle ping on this patch. Please let me know if you
have any feedback or if any changes are needed.
Best regards,
Yuho Choi
^ permalink raw reply
* Re: [PATCH v1] fbdev: savage: fix probe-path EDID cleanup leaks
From: 최유호 @ 2026-05-05 1:35 UTC (permalink / raw)
To: Antonino Daplas, Helge Deller, linux-fbdev
Cc: dri-devel, linux-kernel, Myeonghun Pak, Ijae Kim, Taegyu Kim
In-Reply-To: <20260420051926.28276-1-dbgh9129@gmail.com>
Dear Antonino and Helge,
I am sending a gentle ping on this patch. Please let me know if you
have any feedback or if any changes are needed.
Best regards,
Yuho
On Mon, 20 Apr 2026 at 01:19, Yuho Choi <dbgh9129@gmail.com> wrote:
>
> When CONFIG_FB_SAVAGE_I2C is enabled, savagefb_probe() can build both an
> EDID-derived monspecs.modedb and a modelist from it before later
> failing.
>
> The normal success path frees monspecs.modedb after the initial mode
> selection, but the probe error path only deletes the I2C busses and
> misses the EDID-derived allocations.
>
> Free both the modelist and monspecs.modedb on the failed: unwind path.
>
> Co-developed-by: Myeonghun Pak <mhun512@gmail.com>
> Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
> Co-developed-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Ijae Kim <ae878000@gmail.com>
> Co-developed-by: Taegyu Kim <tmk5904@psu.edu>
> Signed-off-by: Taegyu Kim <tmk5904@psu.edu>
> Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
> ---
> drivers/video/fbdev/savage/savagefb_driver.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c
> index ac41f8f37589f..c2f79357c8da0 100644
> --- a/drivers/video/fbdev/savage/savagefb_driver.c
> +++ b/drivers/video/fbdev/savage/savagefb_driver.c
> @@ -2322,6 +2322,8 @@ static int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id)
> failed:
> #ifdef CONFIG_FB_SAVAGE_I2C
> savagefb_delete_i2c_busses(info);
> + fb_destroy_modelist(&info->modelist);
> + fb_destroy_modedb(info->monspecs.modedb);
> #endif
> fb_alloc_cmap(&info->cmap, 0, 0);
> savage_unmap_video(info);
> --
> 2.50.1 (Apple Git-155)
>
^ permalink raw reply
* [PATCH 6.12.y] fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
From: Sasha Levin @ 2026-05-05 0:14 UTC (permalink / raw)
To: stable; +Cc: Thomas Zimmermann, Helge Deller, linux-fbdev, dri-devel,
Sasha Levin
In-Reply-To: <2026050115-morality-reseal-5c07@gregkh>
From: Thomas Zimmermann <tzimmermann@suse.de>
[ Upstream commit 9ded47ad003f09a94b6a710b5c47f4aa5ceb7429 ]
Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an
instance as part of initializing deferred I/O and remove it only after
the final mapping has been closed. If the fb_info and the contained
deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info
to invalidate the mapping. Any access will then result in a SIGBUS
signal.
Fixes a long-standing problem, where a device hot-unplug happens while
user space still has an active mapping of the graphics memory. The hot-
unplug frees the instance of struct fb_info. Accessing the memory will
operate on undefined state.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support")
Cc: Helge Deller <deller@gmx.de>
Cc: linux-fbdev@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: stable@vger.kernel.org # v2.6.22+
Signed-off-by: Helge Deller <deller@gmx.de>
[ replaced `kzalloc_obj()` with `kzalloc(sizeof(*fbdefio_state), GFP_KERNEL)` ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/video/fbdev/core/fb_defio.c | 179 ++++++++++++++++++++++------
include/linux/fb.h | 4 +-
2 files changed, 145 insertions(+), 38 deletions(-)
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 65363df8e81b5..e0a44298c248d 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -23,6 +23,75 @@
#include <linux/rmap.h>
#include <linux/pagemap.h>
+/*
+ * struct fb_deferred_io_state
+ */
+
+struct fb_deferred_io_state {
+ struct kref ref;
+
+ struct mutex lock; /* mutex that protects the pageref list */
+ /* fields protected by lock */
+ struct fb_info *info;
+};
+
+static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void)
+{
+ struct fb_deferred_io_state *fbdefio_state;
+
+ fbdefio_state = kzalloc(sizeof(*fbdefio_state), GFP_KERNEL);
+ if (!fbdefio_state)
+ return NULL;
+
+ kref_init(&fbdefio_state->ref);
+ mutex_init(&fbdefio_state->lock);
+
+ return fbdefio_state;
+}
+
+static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state)
+{
+ mutex_destroy(&fbdefio_state->lock);
+
+ kfree(fbdefio_state);
+}
+
+static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_get(&fbdefio_state->ref);
+}
+
+static void __fb_deferred_io_state_release(struct kref *ref)
+{
+ struct fb_deferred_io_state *fbdefio_state =
+ container_of(ref, struct fb_deferred_io_state, ref);
+
+ fb_deferred_io_state_release(fbdefio_state);
+}
+
+static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release);
+}
+
+/*
+ * struct vm_operations_struct
+ */
+
+static void fb_deferred_io_vm_open(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_get(fbdefio_state);
+}
+
+static void fb_deferred_io_vm_close(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_put(fbdefio_state);
+}
+
static struct page *fb_deferred_io_get_page(struct fb_info *info, unsigned long offs)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
@@ -128,17 +197,31 @@ static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
/* this is to find and return the vmalloc-ed fb pages */
static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
{
+ struct fb_info *info;
unsigned long offset;
struct page *page;
- struct fb_info *info = vmf->vma->vm_private_data;
+ vm_fault_t ret;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
+
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= info->fix.smem_len)
- return VM_FAULT_SIGBUS;
+ if (offset >= info->fix.smem_len) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
page = fb_deferred_io_get_page(info, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
+ if (!page) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
if (vmf->vma->vm_file)
page->mapping = vmf->vma->vm_file->f_mapping;
@@ -148,8 +231,15 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
BUG_ON(!page->mapping);
page->index = vmf->pgoff; /* for folio_mkclean() */
+ mutex_unlock(&fbdefio_state->lock);
+
vmf->page = page;
+
return 0;
+
+err_mutex_unlock:
+ mutex_unlock(&fbdefio_state->lock);
+ return ret;
}
int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
@@ -176,15 +266,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
* Adds a page to the dirty list. Call this from struct
* vm_operations_struct.page_mkwrite.
*/
-static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
- struct page *page)
+static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state,
+ unsigned long offset, struct page *page)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_info *info;
+ struct fb_deferred_io *fbdefio;
struct fb_deferred_io_pageref *pageref;
vm_fault_t ret;
/* protect against the workqueue changing the page list */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
+
+ fbdefio = info->fbdefio;
pageref = fb_deferred_io_pageref_get(info, offset, page);
if (WARN_ON_ONCE(!pageref)) {
@@ -202,50 +301,38 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long
*/
lock_page(pageref->page);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
/* come back after delay to process the deferred IO */
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
return VM_FAULT_LOCKED;
err_mutex_unlock:
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
return ret;
}
-/*
- * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O
- * @fb_info: The fbdev info structure
- * @vmf: The VM fault
- *
- * This is a callback we get when userspace first tries to
- * write to the page. We schedule a workqueue. That workqueue
- * will eventually mkclean the touched pages and execute the
- * deferred framebuffer IO. Then if userspace touches a page
- * again, we repeat the same scheme.
- *
- * Returns:
- * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
- */
-static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
+static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state,
+ struct vm_fault *vmf)
{
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
struct page *page = vmf->page;
file_update_time(vmf->vma->vm_file);
- return fb_deferred_io_track_page(info, offset, page);
+ return fb_deferred_io_track_page(fbdefio_state, offset, page);
}
-/* vm_ops->page_mkwrite handler */
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
{
- struct fb_info *info = vmf->vma->vm_private_data;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
- return fb_deferred_io_page_mkwrite(info, vmf);
+ return fb_deferred_io_page_mkwrite(fbdefio_state, vmf);
}
static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+ .open = fb_deferred_io_vm_open,
+ .close = fb_deferred_io_vm_close,
.fault = fb_deferred_io_fault,
.page_mkwrite = fb_deferred_io_mkwrite,
};
@@ -262,7 +349,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
if (!(info->flags & FBINFO_VIRTFB))
vm_flags_set(vma, VM_IO);
- vma->vm_private_data = info;
+ vma->vm_private_data = info->fbdefio_state;
+
+ fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */
+
return 0;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
@@ -273,9 +363,10 @@ static void fb_deferred_io_work(struct work_struct *work)
struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
struct fb_deferred_io_pageref *pageref, *next;
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
/* here we mkclean the pages, then do all deferred IO */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
struct folio *folio = page_folio(pageref->page);
@@ -291,12 +382,13 @@ static void fb_deferred_io_work(struct work_struct *work)
list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
fb_deferred_io_pageref_put(pageref, info);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
}
int fb_deferred_io_init(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
struct fb_deferred_io_pageref *pagerefs;
unsigned long npagerefs;
int ret;
@@ -306,7 +398,11 @@ int fb_deferred_io_init(struct fb_info *info)
if (WARN_ON(!info->fix.smem_len))
return -EINVAL;
- mutex_init(&fbdefio->lock);
+ fbdefio_state = fb_deferred_io_state_alloc();
+ if (!fbdefio_state)
+ return -ENOMEM;
+ fbdefio_state->info = info;
+
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
INIT_LIST_HEAD(&fbdefio->pagereflist);
if (fbdefio->delay == 0) /* set a default of 1 s */
@@ -323,10 +419,12 @@ int fb_deferred_io_init(struct fb_info *info)
info->npagerefs = npagerefs;
info->pagerefs = pagerefs;
+ info->fbdefio_state = fbdefio_state;
+
return 0;
err:
- mutex_destroy(&fbdefio->lock);
+ fb_deferred_io_state_release(fbdefio_state);
return ret;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
@@ -364,11 +462,18 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release);
void fb_deferred_io_cleanup(struct fb_info *info)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
fb_deferred_io_lastclose(info);
+ info->fbdefio_state = NULL;
+
+ mutex_lock(&fbdefio_state->lock);
+ fbdefio_state->info = NULL;
+ mutex_unlock(&fbdefio_state->lock);
+
+ fb_deferred_io_state_put(fbdefio_state);
+
kvfree(info->pagerefs);
- mutex_destroy(&fbdefio->lock);
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 267b59ead4321..d87d6547c6e41 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -222,12 +222,13 @@ struct fb_deferred_io {
unsigned long delay;
bool sort_pagereflist; /* sort pagelist by offset */
int open_count; /* number of opened files; protected by fb_info lock */
- struct mutex lock; /* mutex that protects the pageref list */
struct list_head pagereflist; /* list of pagerefs for touched pages */
/* callback */
struct page *(*get_page)(struct fb_info *info, unsigned long offset);
void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
};
+
+struct fb_deferred_io_state;
#endif
/*
@@ -485,6 +486,7 @@ struct fb_info {
unsigned long npagerefs;
struct fb_deferred_io_pageref *pagerefs;
struct fb_deferred_io *fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
#endif
const struct fb_ops *fbops;
--
2.53.0
^ permalink raw reply related
* [PATCH] staging: sm750fb: remove unnecessary initializations
From: Ahmet Sezgin Duran @ 2026-05-04 20:46 UTC (permalink / raw)
To: gregkh; +Cc: linux-fbdev, linux-staging, linux-kernel, Ahmet Sezgin Duran
Remove two instances of `ret = 0` initializations since the variable
is overridden unconditionally before being used.
Signed-off-by: Ahmet Sezgin Duran <ahmet@sezginduran.net>
---
drivers/staging/sm750fb/sm750_hw.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c
index beef05ded98d..780fe6c391d3 100644
--- a/drivers/staging/sm750fb/sm750_hw.c
+++ b/drivers/staging/sm750fb/sm750_hw.c
@@ -29,8 +29,6 @@ int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev)
{
int ret;
- ret = 0;
-
sm750_dev->vidreg_start = pci_resource_start(pdev, 1);
sm750_dev->vidreg_size = SZ_2M;
@@ -243,7 +241,6 @@ int hw_sm750_crtc_set_mode(struct lynxfb_crtc *crtc,
struct sm750_dev *sm750_dev;
struct lynxfb_par *par;
- ret = 0;
par = container_of(crtc, struct lynxfb_par, crtc);
sm750_dev = par->dev;
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v2] staging: sm750fb: add const qualifier to string pointer arrays
From: Greg KH @ 2026-05-04 14:55 UTC (permalink / raw)
To: Francisco Maestre
Cc: sudipm.mukherjee, teddy.wang, linux-fbdev, linux-staging,
linux-kernel
In-Reply-To: <20260503005744.68974-1-francisco@maestretorreblanca.com>
On Sat, May 02, 2026 at 07:57:44PM -0500, Francisco Maestre wrote:
> Add 'const' qualifier to 'g_fbmode' and 'g_def_fbmode' pointer
> declarations to make them 'static const char * const', as the
> pointers themselves are not modified after initialization.
>
> This fixes the following checkpatch.pl warning:
> WARNING: static const char * array should probably be
> static const char * const
>
> Signed-off-by: Francisco Maestre <francisco@maestretorreblanca.com>
> ---
> v2: Resend as individual patch, not part of an unrelated series.
> drivers/staging/sm750fb/sm750.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
> index 62f6e0cdff4d..dc9653ed2108 100644
> --- a/drivers/staging/sm750fb/sm750.c
> +++ b/drivers/staging/sm750fb/sm750.c
> @@ -33,8 +33,8 @@
> static int g_hwcursor = 1;
> static int g_noaccel;
> static int g_nomtrr;
> -static const char *g_fbmode[] = {NULL, NULL};
> -static const char *g_def_fbmode = "1024x768-32@60";
> +static const char * const g_fbmode[] = {NULL, NULL};
> +static const char * const g_def_fbmode = "1024x768-32@60";
> static char *g_settings;
> static int g_dualview;
> static char *g_option;
> --
> 2.50.1 (Apple Git-155)
>
>
Any reason why you did not test build this patch? Always do so so that
you don't get grumpy maintainers asking why you didn't test build your
patch :)
thanks,
greg k-h
^ permalink raw reply
* [PATCH 6.12 107/215] firmware: google: framebuffer: Do not unregister platform device
From: Greg Kroah-Hartman @ 2026-05-04 13:52 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Thomas Zimmermann, Tzung-Bi Shih,
Julius Werner, Javier Martinez Canillas, Hans de Goede,
linux-fbdev
In-Reply-To: <20260504135130.169210693@linuxfoundation.org>
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <tzimmermann@suse.de>
commit 5cd28bd28c8ce426b56ce4230dbd17537181d5ad upstream.
The native driver takes over the framebuffer aperture by removing the
system- framebuffer platform device. Afterwards the pointer in drvdata
is dangling. Remove the entire logic around drvdata and let the kernel's
aperture helpers handle this. The platform device depends on the native
hardware device instead of the coreboot device anyway.
When commit 851b4c14532d ("firmware: coreboot: Add coreboot framebuffer
driver") added the coreboot framebuffer code, the kernel did not support
device-based aperture management. Instead native driviers only removed
the conflicting fbdev device. At that point, unregistering the framebuffer
device most likely worked correctly. It was definitely broken after
commit d9702b2a2171 ("fbdev/simplefb: Do not use struct
fb_info.apertures"). So take this commit for the Fixes tag. Earlier
releases might work depending on the native hardware driver.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: d9702b2a2171 ("fbdev/simplefb: Do not use struct fb_info.apertures")
Acked-by: Tzung-Bi Shih <tzungbi@kernel.org>
Acked-by: Julius Werner <jwerner@chromium.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Hans de Goede <hansg@kernel.org>
Cc: linux-fbdev@vger.kernel.org
Cc: <stable@vger.kernel.org> # v6.3+
Link: https://patch.msgid.link/20260217155836.96267-2-tzimmermann@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/firmware/google/framebuffer-coreboot.c | 10 ----------
1 file changed, 10 deletions(-)
--- a/drivers/firmware/google/framebuffer-coreboot.c
+++ b/drivers/firmware/google/framebuffer-coreboot.c
@@ -67,19 +67,10 @@ static int framebuffer_probe(struct core
sizeof(pdata));
if (IS_ERR(pdev))
pr_warn("coreboot: could not register framebuffer\n");
- else
- dev_set_drvdata(&dev->dev, pdev);
return PTR_ERR_OR_ZERO(pdev);
}
-static void framebuffer_remove(struct coreboot_device *dev)
-{
- struct platform_device *pdev = dev_get_drvdata(&dev->dev);
-
- platform_device_unregister(pdev);
-}
-
static const struct coreboot_device_id framebuffer_ids[] = {
{ .tag = CB_TAG_FRAMEBUFFER },
{ /* sentinel */ }
@@ -88,7 +79,6 @@ MODULE_DEVICE_TABLE(coreboot, framebuffe
static struct coreboot_driver framebuffer_driver = {
.probe = framebuffer_probe,
- .remove = framebuffer_remove,
.drv = {
.name = "framebuffer",
},
^ permalink raw reply
* [PATCH 6.18.y] fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
From: Sasha Levin @ 2026-05-04 14:19 UTC (permalink / raw)
To: stable; +Cc: Thomas Zimmermann, Helge Deller, linux-fbdev, dri-devel,
Sasha Levin
In-Reply-To: <2026050146-growl-viselike-2a67@gregkh>
From: Thomas Zimmermann <tzimmermann@suse.de>
[ Upstream commit 9ded47ad003f09a94b6a710b5c47f4aa5ceb7429 ]
Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an
instance as part of initializing deferred I/O and remove it only after
the final mapping has been closed. If the fb_info and the contained
deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info
to invalidate the mapping. Any access will then result in a SIGBUS
signal.
Fixes a long-standing problem, where a device hot-unplug happens while
user space still has an active mapping of the graphics memory. The hot-
unplug frees the instance of struct fb_info. Accessing the memory will
operate on undefined state.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support")
Cc: Helge Deller <deller@gmx.de>
Cc: linux-fbdev@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: stable@vger.kernel.org # v2.6.22+
Signed-off-by: Helge Deller <deller@gmx.de>
[ replaced kzalloc_obj(*fbdefio_state) with kzalloc(sizeof(*fbdefio_state), GFP_KERNEL) ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/video/fbdev/core/fb_defio.c | 178 ++++++++++++++++++++++------
include/linux/fb.h | 4 +-
2 files changed, 145 insertions(+), 37 deletions(-)
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 8df2e51e33909..0b099a89a8234 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -24,6 +24,75 @@
#include <linux/rmap.h>
#include <linux/pagemap.h>
+/*
+ * struct fb_deferred_io_state
+ */
+
+struct fb_deferred_io_state {
+ struct kref ref;
+
+ struct mutex lock; /* mutex that protects the pageref list */
+ /* fields protected by lock */
+ struct fb_info *info;
+};
+
+static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void)
+{
+ struct fb_deferred_io_state *fbdefio_state;
+
+ fbdefio_state = kzalloc(sizeof(*fbdefio_state), GFP_KERNEL);
+ if (!fbdefio_state)
+ return NULL;
+
+ kref_init(&fbdefio_state->ref);
+ mutex_init(&fbdefio_state->lock);
+
+ return fbdefio_state;
+}
+
+static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state)
+{
+ mutex_destroy(&fbdefio_state->lock);
+
+ kfree(fbdefio_state);
+}
+
+static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_get(&fbdefio_state->ref);
+}
+
+static void __fb_deferred_io_state_release(struct kref *ref)
+{
+ struct fb_deferred_io_state *fbdefio_state =
+ container_of(ref, struct fb_deferred_io_state, ref);
+
+ fb_deferred_io_state_release(fbdefio_state);
+}
+
+static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release);
+}
+
+/*
+ * struct vm_operations_struct
+ */
+
+static void fb_deferred_io_vm_open(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_get(fbdefio_state);
+}
+
+static void fb_deferred_io_vm_close(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_put(fbdefio_state);
+}
+
static struct page *fb_deferred_io_get_page(struct fb_info *info, unsigned long offs)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
@@ -121,25 +190,46 @@ static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
/* this is to find and return the vmalloc-ed fb pages */
static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
{
+ struct fb_info *info;
unsigned long offset;
struct page *page;
- struct fb_info *info = vmf->vma->vm_private_data;
+ vm_fault_t ret;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
+
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= info->fix.smem_len)
- return VM_FAULT_SIGBUS;
+ if (offset >= info->fix.smem_len) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
page = fb_deferred_io_get_page(info, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
+ if (!page) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
if (!vmf->vma->vm_file)
fb_err(info, "no mapping available\n");
BUG_ON(!info->fbdefio->mapping);
+ mutex_unlock(&fbdefio_state->lock);
+
vmf->page = page;
+
return 0;
+
+err_mutex_unlock:
+ mutex_unlock(&fbdefio_state->lock);
+ return ret;
}
int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
@@ -166,15 +256,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
* Adds a page to the dirty list. Call this from struct
* vm_operations_struct.page_mkwrite.
*/
-static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
- struct page *page)
+static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state,
+ unsigned long offset, struct page *page)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_info *info;
+ struct fb_deferred_io *fbdefio;
struct fb_deferred_io_pageref *pageref;
vm_fault_t ret;
/* protect against the workqueue changing the page list */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
+
+ fbdefio = info->fbdefio;
pageref = fb_deferred_io_pageref_get(info, offset, page);
if (WARN_ON_ONCE(!pageref)) {
@@ -192,50 +291,38 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long
*/
lock_page(pageref->page);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
/* come back after delay to process the deferred IO */
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
return VM_FAULT_LOCKED;
err_mutex_unlock:
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
return ret;
}
-/*
- * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O
- * @fb_info: The fbdev info structure
- * @vmf: The VM fault
- *
- * This is a callback we get when userspace first tries to
- * write to the page. We schedule a workqueue. That workqueue
- * will eventually mkclean the touched pages and execute the
- * deferred framebuffer IO. Then if userspace touches a page
- * again, we repeat the same scheme.
- *
- * Returns:
- * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
- */
-static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
+static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state,
+ struct vm_fault *vmf)
{
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
struct page *page = vmf->page;
file_update_time(vmf->vma->vm_file);
- return fb_deferred_io_track_page(info, offset, page);
+ return fb_deferred_io_track_page(fbdefio_state, offset, page);
}
-/* vm_ops->page_mkwrite handler */
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
{
- struct fb_info *info = vmf->vma->vm_private_data;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
- return fb_deferred_io_page_mkwrite(info, vmf);
+ return fb_deferred_io_page_mkwrite(fbdefio_state, vmf);
}
static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+ .open = fb_deferred_io_vm_open,
+ .close = fb_deferred_io_vm_close,
.fault = fb_deferred_io_fault,
.page_mkwrite = fb_deferred_io_mkwrite,
};
@@ -252,7 +339,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
if (!(info->flags & FBINFO_VIRTFB))
vm_flags_set(vma, VM_IO);
- vma->vm_private_data = info;
+ vma->vm_private_data = info->fbdefio_state;
+
+ fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */
+
return 0;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
@@ -263,9 +353,10 @@ static void fb_deferred_io_work(struct work_struct *work)
struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
struct fb_deferred_io_pageref *pageref, *next;
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
/* here we wrprotect the page's mappings, then do all deferred IO. */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
#ifdef CONFIG_MMU
list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
struct page *page = pageref->page;
@@ -283,12 +374,13 @@ static void fb_deferred_io_work(struct work_struct *work)
list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
fb_deferred_io_pageref_put(pageref, info);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
}
int fb_deferred_io_init(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
struct fb_deferred_io_pageref *pagerefs;
unsigned long npagerefs;
int ret;
@@ -298,7 +390,11 @@ int fb_deferred_io_init(struct fb_info *info)
if (WARN_ON(!info->fix.smem_len))
return -EINVAL;
- mutex_init(&fbdefio->lock);
+ fbdefio_state = fb_deferred_io_state_alloc();
+ if (!fbdefio_state)
+ return -ENOMEM;
+ fbdefio_state->info = info;
+
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
INIT_LIST_HEAD(&fbdefio->pagereflist);
if (fbdefio->delay == 0) /* set a default of 1 s */
@@ -315,10 +411,12 @@ int fb_deferred_io_init(struct fb_info *info)
info->npagerefs = npagerefs;
info->pagerefs = pagerefs;
+ info->fbdefio_state = fbdefio_state;
+
return 0;
err:
- mutex_destroy(&fbdefio->lock);
+ fb_deferred_io_state_release(fbdefio_state);
return ret;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
@@ -352,11 +450,19 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release);
void fb_deferred_io_cleanup(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
fb_deferred_io_lastclose(info);
+ info->fbdefio_state = NULL;
+
+ mutex_lock(&fbdefio_state->lock);
+ fbdefio_state->info = NULL;
+ mutex_unlock(&fbdefio_state->lock);
+
+ fb_deferred_io_state_put(fbdefio_state);
+
kvfree(info->pagerefs);
- mutex_destroy(&fbdefio->lock);
fbdefio->mapping = NULL;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index c3302d5135466..da2fdabd18cb3 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -217,13 +217,14 @@ struct fb_deferred_io {
unsigned long delay;
bool sort_pagereflist; /* sort pagelist by offset */
int open_count; /* number of opened files; protected by fb_info lock */
- struct mutex lock; /* mutex that protects the pageref list */
struct list_head pagereflist; /* list of pagerefs for touched pages */
struct address_space *mapping; /* page cache object for fb device */
/* callback */
struct page *(*get_page)(struct fb_info *info, unsigned long offset);
void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
};
+
+struct fb_deferred_io_state;
#endif
/*
@@ -490,6 +491,7 @@ struct fb_info {
unsigned long npagerefs;
struct fb_deferred_io_pageref *pagerefs;
struct fb_deferred_io *fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
#endif
const struct fb_ops *fbops;
--
2.53.0
^ permalink raw reply related
* [PATCH 6.18 151/275] firmware: google: framebuffer: Do not unregister platform device
From: Greg Kroah-Hartman @ 2026-05-04 13:51 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Thomas Zimmermann, Tzung-Bi Shih,
Julius Werner, Javier Martinez Canillas, Hans de Goede,
linux-fbdev
In-Reply-To: <20260504135142.929052779@linuxfoundation.org>
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <tzimmermann@suse.de>
commit 5cd28bd28c8ce426b56ce4230dbd17537181d5ad upstream.
The native driver takes over the framebuffer aperture by removing the
system- framebuffer platform device. Afterwards the pointer in drvdata
is dangling. Remove the entire logic around drvdata and let the kernel's
aperture helpers handle this. The platform device depends on the native
hardware device instead of the coreboot device anyway.
When commit 851b4c14532d ("firmware: coreboot: Add coreboot framebuffer
driver") added the coreboot framebuffer code, the kernel did not support
device-based aperture management. Instead native driviers only removed
the conflicting fbdev device. At that point, unregistering the framebuffer
device most likely worked correctly. It was definitely broken after
commit d9702b2a2171 ("fbdev/simplefb: Do not use struct
fb_info.apertures"). So take this commit for the Fixes tag. Earlier
releases might work depending on the native hardware driver.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: d9702b2a2171 ("fbdev/simplefb: Do not use struct fb_info.apertures")
Acked-by: Tzung-Bi Shih <tzungbi@kernel.org>
Acked-by: Julius Werner <jwerner@chromium.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Hans de Goede <hansg@kernel.org>
Cc: linux-fbdev@vger.kernel.org
Cc: <stable@vger.kernel.org> # v6.3+
Link: https://patch.msgid.link/20260217155836.96267-2-tzimmermann@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/firmware/google/framebuffer-coreboot.c | 10 ----------
1 file changed, 10 deletions(-)
--- a/drivers/firmware/google/framebuffer-coreboot.c
+++ b/drivers/firmware/google/framebuffer-coreboot.c
@@ -81,19 +81,10 @@ static int framebuffer_probe(struct core
sizeof(pdata));
if (IS_ERR(pdev))
pr_warn("coreboot: could not register framebuffer\n");
- else
- dev_set_drvdata(&dev->dev, pdev);
return PTR_ERR_OR_ZERO(pdev);
}
-static void framebuffer_remove(struct coreboot_device *dev)
-{
- struct platform_device *pdev = dev_get_drvdata(&dev->dev);
-
- platform_device_unregister(pdev);
-}
-
static const struct coreboot_device_id framebuffer_ids[] = {
{ .tag = CB_TAG_FRAMEBUFFER },
{ /* sentinel */ }
@@ -102,7 +93,6 @@ MODULE_DEVICE_TABLE(coreboot, framebuffe
static struct coreboot_driver framebuffer_driver = {
.probe = framebuffer_probe,
- .remove = framebuffer_remove,
.drv = {
.name = "framebuffer",
},
^ permalink raw reply
* [PATCH 7.0 179/307] firmware: google: framebuffer: Do not unregister platform device
From: Greg Kroah-Hartman @ 2026-05-04 13:51 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Thomas Zimmermann, Tzung-Bi Shih,
Julius Werner, Javier Martinez Canillas, Hans de Goede,
linux-fbdev
In-Reply-To: <20260504135142.814938198@linuxfoundation.org>
7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <tzimmermann@suse.de>
commit 5cd28bd28c8ce426b56ce4230dbd17537181d5ad upstream.
The native driver takes over the framebuffer aperture by removing the
system- framebuffer platform device. Afterwards the pointer in drvdata
is dangling. Remove the entire logic around drvdata and let the kernel's
aperture helpers handle this. The platform device depends on the native
hardware device instead of the coreboot device anyway.
When commit 851b4c14532d ("firmware: coreboot: Add coreboot framebuffer
driver") added the coreboot framebuffer code, the kernel did not support
device-based aperture management. Instead native driviers only removed
the conflicting fbdev device. At that point, unregistering the framebuffer
device most likely worked correctly. It was definitely broken after
commit d9702b2a2171 ("fbdev/simplefb: Do not use struct
fb_info.apertures"). So take this commit for the Fixes tag. Earlier
releases might work depending on the native hardware driver.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: d9702b2a2171 ("fbdev/simplefb: Do not use struct fb_info.apertures")
Acked-by: Tzung-Bi Shih <tzungbi@kernel.org>
Acked-by: Julius Werner <jwerner@chromium.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Hans de Goede <hansg@kernel.org>
Cc: linux-fbdev@vger.kernel.org
Cc: <stable@vger.kernel.org> # v6.3+
Link: https://patch.msgid.link/20260217155836.96267-2-tzimmermann@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/firmware/google/framebuffer-coreboot.c | 10 ----------
1 file changed, 10 deletions(-)
--- a/drivers/firmware/google/framebuffer-coreboot.c
+++ b/drivers/firmware/google/framebuffer-coreboot.c
@@ -81,19 +81,10 @@ static int framebuffer_probe(struct core
sizeof(pdata));
if (IS_ERR(pdev))
pr_warn("coreboot: could not register framebuffer\n");
- else
- dev_set_drvdata(&dev->dev, pdev);
return PTR_ERR_OR_ZERO(pdev);
}
-static void framebuffer_remove(struct coreboot_device *dev)
-{
- struct platform_device *pdev = dev_get_drvdata(&dev->dev);
-
- platform_device_unregister(pdev);
-}
-
static const struct coreboot_device_id framebuffer_ids[] = {
{ .tag = CB_TAG_FRAMEBUFFER },
{ /* sentinel */ }
@@ -102,7 +93,6 @@ MODULE_DEVICE_TABLE(coreboot, framebuffe
static struct coreboot_driver framebuffer_driver = {
.probe = framebuffer_probe,
- .remove = framebuffer_remove,
.drv = {
.name = "framebuffer",
},
^ permalink raw reply
* [PATCH 7.0 162/307] fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
From: Greg Kroah-Hartman @ 2026-05-04 13:50 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Thomas Zimmermann, Helge Deller,
linux-fbdev, dri-devel
In-Reply-To: <20260504135142.814938198@linuxfoundation.org>
7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <tzimmermann@suse.de>
commit 9ded47ad003f09a94b6a710b5c47f4aa5ceb7429 upstream.
Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an
instance as part of initializing deferred I/O and remove it only after
the final mapping has been closed. If the fb_info and the contained
deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info
to invalidate the mapping. Any access will then result in a SIGBUS
signal.
Fixes a long-standing problem, where a device hot-unplug happens while
user space still has an active mapping of the graphics memory. The hot-
unplug frees the instance of struct fb_info. Accessing the memory will
operate on undefined state.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support")
Cc: Helge Deller <deller@gmx.de>
Cc: linux-fbdev@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: stable@vger.kernel.org # v2.6.22+
Signed-off-by: Helge Deller <deller@gmx.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/video/fbdev/core/fb_defio.c | 178 ++++++++++++++++++++++++++++--------
include/linux/fb.h | 4
2 files changed, 145 insertions(+), 37 deletions(-)
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -24,6 +24,75 @@
#include <linux/rmap.h>
#include <linux/pagemap.h>
+/*
+ * struct fb_deferred_io_state
+ */
+
+struct fb_deferred_io_state {
+ struct kref ref;
+
+ struct mutex lock; /* mutex that protects the pageref list */
+ /* fields protected by lock */
+ struct fb_info *info;
+};
+
+static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void)
+{
+ struct fb_deferred_io_state *fbdefio_state;
+
+ fbdefio_state = kzalloc_obj(*fbdefio_state);
+ if (!fbdefio_state)
+ return NULL;
+
+ kref_init(&fbdefio_state->ref);
+ mutex_init(&fbdefio_state->lock);
+
+ return fbdefio_state;
+}
+
+static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state)
+{
+ mutex_destroy(&fbdefio_state->lock);
+
+ kfree(fbdefio_state);
+}
+
+static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_get(&fbdefio_state->ref);
+}
+
+static void __fb_deferred_io_state_release(struct kref *ref)
+{
+ struct fb_deferred_io_state *fbdefio_state =
+ container_of(ref, struct fb_deferred_io_state, ref);
+
+ fb_deferred_io_state_release(fbdefio_state);
+}
+
+static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state)
+{
+ kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release);
+}
+
+/*
+ * struct vm_operations_struct
+ */
+
+static void fb_deferred_io_vm_open(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_get(fbdefio_state);
+}
+
+static void fb_deferred_io_vm_close(struct vm_area_struct *vma)
+{
+ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
+
+ fb_deferred_io_state_put(fbdefio_state);
+}
+
static struct page *fb_deferred_io_get_page(struct fb_info *info, unsigned long offs)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
@@ -121,25 +190,46 @@ static void fb_deferred_io_pageref_put(s
/* this is to find and return the vmalloc-ed fb pages */
static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
{
+ struct fb_info *info;
unsigned long offset;
struct page *page;
- struct fb_info *info = vmf->vma->vm_private_data;
+ vm_fault_t ret;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
+
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= info->fix.smem_len)
- return VM_FAULT_SIGBUS;
+ if (offset >= info->fix.smem_len) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
page = fb_deferred_io_get_page(info, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
+ if (!page) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
if (!vmf->vma->vm_file)
fb_err(info, "no mapping available\n");
BUG_ON(!info->fbdefio->mapping);
+ mutex_unlock(&fbdefio_state->lock);
+
vmf->page = page;
+
return 0;
+
+err_mutex_unlock:
+ mutex_unlock(&fbdefio_state->lock);
+ return ret;
}
int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
@@ -166,15 +256,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
* Adds a page to the dirty list. Call this from struct
* vm_operations_struct.page_mkwrite.
*/
-static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
- struct page *page)
+static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state,
+ unsigned long offset, struct page *page)
{
- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_info *info;
+ struct fb_deferred_io *fbdefio;
struct fb_deferred_io_pageref *pageref;
vm_fault_t ret;
/* protect against the workqueue changing the page list */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
+
+ info = fbdefio_state->info;
+ if (!info) {
+ ret = VM_FAULT_SIGBUS; /* our device is gone */
+ goto err_mutex_unlock;
+ }
+
+ fbdefio = info->fbdefio;
pageref = fb_deferred_io_pageref_get(info, offset, page);
if (WARN_ON_ONCE(!pageref)) {
@@ -192,50 +291,38 @@ static vm_fault_t fb_deferred_io_track_p
*/
lock_page(pageref->page);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
/* come back after delay to process the deferred IO */
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
return VM_FAULT_LOCKED;
err_mutex_unlock:
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
return ret;
}
-/*
- * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O
- * @fb_info: The fbdev info structure
- * @vmf: The VM fault
- *
- * This is a callback we get when userspace first tries to
- * write to the page. We schedule a workqueue. That workqueue
- * will eventually mkclean the touched pages and execute the
- * deferred framebuffer IO. Then if userspace touches a page
- * again, we repeat the same scheme.
- *
- * Returns:
- * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
- */
-static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
+static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state,
+ struct vm_fault *vmf)
{
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
struct page *page = vmf->page;
file_update_time(vmf->vma->vm_file);
- return fb_deferred_io_track_page(info, offset, page);
+ return fb_deferred_io_track_page(fbdefio_state, offset, page);
}
-/* vm_ops->page_mkwrite handler */
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
{
- struct fb_info *info = vmf->vma->vm_private_data;
+ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
- return fb_deferred_io_page_mkwrite(info, vmf);
+ return fb_deferred_io_page_mkwrite(fbdefio_state, vmf);
}
static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+ .open = fb_deferred_io_vm_open,
+ .close = fb_deferred_io_vm_close,
.fault = fb_deferred_io_fault,
.page_mkwrite = fb_deferred_io_mkwrite,
};
@@ -252,7 +339,10 @@ int fb_deferred_io_mmap(struct fb_info *
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
if (!(info->flags & FBINFO_VIRTFB))
vm_flags_set(vma, VM_IO);
- vma->vm_private_data = info;
+ vma->vm_private_data = info->fbdefio_state;
+
+ fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */
+
return 0;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
@@ -263,9 +353,10 @@ static void fb_deferred_io_work(struct w
struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
struct fb_deferred_io_pageref *pageref, *next;
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
/* here we wrprotect the page's mappings, then do all deferred IO. */
- mutex_lock(&fbdefio->lock);
+ mutex_lock(&fbdefio_state->lock);
#ifdef CONFIG_MMU
list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
struct page *page = pageref->page;
@@ -283,12 +374,13 @@ static void fb_deferred_io_work(struct w
list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
fb_deferred_io_pageref_put(pageref, info);
- mutex_unlock(&fbdefio->lock);
+ mutex_unlock(&fbdefio_state->lock);
}
int fb_deferred_io_init(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
struct fb_deferred_io_pageref *pagerefs;
unsigned long npagerefs;
int ret;
@@ -298,7 +390,11 @@ int fb_deferred_io_init(struct fb_info *
if (WARN_ON(!info->fix.smem_len))
return -EINVAL;
- mutex_init(&fbdefio->lock);
+ fbdefio_state = fb_deferred_io_state_alloc();
+ if (!fbdefio_state)
+ return -ENOMEM;
+ fbdefio_state->info = info;
+
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
INIT_LIST_HEAD(&fbdefio->pagereflist);
if (fbdefio->delay == 0) /* set a default of 1 s */
@@ -315,10 +411,12 @@ int fb_deferred_io_init(struct fb_info *
info->npagerefs = npagerefs;
info->pagerefs = pagerefs;
+ info->fbdefio_state = fbdefio_state;
+
return 0;
err:
- mutex_destroy(&fbdefio->lock);
+ fb_deferred_io_state_release(fbdefio_state);
return ret;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
@@ -352,11 +450,19 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release
void fb_deferred_io_cleanup(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
fb_deferred_io_lastclose(info);
+ info->fbdefio_state = NULL;
+
+ mutex_lock(&fbdefio_state->lock);
+ fbdefio_state->info = NULL;
+ mutex_unlock(&fbdefio_state->lock);
+
+ fb_deferred_io_state_put(fbdefio_state);
+
kvfree(info->pagerefs);
- mutex_destroy(&fbdefio->lock);
fbdefio->mapping = NULL;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -218,13 +218,14 @@ struct fb_deferred_io {
unsigned long delay;
bool sort_pagereflist; /* sort pagelist by offset */
int open_count; /* number of opened files; protected by fb_info lock */
- struct mutex lock; /* mutex that protects the pageref list */
struct list_head pagereflist; /* list of pagerefs for touched pages */
struct address_space *mapping; /* page cache object for fb device */
/* callback */
struct page *(*get_page)(struct fb_info *info, unsigned long offset);
void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
};
+
+struct fb_deferred_io_state;
#endif
/*
@@ -487,6 +488,7 @@ struct fb_info {
unsigned long npagerefs;
struct fb_deferred_io_pageref *pagerefs;
struct fb_deferred_io *fbdefio;
+ struct fb_deferred_io_state *fbdefio_state;
#endif
const struct fb_ops *fbops;
^ permalink raw reply
* Re: [PATCH 1/3] staging: sm750fb: add const qualifier to string pointer arrays
From: Greg KH @ 2026-05-04 9:32 UTC (permalink / raw)
To: Francisco Maestre
Cc: sudipm.mukherjee, teddy.wang, linux-fbdev, linux-staging,
linux-kernel
In-Reply-To: <20260503004134.66693-1-francisco@maestretorreblanca.com>
On Sat, May 02, 2026 at 07:41:34PM -0500, Francisco Maestre wrote:
> Add 'const' qualifier to 'g_fbmode' and 'g_def_fbmode' pointer
> declarations to make them 'static const char * const', as the
> pointers themselves are not modified after initialization.
>
> This fixes the following checkpatch.pl warning:
> WARNING: static const char * array should probably be
> static const char * const
>
> Signed-off-by: Francisco Maestre <francisco@maestretorreblanca.com>
> ---
> drivers/staging/sm750fb/sm750.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
> index 62f6e0cdff4d..dc9653ed2108 100644
> --- a/drivers/staging/sm750fb/sm750.c
> +++ b/drivers/staging/sm750fb/sm750.c
> @@ -33,8 +33,8 @@
> static int g_hwcursor = 1;
> static int g_noaccel;
> static int g_nomtrr;
> -static const char *g_fbmode[] = {NULL, NULL};
> -static const char *g_def_fbmode = "1024x768-32@60";
> +static const char * const g_fbmode[] = {NULL, NULL};
> +static const char * const g_def_fbmode = "1024x768-32@60";
> static char *g_settings;
> static int g_dualview;
> static char *g_option;
> --
> 2.50.1 (Apple Git-155)
>
>
These are not all threaded together, can you please resend them all as a
correct patch series?
thanks,
greg k-h
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox