From: <gregkh@linuxfoundation.org>
To: deller@gmx.de, dri-devel@lists.freedesktop.org,
gregkh@linuxfoundation.org, sashal@kernel.org,
tzimmermann@suse.de
Cc: <stable-commits@vger.kernel.org>
Subject: Patch "fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info" has been added to the 6.18-stable tree
Date: Tue, 12 May 2026 19:25:20 +0200 [thread overview]
Message-ID: <2026051220-smuggler-nineteen-ec5a@gregkh> (raw)
In-Reply-To: <20260504141938.2378270-1-sashal@kernel.org>
This is a note to let you know that I've just added the patch titled
fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
to the 6.18-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch
and it can be found in the queue-6.18 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@vger.kernel.org> know about it.
From stable+bounces-243632-greg=kroah.com@vger.kernel.org Mon May 4 16:34:29 2026
From: Sasha Levin <sashal@kernel.org>
Date: Mon, 4 May 2026 10:19:38 -0400
Subject: fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
To: stable@vger.kernel.org
Cc: Thomas Zimmermann <tzimmermann@suse.de>, Helge Deller <deller@gmx.de>, linux-fbdev@vger.kernel.org, dri-devel@lists.freedesktop.org, Sasha Levin <sashal@kernel.org>
Message-ID: <20260504141938.2378270-1-sashal@kernel.org>
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>
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(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(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
@@ -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;
Patches currently in stable-queue which might be from sashal@kernel.org are
queue-6.18/mmc-core-optimize-time-for-secure-erase-trim-for-some-kingston-emmcs.patch
queue-6.18/dma-mapping-add-__dma_from_device_group_begin-end.patch
queue-6.18/mptcp-sync-the-msk-sndbuf-at-accept-time.patch
queue-6.18/mmc-core-add-quirk-for-incorrect-manufacturing-date.patch
queue-6.18/smb-client-smbdirect-fix-mr-registration-for-coalesc.patch
queue-6.18/kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch
queue-6.18/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch
queue-6.18/crypto-qat-fix-indentation-of-macros-in-qat_hal.c.patch
queue-6.18/crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch
queue-6.18/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch
queue-6.18/octeon_ep_vf-add-null-check-for-napi_build_skb.patch
queue-6.18/printk-add-print_hex_dump_devel.patch
queue-6.18/mm-swap-speed-up-hibernation-allocation-and-writeout.patch
queue-6.18/net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch
queue-6.18/firmware-exynos-acpm-drop-fake-const-on-handle-pointer.patch
queue-6.18/crypto-qat-fix-firmware-loading-failure-for-gen6-devices.patch
queue-6.18/flow_dissector-do-not-dissect-pppoe-pfc-frames.patch
queue-6.18/fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch
queue-6.18/erofs-tidy-up-z_erofs_lz4_handle_overlap.patch
queue-6.18/mmc-core-adjust-mdt-beyond-2025.patch
queue-6.18/erofs-fix-unsigned-underflow-in-z_erofs_lz4_handle_overlap.patch
queue-6.18/net-stmmac-prevent-null-deref-when-rx-memory-exhausted.patch
queue-6.18/ceph-fix-num_ops-off-by-one-when-crypto-allocation-f.patch
queue-6.18/net-stmmac-rename-stmmac_get_entry-stmmac_next_entry.patch
queue-6.18/hwmon-powerz-avoid-cacheline-sharing-for-dma-buffer.patch
prev parent reply other threads:[~2026-05-12 17:25 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-01 12:30 FAILED: patch "[PATCH] fbdev: defio: Disconnect deferred I/O from the lifetime of" failed to apply to 6.18-stable tree gregkh
2026-05-04 14:19 ` [PATCH 6.18.y] fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info Sasha Levin
2026-05-12 17:25 ` gregkh [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2026051220-smuggler-nineteen-ec5a@gregkh \
--to=gregkh@linuxfoundation.org \
--cc=deller@gmx.de \
--cc=dri-devel@lists.freedesktop.org \
--cc=sashal@kernel.org \
--cc=stable-commits@vger.kernel.org \
--cc=tzimmermann@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.