From 903c6e95bc9a4a3556d37e727853fc0ffb7f3acb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 12 Nov 2025 22:00:37 +0000 Subject: f2fs: add a tracepoint to see large folio read submission For example, 1327.539878: f2fs_preload_pages_start: dev = (252,16), ino = 14, i_size = 4294967296 start: 0, end: 8191 1327.539878: page_cache_sync_ra: dev=252:16 ino=e index=0 req_count=8192 order=9 size=0 async_size=0 ra_pages=4096 mmap_miss=0 prev_pos=-1 1327.539879: page_cache_ra_order: dev=252:16 ino=e index=0 order=9 size=4096 async_size=2048 ra_pages=4096 1327.541895: f2fs_readpages: dev = (252,16), ino = 14, start = 0 nrpage = 4096 1327.541930: f2fs_lookup_extent_tree_start: dev = (252,16), ino = 14, pgofs = 0, type = Read 1327.541931: f2fs_lookup_read_extent_tree_end: dev = (252,16), ino = 14, pgofs = 0, read_ext_info(fofs: 0, len: 1048576, blk: 4221440) 1327.541931: f2fs_map_blocks: dev = (252,16), ino = 14, file offset = 0, start blkaddr = 0x406a00, len = 0x1000, flags = 2, seg_type = 8, may_create = 0, multidevice = 0, flag = 0, err = 0 1327.541989: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 0, nr_pages = 512, dirty = 0, uptodate = 0 1327.542012: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 512, nr_pages = 512, dirty = 0, uptodate = 0 1327.542036: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 1024, nr_pages = 512, dirty = 0, uptodate = 0 1327.542080: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 1536, nr_pages = 512, dirty = 0, uptodate = 0 1327.542127: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 2048, nr_pages = 512, dirty = 0, uptodate = 0 1327.542151: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 2560, nr_pages = 512, dirty = 0, uptodate = 0 1327.542196: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 3072, nr_pages = 512, dirty = 0, uptodate = 0 1327.542219: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 3584, nr_pages = 512, dirty = 0, uptodate = 0 1327.542239: f2fs_submit_read_bio: dev = (252,16)/(252,16), rw = READ(R), DATA, sector = 33771520, size = 16777216 1327.542269: page_cache_sync_ra: dev=252:16 ino=e index=4096 req_count=8192 order=9 size=4096 async_size=2048 ra_pages=4096 mmap_miss=0 prev_pos=-1 1327.542289: page_cache_ra_order: dev=252:16 ino=e index=4096 order=9 size=4096 async_size=2048 ra_pages=4096 1327.544485: f2fs_readpages: dev = (252,16), ino = 14, start = 4096 nrpage = 4096 1327.544521: f2fs_lookup_extent_tree_start: dev = (252,16), ino = 14, pgofs = 4096, type = Read 1327.544521: f2fs_lookup_read_extent_tree_end: dev = (252,16), ino = 14, pgofs = 4096, read_ext_info(fofs: 0, len: 1048576, blk: 4221440) 1327.544522: f2fs_map_blocks: dev = (252,16), ino = 14, file offset = 4096, start blkaddr = 0x407a00, len = 0x1000, flags = 2, seg_type = 8, may_create = 0, multidevice = 0, flag = 0, err = 0 1327.544550: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 4096, nr_pages = 512, dirty = 0, uptodate = 0 1327.544575: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 4608, nr_pages = 512, dirty = 0, uptodate = 0 1327.544601: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 5120, nr_pages = 512, dirty = 0, uptodate = 0 1327.544647: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 5632, nr_pages = 512, dirty = 0, uptodate = 0 1327.544692: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 6144, nr_pages = 512, dirty = 0, uptodate = 0 1327.544734: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 6656, nr_pages = 512, dirty = 0, uptodate = 0 1327.544777: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 7168, nr_pages = 512, dirty = 0, uptodate = 0 1327.544805: f2fs_read_folio: dev = (252,16), ino = 14, DATA, FILE, index = 7680, nr_pages = 512, dirty = 0, uptodate = 0 1327.544826: f2fs_submit_read_bio: dev = (252,16)/(252,16), rw = READ(R), DATA, sector = 33804288, size = 16777216 1327.544852: f2fs_preload_pages_end: dev = (252,16), ino = 14, i_size = 4294967296 start: 8192, end: 8191 Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- include/trace/events/f2fs.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'include/trace/events') diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index df4017dcc701..635dcabcf1c7 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1358,6 +1358,7 @@ DECLARE_EVENT_CLASS(f2fs__folio, __field(int, type) __field(int, dir) __field(pgoff_t, index) + __field(pgoff_t, nrpages) __field(int, dirty) __field(int, uptodate) ), @@ -1368,16 +1369,18 @@ DECLARE_EVENT_CLASS(f2fs__folio, __entry->type = type; __entry->dir = S_ISDIR(folio->mapping->host->i_mode); __entry->index = folio->index; + __entry->nrpages= folio_nr_pages(folio); __entry->dirty = folio_test_dirty(folio); __entry->uptodate = folio_test_uptodate(folio); ), - TP_printk("dev = (%d,%d), ino = %lu, %s, %s, index = %lu, " + TP_printk("dev = (%d,%d), ino = %lu, %s, %s, index = %lu, nr_pages = %lu, " "dirty = %d, uptodate = %d", show_dev_ino(__entry), show_block_type(__entry->type), show_file_type(__entry->dir), (unsigned long)__entry->index, + (unsigned long)__entry->nrpages, __entry->dirty, __entry->uptodate) ); @@ -1403,6 +1406,13 @@ DEFINE_EVENT(f2fs__folio, f2fs_readpage, TP_ARGS(folio, type) ); +DEFINE_EVENT(f2fs__folio, f2fs_read_folio, + + TP_PROTO(struct folio *folio, int type), + + TP_ARGS(folio, type) +); + DEFINE_EVENT(f2fs__folio, f2fs_set_page_dirty, TP_PROTO(struct folio *folio, int type), -- cgit v1.2.3 From 281a226314238e8adb08d11b765347687ffb38b9 Mon Sep 17 00:00:00 2001 From: gaoxiang17 Date: Thu, 18 Dec 2025 14:28:53 +0800 Subject: dma-buf: add some tracepoints to debug. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we can only inspect dmabuf by iterating over process FDs or the dmabuf_list, we need to add our own tracepoints to track its status in real time in production. For example: binder:3016_1-3102 [006] ...1. 255.126521: dma_buf_export: exp_name=qcom,system size=12685312 ino=2738 binder:3016_1-3102 [006] ...1. 255.126528: dma_buf_fd: exp_name=qcom,system size=12685312 ino=2738 fd=8 binder:3016_1-3102 [006] ...1. 255.126642: dma_buf_mmap_internal: exp_name=qcom,system size=28672 ino=2739 kworker/6:1-86 [006] ...1. 255.127194: dma_buf_put: exp_name=qcom,system size=12685312 ino=2738 RenderThread-9293 [006] ...1. 316.618179: dma_buf_get: exp_name=qcom,system size=12771328 ino=2762 fd=176 RenderThread-9293 [006] ...1. 316.618195: dma_buf_dynamic_attach: exp_name=qcom,system size=12771328 ino=2762 attachment:ffffff880a18dd00 is_dynamic=0 dev_name=kgsl-3d0 RenderThread-9293 [006] ...1. 318.878220: dma_buf_detach: exp_name=qcom,system size=12771328 ino=2762 attachment:ffffff880a18dd00 is_dynamic=0 dev_name=kgsl-3d0 Signed-off-by: Xiang Gao Reviewed-by: Christian König Link: https://lore.kernel.org/r/20251218062853.819744-1-gxxa03070307@gmail.com Signed-off-by: Christian König --- drivers/dma-buf/dma-buf.c | 51 ++++++++++++- include/trace/events/dma_buf.h | 157 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 include/trace/events/dma_buf.h (limited to 'include/trace/events') diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 4a7716a5b7c6..a4d8f2ff94e4 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -35,6 +35,28 @@ #include "dma-buf-sysfs-stats.h" +#define CREATE_TRACE_POINTS +#include + +/* + * dmabuf->name must be accessed with holding dmabuf->name_lock. + * we need to take the lock around the tracepoint call itself where + * it is called in the code. + * + * Note: FUNC##_enabled() is a static branch that will only + * be set when the trace event is enabled. + */ +#define DMA_BUF_TRACE(FUNC, ...) \ + do { \ + if (FUNC##_enabled()) { \ + guard(spinlock)(&dmabuf->name_lock); \ + FUNC(__VA_ARGS__); \ + } else if (IS_ENABLED(CONFIG_LOCKDEP)) { \ + /* Expose this lock when lockdep is enabled */ \ + guard(spinlock)(&dmabuf->name_lock); \ + } \ + } while (0) + /* Wrapper to hide the sg_table page link from the importer */ struct dma_buf_sg_table_wrapper { struct sg_table *original; @@ -226,6 +248,8 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) dmabuf->size >> PAGE_SHIFT) return -EINVAL; + DMA_BUF_TRACE(trace_dma_buf_mmap_internal, dmabuf); + return dmabuf->ops->mmap(dmabuf, vma); } @@ -751,6 +775,8 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) __dma_buf_list_add(dmabuf); + DMA_BUF_TRACE(trace_dma_buf_export, dmabuf); + return dmabuf; err_dmabuf: @@ -774,10 +800,16 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_export, "DMA_BUF"); */ int dma_buf_fd(struct dma_buf *dmabuf, int flags) { + int fd; + if (!dmabuf || !dmabuf->file) return -EINVAL; - return FD_ADD(flags, dmabuf->file); + fd = FD_ADD(flags, dmabuf->file); + if (fd >= 0) + DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); + + return fd; } EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); @@ -792,6 +824,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); struct dma_buf *dma_buf_get(int fd) { struct file *file; + struct dma_buf *dmabuf; file = fget(fd); @@ -803,7 +836,11 @@ struct dma_buf *dma_buf_get(int fd) return ERR_PTR(-EINVAL); } - return file->private_data; + dmabuf = file->private_data; + + DMA_BUF_TRACE(trace_dma_buf_get, dmabuf, fd); + + return dmabuf; } EXPORT_SYMBOL_NS_GPL(dma_buf_get, "DMA_BUF"); @@ -823,6 +860,8 @@ void dma_buf_put(struct dma_buf *dmabuf) return; fput(dmabuf->file); + + DMA_BUF_TRACE(trace_dma_buf_put, dmabuf); } EXPORT_SYMBOL_NS_GPL(dma_buf_put, "DMA_BUF"); @@ -1015,6 +1054,9 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, list_add(&attach->node, &dmabuf->attachments); dma_resv_unlock(dmabuf->resv); + DMA_BUF_TRACE(trace_dma_buf_dynamic_attach, dmabuf, attach, + dma_buf_attachment_is_dynamic(attach), dev); + return attach; err_attach: @@ -1059,6 +1101,9 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) if (dmabuf->ops->detach) dmabuf->ops->detach(dmabuf, attach); + DMA_BUF_TRACE(trace_dma_buf_detach, dmabuf, attach, + dma_buf_attachment_is_dynamic(attach), attach->dev); + kfree(attach); } EXPORT_SYMBOL_NS_GPL(dma_buf_detach, "DMA_BUF"); @@ -1525,6 +1570,8 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, vma_set_file(vma, dmabuf->file); vma->vm_pgoff = pgoff; + DMA_BUF_TRACE(trace_dma_buf_mmap, dmabuf); + return dmabuf->ops->mmap(dmabuf, vma); } EXPORT_SYMBOL_NS_GPL(dma_buf_mmap, "DMA_BUF"); diff --git a/include/trace/events/dma_buf.h b/include/trace/events/dma_buf.h new file mode 100644 index 000000000000..35f8140095f4 --- /dev/null +++ b/include/trace/events/dma_buf.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM dma_buf + +#if !defined(_TRACE_DMA_BUF_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_DMA_BUF_H + +#include +#include + +DECLARE_EVENT_CLASS(dma_buf, + + TP_PROTO(struct dma_buf *dmabuf), + + TP_ARGS(dmabuf), + + TP_STRUCT__entry( + __string(exp_name, dmabuf->exp_name) + __field(size_t, size) + __field(ino_t, ino) + ), + + TP_fast_assign( + __assign_str(exp_name); + __entry->size = dmabuf->size; + __entry->ino = dmabuf->file->f_inode->i_ino; + ), + + TP_printk("exp_name=%s size=%zu ino=%lu", + __get_str(exp_name), + __entry->size, + __entry->ino) +); + +DECLARE_EVENT_CLASS(dma_buf_attach_dev, + + TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach, + bool is_dynamic, struct device *dev), + + TP_ARGS(dmabuf, attach, is_dynamic, dev), + + TP_STRUCT__entry( + __string(dev_name, dev_name(dev)) + __string(exp_name, dmabuf->exp_name) + __field(size_t, size) + __field(ino_t, ino) + __field(struct dma_buf_attachment *, attach) + __field(bool, is_dynamic) + ), + + TP_fast_assign( + __assign_str(dev_name); + __assign_str(exp_name); + __entry->size = dmabuf->size; + __entry->ino = dmabuf->file->f_inode->i_ino; + __entry->is_dynamic = is_dynamic; + __entry->attach = attach; + ), + + TP_printk("exp_name=%s size=%zu ino=%lu attachment:%p is_dynamic=%d dev_name=%s", + __get_str(exp_name), + __entry->size, + __entry->ino, + __entry->attach, + __entry->is_dynamic, + __get_str(dev_name)) +); + +DECLARE_EVENT_CLASS(dma_buf_fd, + + TP_PROTO(struct dma_buf *dmabuf, int fd), + + TP_ARGS(dmabuf, fd), + + TP_STRUCT__entry( + __string(exp_name, dmabuf->exp_name) + __field(size_t, size) + __field(ino_t, ino) + __field(int, fd) + ), + + TP_fast_assign( + __assign_str(exp_name); + __entry->size = dmabuf->size; + __entry->ino = dmabuf->file->f_inode->i_ino; + __entry->fd = fd; + ), + + TP_printk("exp_name=%s size=%zu ino=%lu fd=%d", + __get_str(exp_name), + __entry->size, + __entry->ino, + __entry->fd) +); + +DEFINE_EVENT(dma_buf, dma_buf_export, + + TP_PROTO(struct dma_buf *dmabuf), + + TP_ARGS(dmabuf) +); + +DEFINE_EVENT(dma_buf, dma_buf_mmap_internal, + + TP_PROTO(struct dma_buf *dmabuf), + + TP_ARGS(dmabuf) +); + +DEFINE_EVENT(dma_buf, dma_buf_mmap, + + TP_PROTO(struct dma_buf *dmabuf), + + TP_ARGS(dmabuf) +); + +DEFINE_EVENT(dma_buf, dma_buf_put, + + TP_PROTO(struct dma_buf *dmabuf), + + TP_ARGS(dmabuf) +); + +DEFINE_EVENT(dma_buf_attach_dev, dma_buf_dynamic_attach, + + TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach, + bool is_dynamic, struct device *dev), + + TP_ARGS(dmabuf, attach, is_dynamic, dev) +); + +DEFINE_EVENT(dma_buf_attach_dev, dma_buf_detach, + + TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach, + bool is_dynamic, struct device *dev), + + TP_ARGS(dmabuf, attach, is_dynamic, dev) +); + +DEFINE_EVENT(dma_buf_fd, dma_buf_fd, + + TP_PROTO(struct dma_buf *dmabuf, int fd), + + TP_ARGS(dmabuf, fd) +); + +DEFINE_EVENT(dma_buf_fd, dma_buf_get, + + TP_PROTO(struct dma_buf *dmabuf, int fd), + + TP_ARGS(dmabuf, fd) +); + +#endif /* _TRACE_DMA_BUF_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From 9e541b3cee70a3bbe86b176c903c23b29fe033cd Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Wed, 10 Dec 2025 21:29:05 +0800 Subject: PCI: trace: Add generic RAS tracepoint for hotplug event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hotplug events are critical indicators for analyzing hardware health, and surprise link downs can significantly impact system performance and reliability. Define a new TRACING_SYSTEM named "pci", add a generic RAS tracepoint for hotplug event to help health checks. Add enum pci_hotplug_event in include/uapi/linux/pci.h so applications like rasdaemon can register tracepoint event handlers for it. The following output is generated when a device is hotplugged: $ echo 1 > /sys/kernel/debug/tracing/events/pci/pci_hp_event/enable $ cat /sys/kernel/debug/tracing/trace_pipe irq/51-pciehp-88 [001] ..... 1311.177459: pci_hp_event: 0000:00:02.0 slot:10, event:CARD_PRESENT irq/51-pciehp-88 [001] ..... 1311.177566: pci_hp_event: 0000:00:02.0 slot:10, event:LINK_UP Suggested-by: Lukas Wunner Signed-off-by: Shuai Xue Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner Reviewed-by: Jonathan Cameron Reviewed-by: Steven Rostedt (Google) # for trace event Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20251210132907.58799-2-xueshuai@linux.alibaba.com --- drivers/pci/Makefile | 3 ++ drivers/pci/hotplug/pciehp_ctrl.c | 31 +++++++++++++---- drivers/pci/trace.c | 11 ++++++ include/trace/events/pci.h | 72 +++++++++++++++++++++++++++++++++++++++ include/uapi/linux/pci.h | 7 ++++ 5 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 drivers/pci/trace.c create mode 100644 include/trace/events/pci.h (limited to 'include/trace/events') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e10cfe5a280b..8c259a9a8796 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -47,3 +47,6 @@ obj-y += controller/ obj-y += switch/ subdir-ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG + +CFLAGS_trace.o := -I$(src) +obj-$(CONFIG_TRACING) += trace.o diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index bcc938d4420f..7805f697a02c 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../pci.h" #include "pciehp.h" @@ -244,12 +245,20 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) case ON_STATE: ctrl->state = POWEROFF_STATE; mutex_unlock(&ctrl->state_lock); - if (events & PCI_EXP_SLTSTA_DLLSC) + if (events & PCI_EXP_SLTSTA_DLLSC) { ctrl_info(ctrl, "Slot(%s): Link Down\n", slot_name(ctrl)); - if (events & PCI_EXP_SLTSTA_PDC) + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_LINK_DOWN); + } + if (events & PCI_EXP_SLTSTA_PDC) { ctrl_info(ctrl, "Slot(%s): Card not present\n", slot_name(ctrl)); + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_CARD_NOT_PRESENT); + } pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); break; default: @@ -269,6 +278,9 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) INDICATOR_NOOP); ctrl_info(ctrl, "Slot(%s): Card not present\n", slot_name(ctrl)); + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_CARD_NOT_PRESENT); } mutex_unlock(&ctrl->state_lock); return; @@ -281,12 +293,19 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) case OFF_STATE: ctrl->state = POWERON_STATE; mutex_unlock(&ctrl->state_lock); - if (present) + if (present) { ctrl_info(ctrl, "Slot(%s): Card present\n", slot_name(ctrl)); - if (link_active) - ctrl_info(ctrl, "Slot(%s): Link Up\n", - slot_name(ctrl)); + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_CARD_PRESENT); + } + if (link_active) { + ctrl_info(ctrl, "Slot(%s): Link Up\n", slot_name(ctrl)); + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_LINK_UP); + } ctrl->request_result = pciehp_enable_slot(ctrl); break; default: diff --git a/drivers/pci/trace.c b/drivers/pci/trace.c new file mode 100644 index 000000000000..cf11abca8602 --- /dev/null +++ b/drivers/pci/trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Tracepoints for PCI system + * + * Copyright (C) 2025 Alibaba Corporation + */ + +#include + +#define CREATE_TRACE_POINTS +#include diff --git a/include/trace/events/pci.h b/include/trace/events/pci.h new file mode 100644 index 000000000000..39e512a167ee --- /dev/null +++ b/include/trace/events/pci.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM pci + +#if !defined(_TRACE_HW_EVENT_PCI_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HW_EVENT_PCI_H + +#include + +#define PCI_HOTPLUG_EVENT \ + EM(PCI_HOTPLUG_LINK_UP, "LINK_UP") \ + EM(PCI_HOTPLUG_LINK_DOWN, "LINK_DOWN") \ + EM(PCI_HOTPLUG_CARD_PRESENT, "CARD_PRESENT") \ + EMe(PCI_HOTPLUG_CARD_NOT_PRESENT, "CARD_NOT_PRESENT") + +/* Enums require being exported to userspace, for user tool parsing */ +#undef EM +#undef EMe +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define EMe(a, b) TRACE_DEFINE_ENUM(a); + +PCI_HOTPLUG_EVENT + +/* + * Now redefine the EM() and EMe() macros to map the enums to the strings + * that will be printed in the output. + */ +#undef EM +#undef EMe +#define EM(a, b) {a, b}, +#define EMe(a, b) {a, b} + +/* + * Note: For generic PCI hotplug events, we pass already-resolved strings + * (port_name, slot) instead of driver-specific structures like 'struct + * controller'. This is because different PCI hotplug drivers (pciehp, cpqphp, + * ibmphp, shpchp) define their own versions of 'struct controller' with + * different fields and helper functions. Using driver-specific structures would + * make the tracepoint interface non-generic and cause compatibility issues + * across different drivers. + */ +TRACE_EVENT(pci_hp_event, + + TP_PROTO(const char *port_name, + const char *slot, + const int event), + + TP_ARGS(port_name, slot, event), + + TP_STRUCT__entry( + __string( port_name, port_name ) + __string( slot, slot ) + __field( int, event ) + ), + + TP_fast_assign( + __assign_str(port_name); + __assign_str(slot); + __entry->event = event; + ), + + TP_printk("%s slot:%s, event:%s\n", + __get_str(port_name), + __get_str(slot), + __print_symbolic(__entry->event, PCI_HOTPLUG_EVENT) + ) +); + +#endif /* _TRACE_HW_EVENT_PCI_H */ + +/* This part must be outside protection */ +#include diff --git a/include/uapi/linux/pci.h b/include/uapi/linux/pci.h index a769eefc5139..4f150028965d 100644 --- a/include/uapi/linux/pci.h +++ b/include/uapi/linux/pci.h @@ -39,4 +39,11 @@ #define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02) /* Set mmap state to MEM space. */ #define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03) /* Enable/disable write-combining. */ +enum pci_hotplug_event { + PCI_HOTPLUG_LINK_UP, + PCI_HOTPLUG_LINK_DOWN, + PCI_HOTPLUG_CARD_PRESENT, + PCI_HOTPLUG_CARD_NOT_PRESENT, +}; + #endif /* _UAPILINUX_PCI_H */ -- cgit v1.2.3 From d4318c1a79ac49f0726dd23a01d1961757b5f98d Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Wed, 10 Dec 2025 21:29:06 +0800 Subject: PCI: trace: Add RAS tracepoint to monitor link speed changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCIe link speed degradation directly impacts system performance and often indicates hardware issues such as faulty devices, physical layer problems, or configuration errors. To this end, add a RAS tracepoint to monitor link speed changes, enabling proactive health checks and diagnostic analysis. The following output is generated when a device is hotplugged: $ echo 1 > /sys/kernel/debug/tracing/events/pci/pcie_link_event/enable $ cat /sys/kernel/debug/tracing/trace_pipe irq/51-pciehp-88 [001] ..... 381.545386: pcie_link_event: 0000:00:02.0 type:4, reason:4, cur_bus_speed:20, max_bus_speed:23, width:1, flit_mode:0, status:DLLLA Suggested-by: Ilpo Järvinen Suggested-by: Matthew W Carlis Suggested-by: Lukas Wunner Signed-off-by: Shuai Xue Signed-off-by: Bjorn Helgaas Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20251210132907.58799-3-xueshuai@linux.alibaba.com --- drivers/pci/hotplug/pciehp_hpc.c | 3 ++- drivers/pci/pci.c | 2 +- drivers/pci/pci.h | 21 +++++++++++++-- drivers/pci/pcie/bwctrl.c | 4 +-- drivers/pci/probe.c | 9 ++++--- include/trace/events/pci.h | 57 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 87 insertions(+), 9 deletions(-) (limited to 'include/trace/events') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index bcc51b26d03d..ad5f28f6a8b1 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -320,7 +320,8 @@ int pciehp_check_link_status(struct controller *ctrl) } pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &linksta2); - __pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status, linksta2); + __pcie_update_link_speed(ctrl->pcie->port->subordinate, PCIE_HOTPLUG, + lnk_status, linksta2); if (!found) { ctrl_info(ctrl, "Slot(%s): No device found\n", diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 13dbb405dc31..f034e173819f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4550,7 +4550,7 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt) * Link Speed. */ if (pdev->subordinate) - pcie_update_link_speed(pdev->subordinate); + pcie_update_link_speed(pdev->subordinate, PCIE_LINK_RETRAIN); return rc; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0e67014aa001..c71cfbe78cc3 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -5,6 +5,7 @@ #include #include #include +#include struct pcie_tlp_log; @@ -555,12 +556,28 @@ const char *pci_speed_string(enum pci_bus_speed speed); void __pcie_print_link_status(struct pci_dev *dev, bool verbose); void pcie_report_downtraining(struct pci_dev *dev); -static inline void __pcie_update_link_speed(struct pci_bus *bus, u16 linksta, u16 linksta2) +enum pcie_link_change_reason { + PCIE_LINK_RETRAIN, + PCIE_ADD_BUS, + PCIE_BWCTRL_ENABLE, + PCIE_BWCTRL_IRQ, + PCIE_HOTPLUG, +}; + +static inline void __pcie_update_link_speed(struct pci_bus *bus, + enum pcie_link_change_reason reason, + u16 linksta, u16 linksta2) { bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS]; bus->flit_mode = (linksta2 & PCI_EXP_LNKSTA2_FLIT) ? 1 : 0; + + trace_pcie_link_event(bus, + reason, + FIELD_GET(PCI_EXP_LNKSTA_NLW, linksta), + linksta & PCI_EXP_LNKSTA_LINK_STATUS_MASK); } -void pcie_update_link_speed(struct pci_bus *bus); + +void pcie_update_link_speed(struct pci_bus *bus, enum pcie_link_change_reason reason); /* Single Root I/O Virtualization */ struct pci_sriov { diff --git a/drivers/pci/pcie/bwctrl.c b/drivers/pci/pcie/bwctrl.c index 36f939f23d34..32f1b30ecb84 100644 --- a/drivers/pci/pcie/bwctrl.c +++ b/drivers/pci/pcie/bwctrl.c @@ -199,7 +199,7 @@ static void pcie_bwnotif_enable(struct pcie_device *srv) * Update after enabling notifications & clearing status bits ensures * link speed is up to date. */ - pcie_update_link_speed(port->subordinate); + pcie_update_link_speed(port->subordinate, PCIE_BWCTRL_ENABLE); } static void pcie_bwnotif_disable(struct pci_dev *port) @@ -234,7 +234,7 @@ static irqreturn_t pcie_bwnotif_irq(int irq, void *context) * speed (inside pcie_update_link_speed()) after LBMS has been * cleared to avoid missing link speed changes. */ - pcie_update_link_speed(port->subordinate); + pcie_update_link_speed(port->subordinate, PCIE_BWCTRL_IRQ); return IRQ_HANDLED; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 41183aed8f5d..392b7dc3d391 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "pci.h" #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ @@ -824,14 +825,16 @@ const char *pci_speed_string(enum pci_bus_speed speed) } EXPORT_SYMBOL_GPL(pci_speed_string); -void pcie_update_link_speed(struct pci_bus *bus) +void pcie_update_link_speed(struct pci_bus *bus, + enum pcie_link_change_reason reason) { struct pci_dev *bridge = bus->self; u16 linksta, linksta2; pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); pcie_capability_read_word(bridge, PCI_EXP_LNKSTA2, &linksta2); - __pcie_update_link_speed(bus, linksta, linksta2); + + __pcie_update_link_speed(bus, reason, linksta, linksta2); } EXPORT_SYMBOL_GPL(pcie_update_link_speed); @@ -918,7 +921,7 @@ static void pci_set_bus_speed(struct pci_bus *bus) pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap); bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS]; - pcie_update_link_speed(bus); + pcie_update_link_speed(bus, PCIE_ADD_BUS); } } diff --git a/include/trace/events/pci.h b/include/trace/events/pci.h index 39e512a167ee..9a9122f62fd3 100644 --- a/include/trace/events/pci.h +++ b/include/trace/events/pci.h @@ -5,6 +5,7 @@ #if !defined(_TRACE_HW_EVENT_PCI_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_HW_EVENT_PCI_H +#include #include #define PCI_HOTPLUG_EVENT \ @@ -66,6 +67,62 @@ TRACE_EVENT(pci_hp_event, ) ); +#define PCI_EXP_LNKSTA_LINK_STATUS_MASK (PCI_EXP_LNKSTA_LBMS | \ + PCI_EXP_LNKSTA_LABS | \ + PCI_EXP_LNKSTA_LT | \ + PCI_EXP_LNKSTA_DLLLA) + +#define LNKSTA_FLAGS \ + { PCI_EXP_LNKSTA_LT, "LT"}, \ + { PCI_EXP_LNKSTA_DLLLA, "DLLLA"}, \ + { PCI_EXP_LNKSTA_LBMS, "LBMS"}, \ + { PCI_EXP_LNKSTA_LABS, "LABS"} + +TRACE_EVENT(pcie_link_event, + + TP_PROTO(struct pci_bus *bus, + unsigned int reason, + unsigned int width, + unsigned int status + ), + + TP_ARGS(bus, reason, width, status), + + TP_STRUCT__entry( + __string( port_name, pci_name(bus->self)) + __field( unsigned int, type ) + __field( unsigned int, reason ) + __field( unsigned int, cur_bus_speed ) + __field( unsigned int, max_bus_speed ) + __field( unsigned int, width ) + __field( unsigned int, flit_mode ) + __field( unsigned int, link_status ) + ), + + TP_fast_assign( + __assign_str(port_name); + __entry->type = pci_pcie_type(bus->self); + __entry->reason = reason; + __entry->cur_bus_speed = bus->cur_bus_speed; + __entry->max_bus_speed = bus->max_bus_speed; + __entry->width = width; + __entry->flit_mode = bus->flit_mode; + __entry->link_status = status; + ), + + TP_printk("%s type:%d, reason:%d, cur_bus_speed:%d, max_bus_speed:%d, width:%u, flit_mode:%u, status:%s\n", + __get_str(port_name), + __entry->type, + __entry->reason, + __entry->cur_bus_speed, + __entry->max_bus_speed, + __entry->width, + __entry->flit_mode, + __print_flags((unsigned long)__entry->link_status, "|", + LNKSTA_FLAGS) + ) +); + #endif /* _TRACE_HW_EVENT_PCI_H */ /* This part must be outside protection */ -- cgit v1.2.3 From 79b3cebc70fcadf914d3ad1ae59d59cc62a47c46 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 4 Jan 2026 10:07:16 +0800 Subject: f2fs: add lock elapsed time trace facility for f2fs rwsemphore This patch adds lock elapsed time trace facility for f2fs rwsemphore. If total elapsed time of critical region covered by lock exceeds a threshold, it will print tracepoint to dump information of lock related context, including: - thread information - CPU/IO priority - lock information - elapsed time - total time - running time (depend on CONFIG_64BIT) - runnable time (depend on CONFIG_SCHED_INFO and CONFIG_SCHEDSTATS) - io sleep time (depend on CONFIG_TASK_DELAY_ACCT and /proc/sys/kernel/task_delayacct) - other time (by default other time will account nonio sleep time, but, if above kconfig is not defined, other time will include runnable time and/or io sleep time as wll) output: f2fs_lock_elapsed_time: dev = (254,52), comm: sh, pid: 13855, prio: 120, ioprio_class: 2, ioprio_data: 4, lock_name: cp_rwsem, lock_type: rlock, total: 1000, running: 993, runnable: 2, io_sleep: 0, other: 5 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 106 ++++++++++++++++++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 51 +++++++++++++++++++-- include/trace/events/f2fs.h | 68 ++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 4 deletions(-) (limited to 'include/trace/events') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 300664269eb6..bc6058a3122b 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include "f2fs.h" #include "node.h" @@ -21,6 +24,109 @@ #include "iostat.h" #include +static inline void get_lock_elapsed_time(struct f2fs_time_stat *ts) +{ + ts->total_time = ktime_get(); +#ifdef CONFIG_64BIT + ts->running_time = current->se.sum_exec_runtime; +#endif +#if defined(CONFIG_SCHED_INFO) && defined(CONFIG_SCHEDSTATS) + ts->runnable_time = current->sched_info.run_delay; +#endif +#ifdef CONFIG_TASK_DELAY_ACCT + if (current->delays) + ts->io_sleep_time = current->delays->blkio_delay; +#endif +} + +static inline void trace_lock_elapsed_time_start(struct f2fs_rwsem *sem, + struct f2fs_lock_context *lc) +{ + lc->lock_trace = trace_f2fs_lock_elapsed_time_enabled(); + if (!lc->lock_trace) + return; + + get_lock_elapsed_time(&lc->ts); +} + +static inline void trace_lock_elapsed_time_end(struct f2fs_rwsem *sem, + struct f2fs_lock_context *lc, bool is_write) +{ + struct f2fs_time_stat tts; + unsigned long long total_time; + unsigned long long running_time = 0; + unsigned long long runnable_time = 0; + unsigned long long io_sleep_time = 0; + unsigned long long other_time = 0; + unsigned npm = NSEC_PER_MSEC; + + if (!lc->lock_trace) + return; + + get_lock_elapsed_time(&tts); + + total_time = div_u64(tts.total_time - lc->ts.total_time, npm); + if (total_time <= MAX_LOCK_ELAPSED_TIME) + return; + +#ifdef CONFIG_64BIT + running_time = div_u64(tts.running_time - lc->ts.running_time, npm); +#endif +#if defined(CONFIG_SCHED_INFO) && defined(CONFIG_SCHEDSTATS) + runnable_time = div_u64(tts.runnable_time - lc->ts.runnable_time, npm); +#endif +#ifdef CONFIG_TASK_DELAY_ACCT + io_sleep_time = div_u64(tts.io_sleep_time - lc->ts.io_sleep_time, npm); +#endif + if (total_time > running_time + io_sleep_time + runnable_time) + other_time = total_time - running_time - + io_sleep_time - runnable_time; + + trace_f2fs_lock_elapsed_time(sem->sbi, sem->name, is_write, current, + get_current_ioprio(), total_time, running_time, + runnable_time, io_sleep_time, other_time); +} + +void f2fs_down_read_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) +{ + f2fs_down_read(sem); + trace_lock_elapsed_time_start(sem, lc); +} + +int f2fs_down_read_trylock_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) +{ + if (!f2fs_down_read_trylock(sem)) + return 0; + trace_lock_elapsed_time_start(sem, lc); + return 1; +} + +void f2fs_up_read_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) +{ + f2fs_up_read(sem); + trace_lock_elapsed_time_end(sem, lc, false); +} + +void f2fs_down_write_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) +{ + f2fs_down_write(sem); + trace_lock_elapsed_time_start(sem, lc); +} + +int f2fs_down_write_trylock_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) +{ + if (!f2fs_down_write_trylock(sem)) + return 0; + trace_lock_elapsed_time_start(sem, lc); + return 1; +} + +void f2fs_up_write_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) +{ + f2fs_up_write(sem); + trace_lock_elapsed_time_end(sem, lc, true); +} + #define DEFAULT_CHECKPOINT_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 3)) static struct kmem_cache *ino_entry_slab; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5d81de7cee70..fc3f532972ed 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -173,6 +173,10 @@ enum device_allocation_policy { ALLOCATE_FORWARD_FROM_HINT, }; +enum f2fs_lock_name { + LOCK_NAME_NONE, +}; + /* * An implementation of an rwsem that is explicitly unfair to readers. This * prevents priority inversion when a low-priority reader acquires the read lock @@ -181,6 +185,8 @@ enum device_allocation_policy { */ struct f2fs_rwsem { + struct f2fs_sb_info *sbi; + enum f2fs_lock_name name; struct rw_semaphore internal_rwsem; #ifdef CONFIG_F2FS_UNFAIR_RWSEM wait_queue_head_t read_waiters; @@ -1409,6 +1415,24 @@ struct f2fs_gc_control { unsigned int nr_free_secs; /* # of free sections to do GC */ }; +struct f2fs_time_stat { + unsigned long long total_time; /* total wall clock time */ +#ifdef CONFIG_64BIT + unsigned long long running_time; /* running time */ +#endif +#if defined(CONFIG_SCHED_INFO) && defined(CONFIG_SCHEDSTATS) + unsigned long long runnable_time; /* runnable(including preempted) time */ +#endif +#ifdef CONFIG_TASK_DELAY_ACCT + unsigned long long io_sleep_time; /* IO sleep time */ +#endif +}; + +struct f2fs_lock_context { + struct f2fs_time_stat ts; + bool lock_trace; +}; + /* * For s_flag in struct f2fs_sb_info * Modification on enum should be synchronized with s_flag array @@ -1525,6 +1549,9 @@ enum f2fs_lookup_mode { LOOKUP_AUTO, }; +/* a threshold of maximum elapsed time in critical region to print tracepoint */ +#define MAX_LOCK_ELAPSED_TIME 500 + static inline int f2fs_test_bit(unsigned int nr, char *addr); static inline void f2fs_set_bit(unsigned int nr, char *addr); static inline void f2fs_clear_bit(unsigned int nr, char *addr); @@ -2263,16 +2290,22 @@ static inline void clear_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) spin_unlock_irqrestore(&sbi->cp_lock, flags); } -#define init_f2fs_rwsem(sem) \ +#define init_f2fs_rwsem(sem) __init_f2fs_rwsem(sem, NULL, LOCK_NAME_NONE) +#define init_f2fs_rwsem_trace __init_f2fs_rwsem + +#define __init_f2fs_rwsem(sem, sbi, name) \ do { \ static struct lock_class_key __key; \ \ - __init_f2fs_rwsem((sem), #sem, &__key); \ + do_init_f2fs_rwsem((sem), #sem, &__key, sbi, name); \ } while (0) -static inline void __init_f2fs_rwsem(struct f2fs_rwsem *sem, - const char *sem_name, struct lock_class_key *key) +static inline void do_init_f2fs_rwsem(struct f2fs_rwsem *sem, + const char *sem_name, struct lock_class_key *key, + struct f2fs_sb_info *sbi, enum f2fs_lock_name name) { + sem->sbi = sbi; + sem->name = name; __init_rwsem(&sem->internal_rwsem, sem_name, key); #ifdef CONFIG_F2FS_UNFAIR_RWSEM init_waitqueue_head(&sem->read_waiters); @@ -2341,6 +2374,16 @@ static inline void f2fs_up_write(struct f2fs_rwsem *sem) #endif } +void f2fs_down_read_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc); +int f2fs_down_read_trylock_trace(struct f2fs_rwsem *sem, + struct f2fs_lock_context *lc); +void f2fs_up_read_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc); +void f2fs_down_write_trace(struct f2fs_rwsem *sem, + struct f2fs_lock_context *lc); +int f2fs_down_write_trylock_trace(struct f2fs_rwsem *sem, + struct f2fs_lock_context *lc); +void f2fs_up_write_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc); + static inline void disable_nat_bits(struct f2fs_sb_info *sbi, bool lock) { unsigned long flags; diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 635dcabcf1c7..9a852a16df9c 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -184,6 +184,10 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); { CP_PHASE_FINISH_BLOCK_OPS, "finish block_ops" }, \ { CP_PHASE_FINISH_CHECKPOINT, "finish checkpoint" }) +#define show_lock_name(lock) \ + __print_symbolic(lock, \ + { LOCK_NAME_NONE, "none" }) + struct f2fs_sb_info; struct f2fs_io_info; struct extent_info; @@ -2452,6 +2456,70 @@ DEFINE_EVENT(f2fs__rw_end, f2fs_datawrite_end, TP_ARGS(inode, offset, bytes) ); +TRACE_EVENT(f2fs_lock_elapsed_time, + + TP_PROTO(struct f2fs_sb_info *sbi, enum f2fs_lock_name lock_name, + bool is_write, struct task_struct *p, int ioprio, + unsigned long long total_time, + unsigned long long running_time, + unsigned long long runnable_time, + unsigned long long io_sleep_time, + unsigned long long other_time), + + TP_ARGS(sbi, lock_name, is_write, p, ioprio, total_time, running_time, + runnable_time, io_sleep_time, other_time), + + TP_STRUCT__entry( + __field(dev_t, dev) + __array(char, comm, TASK_COMM_LEN) + __field(pid_t, pid) + __field(int, prio) + __field(int, ioprio_class) + __field(int, ioprio_data) + __field(unsigned int, lock_name) + __field(bool, is_write) + __field(unsigned long long, total_time) + __field(unsigned long long, running_time) + __field(unsigned long long, runnable_time) + __field(unsigned long long, io_sleep_time) + __field(unsigned long long, other_time) + ), + + TP_fast_assign( + __entry->dev = sbi->sb->s_dev; + memcpy(__entry->comm, p->comm, TASK_COMM_LEN); + __entry->pid = p->pid; + __entry->prio = p->prio; + __entry->ioprio_class = IOPRIO_PRIO_CLASS(ioprio); + __entry->ioprio_data = IOPRIO_PRIO_DATA(ioprio); + __entry->lock_name = lock_name; + __entry->is_write = is_write; + __entry->total_time = total_time; + __entry->running_time = running_time; + __entry->runnable_time = runnable_time; + __entry->io_sleep_time = io_sleep_time; + __entry->other_time = other_time; + ), + + TP_printk("dev = (%d,%d), comm: %s, pid: %d, prio: %d, " + "ioprio_class: %d, ioprio_data: %d, lock_name: %s, " + "lock_type: %s, total: %llu, running: %llu, " + "runnable: %llu, io_sleep: %llu, other: %llu", + show_dev(__entry->dev), + __entry->comm, + __entry->pid, + __entry->prio, + __entry->ioprio_class, + __entry->ioprio_data, + show_lock_name(__entry->lock_name), + __entry->is_write ? "wlock" : "rlock", + __entry->total_time, + __entry->running_time, + __entry->runnable_time, + __entry->io_sleep_time, + __entry->other_time) +); + #endif /* _TRACE_F2FS_H */ /* This part must be outside protection */ -- cgit v1.2.3 From 66e9e0d55d117a7de2c00a9a06fb943ead56e1c2 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 4 Jan 2026 10:07:18 +0800 Subject: f2fs: trace elapsed time for cp_rwsem lock Use f2fs_{down,up}_read_trace for cp_rwsem to trace lock elapsed time. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 28 +++++++++++++++++++ fs/f2fs/compress.c | 12 +++++---- fs/f2fs/data.c | 47 +++++++++++++++++++------------- fs/f2fs/f2fs.h | 33 ++++------------------- fs/f2fs/file.c | 65 +++++++++++++++++++++++++++------------------ fs/f2fs/gc.c | 5 ++-- fs/f2fs/inline.c | 10 ++++--- fs/f2fs/inode.c | 10 ++++--- fs/f2fs/namei.c | 65 ++++++++++++++++++++++++++------------------- fs/f2fs/segment.c | 10 ++++--- fs/f2fs/super.c | 7 ++--- fs/f2fs/xattr.c | 5 ++-- include/trace/events/f2fs.h | 2 +- 13 files changed, 173 insertions(+), 126 deletions(-) (limited to 'include/trace/events') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 61bcf227d8ca..dfd54cba1b35 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -127,6 +127,34 @@ void f2fs_up_write_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) trace_lock_elapsed_time_end(sem, lc, true); } +void f2fs_lock_op(struct f2fs_sb_info *sbi, struct f2fs_lock_context *lc) +{ + f2fs_down_read_trace(&sbi->cp_rwsem, lc); +} + +int f2fs_trylock_op(struct f2fs_sb_info *sbi, struct f2fs_lock_context *lc) +{ + if (time_to_inject(sbi, FAULT_LOCK_OP)) + return 0; + + return f2fs_down_read_trylock_trace(&sbi->cp_rwsem, lc); +} + +void f2fs_unlock_op(struct f2fs_sb_info *sbi, struct f2fs_lock_context *lc) +{ + f2fs_up_read_trace(&sbi->cp_rwsem, lc); +} + +static inline void f2fs_lock_all(struct f2fs_sb_info *sbi) +{ + f2fs_down_write(&sbi->cp_rwsem); +} + +static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi) +{ + f2fs_up_write(&sbi->cp_rwsem); +} + #define DEFAULT_CHECKPOINT_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 3)) static struct kmem_cache *ino_entry_slab; diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 7b68bf22989d..3155d30b2448 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1290,6 +1290,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, struct dnode_of_data dn; struct node_info ni; struct compress_io_ctx *cic; + struct f2fs_lock_context lc; pgoff_t start_idx = start_idx_of_cluster(cc); unsigned int last_index = cc->cluster_size - 1; loff_t psize; @@ -1309,7 +1310,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, * the below discard race condition. */ f2fs_down_read(&sbi->node_write); - } else if (!f2fs_trylock_op(sbi)) { + } else if (!f2fs_trylock_op(sbi, &lc)) { goto out_free; } @@ -1435,7 +1436,7 @@ unlock_continue: if (quota_inode) f2fs_up_read(&sbi->node_write); else - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); spin_lock(&fi->i_size_lock); if (fi->last_disk_size < psize) @@ -1464,7 +1465,7 @@ out_unlock_op: if (quota_inode) f2fs_up_read(&sbi->node_write); else - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); out_free: for (i = 0; i < cc->valid_nr_cpages; i++) { f2fs_compress_free_page(cc->cpages[i]); @@ -1511,6 +1512,7 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc, { struct address_space *mapping = cc->inode->i_mapping; struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); + struct f2fs_lock_context lc; int submitted, compr_blocks, i; int ret = 0; @@ -1529,7 +1531,7 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc, /* overwrite compressed cluster w/ normal cluster */ if (compr_blocks > 0) - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); for (i = 0; i < cc->cluster_size; i++) { struct folio *folio; @@ -1585,7 +1587,7 @@ continue_unlock: out: if (compr_blocks > 0) - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_balance_fs(sbi, true); return ret; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 471e52c6c1e0..73fcafbc8191 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1466,34 +1466,39 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type) return 0; } -static void f2fs_map_lock(struct f2fs_sb_info *sbi, int flag) +static void f2fs_map_lock(struct f2fs_sb_info *sbi, + struct f2fs_lock_context *lc, + int flag) { f2fs_down_read(&sbi->cp_enable_rwsem); if (flag == F2FS_GET_BLOCK_PRE_AIO) f2fs_down_read(&sbi->node_change); else - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, lc); } -static void f2fs_map_unlock(struct f2fs_sb_info *sbi, int flag) +static void f2fs_map_unlock(struct f2fs_sb_info *sbi, + struct f2fs_lock_context *lc, + int flag) { if (flag == F2FS_GET_BLOCK_PRE_AIO) f2fs_up_read(&sbi->node_change); else - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, lc); f2fs_up_read(&sbi->cp_enable_rwsem); } int f2fs_get_block_locked(struct dnode_of_data *dn, pgoff_t index) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct f2fs_lock_context lc; int err = 0; - f2fs_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO); + f2fs_map_lock(sbi, &lc, F2FS_GET_BLOCK_PRE_AIO); if (!f2fs_lookup_read_extent_cache_block(dn->inode, index, &dn->data_blkaddr)) err = f2fs_reserve_block(dn, index); - f2fs_map_unlock(sbi, F2FS_GET_BLOCK_PRE_AIO); + f2fs_map_unlock(sbi, &lc, F2FS_GET_BLOCK_PRE_AIO); return err; } @@ -1584,6 +1589,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag) unsigned int maxblocks = map->m_len; struct dnode_of_data dn; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_lock_context lc; int mode = map->m_may_create ? ALLOC_NODE : LOOKUP_NODE; pgoff_t pgofs, end_offset, end; int err = 0, ofs = 1; @@ -1622,7 +1628,7 @@ next_dnode: if (map->m_may_create) { if (f2fs_lfs_mode(sbi)) f2fs_balance_fs(sbi, true); - f2fs_map_lock(sbi, flag); + f2fs_map_lock(sbi, &lc, flag); } /* When reading holes, we need its node page */ @@ -1788,7 +1794,7 @@ skip: f2fs_put_dnode(&dn); if (map->m_may_create) { - f2fs_map_unlock(sbi, flag); + f2fs_map_unlock(sbi, &lc, flag); f2fs_balance_fs(sbi, dn.node_changed); } goto next_dnode; @@ -1835,7 +1841,7 @@ sync_out: f2fs_put_dnode(&dn); unlock_out: if (map->m_may_create) { - f2fs_map_unlock(sbi, flag); + f2fs_map_unlock(sbi, &lc, flag); f2fs_balance_fs(sbi, dn.node_changed); } out: @@ -2865,6 +2871,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) struct inode *inode = folio->mapping->host; struct dnode_of_data dn; struct node_info ni; + struct f2fs_lock_context lc; bool ipu_force = false; bool atomic_commit; int err = 0; @@ -2890,7 +2897,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) } /* Deadlock due to between page->lock and f2fs_lock_op */ - if (fio->need_lock == LOCK_REQ && !f2fs_trylock_op(fio->sbi)) + if (fio->need_lock == LOCK_REQ && !f2fs_trylock_op(fio->sbi, &lc)) return -EAGAIN; err = f2fs_get_dnode_of_data(&dn, folio->index, LOOKUP_NODE); @@ -2931,7 +2938,7 @@ got_it: folio_start_writeback(folio); f2fs_put_dnode(&dn); if (fio->need_lock == LOCK_REQ) - f2fs_unlock_op(fio->sbi); + f2fs_unlock_op(fio->sbi, &lc); err = f2fs_inplace_write_data(fio); if (err) { if (fscrypt_inode_uses_fs_layer_crypto(inode)) @@ -2945,7 +2952,7 @@ got_it: } if (fio->need_lock == LOCK_RETRY) { - if (!f2fs_trylock_op(fio->sbi)) { + if (!f2fs_trylock_op(fio->sbi, &lc)) { err = -EAGAIN; goto out_writepage; } @@ -2977,7 +2984,7 @@ out_writepage: f2fs_put_dnode(&dn); out: if (fio->need_lock == LOCK_REQ) - f2fs_unlock_op(fio->sbi); + f2fs_unlock_op(fio->sbi, &lc); return err; } @@ -3570,6 +3577,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, struct inode *inode = folio->mapping->host; pgoff_t index = folio->index; struct dnode_of_data dn; + struct f2fs_lock_context lc; struct folio *ifolio; bool locked = false; int flag = F2FS_GET_BLOCK_PRE_AIO; @@ -3586,10 +3594,10 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, if (f2fs_has_inline_data(inode)) { if (pos + len > MAX_INLINE_DATA(inode)) flag = F2FS_GET_BLOCK_DEFAULT; - f2fs_map_lock(sbi, flag); + f2fs_map_lock(sbi, &lc, flag); locked = true; } else if ((pos & PAGE_MASK) >= i_size_read(inode)) { - f2fs_map_lock(sbi, flag); + f2fs_map_lock(sbi, &lc, flag); locked = true; } @@ -3633,7 +3641,7 @@ restart: if (!err && dn.data_blkaddr != NULL_ADDR) goto out; f2fs_put_dnode(&dn); - f2fs_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO); + f2fs_map_lock(sbi, &lc, F2FS_GET_BLOCK_PRE_AIO); WARN_ON(flag != F2FS_GET_BLOCK_PRE_AIO); locked = true; goto restart; @@ -3647,7 +3655,7 @@ out: f2fs_put_dnode(&dn); unlock_out: if (locked) - f2fs_map_unlock(sbi, flag); + f2fs_map_unlock(sbi, &lc, flag); return err; } @@ -3683,10 +3691,11 @@ static int __reserve_data_block(struct inode *inode, pgoff_t index, { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct dnode_of_data dn; + struct f2fs_lock_context lc; struct folio *ifolio; int err = 0; - f2fs_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO); + f2fs_map_lock(sbi, &lc, F2FS_GET_BLOCK_PRE_AIO); ifolio = f2fs_get_inode_folio(sbi, inode->i_ino); if (IS_ERR(ifolio)) { @@ -3704,7 +3713,7 @@ static int __reserve_data_block(struct inode *inode, pgoff_t index, f2fs_put_dnode(&dn); unlock_out: - f2fs_map_unlock(sbi, F2FS_GET_BLOCK_PRE_AIO); + f2fs_map_unlock(sbi, &lc, F2FS_GET_BLOCK_PRE_AIO); return err; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7f2675ed7f86..58244bb87fef 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -175,6 +175,7 @@ enum device_allocation_policy { enum f2fs_lock_name { LOCK_NAME_NONE, + LOCK_NAME_CP_RWSEM, }; /* @@ -2417,33 +2418,6 @@ static inline bool enabled_nat_bits(struct f2fs_sb_info *sbi, return (cpc) ? (cpc->reason & CP_UMOUNT) && set : set; } -static inline void f2fs_lock_op(struct f2fs_sb_info *sbi) -{ - f2fs_down_read(&sbi->cp_rwsem); -} - -static inline int f2fs_trylock_op(struct f2fs_sb_info *sbi) -{ - if (time_to_inject(sbi, FAULT_LOCK_OP)) - return 0; - return f2fs_down_read_trylock(&sbi->cp_rwsem); -} - -static inline void f2fs_unlock_op(struct f2fs_sb_info *sbi) -{ - f2fs_up_read(&sbi->cp_rwsem); -} - -static inline void f2fs_lock_all(struct f2fs_sb_info *sbi) -{ - f2fs_down_write(&sbi->cp_rwsem); -} - -static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi) -{ - f2fs_up_write(&sbi->cp_rwsem); -} - static inline int __get_cp_reason(struct f2fs_sb_info *sbi) { int reason = CP_SYNC; @@ -3770,7 +3744,7 @@ void f2fs_update_inode_page(struct inode *inode); int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc); void f2fs_remove_donate_inode(struct inode *inode); void f2fs_evict_inode(struct inode *inode); -void f2fs_handle_failed_inode(struct inode *inode); +void f2fs_handle_failed_inode(struct inode *inode, struct f2fs_lock_context *lc); /* * namei.c @@ -4037,6 +4011,9 @@ static inline bool f2fs_need_rand_seg(struct f2fs_sb_info *sbi) /* * checkpoint.c */ +void f2fs_lock_op(struct f2fs_sb_info *sbi, struct f2fs_lock_context *lc); +int f2fs_trylock_op(struct f2fs_sb_info *sbi, struct f2fs_lock_context *lc); +void f2fs_unlock_op(struct f2fs_sb_info *sbi, struct f2fs_lock_context *lc); void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io, unsigned char reason); void f2fs_flush_ckpt_thread(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e75e61ac50d7..1cdbbc2e1005 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -774,6 +774,7 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct dnode_of_data dn; + struct f2fs_lock_context lc; pgoff_t free_from; int count = 0, err = 0; struct folio *ifolio; @@ -792,7 +793,7 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock) goto free_partial; if (lock) - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); ifolio = f2fs_get_inode_folio(sbi, inode->i_ino); if (IS_ERR(ifolio)) { @@ -843,7 +844,7 @@ free_next: err = f2fs_truncate_inode_blocks(inode, free_from); out: if (lock) - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); free_partial: /* lastly zero out the first data page */ if (!err) @@ -1118,11 +1119,13 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, } if (i_uid_needs_update(idmap, attr, inode) || i_gid_needs_update(idmap, attr, inode)) { - f2fs_lock_op(sbi); + struct f2fs_lock_context lc; + + f2fs_lock_op(sbi, &lc); err = dquot_transfer(idmap, inode, attr); if (err) { set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); return err; } /* @@ -1132,7 +1135,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, i_uid_update(idmap, attr, inode); i_gid_update(idmap, attr, inode); f2fs_mark_inode_dirty_sync(inode, true); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); } if (attr->ia_valid & ATTR_SIZE) { @@ -1216,15 +1219,16 @@ static int fill_zero(struct inode *inode, pgoff_t index, { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct folio *folio; + struct f2fs_lock_context lc; if (!len) return 0; f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); folio = f2fs_get_new_data_folio(inode, NULL, index, false); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (IS_ERR(folio)) return PTR_ERR(folio); @@ -1307,6 +1311,7 @@ static int f2fs_punch_hole(struct inode *inode, loff_t offset, loff_t len) if (pg_start < pg_end) { loff_t blk_start, blk_end; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_lock_context lc; f2fs_balance_fs(sbi, true); @@ -1318,9 +1323,9 @@ static int f2fs_punch_hole(struct inode *inode, loff_t offset, loff_t len) truncate_pagecache_range(inode, blk_start, blk_end - 1); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); ret = f2fs_truncate_hole(inode, pg_start, pg_end); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); filemap_invalidate_unlock(inode->i_mapping); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); @@ -1552,6 +1557,7 @@ roll_back: static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_lock_context lc; pgoff_t nrpages = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); pgoff_t start = offset >> PAGE_SHIFT; pgoff_t end = (offset + len) >> PAGE_SHIFT; @@ -1565,11 +1571,11 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len) f2fs_zero_post_eof_page(inode, offset + len, false); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); f2fs_drop_extent_tree(inode); truncate_pagecache(inode, offset); ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); filemap_invalidate_unlock(inode->i_mapping); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); @@ -1717,6 +1723,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, for (index = pg_start; index < pg_end;) { struct dnode_of_data dn; + struct f2fs_lock_context lc; unsigned int end_offset; pgoff_t end; @@ -1727,12 +1734,12 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, (loff_t)index << PAGE_SHIFT, ((loff_t)pg_end << PAGE_SHIFT) - 1); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); set_new_dnode(&dn, inode, NULL, NULL, 0); ret = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE); if (ret) { - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); filemap_invalidate_unlock(mapping); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); goto out; @@ -1744,7 +1751,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, ret = f2fs_do_zero_range(&dn, index, end); f2fs_put_dnode(&dn); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); filemap_invalidate_unlock(mapping); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); @@ -1827,17 +1834,19 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) truncate_pagecache(inode, offset); while (!ret && idx > pg_start) { + struct f2fs_lock_context lc; + nr = idx - pg_start; if (nr > delta) nr = delta; idx -= nr; - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); f2fs_drop_extent_tree(inode); ret = __exchange_data_block(inode, inode, idx, idx + delta, nr, false); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); } filemap_invalidate_unlock(mapping); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); @@ -3093,6 +3102,7 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, struct inode *src = file_inode(file_in); struct inode *dst = file_inode(file_out); struct f2fs_sb_info *sbi = F2FS_I_SB(src); + struct f2fs_lock_context lc; size_t olen = len, dst_max_i_size = 0; size_t dst_osize; int ret; @@ -3188,7 +3198,7 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, goto out_src; } - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); ret = __exchange_data_block(src, dst, F2FS_BYTES_TO_BLK(pos_in), F2FS_BYTES_TO_BLK(pos_out), F2FS_BYTES_TO_BLK(len), false); @@ -3199,7 +3209,7 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, else if (dst_osize != dst->i_size) f2fs_i_size_write(dst, dst_osize); } - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (src != dst) f2fs_up_write(&F2FS_I(dst)->i_gc_rwsem[WRITE]); @@ -3367,6 +3377,7 @@ static int f2fs_ioc_setproject(struct inode *inode, __u32 projid) struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode *ri = NULL; + struct f2fs_lock_context lc; kprojid_t kprojid; int err; @@ -3397,7 +3408,7 @@ static int f2fs_ioc_setproject(struct inode *inode, __u32 projid) if (err) return err; - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_transfer_project_quota(inode, kprojid); if (err) goto out_unlock; @@ -3406,7 +3417,7 @@ static int f2fs_ioc_setproject(struct inode *inode, __u32 projid) inode_set_ctime_current(inode); f2fs_mark_inode_dirty_sync(inode, true); out_unlock: - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); return err; } #else @@ -3839,6 +3850,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) struct inode *inode = file_inode(filp); struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_lock_context lc; pgoff_t page_idx = 0, last_idx; unsigned int released_blocks = 0; int ret; @@ -3893,12 +3905,12 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) struct dnode_of_data dn; pgoff_t end_offset, count; - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); set_new_dnode(&dn, inode, NULL, NULL, 0); ret = f2fs_get_dnode_of_data(&dn, page_idx, LOOKUP_NODE); if (ret) { - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (ret == -ENOENT) { page_idx = f2fs_get_next_page_offset(&dn, page_idx); @@ -3916,7 +3928,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) f2fs_put_dnode(&dn); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (ret < 0) break; @@ -4069,14 +4081,15 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) while (page_idx < last_idx) { struct dnode_of_data dn; + struct f2fs_lock_context lc; pgoff_t end_offset, count; - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); set_new_dnode(&dn, inode, NULL, NULL, 0); ret = f2fs_get_dnode_of_data(&dn, page_idx, LOOKUP_NODE); if (ret) { - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (ret == -ENOENT) { page_idx = f2fs_get_next_page_offset(&dn, page_idx); @@ -4094,7 +4107,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) f2fs_put_dnode(&dn); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (ret < 0) break; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ba66d8bc9b5f..8999829a9559 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -2263,6 +2263,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count) struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp)); __u64 old_block_count, shrunk_blocks; struct cp_control cpc = { CP_RESIZE, 0, 0, 0 }; + struct f2fs_lock_context lc; unsigned int secs; int err = 0; __u32 rem; @@ -2312,7 +2313,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count) } /* stop CP to protect MAIN_SEC in free_segment_range */ - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); spin_lock(&sbi->stat_lock); if (shrunk_blocks + valid_user_blocks(sbi) + @@ -2327,7 +2328,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count) err = free_segment_range(sbi, secs, true); out_unlock: - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_up_write(&sbi->gc_lock); out_drop_write: mnt_drop_write_file(filp); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e5c6a08b7e4f..0a1052d5ee62 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -218,6 +218,7 @@ int f2fs_convert_inline_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct dnode_of_data dn; + struct f2fs_lock_context lc; struct folio *ifolio, *folio; int err = 0; @@ -235,7 +236,7 @@ int f2fs_convert_inline_inode(struct inode *inode) if (IS_ERR(folio)) return PTR_ERR(folio); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); ifolio = f2fs_get_inode_folio(sbi, inode->i_ino); if (IS_ERR(ifolio)) { @@ -250,7 +251,7 @@ int f2fs_convert_inline_inode(struct inode *inode) f2fs_put_dnode(&dn); out: - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_folio_put(folio, true); @@ -597,13 +598,14 @@ int f2fs_try_convert_inline_dir(struct inode *dir, struct dentry *dentry) struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct folio *ifolio; struct f2fs_filename fname; + struct f2fs_lock_context lc; void *inline_dentry = NULL; int err = 0; if (!f2fs_has_inline_dentry(dir)) return 0; - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_setup_filename(dir, &dentry->d_name, 0, &fname); if (err) @@ -628,7 +630,7 @@ int f2fs_try_convert_inline_dir(struct inode *dir, struct dentry *dentry) out_fname: f2fs_free_filename(&fname); out: - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); return err; } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index d1270b25ad7d..b8cf1fab6391 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -910,9 +910,11 @@ retry: err = -EIO; if (!err) { - f2fs_lock_op(sbi); + struct f2fs_lock_context lc; + + f2fs_lock_op(sbi, &lc); err = f2fs_remove_inode_page(inode); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (err == -ENOENT) { err = 0; @@ -1009,7 +1011,7 @@ out_clear: } /* caller should call f2fs_lock_op() */ -void f2fs_handle_failed_inode(struct inode *inode) +void f2fs_handle_failed_inode(struct inode *inode, struct f2fs_lock_context *lc) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct node_info ni; @@ -1058,7 +1060,7 @@ void f2fs_handle_failed_inode(struct inode *inode) } out: - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, lc); /* iput will drop the inode object */ iput(inode); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 043d20516a21..e360f08a9586 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -354,6 +354,7 @@ static int f2fs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct f2fs_lock_context lc; struct inode *inode; nid_t ino = 0; int err; @@ -376,11 +377,11 @@ static int f2fs_create(struct mnt_idmap *idmap, struct inode *dir, inode->i_mapping->a_ops = &f2fs_dblock_aops; ino = inode->i_ino; - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_add_link(dentry, inode); if (err) goto out; - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_alloc_nid_done(sbi, ino); @@ -392,7 +393,7 @@ static int f2fs_create(struct mnt_idmap *idmap, struct inode *dir, f2fs_balance_fs(sbi, true); return 0; out: - f2fs_handle_failed_inode(inode); + f2fs_handle_failed_inode(inode, &lc); return err; } @@ -401,6 +402,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, { struct inode *inode = d_inode(old_dentry); struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct f2fs_lock_context lc; int err; if (unlikely(f2fs_cp_error(sbi))) @@ -427,11 +429,11 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, ihold(inode); set_inode_flag(inode, FI_INC_LINK); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_add_link(dentry, inode); if (err) goto out; - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); d_instantiate(dentry, inode); @@ -441,7 +443,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, out: clear_inode_flag(inode, FI_INC_LINK); iput(inode); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); return err; } @@ -545,6 +547,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct inode *inode = d_inode(dentry); struct f2fs_dir_entry *de; + struct f2fs_lock_context lc; struct folio *folio; int err; @@ -581,15 +584,15 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_acquire_orphan_inode(sbi); if (err) { - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_folio_put(folio, false); goto out; } f2fs_delete_entry(de, folio, dir, inode); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); /* VFS negative dentries are incompatible with Encoding and * Case-insensitiveness. Eventually we'll want avoid @@ -632,6 +635,7 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, const char *symname) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct f2fs_lock_context lc; struct inode *inode; size_t len = strlen(symname); struct fscrypt_str disk_link; @@ -662,11 +666,11 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struct inode *dir, inode_nohighmem(inode); inode->i_mapping->a_ops = &f2fs_dblock_aops; - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_add_link(dentry, inode); if (err) goto out_f2fs_handle_failed_inode; - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_alloc_nid_done(sbi, inode->i_ino); err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link); @@ -701,7 +705,7 @@ err_out: goto out_free_encrypted_link; out_f2fs_handle_failed_inode: - f2fs_handle_failed_inode(inode); + f2fs_handle_failed_inode(inode, &lc); out_free_encrypted_link: if (disk_link.name != (unsigned char *)symname) kfree(disk_link.name); @@ -712,6 +716,7 @@ static struct dentry *f2fs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct f2fs_lock_context lc; struct inode *inode; int err; @@ -732,11 +737,11 @@ static struct dentry *f2fs_mkdir(struct mnt_idmap *idmap, struct inode *dir, mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); set_inode_flag(inode, FI_INC_LINK); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_add_link(dentry, inode); if (err) goto out_fail; - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_alloc_nid_done(sbi, inode->i_ino); @@ -750,7 +755,7 @@ static struct dentry *f2fs_mkdir(struct mnt_idmap *idmap, struct inode *dir, out_fail: clear_inode_flag(inode, FI_INC_LINK); - f2fs_handle_failed_inode(inode); + f2fs_handle_failed_inode(inode, &lc); return ERR_PTR(err); } @@ -767,6 +772,7 @@ static int f2fs_mknod(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct f2fs_lock_context lc; struct inode *inode; int err = 0; @@ -786,11 +792,11 @@ static int f2fs_mknod(struct mnt_idmap *idmap, struct inode *dir, init_special_inode(inode, inode->i_mode, rdev); inode->i_op = &f2fs_special_inode_operations; - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_add_link(dentry, inode); if (err) goto out; - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_alloc_nid_done(sbi, inode->i_ino); @@ -802,7 +808,7 @@ static int f2fs_mknod(struct mnt_idmap *idmap, struct inode *dir, f2fs_balance_fs(sbi, true); return 0; out: - f2fs_handle_failed_inode(inode); + f2fs_handle_failed_inode(inode, &lc); return err; } @@ -811,6 +817,7 @@ static int __f2fs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, struct inode **new_inode, struct f2fs_filename *fname) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct f2fs_lock_context lc; struct inode *inode; int err; @@ -831,7 +838,7 @@ static int __f2fs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, inode->i_mapping->a_ops = &f2fs_dblock_aops; } - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_acquire_orphan_inode(sbi); if (err) goto out; @@ -860,7 +867,7 @@ static int __f2fs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, f2fs_i_links_write(inode, false); } /* link_count was changed by d_tmpfile as well. */ - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); unlock_new_inode(inode); if (new_inode) @@ -872,7 +879,7 @@ static int __f2fs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, release_out: f2fs_release_orphan_inode(sbi); out: - f2fs_handle_failed_inode(inode); + f2fs_handle_failed_inode(inode, &lc); return err; } @@ -920,6 +927,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct f2fs_dir_entry *old_dir_entry = NULL; struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *new_entry; + struct f2fs_lock_context lc; bool old_is_dir = S_ISDIR(old_inode->i_mode); int err; @@ -1008,7 +1016,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir, f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_acquire_orphan_inode(sbi); if (err) @@ -1031,11 +1039,11 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir, } else { f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_add_link(new_dentry, old_inode); if (err) { - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); goto out_dir; } @@ -1084,7 +1092,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir, TRANS_DIR_INO); } - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) f2fs_sync_fs(sbi->sb, 1); @@ -1093,7 +1101,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir, return 0; put_out_dir: - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_folio_put(new_folio, false); out_dir: if (old_dir_entry) @@ -1115,6 +1123,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, struct folio *old_folio, *new_folio; struct f2fs_dir_entry *old_dir_entry = NULL, *new_dir_entry = NULL; struct f2fs_dir_entry *old_entry, *new_entry; + struct f2fs_lock_context lc; int old_nlink = 0, new_nlink = 0; int err; @@ -1194,7 +1203,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); /* update ".." directory entry info of old dentry */ if (old_dir_entry) @@ -1247,7 +1256,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO); } - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) f2fs_sync_fs(sbi->sb, 1); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c0c5b7075b04..e4a8daf433a8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -400,6 +400,7 @@ int f2fs_commit_atomic_write(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_lock_context lc; int err; err = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); @@ -407,11 +408,11 @@ int f2fs_commit_atomic_write(struct inode *inode) return err; f2fs_down_write(&fi->i_gc_rwsem[WRITE]); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = __f2fs_commit_atomic_write(inode); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_up_write(&fi->i_gc_rwsem[WRITE]); return err; @@ -3362,13 +3363,14 @@ int f2fs_allocate_new_section(struct f2fs_sb_info *sbi, int type, bool force) int f2fs_allocate_pinning_section(struct f2fs_sb_info *sbi) { + struct f2fs_lock_context lc; int err; bool gc_required = true; retry: - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); err = f2fs_allocate_new_section(sbi, CURSEG_COLD_DATA_PINNED, false); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (f2fs_sb_has_blkzoned(sbi) && err == -EAGAIN && gc_required) { f2fs_down_write(&sbi->gc_lock); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8fe1ac8a609c..d70567b48d12 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3325,6 +3325,7 @@ int f2fs_do_quota_sync(struct super_block *sb, int type) * that userspace sees the changes. */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + struct f2fs_lock_context lc; if (type != -1 && cnt != type) continue; @@ -3344,13 +3345,13 @@ int f2fs_do_quota_sync(struct super_block *sb, int type) * block_operation * f2fs_down_read(quota_sem) */ - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); f2fs_down_read(&sbi->quota_sem); ret = f2fs_quota_sync_file(sbi, cnt); f2fs_up_read(&sbi->quota_sem); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); if (!f2fs_sb_has_quota_ino(sbi)) inode_unlock(dqopt->files[cnt]); @@ -4898,7 +4899,7 @@ try_onemore: init_f2fs_rwsem(&sbi->node_write); init_f2fs_rwsem(&sbi->node_change); spin_lock_init(&sbi->stat_lock); - init_f2fs_rwsem(&sbi->cp_rwsem); + init_f2fs_rwsem_trace(&sbi->cp_rwsem, sbi, LOCK_NAME_CP_RWSEM); init_f2fs_rwsem(&sbi->cp_enable_rwsem); init_f2fs_rwsem(&sbi->quota_sem); init_waitqueue_head(&sbi->cp_wait); diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index b4e5c406632f..941dc62a6d6f 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -804,6 +804,7 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name, struct folio *ifolio, int flags) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_lock_context lc; int err; if (unlikely(f2fs_cp_error(sbi))) @@ -821,11 +822,11 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name, size, ifolio, flags); f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); + f2fs_lock_op(sbi, &lc); f2fs_down_write(&F2FS_I(inode)->i_xattr_sem); err = __f2fs_setxattr(inode, index, name, value, size, NULL, flags); f2fs_up_write(&F2FS_I(inode)->i_xattr_sem); - f2fs_unlock_op(sbi); + f2fs_unlock_op(sbi, &lc); f2fs_update_time(sbi, REQ_TIME); return err; diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 9a852a16df9c..f4f13ddbe104 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -186,7 +186,7 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); #define show_lock_name(lock) \ __print_symbolic(lock, \ - { LOCK_NAME_NONE, "none" }) + { LOCK_NAME_CP_RWSEM, "cp_rwsem" }) struct f2fs_sb_info; struct f2fs_io_info; -- cgit v1.2.3 From f9f93602512bceb28865942abfab54021e3a3d86 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 4 Jan 2026 10:07:19 +0800 Subject: f2fs: trace elapsed time for node_change lock Use f2fs_{down,up}_read_trace for node_change to trace lock elapsed time. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 2 +- include/trace/events/f2fs.h | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/trace/events') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 73fcafbc8191..5469547142e7 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1472,7 +1472,7 @@ static void f2fs_map_lock(struct f2fs_sb_info *sbi, { f2fs_down_read(&sbi->cp_enable_rwsem); if (flag == F2FS_GET_BLOCK_PRE_AIO) - f2fs_down_read(&sbi->node_change); + f2fs_down_read_trace(&sbi->node_change, lc); else f2fs_lock_op(sbi, lc); } @@ -1482,7 +1482,7 @@ static void f2fs_map_unlock(struct f2fs_sb_info *sbi, int flag) { if (flag == F2FS_GET_BLOCK_PRE_AIO) - f2fs_up_read(&sbi->node_change); + f2fs_up_read_trace(&sbi->node_change, lc); else f2fs_unlock_op(sbi, lc); f2fs_up_read(&sbi->cp_enable_rwsem); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 58244bb87fef..8f6a255f9e57 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -176,6 +176,7 @@ enum device_allocation_policy { enum f2fs_lock_name { LOCK_NAME_NONE, LOCK_NAME_CP_RWSEM, + LOCK_NAME_NODE_CHANGE, }; /* diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d70567b48d12..247638b98cfb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4897,7 +4897,7 @@ try_onemore: mutex_init(&sbi->writepages); init_f2fs_rwsem(&sbi->cp_global_sem); init_f2fs_rwsem(&sbi->node_write); - init_f2fs_rwsem(&sbi->node_change); + init_f2fs_rwsem_trace(&sbi->node_change, sbi, LOCK_NAME_NODE_CHANGE); spin_lock_init(&sbi->stat_lock); init_f2fs_rwsem_trace(&sbi->cp_rwsem, sbi, LOCK_NAME_CP_RWSEM); init_f2fs_rwsem(&sbi->cp_enable_rwsem); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index f4f13ddbe104..d472f47eedec 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -186,7 +186,8 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); #define show_lock_name(lock) \ __print_symbolic(lock, \ - { LOCK_NAME_CP_RWSEM, "cp_rwsem" }) + { LOCK_NAME_CP_RWSEM, "cp_rwsem" }, \ + { LOCK_NAME_NODE_CHANGE, "node_change" }) struct f2fs_sb_info; struct f2fs_io_info; -- cgit v1.2.3 From bb28b66875cca72fcb62ee572fb32e0d4267a5f9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 4 Jan 2026 10:07:20 +0800 Subject: f2fs: trace elapsed time for node_write lock Use f2fs_{down,up}_read_trace for node_write to trace lock elapsed time. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/compress.c | 6 +++--- fs/f2fs/data.c | 6 ++++-- fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 9 +++++---- fs/f2fs/super.c | 2 +- include/trace/events/f2fs.h | 3 ++- 6 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include/trace/events') diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 3155d30b2448..316bc3e6d2d4 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1309,7 +1309,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, * checkpoint. This can only happen to quota writes which can cause * the below discard race condition. */ - f2fs_down_read(&sbi->node_write); + f2fs_down_read_trace(&sbi->node_write, &lc); } else if (!f2fs_trylock_op(sbi, &lc)) { goto out_free; } @@ -1434,7 +1434,7 @@ unlock_continue: f2fs_put_dnode(&dn); if (quota_inode) - f2fs_up_read(&sbi->node_write); + f2fs_up_read_trace(&sbi->node_write, &lc); else f2fs_unlock_op(sbi, &lc); @@ -1463,7 +1463,7 @@ out_put_dnode: f2fs_put_dnode(&dn); out_unlock_op: if (quota_inode) - f2fs_up_read(&sbi->node_write); + f2fs_up_read_trace(&sbi->node_write, &lc); else f2fs_unlock_op(sbi, &lc); out_free: diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5469547142e7..79455d7acba5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3064,19 +3064,21 @@ int f2fs_write_single_data_page(struct folio *folio, int *submitted, write: /* Dentry/quota blocks are controlled by checkpoint */ if (S_ISDIR(inode->i_mode) || quota_inode) { + struct f2fs_lock_context lc; + /* * We need to wait for node_write to avoid block allocation during * checkpoint. This can only happen to quota writes which can cause * the below discard race condition. */ if (quota_inode) - f2fs_down_read(&sbi->node_write); + f2fs_down_read_trace(&sbi->node_write, &lc); fio.need_lock = LOCK_DONE; err = f2fs_do_write_data_page(&fio); if (quota_inode) - f2fs_up_read(&sbi->node_write); + f2fs_up_read_trace(&sbi->node_write, &lc); goto done; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8f6a255f9e57..a31394f1b493 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -177,6 +177,7 @@ enum f2fs_lock_name { LOCK_NAME_NONE, LOCK_NAME_CP_RWSEM, LOCK_NAME_NODE_CHANGE, + LOCK_NAME_NODE_WRITE, }; /* diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 3a80da524739..d378549010e6 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1738,6 +1738,7 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted .io_type = io_type, .io_wbc = wbc, }; + struct f2fs_lock_context lc; unsigned int seq; trace_f2fs_writepage(folio, NODE); @@ -1767,13 +1768,13 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted if (f2fs_get_node_info(sbi, nid, &ni, !do_balance)) goto redirty_out; - f2fs_down_read(&sbi->node_write); + f2fs_down_read_trace(&sbi->node_write, &lc); /* This page is already truncated */ if (unlikely(ni.blk_addr == NULL_ADDR)) { folio_clear_uptodate(folio); dec_page_count(sbi, F2FS_DIRTY_NODES); - f2fs_up_read(&sbi->node_write); + f2fs_up_read_trace(&sbi->node_write, &lc); folio_unlock(folio); return true; } @@ -1781,7 +1782,7 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted if (__is_valid_data_blkaddr(ni.blk_addr) && !f2fs_is_valid_blkaddr(sbi, ni.blk_addr, DATA_GENERIC_ENHANCE)) { - f2fs_up_read(&sbi->node_write); + f2fs_up_read_trace(&sbi->node_write, &lc); goto redirty_out; } @@ -1801,7 +1802,7 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted f2fs_do_write_node_page(nid, &fio); set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(folio)); dec_page_count(sbi, F2FS_DIRTY_NODES); - f2fs_up_read(&sbi->node_write); + f2fs_up_read_trace(&sbi->node_write, &lc); folio_unlock(folio); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 247638b98cfb..8cd519bb3c97 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4896,7 +4896,7 @@ try_onemore: init_f2fs_rwsem(&sbi->gc_lock); mutex_init(&sbi->writepages); init_f2fs_rwsem(&sbi->cp_global_sem); - init_f2fs_rwsem(&sbi->node_write); + init_f2fs_rwsem_trace(&sbi->node_write, sbi, LOCK_NAME_NODE_WRITE); init_f2fs_rwsem_trace(&sbi->node_change, sbi, LOCK_NAME_NODE_CHANGE); spin_lock_init(&sbi->stat_lock); init_f2fs_rwsem_trace(&sbi->cp_rwsem, sbi, LOCK_NAME_CP_RWSEM); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index d472f47eedec..e5cfb8ad0d5e 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -187,7 +187,8 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); #define show_lock_name(lock) \ __print_symbolic(lock, \ { LOCK_NAME_CP_RWSEM, "cp_rwsem" }, \ - { LOCK_NAME_NODE_CHANGE, "node_change" }) + { LOCK_NAME_NODE_CHANGE, "node_change" }, \ + { LOCK_NAME_NODE_WRITE, "node_write" }) struct f2fs_sb_info; struct f2fs_io_info; -- cgit v1.2.3 From e605302c14ffda051dc7fbc5f27e1fecc9f681e3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 4 Jan 2026 18:34:12 +0800 Subject: f2fs: trace elapsed time for gc_lock lock Use f2fs_{down,up}_write_trace for gc_lock to trace lock elapsed time. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++++++---- fs/f2fs/f2fs.h | 22 ++++++++++++---------- fs/f2fs/file.c | 13 +++++++------ fs/f2fs/gc.c | 23 +++++++++++++---------- fs/f2fs/segment.c | 11 ++++++----- fs/f2fs/super.c | 14 ++++++++------ include/trace/events/f2fs.h | 3 ++- 7 files changed, 54 insertions(+), 42 deletions(-) (limited to 'include/trace/events') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index dfd54cba1b35..da7bcfa2a178 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1930,11 +1930,12 @@ void f2fs_destroy_checkpoint_caches(void) static int __write_checkpoint_sync(struct f2fs_sb_info *sbi) { struct cp_control cpc = { .reason = CP_SYNC, }; + struct f2fs_lock_context lc; int err; - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &lc); err = f2fs_write_checkpoint(sbi, &cpc); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &lc); return err; } @@ -2022,11 +2023,12 @@ int f2fs_issue_checkpoint(struct f2fs_sb_info *sbi) cpc.reason = __get_cp_reason(sbi); if (!test_opt(sbi, MERGE_CHECKPOINT) || cpc.reason != CP_SYNC || sbi->umount_lock_holder == current) { + struct f2fs_lock_context lc; int ret; - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &lc); ret = f2fs_write_checkpoint(sbi, &cpc); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &lc); return ret; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a31394f1b493..3f3faa5e8ff1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -178,6 +178,7 @@ enum f2fs_lock_name { LOCK_NAME_CP_RWSEM, LOCK_NAME_NODE_CHANGE, LOCK_NAME_NODE_WRITE, + LOCK_NAME_GC_LOCK, }; /* @@ -1408,16 +1409,6 @@ struct atgc_management { unsigned long long age_threshold; /* age threshold */ }; -struct f2fs_gc_control { - unsigned int victim_segno; /* target victim segment number */ - int init_gc_type; /* FG_GC or BG_GC */ - bool no_bg_gc; /* check the space and stop bg_gc */ - bool should_migrate_blocks; /* should migrate blocks */ - bool err_gc_skipped; /* return EAGAIN if GC skipped */ - bool one_time; /* require one time GC in one migration unit */ - unsigned int nr_free_secs; /* # of free sections to do GC */ -}; - struct f2fs_time_stat { unsigned long long total_time; /* total wall clock time */ #ifdef CONFIG_64BIT @@ -1436,6 +1427,17 @@ struct f2fs_lock_context { bool lock_trace; }; +struct f2fs_gc_control { + unsigned int victim_segno; /* target victim segment number */ + int init_gc_type; /* FG_GC or BG_GC */ + bool no_bg_gc; /* check the space and stop bg_gc */ + bool should_migrate_blocks; /* should migrate blocks */ + bool err_gc_skipped; /* return EAGAIN if GC skipped */ + bool one_time; /* require one time GC in one migration unit */ + unsigned int nr_free_secs; /* # of free sections to do GC */ + struct f2fs_lock_context lc; /* lock context for gc_lock */ +}; + /* * For s_flag in struct f2fs_sb_info * Modification on enum should be synchronized with s_flag array diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 1cdbbc2e1005..ce291f152bc3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1928,7 +1928,7 @@ next_alloc: if (has_not_enough_free_secs(sbi, 0, sbi->reserved_pin_section)) { - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); stat_inc_gc_call_count(sbi, FOREGROUND); err = f2fs_gc(sbi, &gc_control); if (err && err != -ENODATA) { @@ -2779,12 +2779,13 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg) return ret; if (!sync) { - if (!f2fs_down_write_trylock(&sbi->gc_lock)) { + if (!f2fs_down_write_trylock_trace(&sbi->gc_lock, + &gc_control.lc)) { ret = -EBUSY; goto out; } } else { - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); } gc_control.init_gc_type = sync ? FG_GC : BG_GC; @@ -2824,12 +2825,12 @@ static int __f2fs_ioc_gc_range(struct file *filp, struct f2fs_gc_range *range) do_more: if (!range->sync) { - if (!f2fs_down_write_trylock(&sbi->gc_lock)) { + if (!f2fs_down_write_trylock_trace(&sbi->gc_lock, &gc_control.lc)) { ret = -EBUSY; goto out; } } else { - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); } gc_control.victim_segno = GET_SEGNO(sbi, range->start); @@ -3320,7 +3321,7 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg) end_segno = min(start_segno + range.segments, dev_end_segno); while (start_segno < end_segno) { - if (!f2fs_down_write_trylock(&sbi->gc_lock)) { + if (!f2fs_down_write_trylock_trace(&sbi->gc_lock, &gc_control.lc)) { ret = -EBUSY; goto out; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8999829a9559..391e66064c7e 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -102,21 +102,22 @@ static int gc_thread_func(void *data) if (sbi->gc_mode == GC_URGENT_HIGH || sbi->gc_mode == GC_URGENT_MID) { wait_ms = gc_th->urgent_sleep_time; - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); goto do_gc; } if (foreground) { - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); goto do_gc; - } else if (!f2fs_down_write_trylock(&sbi->gc_lock)) { + } else if (!f2fs_down_write_trylock_trace(&sbi->gc_lock, + &gc_control.lc)) { stat_other_skip_bggc_count(sbi); goto next; } if (!is_idle(sbi, GC_TIME)) { increase_sleep_time(gc_th, &wait_ms); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &gc_control.lc); stat_io_skip_bggc_count(sbi); goto next; } @@ -125,7 +126,8 @@ static int gc_thread_func(void *data) if (has_enough_free_blocks(sbi, gc_th->no_zoned_gc_percent)) { wait_ms = gc_th->no_gc_sleep_time; - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, + &gc_control.lc); goto next; } if (wait_ms == gc_th->no_gc_sleep_time) @@ -2046,7 +2048,7 @@ stop: reserved_segments(sbi), prefree_segments(sbi)); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &gc_control->lc); put_gc_inode(&gc_list); @@ -2264,6 +2266,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count) __u64 old_block_count, shrunk_blocks; struct cp_control cpc = { CP_RESIZE, 0, 0, 0 }; struct f2fs_lock_context lc; + struct f2fs_lock_context glc; unsigned int secs; int err = 0; __u32 rem; @@ -2307,7 +2310,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count) secs = div_u64(shrunk_blocks, BLKS_PER_SEC(sbi)); /* stop other GC */ - if (!f2fs_down_write_trylock(&sbi->gc_lock)) { + if (!f2fs_down_write_trylock_trace(&sbi->gc_lock, &glc)) { err = -EAGAIN; goto out_drop_write; } @@ -2329,7 +2332,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count) out_unlock: f2fs_unlock_op(sbi, &lc); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &glc); out_drop_write: mnt_drop_write_file(filp); if (err) @@ -2346,7 +2349,7 @@ out_drop_write: return -EROFS; } - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &glc); f2fs_down_write(&sbi->cp_global_sem); spin_lock(&sbi->stat_lock); @@ -2396,7 +2399,7 @@ recover_out: } out_err: f2fs_up_write(&sbi->cp_global_sem); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &glc); thaw_super(sbi->sb, FREEZE_HOLDER_KERNEL, NULL); return err; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e4a8daf433a8..776b0df828ed 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -462,7 +462,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) .should_migrate_blocks = false, .err_gc_skipped = false, .nr_free_secs = 1 }; - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); stat_inc_gc_call_count(sbi, FOREGROUND); f2fs_gc(sbi, &gc_control); } @@ -3373,10 +3373,10 @@ retry: f2fs_unlock_op(sbi, &lc); if (f2fs_sb_has_blkzoned(sbi) && err == -EAGAIN && gc_required) { - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &lc); err = f2fs_gc_range(sbi, 0, sbi->first_seq_zone_segno - 1, true, ZONED_PIN_SEC_REQUIRED_COUNT); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &lc); gc_required = false; if (!err) @@ -3496,6 +3496,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) block_t start_block, end_block; struct cp_control cpc; struct discard_policy dpolicy; + struct f2fs_lock_context lc; unsigned long long trimmed = 0; int err = 0; bool need_align = f2fs_lfs_mode(sbi) && __is_large_section(sbi); @@ -3528,10 +3529,10 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) if (sbi->discard_blks == 0) goto out; - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &lc); stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_write_checkpoint(sbi, &cpc); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &lc); if (err) goto out; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8cd519bb3c97..da3316528d1b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2559,6 +2559,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) { unsigned int s_flags = sbi->sb->s_flags; struct cp_control cpc; + struct f2fs_lock_context lc; unsigned int gc_mode = sbi->gc_mode; int err = 0; int ret; @@ -2588,7 +2589,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) .no_bg_gc = true, .nr_free_secs = 1 }; - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); stat_inc_gc_call_count(sbi, FOREGROUND); err = f2fs_gc(sbi, &gc_control); if (err == -ENODATA) { @@ -2612,7 +2613,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) } skip_gc: - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &lc); cpc.reason = CP_PAUSE; set_sbi_flag(sbi, SBI_CP_DISABLED); stat_inc_cp_call_count(sbi, TOTAL_CALL); @@ -2625,7 +2626,7 @@ skip_gc: spin_unlock(&sbi->stat_lock); out_unlock: - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &lc); restore_flag: sbi->gc_mode = gc_mode; sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */ @@ -2638,6 +2639,7 @@ static int f2fs_enable_checkpoint(struct f2fs_sb_info *sbi) unsigned int nr_pages = get_pages(sbi, F2FS_DIRTY_DATA) / 16; long long start, writeback, lock, sync_inode, end; int ret; + struct f2fs_lock_context lc; f2fs_info(sbi, "%s start, meta: %lld, node: %lld, data: %lld", __func__, @@ -2672,12 +2674,12 @@ static int f2fs_enable_checkpoint(struct f2fs_sb_info *sbi) sync_inode = ktime_get(); - f2fs_down_write(&sbi->gc_lock); + f2fs_down_write_trace(&sbi->gc_lock, &lc); f2fs_dirty_to_prefree(sbi); clear_sbi_flag(sbi, SBI_CP_DISABLED); set_sbi_flag(sbi, SBI_IS_DIRTY); - f2fs_up_write(&sbi->gc_lock); + f2fs_up_write_trace(&sbi->gc_lock, &lc); f2fs_info(sbi, "%s sync_fs, meta: %lld, imeta: %lld, node: %lld, dents: %lld, qdata: %lld", __func__, @@ -4893,7 +4895,7 @@ try_onemore: sbi->sb = sb; /* initialize locks within allocated memory */ - init_f2fs_rwsem(&sbi->gc_lock); + init_f2fs_rwsem_trace(&sbi->gc_lock, sbi, LOCK_NAME_GC_LOCK); mutex_init(&sbi->writepages); init_f2fs_rwsem(&sbi->cp_global_sem); init_f2fs_rwsem_trace(&sbi->node_write, sbi, LOCK_NAME_NODE_WRITE); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index e5cfb8ad0d5e..bf353e7e024d 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -188,7 +188,8 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); __print_symbolic(lock, \ { LOCK_NAME_CP_RWSEM, "cp_rwsem" }, \ { LOCK_NAME_NODE_CHANGE, "node_change" }, \ - { LOCK_NAME_NODE_WRITE, "node_write" }) + { LOCK_NAME_NODE_WRITE, "node_write" }, \ + { LOCK_NAME_GC_LOCK, "gc_lock" }) struct f2fs_sb_info; struct f2fs_io_info; -- cgit v1.2.3 From ce9fe67c9cdb21a0321f8ea37b725b3258d2b3cd Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 4 Jan 2026 10:07:22 +0800 Subject: f2fs: trace elapsed time for cp_global_sem lock Use f2fs_{down,up}_write_trace for cp_global_sem to trace lock elapsed time. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++++++---- fs/f2fs/f2fs.h | 1 + fs/f2fs/gc.c | 5 +++-- fs/f2fs/recovery.c | 5 +++-- fs/f2fs/super.c | 2 +- include/trace/events/f2fs.h | 3 ++- 6 files changed, 16 insertions(+), 10 deletions(-) (limited to 'include/trace/events') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index da7bcfa2a178..86656231ce83 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -513,6 +513,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); + struct f2fs_lock_context lc; long diff, written; if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) @@ -525,13 +526,13 @@ static int f2fs_write_meta_pages(struct address_space *mapping, goto skip_write; /* if locked failed, cp will flush dirty pages instead */ - if (!f2fs_down_write_trylock(&sbi->cp_global_sem)) + if (!f2fs_down_write_trylock_trace(&sbi->cp_global_sem, &lc)) goto skip_write; trace_f2fs_writepages(mapping->host, wbc, META); diff = nr_pages_to_write(sbi, META, wbc); written = f2fs_sync_meta_pages(sbi, META, wbc->nr_to_write, FS_META_IO); - f2fs_up_write(&sbi->cp_global_sem); + f2fs_up_write_trace(&sbi->cp_global_sem, &lc); wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff); return 0; @@ -1780,6 +1781,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct f2fs_lock_context lc; unsigned long long ckpt_ver; int err = 0; @@ -1794,7 +1796,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_warn(sbi, "Start checkpoint disabled!"); } if (cpc->reason != CP_RESIZE) - f2fs_down_write(&sbi->cp_global_sem); + f2fs_down_write_trace(&sbi->cp_global_sem, &lc); stat_cp_time(cpc, CP_TIME_LOCK); @@ -1884,7 +1886,7 @@ stop: trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, CP_PHASE_FINISH_CHECKPOINT); out: if (cpc->reason != CP_RESIZE) - f2fs_up_write(&sbi->cp_global_sem); + f2fs_up_write_trace(&sbi->cp_global_sem, &lc); return err; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3f3faa5e8ff1..dc08f6c06810 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -179,6 +179,7 @@ enum f2fs_lock_name { LOCK_NAME_NODE_CHANGE, LOCK_NAME_NODE_WRITE, LOCK_NAME_GC_LOCK, + LOCK_NAME_CP_GLOBAL, }; /* diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 391e66064c7e..1538f5b0a644 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -2267,6 +2267,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count) struct cp_control cpc = { CP_RESIZE, 0, 0, 0 }; struct f2fs_lock_context lc; struct f2fs_lock_context glc; + struct f2fs_lock_context clc; unsigned int secs; int err = 0; __u32 rem; @@ -2350,7 +2351,7 @@ out_drop_write: } f2fs_down_write_trace(&sbi->gc_lock, &glc); - f2fs_down_write(&sbi->cp_global_sem); + f2fs_down_write_trace(&sbi->cp_global_sem, &clc); spin_lock(&sbi->stat_lock); if (shrunk_blocks + valid_user_blocks(sbi) + @@ -2398,7 +2399,7 @@ recover_out: spin_unlock(&sbi->stat_lock); } out_err: - f2fs_up_write(&sbi->cp_global_sem); + f2fs_up_write_trace(&sbi->cp_global_sem, &clc); f2fs_up_write_trace(&sbi->gc_lock, &glc); thaw_super(sbi->sb, FREEZE_HOLDER_KERNEL, NULL); return err; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index c3415ebb9f50..39f6e9830a9c 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -875,6 +875,7 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) LIST_HEAD(inode_list); LIST_HEAD(tmp_inode_list); LIST_HEAD(dir_list); + struct f2fs_lock_context lc; int err; int ret = 0; unsigned long s_flags = sbi->sb->s_flags; @@ -888,7 +889,7 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) f2fs_info(sbi, "recover fsync data on readonly fs"); /* prevent checkpoint */ - f2fs_down_write(&sbi->cp_global_sem); + f2fs_down_write_trace(&sbi->cp_global_sem, &lc); /* step #1: find fsynced inode numbers */ err = find_fsync_dnodes(sbi, &inode_list, check_only, &new_inode); @@ -932,7 +933,7 @@ skip: if (!err) clear_sbi_flag(sbi, SBI_POR_DOING); - f2fs_up_write(&sbi->cp_global_sem); + f2fs_up_write_trace(&sbi->cp_global_sem, &lc); /* let's drop all the directory inodes for clean checkpoint */ destroy_fsync_dnodes(&dir_list, err); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index da3316528d1b..be01eacbe5fa 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4897,7 +4897,7 @@ try_onemore: /* initialize locks within allocated memory */ init_f2fs_rwsem_trace(&sbi->gc_lock, sbi, LOCK_NAME_GC_LOCK); mutex_init(&sbi->writepages); - init_f2fs_rwsem(&sbi->cp_global_sem); + init_f2fs_rwsem_trace(&sbi->cp_global_sem, sbi, LOCK_NAME_CP_GLOBAL); init_f2fs_rwsem_trace(&sbi->node_write, sbi, LOCK_NAME_NODE_WRITE); init_f2fs_rwsem_trace(&sbi->node_change, sbi, LOCK_NAME_NODE_CHANGE); spin_lock_init(&sbi->stat_lock); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index bf353e7e024d..859de7c8d1c7 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -189,7 +189,8 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); { LOCK_NAME_CP_RWSEM, "cp_rwsem" }, \ { LOCK_NAME_NODE_CHANGE, "node_change" }, \ { LOCK_NAME_NODE_WRITE, "node_write" }, \ - { LOCK_NAME_GC_LOCK, "gc_lock" }) + { LOCK_NAME_GC_LOCK, "gc_lock" }, \ + { LOCK_NAME_CP_GLOBAL, "cp_global" }) struct f2fs_sb_info; struct f2fs_io_info; -- cgit v1.2.3 From 67972c2b89749356bc9823bd58f7f14b28e681e4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 4 Jan 2026 10:07:23 +0800 Subject: f2fs: trace elapsed time for io_rwsem lock Use f2fs_{down,up}_{read,write}_trace for io_rwsem to trace lock elapsed time. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 18 +++++++++++------- fs/f2fs/f2fs.h | 1 + include/trace/events/f2fs.h | 3 ++- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include/trace/events') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 79455d7acba5..7dee58fbfc0b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -620,7 +620,8 @@ int f2fs_init_write_merge_io(struct f2fs_sb_info *sbi) for (j = HOT; j < n; j++) { struct f2fs_bio_info *io = &sbi->write_io[i][j]; - init_f2fs_rwsem(&io->io_rwsem); + init_f2fs_rwsem_trace(&io->io_rwsem, sbi, + LOCK_NAME_IO_RWSEM); io->sbi = sbi; io->bio = NULL; io->last_block_in_bio = 0; @@ -644,8 +645,9 @@ static void __f2fs_submit_merged_write(struct f2fs_sb_info *sbi, { enum page_type btype = PAGE_TYPE_OF_BIO(type); struct f2fs_bio_info *io = sbi->write_io[btype] + temp; + struct f2fs_lock_context lc; - f2fs_down_write(&io->io_rwsem); + f2fs_down_write_trace(&io->io_rwsem, &lc); if (!io->bio) goto unlock_out; @@ -659,7 +661,7 @@ static void __f2fs_submit_merged_write(struct f2fs_sb_info *sbi, } __submit_merged_bio(io); unlock_out: - f2fs_up_write(&io->io_rwsem); + f2fs_up_write_trace(&io->io_rwsem, &lc); } static void __submit_merged_write_cond(struct f2fs_sb_info *sbi, @@ -674,10 +676,11 @@ static void __submit_merged_write_cond(struct f2fs_sb_info *sbi, if (!force) { enum page_type btype = PAGE_TYPE_OF_BIO(type); struct f2fs_bio_info *io = sbi->write_io[btype] + temp; + struct f2fs_lock_context lc; - f2fs_down_read(&io->io_rwsem); + f2fs_down_read_trace(&io->io_rwsem, &lc); ret = __has_merged_page(io->bio, inode, folio, ino); - f2fs_up_read(&io->io_rwsem); + f2fs_up_read_trace(&io->io_rwsem, &lc); } if (ret) { __f2fs_submit_merged_write(sbi, type, temp); @@ -987,11 +990,12 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio) enum page_type btype = PAGE_TYPE_OF_BIO(fio->type); struct f2fs_bio_info *io = sbi->write_io[btype] + fio->temp; struct folio *bio_folio; + struct f2fs_lock_context lc; enum count_type type; f2fs_bug_on(sbi, is_read_io(fio->op)); - f2fs_down_write(&io->io_rwsem); + f2fs_down_write_trace(&io->io_rwsem, &lc); next: #ifdef CONFIG_BLK_DEV_ZONED if (f2fs_sb_has_blkzoned(sbi) && btype < META && io->zone_pending_bio) { @@ -1073,7 +1077,7 @@ out: if (is_sbi_flag_set(sbi, SBI_IS_SHUTDOWN) || !f2fs_is_checkpoint_ready(sbi)) __submit_merged_bio(io); - f2fs_up_write(&io->io_rwsem); + f2fs_up_write_trace(&io->io_rwsem, &lc); } static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dc08f6c06810..dc9f834bbe0d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -180,6 +180,7 @@ enum f2fs_lock_name { LOCK_NAME_NODE_WRITE, LOCK_NAME_GC_LOCK, LOCK_NAME_CP_GLOBAL, + LOCK_NAME_IO_RWSEM, }; /* diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 859de7c8d1c7..c3b6b509472f 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -190,7 +190,8 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); { LOCK_NAME_NODE_CHANGE, "node_change" }, \ { LOCK_NAME_NODE_WRITE, "node_write" }, \ { LOCK_NAME_GC_LOCK, "gc_lock" }, \ - { LOCK_NAME_CP_GLOBAL, "cp_global" }) + { LOCK_NAME_CP_GLOBAL, "cp_global" }, \ + { LOCK_NAME_IO_RWSEM, "io_rwsem" }) struct f2fs_sb_info; struct f2fs_io_info; -- cgit v1.2.3 From 188344c8ac0b740ee2e5deebda2004b39ccbee74 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 8 Jan 2026 15:19:06 +0100 Subject: fs: factor out a sync_lazytime helper Centralize how we synchronize a lazytime update into the actual on-disk timestamp into a single helper. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260108141934.2052404-7-hch@lst.de Reviewed-by: Jan Kara Reviewed-by: Chaitanya Kulkarni Reviewed-by: Jeff Layton Signed-off-by: Christian Brauner --- fs/fs-writeback.c | 22 +++++++++++++++------- fs/inode.c | 5 +---- fs/internal.h | 3 ++- fs/sync.c | 4 ++-- include/trace/events/writeback.h | 6 ------ 5 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include/trace/events') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 6800886c4d10..3d68b757136c 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1711,6 +1711,16 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, } } +bool sync_lazytime(struct inode *inode) +{ + if (!(inode_state_read_once(inode) & I_DIRTY_TIME)) + return false; + + trace_writeback_lazytime(inode); + mark_inode_dirty_sync(inode); + return true; +} + /* * Write out an inode and its dirty pages (or some of its dirty pages, depending * on @wbc->nr_to_write), and clear the relevant dirty flags from i_state. @@ -1750,17 +1760,15 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) } /* - * If the inode has dirty timestamps and we need to write them, call - * mark_inode_dirty_sync() to notify the filesystem about it and to - * change I_DIRTY_TIME into I_DIRTY_SYNC. + * For data integrity writeback, or when the dirty interval expired, + * ask the file system to propagata lazy timestamp updates into real + * dirty state. */ if ((inode_state_read_once(inode) & I_DIRTY_TIME) && (wbc->sync_mode == WB_SYNC_ALL || time_after(jiffies, inode->dirtied_time_when + - dirtytime_expire_interval * HZ))) { - trace_writeback_lazytime(inode); - mark_inode_dirty_sync(inode); - } + dirtytime_expire_interval * HZ))) + sync_lazytime(inode); /* * Get and clear the dirty flags from i_state. This needs to be done diff --git a/fs/inode.c b/fs/inode.c index a0dd11a05473..0cafe74bff2d 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1979,11 +1979,8 @@ retry: if (atomic_add_unless(&inode->i_count, -1, 1)) return; - if ((inode_state_read_once(inode) & I_DIRTY_TIME) && inode->i_nlink) { - trace_writeback_lazytime_iput(inode); - mark_inode_dirty_sync(inode); + if (inode->i_nlink && sync_lazytime(inode)) goto retry; - } spin_lock(&inode->i_lock); if (unlikely((inode_state_read(inode) & I_DIRTY_TIME) && inode->i_nlink)) { diff --git a/fs/internal.h b/fs/internal.h index ab638d41ab81..18a062c1b5b0 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -214,7 +214,8 @@ bool in_group_or_capable(struct mnt_idmap *idmap, /* * fs-writeback.c */ -extern long get_nr_dirty_inodes(void); +long get_nr_dirty_inodes(void); +bool sync_lazytime(struct inode *inode); /* * dcache.c diff --git a/fs/sync.c b/fs/sync.c index 431fc5f5be06..4283af7119d1 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -183,8 +183,8 @@ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) if (!file->f_op->fsync) return -EINVAL; - if (!datasync && (inode_state_read_once(inode) & I_DIRTY_TIME)) - mark_inode_dirty_sync(inode); + if (!datasync) + sync_lazytime(inode); return file->f_op->fsync(file, start, end, datasync); } EXPORT_SYMBOL(vfs_fsync_range); diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 311a341e6fe4..7162d03e69a5 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -856,12 +856,6 @@ DEFINE_EVENT(writeback_inode_template, writeback_lazytime, TP_ARGS(inode) ); -DEFINE_EVENT(writeback_inode_template, writeback_lazytime_iput, - TP_PROTO(struct inode *inode), - - TP_ARGS(inode) -); - DEFINE_EVENT(writeback_inode_template, writeback_dirty_inode_enqueue, TP_PROTO(struct inode *inode), -- cgit v1.2.3 From 2c28769a51deb6022d7fbd499987e237a01dd63a Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 14 Jan 2026 22:03:23 +0000 Subject: rxrpc: Fix recvmsg() unconditional requeue If rxrpc_recvmsg() fails because MSG_DONTWAIT was specified but the call at the front of the recvmsg queue already has its mutex locked, it requeues the call - whether or not the call is already queued. The call may be on the queue because MSG_PEEK was also passed and so the call was not dequeued or because the I/O thread requeued it. The unconditional requeue may then corrupt the recvmsg queue, leading to things like UAFs or refcount underruns. Fix this by only requeuing the call if it isn't already on the queue - and moving it to the front if it is already queued. If we don't queue it, we have to put the ref we obtained by dequeuing it. Also, MSG_PEEK doesn't dequeue the call so shouldn't call rxrpc_notify_socket() for the call if we didn't use up all the data on the queue, so fix that also. Fixes: 540b1c48c37a ("rxrpc: Fix deadlock between call creation and sendmsg/recvmsg") Reported-by: Faith Reported-by: Pumpkin Chang Signed-off-by: David Howells Acked-by: Marc Dionne cc: Nir Ohfeld cc: Willy Tarreau cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/95163.1768428203@warthog.procyon.org.uk Signed-off-by: Jakub Kicinski --- include/trace/events/rxrpc.h | 4 ++++ net/rxrpc/recvmsg.c | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include/trace/events') diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index de6f6d25767c..869f97c9bf73 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -322,6 +322,7 @@ EM(rxrpc_call_put_kernel, "PUT kernel ") \ EM(rxrpc_call_put_poke, "PUT poke ") \ EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \ + EM(rxrpc_call_put_recvmsg_peek_nowait, "PUT peek-nwt") \ EM(rxrpc_call_put_release_recvmsg_q, "PUT rls-rcmq") \ EM(rxrpc_call_put_release_sock, "PUT rls-sock") \ EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \ @@ -340,6 +341,9 @@ EM(rxrpc_call_see_input, "SEE input ") \ EM(rxrpc_call_see_notify_released, "SEE nfy-rlsd") \ EM(rxrpc_call_see_recvmsg, "SEE recvmsg ") \ + EM(rxrpc_call_see_recvmsg_requeue, "SEE recv-rqu") \ + EM(rxrpc_call_see_recvmsg_requeue_first, "SEE recv-rqF") \ + EM(rxrpc_call_see_recvmsg_requeue_move, "SEE recv-rqM") \ EM(rxrpc_call_see_release, "SEE release ") \ EM(rxrpc_call_see_userid_exists, "SEE u-exists") \ EM(rxrpc_call_see_waiting_call, "SEE q-conn ") \ diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 7fa7e77f6bb9..e1f7513a46db 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -518,7 +518,8 @@ try_again: if (rxrpc_call_has_failed(call)) goto call_failed; - if (!skb_queue_empty(&call->recvmsg_queue)) + if (!(flags & MSG_PEEK) && + !skb_queue_empty(&call->recvmsg_queue)) rxrpc_notify_socket(call); goto not_yet_complete; @@ -549,11 +550,21 @@ error_unlock_call: error_requeue_call: if (!(flags & MSG_PEEK)) { spin_lock_irq(&rx->recvmsg_lock); - list_add(&call->recvmsg_link, &rx->recvmsg_q); - spin_unlock_irq(&rx->recvmsg_lock); + if (list_empty(&call->recvmsg_link)) { + list_add(&call->recvmsg_link, &rx->recvmsg_q); + rxrpc_see_call(call, rxrpc_call_see_recvmsg_requeue); + spin_unlock_irq(&rx->recvmsg_lock); + } else if (list_is_first(&call->recvmsg_link, &rx->recvmsg_q)) { + spin_unlock_irq(&rx->recvmsg_lock); + rxrpc_put_call(call, rxrpc_call_see_recvmsg_requeue_first); + } else { + list_move(&call->recvmsg_link, &rx->recvmsg_q); + spin_unlock_irq(&rx->recvmsg_lock); + rxrpc_put_call(call, rxrpc_call_see_recvmsg_requeue_move); + } trace_rxrpc_recvmsg(call_debug_id, rxrpc_recvmsg_requeue, 0); } else { - rxrpc_put_call(call, rxrpc_call_put_recvmsg); + rxrpc_put_call(call, rxrpc_call_put_recvmsg_peek_nowait); } error_no_call: release_sock(&rx->sk); -- cgit v1.2.3 From 87e79fa122bc9a6576f1690ee264fcbd77d3ab58 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Thu, 11 Dec 2025 19:51:38 +0800 Subject: ext4: mark inode format migration fast-commit ineligible Fast commits only log operations that have dedicated replay support. Inode format migration (indirect<->extent layout changes via EXT4_IOC_MIGRATE or toggling EXT4_EXTENTS_FL) rewrites the block mapping representation without going through the fast commit tracking paths. In practice these migrations are rare and usually followed by further updates, but mixing them into a fast commit makes the overall semantics harder to reason about and risks replay gaps if new call sites appear. Teach ext4 to mark the filesystem fast-commit ineligible when ext4_ext_migrate() or ext4_ind_migrate() start their journal transactions. This forces those transactions to fall back to a full commit, ensuring that the entire inode layout change is captured by the normal journal rather than partially encoded in fast commit TLVs. This change should not affect common workloads but makes format migrations safer and easier to reason about under fast commit. Testing: 1. prepare: dd if=/dev/zero of=/root/fc.img bs=1M count=0 seek=128 mkfs.ext4 -O fast_commit -F /root/fc.img mkdir -p /mnt/fc && mount -t ext4 -o loop /root/fc.img /mnt/fc 2. Created a test file and toggled the extents flag to exercise both ext4_ind_migrate() and ext4_ext_migrate(): touch /mnt/fc/migtest chattr -e /mnt/fc/migtest chattr +e /mnt/fc/migtest 3. Verified fast-commit ineligible statistics: tail -n 1 /proc/fs/ext4/loop0/fc_info "Inode format migration": 2 Signed-off-by: Li Chen Link: https://patch.msgid.link/20251211115146.897420-2-me@linux.beauty Signed-off-by: Theodore Ts'o --- fs/ext4/fast_commit.c | 1 + fs/ext4/fast_commit.h | 1 + fs/ext4/migrate.c | 12 ++++++++++++ include/trace/events/ext4.h | 4 +++- 4 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include/trace/events') diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index fa66b08de999..afb28b3e52bb 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -2302,6 +2302,7 @@ static const char * const fc_ineligible_reasons[] = { [EXT4_FC_REASON_FALLOC_RANGE] = "Falloc range op", [EXT4_FC_REASON_INODE_JOURNAL_DATA] = "Data journalling", [EXT4_FC_REASON_ENCRYPTED_FILENAME] = "Encrypted filename", + [EXT4_FC_REASON_MIGRATE] = "Inode format migration", }; int ext4_fc_info_show(struct seq_file *seq, void *v) diff --git a/fs/ext4/fast_commit.h b/fs/ext4/fast_commit.h index 3bd534e4dbbf..be3b84a74c32 100644 --- a/fs/ext4/fast_commit.h +++ b/fs/ext4/fast_commit.h @@ -97,6 +97,7 @@ enum { EXT4_FC_REASON_FALLOC_RANGE, EXT4_FC_REASON_INODE_JOURNAL_DATA, EXT4_FC_REASON_ENCRYPTED_FILENAME, + EXT4_FC_REASON_MIGRATE, EXT4_FC_REASON_MAX }; diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index 1b0dfd963d3f..96ab95167bd6 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -449,6 +449,12 @@ int ext4_ext_migrate(struct inode *inode) retval = PTR_ERR(handle); goto out_unlock; } + /* + * This operation rewrites the inode's block mapping layout + * (indirect to extents) and is not tracked in the fast commit + * log, so disable fast commits for this transaction. + */ + ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_MIGRATE, handle); goal = (((inode->i_ino - 1) / EXT4_INODES_PER_GROUP(inode->i_sb)) * EXT4_INODES_PER_GROUP(inode->i_sb)) + 1; owner[0] = i_uid_read(inode); @@ -630,6 +636,12 @@ int ext4_ind_migrate(struct inode *inode) ret = PTR_ERR(handle); goto out_unlock; } + /* + * This operation rewrites the inode's block mapping layout + * (extents to indirect blocks) and is not tracked in the fast + * commit log, so disable fast commits for this transaction. + */ + ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_MIGRATE, handle); down_write(&EXT4_I(inode)->i_data_sem); ret = ext4_ext_check_inode(inode); diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index fd76d14c2776..31598a7810d6 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -102,6 +102,7 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_RENAME_DIR); TRACE_DEFINE_ENUM(EXT4_FC_REASON_FALLOC_RANGE); TRACE_DEFINE_ENUM(EXT4_FC_REASON_INODE_JOURNAL_DATA); TRACE_DEFINE_ENUM(EXT4_FC_REASON_ENCRYPTED_FILENAME); +TRACE_DEFINE_ENUM(EXT4_FC_REASON_MIGRATE); TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX); #define show_fc_reason(reason) \ @@ -115,7 +116,8 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX); { EXT4_FC_REASON_RENAME_DIR, "RENAME_DIR"}, \ { EXT4_FC_REASON_FALLOC_RANGE, "FALLOC_RANGE"}, \ { EXT4_FC_REASON_INODE_JOURNAL_DATA, "INODE_JOURNAL_DATA"}, \ - { EXT4_FC_REASON_ENCRYPTED_FILENAME, "ENCRYPTED_FILENAME"}) + { EXT4_FC_REASON_ENCRYPTED_FILENAME, "ENCRYPTED_FILENAME"}, \ + { EXT4_FC_REASON_MIGRATE, "MIGRATE"}) TRACE_DEFINE_ENUM(CR_POWER2_ALIGNED); TRACE_DEFINE_ENUM(CR_GOAL_LEN_FAST); -- cgit v1.2.3 From 16d43b9748c655b36a675cc55789f40fd827e9b1 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Thu, 11 Dec 2025 19:51:39 +0800 Subject: ext4: mark fs-verity enable fast-commit ineligible Fast commits only log operations that have dedicated replay support. Enabling fs-verity builds a Merkle tree and updates inode and orphan state in ways that are not described by the fast commit replay tags. In practice these operations are rare and usually followed by further updates, but mixing them into a fast commit makes the overall semantics harder to reason about and risks replay gaps if new call sites appear. Teach ext4 to mark the filesystem fast-commit ineligible when ext4_end_enable_verity() starts its journal transaction. This forces that transaction to fall back to a full commit, ensuring that the fs-verity enable changes are captured by the normal journal rather than partially encoded in fast commit TLVs. This change should not affect common workloads but makes fs-verity enable safer and easier to reason about under fast commit. Testing: 1. prepare: dd if=/dev/zero of=/root/fc_verity.img bs=1M count=0 seek=128 mkfs.ext4 -O fast_commit,verity -F /root/fc_verity.img mkdir -p /mnt/fc_verity && mount -t ext4 -o loop /root/fc_verity.img /mnt/fc_verity 2. Enabled fs-verity on a file and verified reason accounting: echo "data" > /mnt/fc_verity/verityfile /root/enable_verity /mnt/fc_verity/verityfile sync tail -n 1 /proc/fs/ext4/loop0/fc_info "fs-verity enable": 1 3. Enabled fs-verity on a second file, fsynced it, and checked that the ineligible commit counter is updated too: echo "data2" > /mnt/fc_verity/verityfile2 /root/enable_verity /mnt/fc_verity/verityfile2 /root/fsync_file /mnt/fc_verity/verityfile2 sync /proc/fs/ext4/loop0/fc_info shows "fs-verity enable" incremented and fc stats ineligible increased accordingly. Signed-off-by: Li Chen Link: https://patch.msgid.link/20251211115146.897420-3-me@linux.beauty Signed-off-by: Theodore Ts'o --- fs/ext4/fast_commit.c | 1 + fs/ext4/fast_commit.h | 1 + fs/ext4/verity.c | 2 ++ include/trace/events/ext4.h | 4 +++- 4 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/trace/events') diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index afb28b3e52bb..242b69e5fe13 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -2303,6 +2303,7 @@ static const char * const fc_ineligible_reasons[] = { [EXT4_FC_REASON_INODE_JOURNAL_DATA] = "Data journalling", [EXT4_FC_REASON_ENCRYPTED_FILENAME] = "Encrypted filename", [EXT4_FC_REASON_MIGRATE] = "Inode format migration", + [EXT4_FC_REASON_VERITY] = "fs-verity enable", }; int ext4_fc_info_show(struct seq_file *seq, void *v) diff --git a/fs/ext4/fast_commit.h b/fs/ext4/fast_commit.h index be3b84a74c32..20f65135208f 100644 --- a/fs/ext4/fast_commit.h +++ b/fs/ext4/fast_commit.h @@ -98,6 +98,7 @@ enum { EXT4_FC_REASON_INODE_JOURNAL_DATA, EXT4_FC_REASON_ENCRYPTED_FILENAME, EXT4_FC_REASON_MIGRATE, + EXT4_FC_REASON_VERITY, EXT4_FC_REASON_MAX }; diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c index 415d9c4d8a32..667f9e8d4da9 100644 --- a/fs/ext4/verity.c +++ b/fs/ext4/verity.c @@ -231,6 +231,8 @@ static int ext4_end_enable_verity(struct file *filp, const void *desc, goto cleanup; } + ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_VERITY, handle); + err = ext4_orphan_del(handle, inode); if (err) goto stop_and_cleanup; diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 31598a7810d6..726705bce9d3 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -103,6 +103,7 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_FALLOC_RANGE); TRACE_DEFINE_ENUM(EXT4_FC_REASON_INODE_JOURNAL_DATA); TRACE_DEFINE_ENUM(EXT4_FC_REASON_ENCRYPTED_FILENAME); TRACE_DEFINE_ENUM(EXT4_FC_REASON_MIGRATE); +TRACE_DEFINE_ENUM(EXT4_FC_REASON_VERITY); TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX); #define show_fc_reason(reason) \ @@ -117,7 +118,8 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX); { EXT4_FC_REASON_FALLOC_RANGE, "FALLOC_RANGE"}, \ { EXT4_FC_REASON_INODE_JOURNAL_DATA, "INODE_JOURNAL_DATA"}, \ { EXT4_FC_REASON_ENCRYPTED_FILENAME, "ENCRYPTED_FILENAME"}, \ - { EXT4_FC_REASON_MIGRATE, "MIGRATE"}) + { EXT4_FC_REASON_MIGRATE, "MIGRATE"}, \ + { EXT4_FC_REASON_VERITY, "VERITY"}) TRACE_DEFINE_ENUM(CR_POWER2_ALIGNED); TRACE_DEFINE_ENUM(CR_GOAL_LEN_FAST); -- cgit v1.2.3 From 690558921d9f9388c6bc83610451d8cb393e4d88 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Thu, 11 Dec 2025 19:51:40 +0800 Subject: ext4: mark move extents fast-commit ineligible Fast commits only log operations that have dedicated replay support. EXT4_IOC_MOVE_EXT swaps extents between regular files and may copy data, rewriting the affected inodes' block mapping layout without going through the fast commit tracking paths. In practice these operations are rare and usually followed by further updates, but mixing them into a fast commit makes the overall semantics harder to reason about and risks replay gaps if new call sites appear. Teach ext4 to mark the filesystem fast-commit ineligible for the journal transactions used by move_extent_per_page() when EXT4_IOC_MOVE_EXT runs. This forces those transactions to fall back to a full commit, ensuring that these multi-inode extent swaps are captured by the normal journal rather than partially encoded in fast commit TLVs. This change should not affect common workloads but makes online defragmentation safer and easier to reason about under fast commit. Testing: 1. prepare: dd if=/dev/zero of=/root/fc_move.img bs=1M count=0 seek=256 mkfs.ext4 -O fast_commit -F /root/fc_move.img mkdir -p /mnt/fc_move && mount -t ext4 -o loop \ /root/fc_move.img /mnt/fc_move 2. Created two files, ran EXT4_IOC_MOVE_EXT via e4defrag, and checked the ineligible reason statistics: fallocate -l 64M /mnt/fc_move/file1 cp /mnt/fc_move/file1 /mnt/fc_move/file2 e4defrag /mnt/fc_move/file1 cat /proc/fs/ext4/loop0/fc_info shows "Move extents": > 0 and fc stats ineligible > 0. Signed-off-by: Li Chen Link: https://patch.msgid.link/20251211115146.897420-4-me@linux.beauty Signed-off-by: Theodore Ts'o --- fs/ext4/fast_commit.c | 1 + fs/ext4/fast_commit.h | 1 + fs/ext4/move_extent.c | 2 ++ include/trace/events/ext4.h | 4 +++- 4 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/trace/events') diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index 242b69e5fe13..0ef2154a2b1f 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -2304,6 +2304,7 @@ static const char * const fc_ineligible_reasons[] = { [EXT4_FC_REASON_ENCRYPTED_FILENAME] = "Encrypted filename", [EXT4_FC_REASON_MIGRATE] = "Inode format migration", [EXT4_FC_REASON_VERITY] = "fs-verity enable", + [EXT4_FC_REASON_MOVE_EXT] = "Move extents", }; int ext4_fc_info_show(struct seq_file *seq, void *v) diff --git a/fs/ext4/fast_commit.h b/fs/ext4/fast_commit.h index 20f65135208f..2f77a37fb101 100644 --- a/fs/ext4/fast_commit.h +++ b/fs/ext4/fast_commit.h @@ -99,6 +99,7 @@ enum { EXT4_FC_REASON_ENCRYPTED_FILENAME, EXT4_FC_REASON_MIGRATE, EXT4_FC_REASON_VERITY, + EXT4_FC_REASON_MOVE_EXT, EXT4_FC_REASON_MAX }; diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 635fb8a52e0c..ce1f738dff93 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -321,6 +321,8 @@ static int mext_move_extent(struct mext_data *mext, u64 *m_len) ret = PTR_ERR(handle); goto out; } + ext4_fc_mark_ineligible(orig_inode->i_sb, EXT4_FC_REASON_MOVE_EXT, + handle); ret = mext_move_begin(mext, folio, &move_type); if (ret) diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 726705bce9d3..a3e8fe414df8 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -104,6 +104,7 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_INODE_JOURNAL_DATA); TRACE_DEFINE_ENUM(EXT4_FC_REASON_ENCRYPTED_FILENAME); TRACE_DEFINE_ENUM(EXT4_FC_REASON_MIGRATE); TRACE_DEFINE_ENUM(EXT4_FC_REASON_VERITY); +TRACE_DEFINE_ENUM(EXT4_FC_REASON_MOVE_EXT); TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX); #define show_fc_reason(reason) \ @@ -119,7 +120,8 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX); { EXT4_FC_REASON_INODE_JOURNAL_DATA, "INODE_JOURNAL_DATA"}, \ { EXT4_FC_REASON_ENCRYPTED_FILENAME, "ENCRYPTED_FILENAME"}, \ { EXT4_FC_REASON_MIGRATE, "MIGRATE"}, \ - { EXT4_FC_REASON_VERITY, "VERITY"}) + { EXT4_FC_REASON_VERITY, "VERITY"}, \ + { EXT4_FC_REASON_MOVE_EXT, "MOVE_EXT"}) TRACE_DEFINE_ENUM(CR_POWER2_ALIGNED); TRACE_DEFINE_ENUM(CR_GOAL_LEN_FAST); -- cgit v1.2.3 From 804c26b961da295bd70c86a3c9dc4bea0b09de88 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 16 Dec 2025 00:01:25 -0800 Subject: mm/damon/core: add trace point for damos stat per apply interval DAMON users can read DAMOS stats via DAMON sysfs interface. It enables efficient, simple and flexible usages of the stats. Especially for systems not having advanced tools like perf or bpftrace, that can be useful. But if the advanced tools are available, exposing the stats via tracepoint can reduce unnecessary reimplementation of the wheels. Add a new tracepoint for DAMOS stats, namely damos_stat_after_apply_interval. The tracepoint is triggered for each scheme's apply interval and exposes the whole stat values. If the user needs sub-apply interval information for any chance, damos_before_apply tracepoint could be used. Link: https://lkml.kernel.org/r/20251216080128.42991-13-sj@kernel.org Signed-off-by: SeongJae Park Reviewed-by: Steven Rostedt (Google) Cc: Jonathan Corbet Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/trace/events/damon.h | 41 +++++++++++++++++++++++++++++++++++++++++ mm/damon/core.c | 17 +++++++++++++++++ 2 files changed, 58 insertions(+) (limited to 'include/trace/events') diff --git a/include/trace/events/damon.h b/include/trace/events/damon.h index 852d725afea2..24fc402ab3c8 100644 --- a/include/trace/events/damon.h +++ b/include/trace/events/damon.h @@ -9,6 +9,47 @@ #include #include +TRACE_EVENT(damos_stat_after_apply_interval, + + TP_PROTO(unsigned int context_idx, unsigned int scheme_idx, + struct damos_stat *stat), + + TP_ARGS(context_idx, scheme_idx, stat), + + TP_STRUCT__entry( + __field(unsigned int, context_idx) + __field(unsigned int, scheme_idx) + __field(unsigned long, nr_tried) + __field(unsigned long, sz_tried) + __field(unsigned long, nr_applied) + __field(unsigned long, sz_applied) + __field(unsigned long, sz_ops_filter_passed) + __field(unsigned long, qt_exceeds) + __field(unsigned long, nr_snapshots) + ), + + TP_fast_assign( + __entry->context_idx = context_idx; + __entry->scheme_idx = scheme_idx; + __entry->nr_tried = stat->nr_tried; + __entry->sz_tried = stat->sz_tried; + __entry->nr_applied = stat->nr_applied; + __entry->sz_applied = stat->sz_applied; + __entry->sz_ops_filter_passed = stat->sz_ops_filter_passed; + __entry->qt_exceeds = stat->qt_exceeds; + __entry->nr_snapshots = stat->nr_snapshots; + ), + + TP_printk("ctx_idx=%u scheme_idx=%u nr_tried=%lu sz_tried=%lu " + "nr_applied=%lu sz_tried=%lu sz_ops_filter_passed=%lu " + "qt_exceeds=%lu nr_snapshots=%lu", + __entry->context_idx, __entry->scheme_idx, + __entry->nr_tried, __entry->sz_tried, + __entry->nr_applied, __entry->sz_applied, + __entry->sz_ops_filter_passed, __entry->qt_exceeds, + __entry->nr_snapshots) +); + TRACE_EVENT(damos_esz, TP_PROTO(unsigned int context_idx, unsigned int scheme_idx, diff --git a/mm/damon/core.c b/mm/damon/core.c index 344773f53f64..f4d83e12ba0e 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2289,6 +2289,22 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) quota->min_score = score; } +static void damos_trace_stat(struct damon_ctx *c, struct damos *s) +{ + unsigned int cidx = 0, sidx = 0; + struct damos *siter; + + if (!trace_damos_stat_after_apply_interval_enabled()) + return; + + damon_for_each_scheme(siter, c) { + if (siter == s) + break; + sidx++; + } + trace_damos_stat_after_apply_interval(cidx, sidx, &s->stat); +} + static void kdamond_apply_schemes(struct damon_ctx *c) { struct damon_target *t; @@ -2330,6 +2346,7 @@ static void kdamond_apply_schemes(struct damon_ctx *c) (s->apply_interval_us ? s->apply_interval_us : c->attrs.aggr_interval) / sample_interval; s->last_applied = NULL; + damos_trace_stat(c, s); } mutex_unlock(&c->walk_control_lock); } -- cgit v1.2.3 From 64dd89ae01f2708a508e028c28b7906e4702a9a7 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Mon, 15 Dec 2025 12:57:53 -0500 Subject: mm/block/fs: remove laptop_mode Laptop mode was introduced to save battery, by delaying and consolidating writes and thereby maximize the time rotating hard drives wouldn't have to spin. Luckily, rotating hard drives, with their high spin-up times and power draw, are a thing of the past for battery-powered devices. Reclaim has also since changed to not write single filesystem pages anymore, and regular filesystem writeback is lumpy by design. The juice doesn't appear worth the squeeze anymore. The footprint of the feature is small, but nevertheless it's a complicating factor in mm, block, filesystems. Developers don't think about it, and it likely hasn't been tested with new reclaim and writeback changes in years. Let's sunset it. Keep the sysctl with a deprecation warning around for a few more cycles, but remove all functionality behind it. [akpm@linux-foundation.org: fix Documentation/admin-guide/laptops/index.rst] Link: https://lkml.kernel.org/r/20251216185201.GH905277@cmpxchg.org Signed-off-by: Johannes Weiner Suggested-by: Christoph Hellwig Reviewed-by: Christoph Hellwig Acked-by: Jens Axboe Reviewed-by: Shakeel Butt Acked-by: Michal Hocko Cc: Deepanshu Kartikey Signed-off-by: Andrew Morton --- Documentation/admin-guide/laptops/index.rst | 1 - Documentation/admin-guide/laptops/laptop-mode.rst | 770 ---------------------- Documentation/admin-guide/sysctl/vm.rst | 8 - block/blk-mq.c | 3 - fs/ext4/inode.c | 3 +- fs/sync.c | 2 - fs/xfs/xfs_super.c | 9 - include/linux/backing-dev-defs.h | 3 - include/linux/writeback.h | 4 - include/trace/events/writeback.h | 1 - include/uapi/linux/sysctl.h | 2 +- mm/backing-dev.c | 3 - mm/page-writeback.c | 74 +-- mm/vmscan.c | 30 +- 14 files changed, 25 insertions(+), 888 deletions(-) delete mode 100644 Documentation/admin-guide/laptops/laptop-mode.rst (limited to 'include/trace/events') diff --git a/Documentation/admin-guide/laptops/index.rst b/Documentation/admin-guide/laptops/index.rst index 6432c251dc95..c0b911d05c59 100644 --- a/Documentation/admin-guide/laptops/index.rst +++ b/Documentation/admin-guide/laptops/index.rst @@ -10,7 +10,6 @@ Laptop Drivers alienware-wmi asus-laptop disk-shock-protection - laptop-mode lg-laptop samsung-galaxybook sony-laptop diff --git a/Documentation/admin-guide/laptops/laptop-mode.rst b/Documentation/admin-guide/laptops/laptop-mode.rst deleted file mode 100644 index 66eb9cd918b5..000000000000 --- a/Documentation/admin-guide/laptops/laptop-mode.rst +++ /dev/null @@ -1,770 +0,0 @@ -=============================================== -How to conserve battery power using laptop-mode -=============================================== - -Document Author: Bart Samwel (bart@samwel.tk) - -Date created: January 2, 2004 - -Last modified: December 06, 2004 - -Introduction ------------- - -Laptop mode is used to minimize the time that the hard disk needs to be spun up, -to conserve battery power on laptops. It has been reported to cause significant -power savings. - -.. Contents - - * Introduction - * Installation - * Caveats - * The Details - * Tips & Tricks - * Control script - * ACPI integration - * Monitoring tool - - -Installation ------------- - -To use laptop mode, you don't need to set any kernel configuration options -or anything. Simply install all the files included in this document, and -laptop mode will automatically be started when you're on battery. For -your convenience, a tarball containing an installer can be downloaded at: - - http://www.samwel.tk/laptop_mode/laptop_mode/ - -To configure laptop mode, you need to edit the configuration file, which is -located in /etc/default/laptop-mode on Debian-based systems, or in -/etc/sysconfig/laptop-mode on other systems. - -Unfortunately, automatic enabling of laptop mode does not work for -laptops that don't have ACPI. On those laptops, you need to start laptop -mode manually. To start laptop mode, run "laptop_mode start", and to -stop it, run "laptop_mode stop". (Note: The laptop mode tools package now -has experimental support for APM, you might want to try that first.) - - -Caveats -------- - -* The downside of laptop mode is that you have a chance of losing up to 10 - minutes of work. If you cannot afford this, don't use it! The supplied ACPI - scripts automatically turn off laptop mode when the battery almost runs out, - so that you won't lose any data at the end of your battery life. - -* Most desktop hard drives have a very limited lifetime measured in spindown - cycles, typically about 50.000 times (it's usually listed on the spec sheet). - Check your drive's rating, and don't wear down your drive's lifetime if you - don't need to. - -* If you mount some of your ext3 filesystems with the -n option, then - the control script will not be able to remount them correctly. You must set - DO_REMOUNTS=0 in the control script, otherwise it will remount them with the - wrong options -- or it will fail because it cannot write to /etc/mtab. - -* If you have your filesystems listed as type "auto" in fstab, like I did, then - the control script will not recognize them as filesystems that need remounting. - You must list the filesystems with their true type instead. - -* It has been reported that some versions of the mutt mail client use file access - times to determine whether a folder contains new mail. If you use mutt and - experience this, you must disable the noatime remounting by setting the option - DO_REMOUNT_NOATIME to 0 in the configuration file. - - -The Details ------------ - -Laptop mode is controlled by the knob /proc/sys/vm/laptop_mode. This knob is -present for all kernels that have the laptop mode patch, regardless of any -configuration options. When the knob is set, any physical disk I/O (that might -have caused the hard disk to spin up) causes Linux to flush all dirty blocks. The -result of this is that after a disk has spun down, it will not be spun up -anymore to write dirty blocks, because those blocks had already been written -immediately after the most recent read operation. The value of the laptop_mode -knob determines the time between the occurrence of disk I/O and when the flush -is triggered. A sensible value for the knob is 5 seconds. Setting the knob to -0 disables laptop mode. - -To increase the effectiveness of the laptop_mode strategy, the laptop_mode -control script increases dirty_expire_centisecs and dirty_writeback_centisecs in -/proc/sys/vm to about 10 minutes (by default), which means that pages that are -dirtied are not forced to be written to disk as often. The control script also -changes the dirty background ratio, so that background writeback of dirty pages -is not done anymore. Combined with a higher commit value (also 10 minutes) for -ext3 filesystem (also done automatically by the control script), -this results in concentration of disk activity in a small time interval which -occurs only once every 10 minutes, or whenever the disk is forced to spin up by -a cache miss. The disk can then be spun down in the periods of inactivity. - - -Configuration -------------- - -The laptop mode configuration file is located in /etc/default/laptop-mode on -Debian-based systems, or in /etc/sysconfig/laptop-mode on other systems. It -contains the following options: - -MAX_AGE: - -Maximum time, in seconds, of hard drive spindown time that you are -comfortable with. Worst case, it's possible that you could lose this -amount of work if your battery fails while you're in laptop mode. - -MINIMUM_BATTERY_MINUTES: - -Automatically disable laptop mode if the remaining number of minutes of -battery power is less than this value. Default is 10 minutes. - -AC_HD/BATT_HD: - -The idle timeout that should be set on your hard drive when laptop mode -is active (BATT_HD) and when it is not active (AC_HD). The defaults are -20 seconds (value 4) for BATT_HD and 2 hours (value 244) for AC_HD. The -possible values are those listed in the manual page for "hdparm" for the -"-S" option. - -HD: - -The devices for which the spindown timeout should be adjusted by laptop mode. -Default is /dev/hda. If you specify multiple devices, separate them by a space. - -READAHEAD: - -Disk readahead, in 512-byte sectors, while laptop mode is active. A large -readahead can prevent disk accesses for things like executable pages (which are -loaded on demand while the application executes) and sequentially accessed data -(MP3s). - -DO_REMOUNTS: - -The control script automatically remounts any mounted journaled filesystems -with appropriate commit interval options. When this option is set to 0, this -feature is disabled. - -DO_REMOUNT_NOATIME: - -When remounting, should the filesystems be remounted with the noatime option? -Normally, this is set to "1" (enabled), but there may be programs that require -access time recording. - -DIRTY_RATIO: - -The percentage of memory that is allowed to contain "dirty" or unsaved data -before a writeback is forced, while laptop mode is active. Corresponds to -the /proc/sys/vm/dirty_ratio sysctl. - -DIRTY_BACKGROUND_RATIO: - -The percentage of memory that is allowed to contain "dirty" or unsaved data -after a forced writeback is done due to an exceeding of DIRTY_RATIO. Set -this nice and low. This corresponds to the /proc/sys/vm/dirty_background_ratio -sysctl. - -Note that the behaviour of dirty_background_ratio is quite different -when laptop mode is active and when it isn't. When laptop mode is inactive, -dirty_background_ratio is the threshold percentage at which background writeouts -start taking place. When laptop mode is active, however, background writeouts -are disabled, and the dirty_background_ratio only determines how much writeback -is done when dirty_ratio is reached. - -DO_CPU: - -Enable CPU frequency scaling when in laptop mode. (Requires CPUFreq to be setup. -See Documentation/admin-guide/pm/cpufreq.rst for more info. Disabled by default.) - -CPU_MAXFREQ: - -When on battery, what is the maximum CPU speed that the system should use? Legal -values are "slowest" for the slowest speed that your CPU is able to operate at, -or a value listed in /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies. - - -Tips & Tricks -------------- - -* Bartek Kania reports getting up to 50 minutes of extra battery life (on top - of his regular 3 to 3.5 hours) using a spindown time of 5 seconds (BATT_HD=1). - -* You can spin down the disk while playing MP3, by setting disk readahead - to 8MB (READAHEAD=16384). Effectively, the disk will read a complete MP3 at - once, and will then spin down while the MP3 is playing. (Thanks to Bartek - Kania.) - -* Drew Scott Daniels observed: "I don't know why, but when I decrease the number - of colours that my display uses it consumes less battery power. I've seen - this on powerbooks too. I hope that this is a piece of information that - might be useful to the Laptop Mode patch or its users." - -* In syslog.conf, you can prefix entries with a dash `-` to omit syncing the - file after every logging. When you're using laptop-mode and your disk doesn't - spin down, this is a likely culprit. - -* Richard Atterer observed that laptop mode does not work well with noflushd - (http://noflushd.sourceforge.net/), it seems that noflushd prevents laptop-mode - from doing its thing. - -* If you're worried about your data, you might want to consider using a USB - memory stick or something like that as a "working area". (Be aware though - that flash memory can only handle a limited number of writes, and overuse - may wear out your memory stick pretty quickly. Do _not_ use journalling - filesystems on flash memory sticks.) - - -Configuration file for control and ACPI battery scripts -------------------------------------------------------- - -This allows the tunables to be changed for the scripts via an external -configuration file - -It should be installed as /etc/default/laptop-mode on Debian, and as -/etc/sysconfig/laptop-mode on Red Hat, SUSE, Mandrake, and other work-alikes. - -Config file:: - - # Maximum time, in seconds, of hard drive spindown time that you are - # comfortable with. Worst case, it's possible that you could lose this - # amount of work if your battery fails you while in laptop mode. - #MAX_AGE=600 - - # Automatically disable laptop mode when the number of minutes of battery - # that you have left goes below this threshold. - MINIMUM_BATTERY_MINUTES=10 - - # Read-ahead, in 512-byte sectors. You can spin down the disk while playing MP3/OGG - # by setting the disk readahead to 8MB (READAHEAD=16384). Effectively, the disk - # will read a complete MP3 at once, and will then spin down while the MP3/OGG is - # playing. - #READAHEAD=4096 - - # Shall we remount journaled fs. with appropriate commit interval? (1=yes) - #DO_REMOUNTS=1 - - # And shall we add the "noatime" option to that as well? (1=yes) - #DO_REMOUNT_NOATIME=1 - - # Dirty synchronous ratio. At this percentage of dirty pages the process - # which - # calls write() does its own writeback - #DIRTY_RATIO=40 - - # - # Allowed dirty background ratio, in percent. Once DIRTY_RATIO has been - # exceeded, the kernel will wake flusher threads which will then reduce the - # amount of dirty memory to dirty_background_ratio. Set this nice and low, - # so once some writeout has commenced, we do a lot of it. - # - #DIRTY_BACKGROUND_RATIO=5 - - # kernel default dirty buffer age - #DEF_AGE=30 - #DEF_UPDATE=5 - #DEF_DIRTY_BACKGROUND_RATIO=10 - #DEF_DIRTY_RATIO=40 - #DEF_XFS_AGE_BUFFER=15 - #DEF_XFS_SYNC_INTERVAL=30 - #DEF_XFS_BUFD_INTERVAL=1 - - # This must be adjusted manually to the value of HZ in the running kernel - # on 2.4, until the XFS people change their 2.4 external interfaces to work in - # centisecs. This can be automated, but it's a work in progress that still - # needs# some fixes. On 2.6 kernels, XFS uses USER_HZ instead of HZ for - # external interfaces, and that is currently always set to 100. So you don't - # need to change this on 2.6. - #XFS_HZ=100 - - # Should the maximum CPU frequency be adjusted down while on battery? - # Requires CPUFreq to be setup. - # See Documentation/admin-guide/pm/cpufreq.rst for more info - #DO_CPU=0 - - # When on battery what is the maximum CPU speed that the system should - # use? Legal values are "slowest" for the slowest speed that your - # CPU is able to operate at, or a value listed in: - # /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies - # Only applicable if DO_CPU=1. - #CPU_MAXFREQ=slowest - - # Idle timeout for your hard drive (man hdparm for valid values, -S option) - # Default is 2 hours on AC (AC_HD=244) and 20 seconds for battery (BATT_HD=4). - #AC_HD=244 - #BATT_HD=4 - - # The drives for which to adjust the idle timeout. Separate them by a space, - # e.g. HD="/dev/hda /dev/hdb". - #HD="/dev/hda" - - # Set the spindown timeout on a hard drive? - #DO_HD=1 - - -Control script --------------- - -Please note that this control script works for the Linux 2.4 and 2.6 series (thanks -to Kiko Piris). - -Control script:: - - #!/bin/bash - - # start or stop laptop_mode, best run by a power management daemon when - # ac gets connected/disconnected from a laptop - # - # install as /sbin/laptop_mode - # - # Contributors to this script: Kiko Piris - # Bart Samwel - # Micha Feigin - # Andrew Morton - # Herve Eychenne - # Dax Kelson - # - # Original Linux 2.4 version by: Jens Axboe - - ############################################################################# - - # Source config - if [ -f /etc/default/laptop-mode ] ; then - # Debian - . /etc/default/laptop-mode - elif [ -f /etc/sysconfig/laptop-mode ] ; then - # Others - . /etc/sysconfig/laptop-mode - fi - - # Don't raise an error if the config file is incomplete - # set defaults instead: - - # Maximum time, in seconds, of hard drive spindown time that you are - # comfortable with. Worst case, it's possible that you could lose this - # amount of work if your battery fails you while in laptop mode. - MAX_AGE=${MAX_AGE:-'600'} - - # Read-ahead, in kilobytes - READAHEAD=${READAHEAD:-'4096'} - - # Shall we remount journaled fs. with appropriate commit interval? (1=yes) - DO_REMOUNTS=${DO_REMOUNTS:-'1'} - - # And shall we add the "noatime" option to that as well? (1=yes) - DO_REMOUNT_NOATIME=${DO_REMOUNT_NOATIME:-'1'} - - # Shall we adjust the idle timeout on a hard drive? - DO_HD=${DO_HD:-'1'} - - # Adjust idle timeout on which hard drive? - HD="${HD:-'/dev/hda'}" - - # spindown time for HD (hdparm -S values) - AC_HD=${AC_HD:-'244'} - BATT_HD=${BATT_HD:-'4'} - - # Dirty synchronous ratio. At this percentage of dirty pages the process which - # calls write() does its own writeback - DIRTY_RATIO=${DIRTY_RATIO:-'40'} - - # cpu frequency scaling - # See Documentation/admin-guide/pm/cpufreq.rst for more info - DO_CPU=${CPU_MANAGE:-'0'} - CPU_MAXFREQ=${CPU_MAXFREQ:-'slowest'} - - # - # Allowed dirty background ratio, in percent. Once DIRTY_RATIO has been - # exceeded, the kernel will wake flusher threads which will then reduce the - # amount of dirty memory to dirty_background_ratio. Set this nice and low, - # so once some writeout has commenced, we do a lot of it. - # - DIRTY_BACKGROUND_RATIO=${DIRTY_BACKGROUND_RATIO:-'5'} - - # kernel default dirty buffer age - DEF_AGE=${DEF_AGE:-'30'} - DEF_UPDATE=${DEF_UPDATE:-'5'} - DEF_DIRTY_BACKGROUND_RATIO=${DEF_DIRTY_BACKGROUND_RATIO:-'10'} - DEF_DIRTY_RATIO=${DEF_DIRTY_RATIO:-'40'} - DEF_XFS_AGE_BUFFER=${DEF_XFS_AGE_BUFFER:-'15'} - DEF_XFS_SYNC_INTERVAL=${DEF_XFS_SYNC_INTERVAL:-'30'} - DEF_XFS_BUFD_INTERVAL=${DEF_XFS_BUFD_INTERVAL:-'1'} - - # This must be adjusted manually to the value of HZ in the running kernel - # on 2.4, until the XFS people change their 2.4 external interfaces to work in - # centisecs. This can be automated, but it's a work in progress that still needs - # some fixes. On 2.6 kernels, XFS uses USER_HZ instead of HZ for external - # interfaces, and that is currently always set to 100. So you don't need to - # change this on 2.6. - XFS_HZ=${XFS_HZ:-'100'} - - ############################################################################# - - KLEVEL="$(uname -r | - { - IFS='.' read a b c - echo $a.$b - } - )" - case "$KLEVEL" in - "2.4"|"2.6") - ;; - *) - echo "Unhandled kernel version: $KLEVEL ('uname -r' = '$(uname -r)')" >&2 - exit 1 - ;; - esac - - if [ ! -e /proc/sys/vm/laptop_mode ] ; then - echo "Kernel is not patched with laptop_mode patch." >&2 - exit 1 - fi - - if [ ! -w /proc/sys/vm/laptop_mode ] ; then - echo "You do not have enough privileges to enable laptop_mode." >&2 - exit 1 - fi - - # Remove an option (the first parameter) of the form option= from - # a mount options string (the rest of the parameters). - parse_mount_opts () { - OPT="$1" - shift - echo ",$*," | sed \ - -e 's/,'"$OPT"'=[0-9]*,/,/g' \ - -e 's/,,*/,/g' \ - -e 's/^,//' \ - -e 's/,$//' - } - - # Remove an option (the first parameter) without any arguments from - # a mount option string (the rest of the parameters). - parse_nonumber_mount_opts () { - OPT="$1" - shift - echo ",$*," | sed \ - -e 's/,'"$OPT"',/,/g' \ - -e 's/,,*/,/g' \ - -e 's/^,//' \ - -e 's/,$//' - } - - # Find out the state of a yes/no option (e.g. "atime"/"noatime") in - # fstab for a given filesystem, and use this state to replace the - # value of the option in another mount options string. The device - # is the first argument, the option name the second, and the default - # value the third. The remainder is the mount options string. - # - # Example: - # parse_yesno_opts_wfstab /dev/hda1 atime atime defaults,noatime - # - # If fstab contains, say, "rw" for this filesystem, then the result - # will be "defaults,atime". - parse_yesno_opts_wfstab () { - L_DEV="$1" - OPT="$2" - DEF_OPT="$3" - shift 3 - L_OPTS="$*" - PARSEDOPTS1="$(parse_nonumber_mount_opts $OPT $L_OPTS)" - PARSEDOPTS1="$(parse_nonumber_mount_opts no$OPT $PARSEDOPTS1)" - # Watch for a default atime in fstab - FSTAB_OPTS="$(awk '$1 == "'$L_DEV'" { print $4 }' /etc/fstab)" - if echo "$FSTAB_OPTS" | grep "$OPT" > /dev/null ; then - # option specified in fstab: extract the value and use it - if echo "$FSTAB_OPTS" | grep "no$OPT" > /dev/null ; then - echo "$PARSEDOPTS1,no$OPT" - else - # no$OPT not found -- so we must have $OPT. - echo "$PARSEDOPTS1,$OPT" - fi - else - # option not specified in fstab -- choose the default. - echo "$PARSEDOPTS1,$DEF_OPT" - fi - } - - # Find out the state of a numbered option (e.g. "commit=NNN") in - # fstab for a given filesystem, and use this state to replace the - # value of the option in another mount options string. The device - # is the first argument, and the option name the second. The - # remainder is the mount options string in which the replacement - # must be done. - # - # Example: - # parse_mount_opts_wfstab /dev/hda1 commit defaults,commit=7 - # - # If fstab contains, say, "commit=3,rw" for this filesystem, then the - # result will be "rw,commit=3". - parse_mount_opts_wfstab () { - L_DEV="$1" - OPT="$2" - shift 2 - L_OPTS="$*" - PARSEDOPTS1="$(parse_mount_opts $OPT $L_OPTS)" - # Watch for a default commit in fstab - FSTAB_OPTS="$(awk '$1 == "'$L_DEV'" { print $4 }' /etc/fstab)" - if echo "$FSTAB_OPTS" | grep "$OPT=" > /dev/null ; then - # option specified in fstab: extract the value, and use it - echo -n "$PARSEDOPTS1,$OPT=" - echo ",$FSTAB_OPTS," | sed \ - -e 's/.*,'"$OPT"'=//' \ - -e 's/,.*//' - else - # option not specified in fstab: set it to 0 - echo "$PARSEDOPTS1,$OPT=0" - fi - } - - deduce_fstype () { - MP="$1" - # My root filesystem unfortunately has - # type "unknown" in /etc/mtab. If we encounter - # "unknown", we try to get the type from fstab. - cat /etc/fstab | - grep -v '^#' | - while read FSTAB_DEV FSTAB_MP FSTAB_FST FSTAB_OPTS FSTAB_DUMP FSTAB_DUMP ; do - if [ "$FSTAB_MP" = "$MP" ]; then - echo $FSTAB_FST - exit 0 - fi - done - } - - if [ $DO_REMOUNT_NOATIME -eq 1 ] ; then - NOATIME_OPT=",noatime" - fi - - case "$1" in - start) - AGE=$((100*$MAX_AGE)) - XFS_AGE=$(($XFS_HZ*$MAX_AGE)) - echo -n "Starting laptop_mode" - - if [ -d /proc/sys/vm/pagebuf ] ; then - # (For 2.4 and early 2.6.) - # This only needs to be set, not reset -- it is only used when - # laptop mode is enabled. - echo $XFS_AGE > /proc/sys/vm/pagebuf/lm_flush_age - echo $XFS_AGE > /proc/sys/fs/xfs/lm_sync_interval - elif [ -f /proc/sys/fs/xfs/lm_age_buffer ] ; then - # (A couple of early 2.6 laptop mode patches had these.) - # The same goes for these. - echo $XFS_AGE > /proc/sys/fs/xfs/lm_age_buffer - echo $XFS_AGE > /proc/sys/fs/xfs/lm_sync_interval - elif [ -f /proc/sys/fs/xfs/age_buffer ] ; then - # (2.6.6) - # But not for these -- they are also used in normal - # operation. - echo $XFS_AGE > /proc/sys/fs/xfs/age_buffer - echo $XFS_AGE > /proc/sys/fs/xfs/sync_interval - elif [ -f /proc/sys/fs/xfs/age_buffer_centisecs ] ; then - # (2.6.7 upwards) - # And not for these either. These are in centisecs, - # not USER_HZ, so we have to use $AGE, not $XFS_AGE. - echo $AGE > /proc/sys/fs/xfs/age_buffer_centisecs - echo $AGE > /proc/sys/fs/xfs/xfssyncd_centisecs - echo 3000 > /proc/sys/fs/xfs/xfsbufd_centisecs - fi - - case "$KLEVEL" in - "2.4") - echo 1 > /proc/sys/vm/laptop_mode - echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush - ;; - "2.6") - echo 5 > /proc/sys/vm/laptop_mode - echo "$AGE" > /proc/sys/vm/dirty_writeback_centisecs - echo "$AGE" > /proc/sys/vm/dirty_expire_centisecs - echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_ratio - echo "$DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio - ;; - esac - if [ $DO_REMOUNTS -eq 1 ]; then - cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do - PARSEDOPTS="$(parse_mount_opts "$OPTS")" - if [ "$FST" = 'unknown' ]; then - FST=$(deduce_fstype $MP) - fi - case "$FST" in - "ext3") - PARSEDOPTS="$(parse_mount_opts commit "$OPTS")" - mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE$NOATIME_OPT - ;; - "xfs") - mount $DEV -t $FST $MP -o remount,$OPTS$NOATIME_OPT - ;; - esac - if [ -b $DEV ] ; then - blockdev --setra $(($READAHEAD * 2)) $DEV - fi - done - fi - if [ $DO_HD -eq 1 ] ; then - for THISHD in $HD ; do - /sbin/hdparm -S $BATT_HD $THISHD > /dev/null 2>&1 - /sbin/hdparm -B 1 $THISHD > /dev/null 2>&1 - done - fi - if [ $DO_CPU -eq 1 -a -e /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq ]; then - if [ $CPU_MAXFREQ = 'slowest' ]; then - CPU_MAXFREQ=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq` - fi - echo $CPU_MAXFREQ > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq - fi - echo "." - ;; - stop) - U_AGE=$((100*$DEF_UPDATE)) - B_AGE=$((100*$DEF_AGE)) - echo -n "Stopping laptop_mode" - echo 0 > /proc/sys/vm/laptop_mode - if [ -f /proc/sys/fs/xfs/age_buffer -a ! -f /proc/sys/fs/xfs/lm_age_buffer ] ; then - # These need to be restored, if there are no lm_*. - echo $(($XFS_HZ*$DEF_XFS_AGE_BUFFER)) > /proc/sys/fs/xfs/age_buffer - echo $(($XFS_HZ*$DEF_XFS_SYNC_INTERVAL)) > /proc/sys/fs/xfs/sync_interval - elif [ -f /proc/sys/fs/xfs/age_buffer_centisecs ] ; then - # These need to be restored as well. - echo $((100*$DEF_XFS_AGE_BUFFER)) > /proc/sys/fs/xfs/age_buffer_centisecs - echo $((100*$DEF_XFS_SYNC_INTERVAL)) > /proc/sys/fs/xfs/xfssyncd_centisecs - echo $((100*$DEF_XFS_BUFD_INTERVAL)) > /proc/sys/fs/xfs/xfsbufd_centisecs - fi - case "$KLEVEL" in - "2.4") - echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush - ;; - "2.6") - echo "$U_AGE" > /proc/sys/vm/dirty_writeback_centisecs - echo "$B_AGE" > /proc/sys/vm/dirty_expire_centisecs - echo "$DEF_DIRTY_RATIO" > /proc/sys/vm/dirty_ratio - echo "$DEF_DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio - ;; - esac - if [ $DO_REMOUNTS -eq 1 ] ; then - cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do - # Reset commit and atime options to defaults. - if [ "$FST" = 'unknown' ]; then - FST=$(deduce_fstype $MP) - fi - case "$FST" in - "ext3") - PARSEDOPTS="$(parse_mount_opts_wfstab $DEV commit $OPTS)" - PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $PARSEDOPTS)" - mount $DEV -t $FST $MP -o remount,$PARSEDOPTS - ;; - "xfs") - PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $OPTS)" - mount $DEV -t $FST $MP -o remount,$PARSEDOPTS - ;; - esac - if [ -b $DEV ] ; then - blockdev --setra 256 $DEV - fi - done - fi - if [ $DO_HD -eq 1 ] ; then - for THISHD in $HD ; do - /sbin/hdparm -S $AC_HD $THISHD > /dev/null 2>&1 - /sbin/hdparm -B 255 $THISHD > /dev/null 2>&1 - done - fi - if [ $DO_CPU -eq 1 -a -e /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq ]; then - echo `cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq` > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq - fi - echo "." - ;; - *) - echo "Usage: $0 {start|stop}" 2>&1 - exit 1 - ;; - - esac - - exit 0 - - -ACPI integration ----------------- - -Dax Kelson submitted this so that the ACPI acpid daemon will -kick off the laptop_mode script and run hdparm. The part that -automatically disables laptop mode when the battery is low was -written by Jan Topinski. - -/etc/acpi/events/ac_adapter:: - - event=ac_adapter - action=/etc/acpi/actions/ac.sh %e - -/etc/acpi/events/battery:: - - event=battery.* - action=/etc/acpi/actions/battery.sh %e - -/etc/acpi/actions/ac.sh:: - - #!/bin/bash - - # ac on/offline event handler - - status=`awk '/^state: / { print $2 }' /proc/acpi/ac_adapter/$2/state` - - case $status in - "on-line") - /sbin/laptop_mode stop - exit 0 - ;; - "off-line") - /sbin/laptop_mode start - exit 0 - ;; - esac - - -/etc/acpi/actions/battery.sh:: - - #! /bin/bash - - # Automatically disable laptop mode when the battery almost runs out. - - BATT_INFO=/proc/acpi/battery/$2/state - - if [[ -f /proc/sys/vm/laptop_mode ]] - then - LM=`cat /proc/sys/vm/laptop_mode` - if [[ $LM -gt 0 ]] - then - if [[ -f $BATT_INFO ]] - then - # Source the config file only now that we know we need - if [ -f /etc/default/laptop-mode ] ; then - # Debian - . /etc/default/laptop-mode - elif [ -f /etc/sysconfig/laptop-mode ] ; then - # Others - . /etc/sysconfig/laptop-mode - fi - MINIMUM_BATTERY_MINUTES=${MINIMUM_BATTERY_MINUTES:-'10'} - - ACTION="`cat $BATT_INFO | grep charging | cut -c 26-`" - if [[ ACTION -eq "discharging" ]] - then - PRESENT_RATE=`cat $BATT_INFO | grep "present rate:" | sed "s/.* \([0-9][0-9]* \).*/\1/" ` - REMAINING=`cat $BATT_INFO | grep "remaining capacity:" | sed "s/.* \([0-9][0-9]* \).*/\1/" ` - fi - if (($REMAINING * 60 / $PRESENT_RATE < $MINIMUM_BATTERY_MINUTES)) - then - /sbin/laptop_mode stop - fi - else - logger -p daemon.warning "You are using laptop mode and your battery interface $BATT_INFO is missing. This may lead to loss of data when the battery runs out. Check kernel ACPI support and /proc/acpi/battery folder, and edit /etc/acpi/battery.sh to set BATT_INFO to the correct path." - fi - fi - fi - - -Monitoring tool ---------------- - -Bartek Kania submitted this, it can be used to measure how much time your disk -spends spun up/down. See tools/laptop/dslm/dslm.c diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst index 245bf6394935..ca6ebeb5171c 100644 --- a/Documentation/admin-guide/sysctl/vm.rst +++ b/Documentation/admin-guide/sysctl/vm.rst @@ -41,7 +41,6 @@ Currently, these files are in /proc/sys/vm: - extfrag_threshold - highmem_is_dirtyable - hugetlb_shm_group -- laptop_mode - legacy_va_layout - lowmem_reserve_ratio - max_map_count @@ -363,13 +362,6 @@ hugetlb_shm_group contains group id that is allowed to create SysV shared memory segment using hugetlb page. -laptop_mode -=========== - -laptop_mode is a knob that controls "laptop mode". All the things that are -controlled by this knob are discussed in Documentation/admin-guide/laptops/laptop-mode.rst. - - legacy_va_layout ================ diff --git a/block/blk-mq.c b/block/blk-mq.c index a29d8ac9d3e3..4bae7c4c664e 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -811,9 +811,6 @@ void blk_mq_free_request(struct request *rq) blk_mq_finish_request(rq); - if (unlikely(laptop_mode && !blk_rq_is_passthrough(rq))) - laptop_io_completion(q->disk->bdi); - rq_qos_done(q, rq); WRITE_ONCE(rq->state, MQ_RQ_IDLE); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0c466ccbed69..15eb463d5a9b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3305,8 +3305,7 @@ int ext4_alloc_da_blocks(struct inode *inode) /* * We do something simple for now. The filemap_flush() will * also start triggering a write of the data blocks, which is - * not strictly speaking necessary (and for users of - * laptop_mode, not even desirable). However, to do otherwise + * not strictly speaking necessary. However, to do otherwise * would require replicating code paths in: * * ext4_writepages() -> diff --git a/fs/sync.c b/fs/sync.c index 431fc5f5be06..6330150792f6 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -104,8 +104,6 @@ void ksys_sync(void) iterate_supers(sync_fs_one_sb, &wait); sync_bdevs(false); sync_bdevs(true); - if (unlikely(laptop_mode)) - laptop_sync_completion(); } SYSCALL_DEFINE0(sync) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index bc71aa9dcee8..a2014fb1bc66 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -845,15 +845,6 @@ xfs_fs_sync_fs( if (error) return error; - if (laptop_mode) { - /* - * The disk must be active because we're syncing. - * We schedule log work now (now that the disk is - * active) instead of later (when it might not be). - */ - flush_delayed_work(&mp->m_log->l_work); - } - /* * If we are called with page faults frozen out, it means we are about * to freeze the transaction subsystem. Take the opportunity to shut diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h index 0217c1073735..c88fd4d37d1f 100644 --- a/include/linux/backing-dev-defs.h +++ b/include/linux/backing-dev-defs.h @@ -46,7 +46,6 @@ enum wb_reason { WB_REASON_VMSCAN, WB_REASON_SYNC, WB_REASON_PERIODIC, - WB_REASON_LAPTOP_TIMER, WB_REASON_FS_FREE_SPACE, /* * There is no bdi forker thread any more and works are done @@ -204,8 +203,6 @@ struct backing_dev_info { char dev_name[64]; struct device *owner; - struct timer_list laptop_mode_wb_timer; - #ifdef CONFIG_DEBUG_FS struct dentry *debug_dir; #endif diff --git a/include/linux/writeback.h b/include/linux/writeback.h index f48e8ccffe81..e530112c4b3a 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -328,9 +328,6 @@ struct dirty_throttle_control { bool dirty_exceeded; }; -void laptop_io_completion(struct backing_dev_info *info); -void laptop_sync_completion(void); -void laptop_mode_timer_fn(struct timer_list *t); bool node_dirty_ok(struct pglist_data *pgdat); int wb_domain_init(struct wb_domain *dom, gfp_t gfp); #ifdef CONFIG_CGROUP_WRITEBACK @@ -342,7 +339,6 @@ extern struct wb_domain global_wb_domain; /* These are exported to sysctl. */ extern unsigned int dirty_writeback_interval; extern unsigned int dirty_expire_interval; -extern int laptop_mode; void global_dirty_limits(unsigned long *pbackground, unsigned long *pdirty); unsigned long wb_calc_thresh(struct bdi_writeback *wb, unsigned long thresh); diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 311a341e6fe4..b6f94e97788a 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -42,7 +42,6 @@ EM( WB_REASON_VMSCAN, "vmscan") \ EM( WB_REASON_SYNC, "sync") \ EM( WB_REASON_PERIODIC, "periodic") \ - EM( WB_REASON_LAPTOP_TIMER, "laptop_timer") \ EM( WB_REASON_FS_FREE_SPACE, "fs_free_space") \ EM( WB_REASON_FORKER_THREAD, "forker_thread") \ EMe(WB_REASON_FOREIGN_FLUSH, "foreign_flush") diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h index 63d1464cb71c..6ea9ea8413fa 100644 --- a/include/uapi/linux/sysctl.h +++ b/include/uapi/linux/sysctl.h @@ -183,7 +183,7 @@ enum VM_LOWMEM_RESERVE_RATIO=20,/* reservation ratio for lower memory zones */ VM_MIN_FREE_KBYTES=21, /* Minimum free kilobytes to maintain */ VM_MAX_MAP_COUNT=22, /* int: Maximum number of mmaps/address-space */ - VM_LAPTOP_MODE=23, /* vm laptop mode */ + VM_BLOCK_DUMP=24, /* block dump mode */ VM_HUGETLB_GROUP=25, /* permitted hugetlb group */ VM_VFS_CACHE_PRESSURE=26, /* dcache/icache reclaim pressure */ diff --git a/mm/backing-dev.c b/mm/backing-dev.c index c5740c6d37a2..a0e26d1b717f 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -1034,7 +1034,6 @@ struct backing_dev_info *bdi_alloc(int node_id) bdi->capabilities = BDI_CAP_WRITEBACK; bdi->ra_pages = VM_READAHEAD_PAGES; bdi->io_pages = VM_READAHEAD_PAGES; - timer_setup(&bdi->laptop_mode_wb_timer, laptop_mode_timer_fn, 0); return bdi; } EXPORT_SYMBOL(bdi_alloc); @@ -1156,8 +1155,6 @@ static void bdi_remove_from_list(struct backing_dev_info *bdi) void bdi_unregister(struct backing_dev_info *bdi) { - timer_delete_sync(&bdi->laptop_mode_wb_timer); - /* make sure nobody finds us on the bdi_list anymore */ bdi_remove_from_list(bdi); wb_shutdown(&bdi->wb); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index ccdeb0e84d39..601a5e048d12 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -109,14 +109,6 @@ EXPORT_SYMBOL_GPL(dirty_writeback_interval); */ unsigned int dirty_expire_interval = 30 * 100; /* centiseconds */ -/* - * Flag that puts the machine in "laptop mode". Doubles as a timeout in jiffies: - * a full sync is triggered after this time elapses without any disk activity. - */ -int laptop_mode; - -EXPORT_SYMBOL(laptop_mode); - /* End of sysctl-exported parameters */ struct wb_domain global_wb_domain; @@ -1843,17 +1835,7 @@ static int balance_dirty_pages(struct bdi_writeback *wb, balance_domain_limits(mdtc, strictlimit); } - /* - * In laptop mode, we wait until hitting the higher threshold - * before starting background writeout, and then write out all - * the way down to the lower threshold. So slow writers cause - * minimal disk activity. - * - * In normal mode, we start background writeout at the lower - * background_thresh, to keep the amount of dirty memory low. - */ - if (!laptop_mode && nr_dirty > gdtc->bg_thresh && - !writeback_in_progress(wb)) + if (nr_dirty > gdtc->bg_thresh && !writeback_in_progress(wb)) wb_start_background_writeback(wb); /* @@ -1876,10 +1858,6 @@ free_running: break; } - /* Start writeback even when in laptop mode */ - if (unlikely(!writeback_in_progress(wb))) - wb_start_background_writeback(wb); - mem_cgroup_flush_foreign(wb); /* @@ -2198,41 +2176,6 @@ static int dirty_writeback_centisecs_handler(const struct ctl_table *table, int } #endif -void laptop_mode_timer_fn(struct timer_list *t) -{ - struct backing_dev_info *backing_dev_info = - timer_container_of(backing_dev_info, t, laptop_mode_wb_timer); - - wakeup_flusher_threads_bdi(backing_dev_info, WB_REASON_LAPTOP_TIMER); -} - -/* - * We've spun up the disk and we're in laptop mode: schedule writeback - * of all dirty data a few seconds from now. If the flush is already scheduled - * then push it back - the user is still using the disk. - */ -void laptop_io_completion(struct backing_dev_info *info) -{ - mod_timer(&info->laptop_mode_wb_timer, jiffies + laptop_mode); -} - -/* - * We're in laptop mode and we've just synced. The sync's writes will have - * caused another writeback to be scheduled by laptop_io_completion. - * Nothing needs to be written back anymore, so we unschedule the writeback. - */ -void laptop_sync_completion(void) -{ - struct backing_dev_info *bdi; - - rcu_read_lock(); - - list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) - timer_delete(&bdi->laptop_mode_wb_timer); - - rcu_read_unlock(); -} - /* * If ratelimit_pages is too high then we can get into dirty-data overload * if a large number of processes all perform writes at the same time. @@ -2263,6 +2206,19 @@ static int page_writeback_cpu_online(unsigned int cpu) #ifdef CONFIG_SYSCTL +static int laptop_mode; +static int laptop_mode_handler(const struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + int ret = proc_dointvec_jiffies(table, write, buffer, lenp, ppos); + + if (!ret && write) + pr_warn("%s: vm.laptop_mode is deprecated. Ignoring setting.\n", + current->comm); + + return ret; +} + /* this is needed for the proc_doulongvec_minmax of vm_dirty_bytes */ static const unsigned long dirty_bytes_min = 2 * PAGE_SIZE; @@ -2332,7 +2288,7 @@ static const struct ctl_table vm_page_writeback_sysctls[] = { .data = &laptop_mode, .maxlen = sizeof(laptop_mode), .mode = 0644, - .proc_handler = proc_dointvec_jiffies, + .proc_handler = laptop_mode_handler, }, }; #endif diff --git a/mm/vmscan.c b/mm/vmscan.c index 1c87945fa761..fc5691afb998 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -104,13 +104,13 @@ struct scan_control { unsigned int force_deactivate:1; unsigned int skipped_deactivate:1; - /* Writepage batching in laptop mode; RECLAIM_WRITE */ + /* zone_reclaim_mode, boost reclaim */ unsigned int may_writepage:1; - /* Can mapped folios be reclaimed? */ + /* zone_reclaim_mode */ unsigned int may_unmap:1; - /* Can folios be swapped as part of reclaim? */ + /* zome_reclaim_mode, boost reclaim, cgroup restrictions */ unsigned int may_swap:1; /* Not allow cache_trim_mode to be turned on as part of reclaim? */ @@ -6365,13 +6365,6 @@ retry: if (sc->compaction_ready) break; - - /* - * If we're getting trouble reclaiming, start doing - * writepage even in laptop mode. - */ - if (sc->priority < DEF_PRIORITY - 2) - sc->may_writepage = 1; } while (--sc->priority >= 0); last_pgdat = NULL; @@ -6580,7 +6573,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, .order = order, .nodemask = nodemask, .priority = DEF_PRIORITY, - .may_writepage = !laptop_mode, + .may_writepage = 1, .may_unmap = 1, .may_swap = 1, }; @@ -6624,7 +6617,7 @@ unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg, struct scan_control sc = { .nr_to_reclaim = SWAP_CLUSTER_MAX, .target_mem_cgroup = memcg, - .may_writepage = !laptop_mode, + .may_writepage = 1, .may_unmap = 1, .reclaim_idx = MAX_NR_ZONES - 1, .may_swap = !noswap, @@ -6670,7 +6663,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, .reclaim_idx = MAX_NR_ZONES - 1, .target_mem_cgroup = memcg, .priority = DEF_PRIORITY, - .may_writepage = !laptop_mode, + .may_writepage = 1, .may_unmap = 1, .may_swap = !!(reclaim_options & MEMCG_RECLAIM_MAY_SWAP), .proactive = !!(reclaim_options & MEMCG_RECLAIM_PROACTIVE), @@ -7051,7 +7044,7 @@ restart: * from reclaim context. If no pages are reclaimed, the * reclaim will be aborted. */ - sc.may_writepage = !laptop_mode && !nr_boost_reclaim; + sc.may_writepage = !nr_boost_reclaim; sc.may_swap = !nr_boost_reclaim; /* @@ -7061,13 +7054,6 @@ restart: */ kswapd_age_node(pgdat, &sc); - /* - * If we're getting trouble reclaiming, start doing writepage - * even in laptop mode. - */ - if (sc.priority < DEF_PRIORITY - 2) - sc.may_writepage = 1; - /* Call soft limit reclaim before calling shrink_node. */ sc.nr_scanned = 0; nr_soft_scanned = 0; @@ -7799,7 +7785,7 @@ int user_proactive_reclaim(char *buf, .reclaim_idx = gfp_zone(gfp_mask), .proactive_swappiness = swappiness == -1 ? NULL : &swappiness, .priority = DEF_PRIORITY, - .may_writepage = !laptop_mode, + .may_writepage = 1, .nr_to_reclaim = max(batch_size, SWAP_CLUSTER_MAX), .may_unmap = 1, .may_swap = 1, -- cgit v1.2.3 From 3b3ddafde1c2b5262628b4463c7b6f4ccc6430b5 Mon Sep 17 00:00:00 2001 From: gaoxiang17 Date: Fri, 9 Jan 2026 19:54:11 +0800 Subject: dma-buf: add some tracepoints to debug. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we can only inspect dmabuf by iterating over process FDs or the dmabuf_list, we need to add our own tracepoints to track its status in real time in production. For example: binder:3016_1-3102 [006] ...1. 255.126521: dma_buf_export: exp_name=qcom,system size=12685312 ino=2738 binder:3016_1-3102 [006] ...1. 255.126528: dma_buf_fd: exp_name=qcom,system size=12685312 ino=2738 fd=8 binder:3016_1-3102 [006] ...1. 255.126642: dma_buf_mmap_internal: exp_name=qcom,system size=28672 ino=2739 kworker/6:1-86 [006] ...1. 255.127194: dma_buf_put: exp_name=qcom,system size=12685312 ino=2738 RenderThread-9293 [006] ...1. 316.618179: dma_buf_get: exp_name=qcom,system size=12771328 ino=2762 fd=176 RenderThread-9293 [006] ...1. 316.618195: dma_buf_dynamic_attach: exp_name=qcom,system size=12771328 ino=2762 attachment:ffffff880a18dd00 is_dynamic=0 dev_name=kgsl-3d0 RenderThread-9293 [006] ...1. 318.878220: dma_buf_detach: exp_name=qcom,system size=12771328 ino=2762 attachment:ffffff880a18dd00 is_dynamic=0 dev_name=kgsl-3d0 Signed-off-by: Xiang Gao Reviewed-by: Steven Rostedt (Google) Reviewed-by: Christian König Signed-off-by: Christian König Link: https://lore.kernel.org/r/20260109115411.115270-1-gxxa03070307@gmail.com --- drivers/dma-buf/dma-buf.c | 9 +++----- include/trace/events/dma_buf.h | 50 ++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 30 deletions(-) (limited to 'include/trace/events') diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 8e23580f1754..77555096e4c7 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -46,12 +46,10 @@ */ #define DMA_BUF_TRACE(FUNC, ...) \ do { \ - if (FUNC##_enabled()) { \ + /* Always expose lock if lockdep is enabled */ \ + if (IS_ENABLED(CONFIG_LOCKDEP) || FUNC##_enabled()) { \ guard(spinlock)(&dmabuf->name_lock); \ FUNC(__VA_ARGS__); \ - } else if (IS_ENABLED(CONFIG_LOCKDEP)) { \ - /* Expose this lock when lockdep is enabled */ \ - guard(spinlock)(&dmabuf->name_lock); \ } \ } while (0) @@ -795,8 +793,7 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags) return -EINVAL; fd = FD_ADD(flags, dmabuf->file); - if (fd >= 0) - DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); + DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); return fd; } diff --git a/include/trace/events/dma_buf.h b/include/trace/events/dma_buf.h index 35f8140095f4..3bb88d05bcc8 100644 --- a/include/trace/events/dma_buf.h +++ b/include/trace/events/dma_buf.h @@ -15,15 +15,15 @@ DECLARE_EVENT_CLASS(dma_buf, TP_ARGS(dmabuf), TP_STRUCT__entry( - __string(exp_name, dmabuf->exp_name) - __field(size_t, size) - __field(ino_t, ino) + __string( exp_name, dmabuf->exp_name) + __field( size_t, size) + __field( ino_t, ino) ), TP_fast_assign( __assign_str(exp_name); - __entry->size = dmabuf->size; - __entry->ino = dmabuf->file->f_inode->i_ino; + __entry->size = dmabuf->size; + __entry->ino = dmabuf->file->f_inode->i_ino; ), TP_printk("exp_name=%s size=%zu ino=%lu", @@ -40,21 +40,21 @@ DECLARE_EVENT_CLASS(dma_buf_attach_dev, TP_ARGS(dmabuf, attach, is_dynamic, dev), TP_STRUCT__entry( - __string(dev_name, dev_name(dev)) - __string(exp_name, dmabuf->exp_name) - __field(size_t, size) - __field(ino_t, ino) - __field(struct dma_buf_attachment *, attach) - __field(bool, is_dynamic) + __string( dev_name, dev_name(dev)) + __string( exp_name, dmabuf->exp_name) + __field( size_t, size) + __field( ino_t, ino) + __field( struct dma_buf_attachment *, attach) + __field( bool, is_dynamic) ), TP_fast_assign( __assign_str(dev_name); __assign_str(exp_name); - __entry->size = dmabuf->size; - __entry->ino = dmabuf->file->f_inode->i_ino; - __entry->is_dynamic = is_dynamic; - __entry->attach = attach; + __entry->size = dmabuf->size; + __entry->ino = dmabuf->file->f_inode->i_ino; + __entry->is_dynamic = is_dynamic; + __entry->attach = attach; ), TP_printk("exp_name=%s size=%zu ino=%lu attachment:%p is_dynamic=%d dev_name=%s", @@ -73,17 +73,17 @@ DECLARE_EVENT_CLASS(dma_buf_fd, TP_ARGS(dmabuf, fd), TP_STRUCT__entry( - __string(exp_name, dmabuf->exp_name) - __field(size_t, size) - __field(ino_t, ino) - __field(int, fd) + __string( exp_name, dmabuf->exp_name) + __field( size_t, size) + __field( ino_t, ino) + __field( int, fd) ), TP_fast_assign( __assign_str(exp_name); - __entry->size = dmabuf->size; - __entry->ino = dmabuf->file->f_inode->i_ino; - __entry->fd = fd; + __entry->size = dmabuf->size; + __entry->ino = dmabuf->file->f_inode->i_ino; + __entry->fd = fd; ), TP_printk("exp_name=%s size=%zu ino=%lu fd=%d", @@ -137,11 +137,13 @@ DEFINE_EVENT(dma_buf_attach_dev, dma_buf_detach, TP_ARGS(dmabuf, attach, is_dynamic, dev) ); -DEFINE_EVENT(dma_buf_fd, dma_buf_fd, +DEFINE_EVENT_CONDITION(dma_buf_fd, dma_buf_fd, TP_PROTO(struct dma_buf *dmabuf, int fd), - TP_ARGS(dmabuf, fd) + TP_ARGS(dmabuf, fd), + + TP_CONDITION(fd >= 0) ); DEFINE_EVENT(dma_buf_fd, dma_buf_get, -- cgit v1.2.3 From a4674aa58be53921f2aba62e143cc338d6ab142c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 20 Jan 2026 12:53:53 +0000 Subject: tcp: preserve const qualifier in tcp_rsk() and inet_rsk() We can change tcp_rsk() and inet_rsk() to propagate their argument const qualifier thanks to container_of_const(). Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260120125353.1470456-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- include/linux/tcp.h | 5 +---- include/net/inet_sock.h | 5 +---- include/trace/events/tcp.h | 2 +- net/ipv4/inet_connection_sock.c | 2 +- net/ipv6/inet6_connection_sock.c | 2 +- 5 files changed, 5 insertions(+), 11 deletions(-) (limited to 'include/trace/events') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 20b8c6e21fef..367b491ddf97 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -181,10 +181,7 @@ struct tcp_request_sock { #endif }; -static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) -{ - return (struct tcp_request_sock *)req; -} +#define tcp_rsk(ptr) container_of_const(ptr, struct tcp_request_sock, req.req) static inline bool tcp_rsk_used_ao(const struct request_sock *req) { diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 3370159556b8..903b2263ec80 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -100,10 +100,7 @@ struct inet_request_sock { }; }; -static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) -{ - return (struct inet_request_sock *)sk; -} +#define inet_rsk(ptr) container_of_const(ptr, struct inet_request_sock, req) static inline u32 inet_request_mark(const struct sock *sk, struct sk_buff *skb) { diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 6757233bd064..f155f95cdb6e 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -305,7 +305,7 @@ TRACE_EVENT(tcp_retransmit_synack, ), TP_fast_assign( - struct inet_request_sock *ireq = inet_rsk(req); + const struct inet_request_sock *ireq = inet_rsk(req); __be32 *p32; __entry->skaddr = sk; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 97d57c52b9ad..13372d2cbed5 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1196,7 +1196,7 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, { struct sock *newsk = sk_clone_lock(sk, priority); struct inet_connection_sock *newicsk; - struct inet_request_sock *ireq; + const struct inet_request_sock *ireq; struct inet_sock *newinet; if (!newsk) diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index ea5cf3fdfdd6..3aedd51c9bdb 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -29,7 +29,7 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk, const struct request_sock *req, u8 proto) { - struct inet_request_sock *ireq = inet_rsk(req); + const struct inet_request_sock *ireq = inet_rsk(req); const struct ipv6_pinfo *np = inet6_sk(sk); struct in6_addr *final_p, final; struct dst_entry *dst; -- cgit v1.2.3 From 69368d2ded39b6a7ca6bd9fd53ee0d1f904e3601 Mon Sep 17 00:00:00 2001 From: Hongbo Li Date: Fri, 23 Jan 2026 01:31:29 +0000 Subject: erofs: pass inode to trace_erofs_read_folio The trace_erofs_read_folio accesses inode information through folio, but this method fails if the real inode is not associated with the folio(such as in the upcoming page cache sharing case). Therefore, we pass the real inode to it so that the inode information can be printed out in that case. Signed-off-by: Hongbo Li Reviewed-by: Gao Xiang Signed-off-by: Gao Xiang --- fs/erofs/data.c | 6 ++---- fs/erofs/fileio.c | 2 +- fs/erofs/zdata.c | 2 +- include/trace/events/erofs.h | 10 +++++----- 4 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include/trace/events') diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 71e23d91123d..ea198defb531 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -385,8 +385,7 @@ static int erofs_read_folio(struct file *file, struct folio *folio) }; struct erofs_iomap_iter_ctx iter_ctx = {}; - trace_erofs_read_folio(folio, true); - + trace_erofs_read_folio(folio_inode(folio), folio, true); iomap_read_folio(&erofs_iomap_ops, &read_ctx, &iter_ctx); return 0; } @@ -400,8 +399,7 @@ static void erofs_readahead(struct readahead_control *rac) struct erofs_iomap_iter_ctx iter_ctx = {}; trace_erofs_readahead(rac->mapping->host, readahead_index(rac), - readahead_count(rac), true); - + readahead_count(rac), true); iomap_readahead(&erofs_iomap_ops, &read_ctx, &iter_ctx); } diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c index 932e8b353ba1..d07dc248d264 100644 --- a/fs/erofs/fileio.c +++ b/fs/erofs/fileio.c @@ -161,7 +161,7 @@ static int erofs_fileio_read_folio(struct file *file, struct folio *folio) struct erofs_fileio io = {}; int err; - trace_erofs_read_folio(folio, true); + trace_erofs_read_folio(folio_inode(folio), folio, true); err = erofs_fileio_scan_folio(&io, folio); erofs_fileio_rq_submit(io.rq); return err; diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 5860ea6dbc60..c5fa07af34f5 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1879,7 +1879,7 @@ static int z_erofs_read_folio(struct file *file, struct folio *folio) Z_EROFS_DEFINE_FRONTEND(f, inode, folio_pos(folio)); int err; - trace_erofs_read_folio(folio, false); + trace_erofs_read_folio(inode, folio, false); z_erofs_pcluster_readmore(&f, NULL, true); err = z_erofs_scan_folio(&f, folio, false); z_erofs_pcluster_readmore(&f, NULL, false); diff --git a/include/trace/events/erofs.h b/include/trace/events/erofs.h index dad7360f42f9..def20d06507b 100644 --- a/include/trace/events/erofs.h +++ b/include/trace/events/erofs.h @@ -82,9 +82,9 @@ TRACE_EVENT(erofs_fill_inode, TRACE_EVENT(erofs_read_folio, - TP_PROTO(struct folio *folio, bool raw), + TP_PROTO(struct inode *inode, struct folio *folio, bool raw), - TP_ARGS(folio, raw), + TP_ARGS(inode, folio, raw), TP_STRUCT__entry( __field(dev_t, dev ) @@ -96,9 +96,9 @@ TRACE_EVENT(erofs_read_folio, ), TP_fast_assign( - __entry->dev = folio->mapping->host->i_sb->s_dev; - __entry->nid = EROFS_I(folio->mapping->host)->nid; - __entry->dir = S_ISDIR(folio->mapping->host->i_mode); + __entry->dev = inode->i_sb->s_dev; + __entry->nid = EROFS_I(inode)->nid; + __entry->dir = S_ISDIR(inode->i_mode); __entry->index = folio->index; __entry->uptodate = folio_test_uptodate(folio); __entry->raw = raw; -- cgit v1.2.3 From 5173ae0a068d64643ccf4915b7cbedf82810a592 Mon Sep 17 00:00:00 2001 From: Shivank Garg Date: Sun, 18 Jan 2026 19:09:40 +0000 Subject: mm/khugepaged: map dirty/writeback pages failures to EAGAIN Patch series "mm/khugepaged: fix dirty page handling for MADV_COLLAPSE", v5. MADV_COLLAPSE on file-backed mappings fails with -EINVAL when TEXT pages are dirty. This affects scenarios like package/container updates or executing binaries immediately after writing them, etc. The issue is that collapse_file() triggers async writeback and returns SCAN_FAIL (maps to -EINVAL), expecting khugepaged to revisit later. But MADV_COLLAPSE is synchronous and userspace expects immediate success or a clear retry signal. Reproduction: - Compile or copy 2MB-aligned executable to XFS/ext4 FS - Call MADV_COLLAPSE on .text section - First call fails with -EINVAL (text pages dirty from copy) - Second call succeeds (async writeback completed) Issue Report: https://lore.kernel.org/all/4e26fe5e-7374-467c-a333-9dd48f85d7cc@amd.com This patch (of 2): When collapse_file encounters dirty or writeback pages in file-backed mappings, it currently returns SCAN_FAIL which maps to -EINVAL. This is misleading as EINVAL suggests invalid arguments, whereas dirty/writeback pages represent transient conditions that may resolve on retry. Introduce SCAN_PAGE_DIRTY_OR_WRITEBACK to cover both dirty and writeback states, mapping it to -EAGAIN. For MADV_COLLAPSE, this provides userspace with a clear signal that retry may succeed after writeback completes. For khugepaged, this is harmless as it will naturally revisit the range during periodic scans after async writeback completes. Link: https://lkml.kernel.org/r/20260118190939.8986-2-shivankg@amd.com Link: https://lkml.kernel.org/r/20260118190939.8986-4-shivankg@amd.com Fixes: 34488399fa08 ("mm/madvise: add file and shmem support to MADV_COLLAPSE") Signed-off-by: Shivank Garg Reported-by: Branden Moore Closes: https://lore.kernel.org/all/4e26fe5e-7374-467c-a333-9dd48f85d7cc@amd.com Reviewed-by: Dev Jain Reviewed-by: Lance Yang Reviewed-by: Baolin Wang Reviewed-by: wang lian Acked-by: David Hildenbrand (Red Hat) Cc: Barry Song Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: Nico Pache Cc: Ryan Roberts Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- include/trace/events/huge_memory.h | 3 ++- mm/khugepaged.c | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include/trace/events') diff --git a/include/trace/events/huge_memory.h b/include/trace/events/huge_memory.h index 4cde53b45a85..4e41bff31888 100644 --- a/include/trace/events/huge_memory.h +++ b/include/trace/events/huge_memory.h @@ -37,7 +37,8 @@ EM( SCAN_PAGE_HAS_PRIVATE, "page_has_private") \ EM( SCAN_STORE_FAILED, "store_failed") \ EM( SCAN_COPY_MC, "copy_poisoned_page") \ - EMe(SCAN_PAGE_FILLED, "page_filled") + EM( SCAN_PAGE_FILLED, "page_filled") \ + EMe(SCAN_PAGE_DIRTY_OR_WRITEBACK, "page_dirty_or_writeback") #undef EM #undef EMe diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 97d1b2824386..219dfa2e523c 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -58,6 +58,7 @@ enum scan_result { SCAN_STORE_FAILED, SCAN_COPY_MC, SCAN_PAGE_FILLED, + SCAN_PAGE_DIRTY_OR_WRITEBACK, }; #define CREATE_TRACE_POINTS @@ -1967,11 +1968,11 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr, */ xas_unlock_irq(&xas); filemap_flush(mapping); - result = SCAN_FAIL; + result = SCAN_PAGE_DIRTY_OR_WRITEBACK; goto xa_unlocked; } else if (folio_test_writeback(folio)) { xas_unlock_irq(&xas); - result = SCAN_FAIL; + result = SCAN_PAGE_DIRTY_OR_WRITEBACK; goto xa_unlocked; } else if (folio_trylock(folio)) { folio_get(folio); @@ -2018,7 +2019,7 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr, * folio is dirty because it hasn't been flushed * since first write. */ - result = SCAN_FAIL; + result = SCAN_PAGE_DIRTY_OR_WRITEBACK; goto out_unlock; } @@ -2747,6 +2748,7 @@ static int madvise_collapse_errno(enum scan_result r) case SCAN_PAGE_LRU: case SCAN_DEL_PAGE_LRU: case SCAN_PAGE_FILLED: + case SCAN_PAGE_DIRTY_OR_WRITEBACK: return -EAGAIN; /* * Other: Trying again likely not to succeed / error intrinsic to -- cgit v1.2.3 From fa19d42cc7915226db416999866171a456dac657 Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Mon, 26 Jan 2026 12:56:58 +0100 Subject: fsverity: add tracepoints fs-verity previously had debug printk but it was removed. This patch adds trace points to similar places, as a better alternative. Signed-off-by: Andrey Albershteyn Reviewed-by: Darrick J. Wong [djwong: fix formatting] Signed-off-by: Darrick J. Wong Link: https://patch.msgid.link/20260126115658.27656-3-aalbersh@kernel.org Signed-off-by: Christian Brauner --- MAINTAINERS | 1 + fs/verity/enable.c | 4 ++ fs/verity/fsverity_private.h | 2 + fs/verity/init.c | 1 + fs/verity/verify.c | 9 +++ include/trace/events/fsverity.h | 146 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 163 insertions(+) create mode 100644 include/trace/events/fsverity.h (limited to 'include/trace/events') diff --git a/MAINTAINERS b/MAINTAINERS index 5b11839cba9d..9761650d2ead 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10311,6 +10311,7 @@ T: git https://git.kernel.org/pub/scm/fs/fsverity/linux.git F: Documentation/filesystems/fsverity.rst F: fs/verity/ F: include/linux/fsverity.h +F: include/trace/events/fsverity.h F: include/uapi/linux/fsverity.h FT260 FTDI USB-HID TO I2C BRIDGE DRIVER diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 95ec42b84797..8718d943b428 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -222,6 +222,8 @@ static int enable_verity(struct file *filp, if (err) goto out; + trace_fsverity_enable(inode, ¶ms); + /* * Start enabling verity on this file, serialized by the inode lock. * Fail if verity is already enabled or is already being enabled. @@ -264,6 +266,8 @@ static int enable_verity(struct file *filp, goto rollback; } + trace_fsverity_tree_done(inode, vi, ¶ms); + /* * Tell the filesystem to finish enabling verity on the file. * Serialized with ->begin_enable_verity() by the inode lock. diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index dd20b138d452..4b7ae1748f4e 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -161,4 +161,6 @@ static inline void fsverity_init_signature(void) void __init fsverity_init_workqueue(void); +#include + #endif /* _FSVERITY_PRIVATE_H */ diff --git a/fs/verity/init.c b/fs/verity/init.c index 6e8d33b50240..d65206608583 100644 --- a/fs/verity/init.c +++ b/fs/verity/init.c @@ -5,6 +5,7 @@ * Copyright 2019 Google LLC */ +#define CREATE_TRACE_POINTS #include "fsverity_private.h" #include diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 86067c8b40cf..940b8b956d7e 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -135,6 +135,9 @@ static bool verify_data_block(struct inode *inode, struct fsverity_info *vi, /* Byte offset of the wanted hash relative to @addr */ unsigned int hoffset; } hblocks[FS_VERITY_MAX_LEVELS]; + + trace_fsverity_verify_data_block(inode, params, data_pos); + /* * The index of the previous level's block within that level; also the * index of that block's hash within the current level. @@ -214,6 +217,9 @@ static bool verify_data_block(struct inode *inode, struct fsverity_info *vi, want_hash = _want_hash; kunmap_local(haddr); put_page(hpage); + trace_fsverity_merkle_hit(inode, data_pos, hblock_idx, + level, + hoffset >> params->log_digestsize); goto descend; } hblocks[level].page = hpage; @@ -232,6 +238,9 @@ descend: unsigned long hblock_idx = hblocks[level - 1].index; unsigned int hoffset = hblocks[level - 1].hoffset; + trace_fsverity_verify_merkle_block(inode, hblock_idx, + level, hoffset >> params->log_digestsize); + fsverity_hash_block(params, haddr, real_hash); if (memcmp(want_hash, real_hash, hsize) != 0) goto corrupted; diff --git a/include/trace/events/fsverity.h b/include/trace/events/fsverity.h new file mode 100644 index 000000000000..a8c52f21cbd5 --- /dev/null +++ b/include/trace/events/fsverity.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM fsverity + +#if !defined(_TRACE_FSVERITY_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_FSVERITY_H + +#include + +struct fsverity_descriptor; +struct merkle_tree_params; +struct fsverity_info; + +TRACE_EVENT(fsverity_enable, + TP_PROTO(const struct inode *inode, + const struct merkle_tree_params *params), + TP_ARGS(inode, params), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(u64, data_size) + __field(u64, tree_size) + __field(unsigned int, merkle_block) + __field(unsigned int, num_levels) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->data_size = i_size_read(inode); + __entry->tree_size = params->tree_size; + __entry->merkle_block = params->block_size; + __entry->num_levels = params->num_levels; + ), + TP_printk("ino %lu data_size %llu tree_size %llu merkle_block %u levels %u", + (unsigned long) __entry->ino, + __entry->data_size, + __entry->tree_size, + __entry->merkle_block, + __entry->num_levels) +); + +TRACE_EVENT(fsverity_tree_done, + TP_PROTO(const struct inode *inode, const struct fsverity_info *vi, + const struct merkle_tree_params *params), + TP_ARGS(inode, vi, params), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(u64, data_size) + __field(u64, tree_size) + __field(unsigned int, merkle_block) + __field(unsigned int, levels) + __dynamic_array(u8, root_hash, params->digest_size) + __dynamic_array(u8, file_digest, params->digest_size) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->data_size = i_size_read(inode); + __entry->tree_size = params->tree_size; + __entry->merkle_block = params->block_size; + __entry->levels = params->num_levels; + memcpy(__get_dynamic_array(root_hash), vi->root_hash, __get_dynamic_array_len(root_hash)); + memcpy(__get_dynamic_array(file_digest), vi->file_digest, __get_dynamic_array_len(file_digest)); + ), + TP_printk("ino %lu data_size %llu tree_size %lld merkle_block %u levels %u root_hash %s digest %s", + (unsigned long) __entry->ino, + __entry->data_size, + __entry->tree_size, + __entry->merkle_block, + __entry->levels, + __print_hex_str(__get_dynamic_array(root_hash), __get_dynamic_array_len(root_hash)), + __print_hex_str(__get_dynamic_array(file_digest), __get_dynamic_array_len(file_digest))) +); + +TRACE_EVENT(fsverity_verify_data_block, + TP_PROTO(const struct inode *inode, + const struct merkle_tree_params *params, + u64 data_pos), + TP_ARGS(inode, params, data_pos), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(u64, data_pos) + __field(unsigned int, merkle_block) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->data_pos = data_pos; + __entry->merkle_block = params->block_size; + ), + TP_printk("ino %lu data_pos %llu merkle_block %u", + (unsigned long) __entry->ino, + __entry->data_pos, + __entry->merkle_block) +); + +TRACE_EVENT(fsverity_merkle_hit, + TP_PROTO(const struct inode *inode, u64 data_pos, + unsigned long hblock_idx, unsigned int level, + unsigned int hidx), + TP_ARGS(inode, data_pos, hblock_idx, level, hidx), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(u64, data_pos) + __field(unsigned long, hblock_idx) + __field(unsigned int, level) + __field(unsigned int, hidx) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->data_pos = data_pos; + __entry->hblock_idx = hblock_idx; + __entry->level = level; + __entry->hidx = hidx; + ), + TP_printk("ino %lu data_pos %llu hblock_idx %lu level %u hidx %u", + (unsigned long) __entry->ino, + __entry->data_pos, + __entry->hblock_idx, + __entry->level, + __entry->hidx) +); + +TRACE_EVENT(fsverity_verify_merkle_block, + TP_PROTO(const struct inode *inode, unsigned long hblock_idx, + unsigned int level, unsigned int hidx), + TP_ARGS(inode, hblock_idx, level, hidx), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(unsigned long, hblock_idx) + __field(unsigned int, level) + __field(unsigned int, hidx) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->hblock_idx = hblock_idx; + __entry->level = level; + __entry->hidx = hidx; + ), + TP_printk("ino %lu hblock_idx %lu level %u hidx %u", + (unsigned long) __entry->ino, + __entry->hblock_idx, + __entry->level, + __entry->hidx) +); + +#endif /* _TRACE_FSVERITY_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From bc367775f60214312befa33f101b31fe74bba48a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 30 Jan 2026 21:28:09 +0800 Subject: f2fs: introduce trace_f2fs_priority_update This patch introduces two new tracepoints for debug purpose. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 17 ++++++++++---- include/trace/events/f2fs.h | 57 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) (limited to 'include/trace/events') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 2f5a03e29d0b..4afa5d9a19fc 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -130,9 +130,13 @@ static void uplift_priority(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc return; set_user_nice(current, lc->new_nice); lc->need_restore = true; + + trace_f2fs_priority_uplift(sem->sbi, sem->name, is_write, current, + NICE_TO_PRIO(lc->orig_nice), NICE_TO_PRIO(lc->new_nice)); } -static void restore_priority(struct f2fs_lock_context *lc) +static void restore_priority(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc, + bool is_write) { if (!lc->need_restore) return; @@ -140,6 +144,9 @@ static void restore_priority(struct f2fs_lock_context *lc) if (task_nice(current) != lc->new_nice) return; set_user_nice(current, lc->orig_nice); + + trace_f2fs_priority_restore(sem->sbi, sem->name, is_write, current, + NICE_TO_PRIO(lc->orig_nice), NICE_TO_PRIO(lc->new_nice)); } void f2fs_down_read_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) @@ -153,7 +160,7 @@ int f2fs_down_read_trylock_trace(struct f2fs_rwsem *sem, struct f2fs_lock_contex { uplift_priority(sem, lc, false); if (!f2fs_down_read_trylock(sem)) { - restore_priority(lc); + restore_priority(sem, lc, false); return 0; } trace_lock_elapsed_time_start(sem, lc); @@ -163,7 +170,7 @@ int f2fs_down_read_trylock_trace(struct f2fs_rwsem *sem, struct f2fs_lock_contex void f2fs_up_read_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) { f2fs_up_read(sem); - restore_priority(lc); + restore_priority(sem, lc, false); trace_lock_elapsed_time_end(sem, lc, false); } @@ -178,7 +185,7 @@ int f2fs_down_write_trylock_trace(struct f2fs_rwsem *sem, struct f2fs_lock_conte { uplift_priority(sem, lc, true); if (!f2fs_down_write_trylock(sem)) { - restore_priority(lc); + restore_priority(sem, lc, true); return 0; } trace_lock_elapsed_time_start(sem, lc); @@ -188,7 +195,7 @@ int f2fs_down_write_trylock_trace(struct f2fs_rwsem *sem, struct f2fs_lock_conte void f2fs_up_write_trace(struct f2fs_rwsem *sem, struct f2fs_lock_context *lc) { f2fs_up_write(sem); - restore_priority(lc); + restore_priority(sem, lc, true); trace_lock_elapsed_time_end(sem, lc, true); } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index c3b6b509472f..9364e6775562 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -2525,6 +2525,63 @@ TRACE_EVENT(f2fs_lock_elapsed_time, __entry->other_time) ); +DECLARE_EVENT_CLASS(f2fs_priority_update, + + TP_PROTO(struct f2fs_sb_info *sbi, enum f2fs_lock_name lock_name, + bool is_write, struct task_struct *p, int orig_prio, + int new_prio), + + TP_ARGS(sbi, lock_name, is_write, p, orig_prio, new_prio), + + TP_STRUCT__entry( + __field(dev_t, dev) + __array(char, comm, TASK_COMM_LEN) + __field(pid_t, pid) + __field(unsigned int, lock_name) + __field(bool, is_write) + __field(int, orig_prio) + __field(int, new_prio) + ), + + TP_fast_assign( + __entry->dev = sbi->sb->s_dev; + memcpy(__entry->comm, p->comm, TASK_COMM_LEN); + __entry->pid = p->pid; + __entry->lock_name = lock_name; + __entry->is_write = is_write; + __entry->orig_prio = orig_prio; + __entry->new_prio = new_prio; + ), + + TP_printk("dev = (%d,%d), comm: %s, pid: %d, lock_name: %s, " + "lock_type: %s, orig_prio: %d, new_prio: %d", + show_dev(__entry->dev), + __entry->comm, + __entry->pid, + show_lock_name(__entry->lock_name), + __entry->is_write ? "wlock" : "rlock", + __entry->orig_prio, + __entry->new_prio) +); + +DEFINE_EVENT(f2fs_priority_update, f2fs_priority_uplift, + + TP_PROTO(struct f2fs_sb_info *sbi, enum f2fs_lock_name lock_name, + bool is_write, struct task_struct *p, int orig_prio, + int new_prio), + + TP_ARGS(sbi, lock_name, is_write, p, orig_prio, new_prio) +); + +DEFINE_EVENT(f2fs_priority_update, f2fs_priority_restore, + + TP_PROTO(struct f2fs_sb_info *sbi, enum f2fs_lock_name lock_name, + bool is_write, struct task_struct *p, int orig_prio, + int new_prio), + + TP_ARGS(sbi, lock_name, is_write, p, orig_prio, new_prio) +); + #endif /* _TRACE_F2FS_H */ /* This part must be outside protection */ -- cgit v1.2.3 From a45088376d8a847a5e3b1982fcfceb41644e3b1d Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Tue, 20 Jan 2026 10:43:49 +0800 Subject: mm/vmscan: add tracepoint and reason for kswapd_failures reset Currently, kswapd_failures is reset in multiple places (kswapd, direct reclaim, PCP freeing, memory-tiers), but there's no way to trace when and why it was reset, making it difficult to debug memory reclaim issues. This patch: 1. Introduce kswapd_clear_hopeless() as a wrapper function to centralize kswapd_failures reset logic. 2. Introduce kswapd_test_hopeless() to encapsulate hopeless node checks, replacing all open-coded kswapd_failures comparisons. 3. Add kswapd_clear_hopeless_reason enum to distinguish reset sources: - KSWAPD_CLEAR_HOPELESS_KSWAPD: reset from kswapd context - KSWAPD_CLEAR_HOPELESS_DIRECT: reset from direct reclaim - KSWAPD_CLEAR_HOPELESS_PCP: reset from PCP page freeing - KSWAPD_CLEAR_HOPELESS_OTHER: reset from other paths 4. Add tracepoints for better observability: - mm_vmscan_kswapd_clear_hopeless: traces each reset with reason - mm_vmscan_kswapd_reclaim_fail: traces each kswapd reclaim failure Test results: $ trace-cmd record -e vmscan:mm_vmscan_kswapd_clear_hopeless -e vmscan:mm_vmscan_kswapd_reclaim_fail $ # generate memory pressure $ trace-cmd report cpus=4 kswapd0-71 [000] 27.216563: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=1 kswapd0-71 [000] 27.217169: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=2 kswapd0-71 [000] 27.217764: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=3 kswapd0-71 [000] 27.218353: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=4 kswapd0-71 [000] 27.218993: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=5 kswapd0-71 [000] 27.219744: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=6 kswapd0-71 [000] 27.220488: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=7 kswapd0-71 [000] 27.221206: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=8 kswapd0-71 [000] 27.221806: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=9 kswapd0-71 [000] 27.222634: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=10 kswapd0-71 [000] 27.223286: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=11 kswapd0-71 [000] 27.223894: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=12 kswapd0-71 [000] 27.224712: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=13 kswapd0-71 [000] 27.225424: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=14 kswapd0-71 [000] 27.226082: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=15 kswapd0-71 [000] 27.226810: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=16 kswapd1-72 [002] 27.386869: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=1 kswapd1-72 [002] 27.387435: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=2 kswapd1-72 [002] 27.388016: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=3 kswapd1-72 [002] 27.388586: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=4 kswapd1-72 [002] 27.389155: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=5 kswapd1-72 [002] 27.389723: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=6 kswapd1-72 [002] 27.390292: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=7 kswapd1-72 [002] 27.392364: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=8 kswapd1-72 [002] 27.392934: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=9 kswapd1-72 [002] 27.393504: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=10 kswapd1-72 [002] 27.394073: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=11 kswapd1-72 [002] 27.394899: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=12 kswapd1-72 [002] 27.395472: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=13 kswapd1-72 [002] 27.396055: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=14 kswapd1-72 [002] 27.396628: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=15 kswapd1-72 [002] 27.397199: mm_vmscan_kswapd_reclaim_fail: nid=1 failures=16 kworker/u18:0-40 [002] 27.410151: mm_vmscan_kswapd_clear_hopeless: nid=0 reason=DIRECT kswapd0-71 [000] 27.439454: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=1 kswapd0-71 [000] 27.440048: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=2 kswapd0-71 [000] 27.440634: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=3 kswapd0-71 [000] 27.441211: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=4 kswapd0-71 [000] 27.441787: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=5 kswapd0-71 [000] 27.442363: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=6 kswapd0-71 [000] 27.443030: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=7 kswapd0-71 [000] 27.443725: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=8 kswapd0-71 [000] 27.444315: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=9 kswapd0-71 [000] 27.444898: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=10 kswapd0-71 [000] 27.445476: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=11 kswapd0-71 [000] 27.446053: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=12 kswapd0-71 [000] 27.446646: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=13 kswapd0-71 [000] 27.447230: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=14 kswapd0-71 [000] 27.447812: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=15 kswapd0-71 [000] 27.448391: mm_vmscan_kswapd_reclaim_fail: nid=0 failures=16 ann-423 [003] 28.028285: mm_vmscan_kswapd_clear_hopeless: nid=0 reason=PCP Link: https://lkml.kernel.org/r/20260120024402.387576-3-jiayuan.chen@linux.dev Signed-off-by: Jiayuan Chen Signed-off-by: Jiayuan Chen Acked-by: Shakeel Butt Suggested-by: Johannes Weiner Reviewed-by: Steven Rostedt (Google) [tracing] Cc: Axel Rasmussen Cc: Brendan Jackman Cc: David Hildenbrand Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Qi Zheng Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Wei Xu Cc: Yuanchu Xie Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/mmzone.h | 19 ++++++++++++---- include/trace/events/vmscan.h | 51 +++++++++++++++++++++++++++++++++++++++++++ mm/memory-tiers.c | 2 +- mm/page_alloc.c | 4 ++-- mm/show_mem.c | 3 +-- mm/vmscan.c | 29 ++++++++++++++++-------- mm/vmstat.c | 2 +- 7 files changed, 91 insertions(+), 19 deletions(-) (limited to 'include/trace/events') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 8881198e85c6..3e51190a55e4 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -1534,16 +1534,27 @@ static inline unsigned long pgdat_end_pfn(pg_data_t *pgdat) #include void build_all_zonelists(pg_data_t *pgdat); -void wakeup_kswapd(struct zone *zone, gfp_t gfp_mask, int order, - enum zone_type highest_zoneidx); -void kswapd_try_clear_hopeless(struct pglist_data *pgdat, - unsigned int order, int highest_zoneidx); bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, int highest_zoneidx, unsigned int alloc_flags, long free_pages); bool zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, int highest_zoneidx, unsigned int alloc_flags); + +enum kswapd_clear_hopeless_reason { + KSWAPD_CLEAR_HOPELESS_OTHER = 0, + KSWAPD_CLEAR_HOPELESS_KSWAPD, + KSWAPD_CLEAR_HOPELESS_DIRECT, + KSWAPD_CLEAR_HOPELESS_PCP, +}; + +void wakeup_kswapd(struct zone *zone, gfp_t gfp_mask, int order, + enum zone_type highest_zoneidx); +void kswapd_try_clear_hopeless(struct pglist_data *pgdat, + unsigned int order, int highest_zoneidx); +void kswapd_clear_hopeless(pg_data_t *pgdat, enum kswapd_clear_hopeless_reason reason); +bool kswapd_test_hopeless(pg_data_t *pgdat); + /* * Memory initialization context, use to differentiate memory added by * the platform statically or via memory hotplug interface. diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h index 490958fa10de..ea58e4656abf 100644 --- a/include/trace/events/vmscan.h +++ b/include/trace/events/vmscan.h @@ -40,6 +40,16 @@ {_VMSCAN_THROTTLE_CONGESTED, "VMSCAN_THROTTLE_CONGESTED"} \ ) : "VMSCAN_THROTTLE_NONE" +TRACE_DEFINE_ENUM(KSWAPD_CLEAR_HOPELESS_OTHER); +TRACE_DEFINE_ENUM(KSWAPD_CLEAR_HOPELESS_KSWAPD); +TRACE_DEFINE_ENUM(KSWAPD_CLEAR_HOPELESS_DIRECT); +TRACE_DEFINE_ENUM(KSWAPD_CLEAR_HOPELESS_PCP); + +#define kswapd_clear_hopeless_reason_ops \ + {KSWAPD_CLEAR_HOPELESS_KSWAPD, "KSWAPD"}, \ + {KSWAPD_CLEAR_HOPELESS_DIRECT, "DIRECT"}, \ + {KSWAPD_CLEAR_HOPELESS_PCP, "PCP"}, \ + {KSWAPD_CLEAR_HOPELESS_OTHER, "OTHER"} #define trace_reclaim_flags(file) ( \ (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \ @@ -535,6 +545,47 @@ TRACE_EVENT(mm_vmscan_throttled, __entry->usec_delayed, show_throttle_flags(__entry->reason)) ); + +TRACE_EVENT(mm_vmscan_kswapd_reclaim_fail, + + TP_PROTO(int nid, int failures), + + TP_ARGS(nid, failures), + + TP_STRUCT__entry( + __field(int, nid) + __field(int, failures) + ), + + TP_fast_assign( + __entry->nid = nid; + __entry->failures = failures; + ), + + TP_printk("nid=%d failures=%d", + __entry->nid, __entry->failures) +); + +TRACE_EVENT(mm_vmscan_kswapd_clear_hopeless, + + TP_PROTO(int nid, int reason), + + TP_ARGS(nid, reason), + + TP_STRUCT__entry( + __field(int, nid) + __field(int, reason) + ), + + TP_fast_assign( + __entry->nid = nid; + __entry->reason = reason; + ), + + TP_printk("nid=%d reason=%s", + __entry->nid, + __print_symbolic(__entry->reason, kswapd_clear_hopeless_reason_ops)) +); #endif /* _TRACE_VMSCAN_H */ /* This part must be outside protection */ diff --git a/mm/memory-tiers.c b/mm/memory-tiers.c index 7ec442776574..0ae8bec86346 100644 --- a/mm/memory-tiers.c +++ b/mm/memory-tiers.c @@ -955,7 +955,7 @@ static ssize_t demotion_enabled_store(struct kobject *kobj, struct pglist_data *pgdat; for_each_online_pgdat(pgdat) - atomic_set(&pgdat->kswapd_failures, 0); + kswapd_clear_hopeless(pgdat, KSWAPD_CLEAR_HOPELESS_OTHER); } return count; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index e779b18168de..2c70ba9d5cc6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2945,9 +2945,9 @@ static bool free_frozen_page_commit(struct zone *zone, * 'hopeless node' to stay in that state for a while. Let * kswapd work again by resetting kswapd_failures. */ - if (atomic_read(&pgdat->kswapd_failures) >= MAX_RECLAIM_RETRIES && + if (kswapd_test_hopeless(pgdat) && next_memory_node(pgdat->node_id) < MAX_NUMNODES) - atomic_set(&pgdat->kswapd_failures, 0); + kswapd_clear_hopeless(pgdat, KSWAPD_CLEAR_HOPELESS_PCP); } return ret; } diff --git a/mm/show_mem.c b/mm/show_mem.c index 3a4b5207635d..24078ac3e6bc 100644 --- a/mm/show_mem.c +++ b/mm/show_mem.c @@ -278,8 +278,7 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z #endif K(node_page_state(pgdat, NR_PAGETABLE)), K(node_page_state(pgdat, NR_SECONDARY_PAGETABLE)), - str_yes_no(atomic_read(&pgdat->kswapd_failures) >= - MAX_RECLAIM_RETRIES), + str_yes_no(kswapd_test_hopeless(pgdat)), K(node_page_state(pgdat, NR_BALLOON_PAGES))); } diff --git a/mm/vmscan.c b/mm/vmscan.c index 5d9b1bce6f01..1d281174164e 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -506,7 +506,7 @@ static bool skip_throttle_noprogress(pg_data_t *pgdat) * If kswapd is disabled, reschedule if necessary but do not * throttle as the system is likely near OOM. */ - if (atomic_read(&pgdat->kswapd_failures) >= MAX_RECLAIM_RETRIES) + if (kswapd_test_hopeless(pgdat)) return true; /* @@ -6437,7 +6437,7 @@ static bool allow_direct_reclaim(pg_data_t *pgdat) int i; bool wmark_ok; - if (atomic_read(&pgdat->kswapd_failures) >= MAX_RECLAIM_RETRIES) + if (kswapd_test_hopeless(pgdat)) return true; for_each_managed_zone_pgdat(zone, pgdat, i, ZONE_NORMAL) { @@ -6846,7 +6846,7 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, wake_up_all(&pgdat->pfmemalloc_wait); /* Hopeless node, leave it to direct reclaim */ - if (atomic_read(&pgdat->kswapd_failures) >= MAX_RECLAIM_RETRIES) + if (kswapd_test_hopeless(pgdat)) return true; if (pgdat_balanced(pgdat, order, highest_zoneidx)) { @@ -7111,8 +7111,11 @@ restart: * watermark_high at this point. We need to avoid increasing the * failure count to prevent the kswapd thread from stopping. */ - if (!sc.nr_reclaimed && !boosted) - atomic_inc(&pgdat->kswapd_failures); + if (!sc.nr_reclaimed && !boosted) { + int fail_cnt = atomic_inc_return(&pgdat->kswapd_failures); + /* kswapd context, low overhead to trace every failure */ + trace_mm_vmscan_kswapd_reclaim_fail(pgdat->node_id, fail_cnt); + } out: clear_reclaim_active(pgdat, highest_zoneidx); @@ -7371,7 +7374,7 @@ void wakeup_kswapd(struct zone *zone, gfp_t gfp_flags, int order, return; /* Hopeless node, leave it to direct reclaim if possible */ - if (atomic_read(&pgdat->kswapd_failures) >= MAX_RECLAIM_RETRIES || + if (kswapd_test_hopeless(pgdat) || (pgdat_balanced(pgdat, order, highest_zoneidx) && !pgdat_watermark_boosted(pgdat, highest_zoneidx))) { /* @@ -7391,9 +7394,11 @@ void wakeup_kswapd(struct zone *zone, gfp_t gfp_flags, int order, wake_up_interruptible(&pgdat->kswapd_wait); } -static void kswapd_clear_hopeless(pg_data_t *pgdat) +void kswapd_clear_hopeless(pg_data_t *pgdat, enum kswapd_clear_hopeless_reason reason) { - atomic_set(&pgdat->kswapd_failures, 0); + /* Only trace actual resets, not redundant zero-to-zero */ + if (atomic_xchg(&pgdat->kswapd_failures, 0)) + trace_mm_vmscan_kswapd_clear_hopeless(pgdat->node_id, reason); } /* @@ -7406,7 +7411,13 @@ void kswapd_try_clear_hopeless(struct pglist_data *pgdat, unsigned int order, int highest_zoneidx) { if (pgdat_balanced(pgdat, order, highest_zoneidx)) - kswapd_clear_hopeless(pgdat); + kswapd_clear_hopeless(pgdat, current_is_kswapd() ? + KSWAPD_CLEAR_HOPELESS_KSWAPD : KSWAPD_CLEAR_HOPELESS_DIRECT); +} + +bool kswapd_test_hopeless(pg_data_t *pgdat) +{ + return atomic_read(&pgdat->kswapd_failures) >= MAX_RECLAIM_RETRIES; } #ifdef CONFIG_HIBERNATION diff --git a/mm/vmstat.c b/mm/vmstat.c index 0f64c898f79f..23e176e1d09d 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1840,7 +1840,7 @@ static void zoneinfo_show_print(struct seq_file *m, pg_data_t *pgdat, "\n start_pfn: %lu" "\n reserved_highatomic: %lu" "\n free_highatomic: %lu", - atomic_read(&pgdat->kswapd_failures) >= MAX_RECLAIM_RETRIES, + kswapd_test_hopeless(pgdat), zone->zone_start_pfn, zone->nr_reserved_highatomic, zone->nr_free_highatomic); -- cgit v1.2.3 From 5eab8c588bf37b7eb498f23a2ac3fb135c258e17 Mon Sep 17 00:00:00 2001 From: Chen Ridong Date: Sat, 31 Jan 2026 03:05:09 +0000 Subject: cgroup: increase maximum subsystem count from 16 to 32 The current cgroup subsystem limit of 16 is insufficient, as the number of existing subsystems has already reached this limit. When adding a new subsystem that is not yet in the mainline kernel, building with `make allmodconfig` requires first bypassing the `BUILD_BUG_ON(CGROUP_SUBSYS_COUNT > 16)` restriction to allow compilation to succeed. However, the kernel still fails to boot afterward. This patch increases the maximum number of supported cgroup subsystems from 16 to 32, providing enough room for future subsystem additions. Signed-off-by: Chen Ridong Acked-by: Waiman Long Tested-by: JP Kobryn Acked-by: JP Kobryn Signed-off-by: Tejun Heo --- include/linux/cgroup-defs.h | 8 +++---- include/trace/events/cgroup.h | 2 +- kernel/cgroup/cgroup-internal.h | 8 +++---- kernel/cgroup/cgroup-v1.c | 12 +++++------ kernel/cgroup/cgroup.c | 46 ++++++++++++++++++++--------------------- kernel/cgroup/debug.c | 2 +- 6 files changed, 39 insertions(+), 39 deletions(-) (limited to 'include/trace/events') diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index b760a3c470a5..2c34e7aac9af 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -535,10 +535,10 @@ struct cgroup { * one which may have more subsystems enabled. Controller knobs * are made available iff it's enabled in ->subtree_control. */ - u16 subtree_control; - u16 subtree_ss_mask; - u16 old_subtree_control; - u16 old_subtree_ss_mask; + u32 subtree_control; + u32 subtree_ss_mask; + u32 old_subtree_control; + u32 old_subtree_ss_mask; /* Private pointers for each registered subsystem */ struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT]; diff --git a/include/trace/events/cgroup.h b/include/trace/events/cgroup.h index ba9229af9a34..b736da06340a 100644 --- a/include/trace/events/cgroup.h +++ b/include/trace/events/cgroup.h @@ -16,7 +16,7 @@ DECLARE_EVENT_CLASS(cgroup_root, TP_STRUCT__entry( __field( int, root ) - __field( u16, ss_mask ) + __field( u32, ss_mask ) __string( name, root->name ) ), diff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h index 22051b4f1ccb..3bfe37693d68 100644 --- a/kernel/cgroup/cgroup-internal.h +++ b/kernel/cgroup/cgroup-internal.h @@ -52,7 +52,7 @@ struct cgroup_fs_context { bool cpuset_clone_children; bool none; /* User explicitly requested empty subsystem */ bool all_ss; /* Seen 'all' option */ - u16 subsys_mask; /* Selected subsystems */ + u32 subsys_mask; /* Selected subsystems */ char *name; /* Hierarchy name */ char *release_agent; /* Path for release notifications */ }; @@ -146,7 +146,7 @@ struct cgroup_mgctx { struct cgroup_taskset tset; /* subsystems affected by migration */ - u16 ss_mask; + u32 ss_mask; }; #define CGROUP_TASKSET_INIT(tset) \ @@ -235,8 +235,8 @@ int cgroup_path_ns_locked(struct cgroup *cgrp, char *buf, size_t buflen, void cgroup_favor_dynmods(struct cgroup_root *root, bool favor); void cgroup_free_root(struct cgroup_root *root); void init_cgroup_root(struct cgroup_fs_context *ctx); -int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask); -int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask); +int cgroup_setup_root(struct cgroup_root *root, u32 ss_mask); +int rebind_subsystems(struct cgroup_root *dst_root, u32 ss_mask); int cgroup_do_get_tree(struct fs_context *fc); int cgroup_migrate_vet_dst(struct cgroup *dst_cgrp); diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index a9e029b570c8..724950c4b690 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -28,7 +28,7 @@ #define CGROUP_PIDLIST_DESTROY_DELAY HZ /* Controllers blocked by the commandline in v1 */ -static u16 cgroup_no_v1_mask; +static u32 cgroup_no_v1_mask; /* disable named v1 mounts */ static bool cgroup_no_v1_named; @@ -1037,13 +1037,13 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param) static int check_cgroupfs_options(struct fs_context *fc) { struct cgroup_fs_context *ctx = cgroup_fc2context(fc); - u16 mask = U16_MAX; - u16 enabled = 0; + u32 mask = U32_MAX; + u32 enabled = 0; struct cgroup_subsys *ss; int i; #ifdef CONFIG_CPUSETS - mask = ~((u16)1 << cpuset_cgrp_id); + mask = ~((u32)1 << cpuset_cgrp_id); #endif for_each_subsys(ss, i) if (cgroup_ssid_enabled(i) && !cgroup1_ssid_disabled(i) && @@ -1095,7 +1095,7 @@ int cgroup1_reconfigure(struct fs_context *fc) struct kernfs_root *kf_root = kernfs_root_from_sb(fc->root->d_sb); struct cgroup_root *root = cgroup_root_from_kf(kf_root); int ret = 0; - u16 added_mask, removed_mask; + u32 added_mask, removed_mask; cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp); @@ -1343,7 +1343,7 @@ static int __init cgroup_no_v1(char *str) continue; if (!strcmp(token, "all")) { - cgroup_no_v1_mask = U16_MAX; + cgroup_no_v1_mask = U32_MAX; continue; } diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 34d6d4d99f97..c86db0943b44 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -206,13 +206,13 @@ EXPORT_SYMBOL_GPL(cgrp_dfl_root); bool cgrp_dfl_visible; /* some controllers are not supported in the default hierarchy */ -static u16 cgrp_dfl_inhibit_ss_mask; +static u32 cgrp_dfl_inhibit_ss_mask; /* some controllers are implicitly enabled on the default hierarchy */ -static u16 cgrp_dfl_implicit_ss_mask; +static u32 cgrp_dfl_implicit_ss_mask; /* some controllers can be threaded on the default hierarchy */ -static u16 cgrp_dfl_threaded_ss_mask; +static u32 cgrp_dfl_threaded_ss_mask; /* The list of hierarchy roots */ LIST_HEAD(cgroup_roots); @@ -234,10 +234,10 @@ static u64 css_serial_nr_next = 1; * These bitmasks identify subsystems with specific features to avoid * having to do iterative checks repeatedly. */ -static u16 have_fork_callback __read_mostly; -static u16 have_exit_callback __read_mostly; -static u16 have_release_callback __read_mostly; -static u16 have_canfork_callback __read_mostly; +static u32 have_fork_callback __read_mostly; +static u32 have_exit_callback __read_mostly; +static u32 have_release_callback __read_mostly; +static u32 have_canfork_callback __read_mostly; static bool have_favordynmods __ro_after_init = IS_ENABLED(CONFIG_CGROUP_FAVOR_DYNMODS); @@ -475,13 +475,13 @@ static bool cgroup_is_valid_domain(struct cgroup *cgrp) } /* subsystems visibly enabled on a cgroup */ -static u16 cgroup_control(struct cgroup *cgrp) +static u32 cgroup_control(struct cgroup *cgrp) { struct cgroup *parent = cgroup_parent(cgrp); - u16 root_ss_mask = cgrp->root->subsys_mask; + u32 root_ss_mask = cgrp->root->subsys_mask; if (parent) { - u16 ss_mask = parent->subtree_control; + u32 ss_mask = parent->subtree_control; /* threaded cgroups can only have threaded controllers */ if (cgroup_is_threaded(cgrp)) @@ -496,12 +496,12 @@ static u16 cgroup_control(struct cgroup *cgrp) } /* subsystems enabled on a cgroup */ -static u16 cgroup_ss_mask(struct cgroup *cgrp) +static u32 cgroup_ss_mask(struct cgroup *cgrp) { struct cgroup *parent = cgroup_parent(cgrp); if (parent) { - u16 ss_mask = parent->subtree_ss_mask; + u32 ss_mask = parent->subtree_ss_mask; /* threaded cgroups can only have threaded controllers */ if (cgroup_is_threaded(cgrp)) @@ -1636,9 +1636,9 @@ static umode_t cgroup_file_mode(const struct cftype *cft) * This function calculates which subsystems need to be enabled if * @subtree_control is to be applied while restricted to @this_ss_mask. */ -static u16 cgroup_calc_subtree_ss_mask(u16 subtree_control, u16 this_ss_mask) +static u32 cgroup_calc_subtree_ss_mask(u32 subtree_control, u32 this_ss_mask) { - u16 cur_ss_mask = subtree_control; + u32 cur_ss_mask = subtree_control; struct cgroup_subsys *ss; int ssid; @@ -1647,7 +1647,7 @@ static u16 cgroup_calc_subtree_ss_mask(u16 subtree_control, u16 this_ss_mask) cur_ss_mask |= cgrp_dfl_implicit_ss_mask; while (true) { - u16 new_ss_mask = cur_ss_mask; + u32 new_ss_mask = cur_ss_mask; do_each_subsys_mask(ss, ssid, cur_ss_mask) { new_ss_mask |= ss->depends_on; @@ -1851,12 +1851,12 @@ err: return ret; } -int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask) +int rebind_subsystems(struct cgroup_root *dst_root, u32 ss_mask) { struct cgroup *dcgrp = &dst_root->cgrp; struct cgroup_subsys *ss; int ssid, ret; - u16 dfl_disable_ss_mask = 0; + u32 dfl_disable_ss_mask = 0; lockdep_assert_held(&cgroup_mutex); @@ -2152,7 +2152,7 @@ void init_cgroup_root(struct cgroup_fs_context *ctx) set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags); } -int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask) +int cgroup_setup_root(struct cgroup_root *root, u32 ss_mask) { LIST_HEAD(tmp_links); struct cgroup *root_cgrp = &root->cgrp; @@ -3134,7 +3134,7 @@ void cgroup_procs_write_finish(struct task_struct *task, put_task_struct(task); } -static void cgroup_print_ss_mask(struct seq_file *seq, u16 ss_mask) +static void cgroup_print_ss_mask(struct seq_file *seq, u32 ss_mask) { struct cgroup_subsys *ss; bool printed = false; @@ -3499,9 +3499,9 @@ static void cgroup_finalize_control(struct cgroup *cgrp, int ret) cgroup_apply_control_disable(cgrp); } -static int cgroup_vet_subtree_control_enable(struct cgroup *cgrp, u16 enable) +static int cgroup_vet_subtree_control_enable(struct cgroup *cgrp, u32 enable) { - u16 domain_enable = enable & ~cgrp_dfl_threaded_ss_mask; + u32 domain_enable = enable & ~cgrp_dfl_threaded_ss_mask; /* if nothing is getting enabled, nothing to worry about */ if (!enable) @@ -3544,7 +3544,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { - u16 enable = 0, disable = 0; + u32 enable = 0, disable = 0; struct cgroup *cgrp, *child; struct cgroup_subsys *ss; char *tok; @@ -6350,7 +6350,7 @@ int __init cgroup_init(void) struct cgroup_subsys *ss; int ssid; - BUILD_BUG_ON(CGROUP_SUBSYS_COUNT > 16); + BUILD_BUG_ON(CGROUP_SUBSYS_COUNT > 32); BUG_ON(cgroup_init_cftypes(NULL, cgroup_base_files)); BUG_ON(cgroup_init_cftypes(NULL, cgroup_psi_files)); BUG_ON(cgroup_init_cftypes(NULL, cgroup1_base_files)); diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c index 81ea38dd6f9d..a5490097fe52 100644 --- a/kernel/cgroup/debug.c +++ b/kernel/cgroup/debug.c @@ -230,7 +230,7 @@ static int cgroup_subsys_states_read(struct seq_file *seq, void *v) } static void cgroup_masks_read_one(struct seq_file *seq, const char *name, - u16 mask) + u32 mask) { struct cgroup_subsys *ss; int ssid; -- cgit v1.2.3 From daafcc0ef0b358d9d622b6e3b7c43767aa3814ee Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Fri, 30 Jan 2026 21:22:15 +0530 Subject: tracing/dma: Cap dma_map_sg tracepoint arrays to prevent buffer overflow The dma_map_sg tracepoint can trigger a perf buffer overflow when tracing large scatter-gather lists. With devices like virtio-gpu creating large DRM buffers, nents can exceed 1000 entries, resulting in: phys_addrs: 1000 * 8 bytes = 8,000 bytes dma_addrs: 1000 * 8 bytes = 8,000 bytes lengths: 1000 * 4 bytes = 4,000 bytes Total: ~20,000 bytes This exceeds PERF_MAX_TRACE_SIZE (8192 bytes), causing: WARNING: CPU: 0 PID: 5497 at kernel/trace/trace_event_perf.c:405 perf buffer not large enough, wanted 24620, have 8192 Cap all three dynamic arrays at 128 entries using min() in the array size calculation. This ensures arrays are only as large as needed (up to the cap), avoiding unnecessary memory allocation for small operations while preventing overflow for large ones. The tracepoint now records the full nents/ents counts and a truncated flag so users can see when data has been capped. Changes in v2: - Use min(nents, DMA_TRACE_MAX_ENTRIES) for dynamic array sizing instead of fixed DMA_TRACE_MAX_ENTRIES allocation (feedback from Steven Rostedt) - This allocates only what's needed up to the cap, avoiding waste for small operations Reported-by: syzbot+28cea38c382fd15e751a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=28cea38c382fd15e751a Tested-by: syzbot+28cea38c382fd15e751a@syzkaller.appspotmail.com Signed-off-by: Deepanshu Kartikey Reviwed-by: Sean Anderson Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20260130155215.69737-1-kartikey406@gmail.com --- include/trace/events/dma.h | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'include/trace/events') diff --git a/include/trace/events/dma.h b/include/trace/events/dma.h index b3fef140ae15..33e99e792f1a 100644 --- a/include/trace/events/dma.h +++ b/include/trace/events/dma.h @@ -275,6 +275,8 @@ TRACE_EVENT(dma_free_sgt, sizeof(u64), sizeof(u64))) ); +#define DMA_TRACE_MAX_ENTRIES 128 + TRACE_EVENT(dma_map_sg, TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents, int ents, enum dma_data_direction dir, unsigned long attrs), @@ -282,9 +284,12 @@ TRACE_EVENT(dma_map_sg, TP_STRUCT__entry( __string(device, dev_name(dev)) - __dynamic_array(u64, phys_addrs, nents) - __dynamic_array(u64, dma_addrs, ents) - __dynamic_array(unsigned int, lengths, ents) + __field(int, full_nents) + __field(int, full_ents) + __field(bool, truncated) + __dynamic_array(u64, phys_addrs, min(nents, DMA_TRACE_MAX_ENTRIES)) + __dynamic_array(u64, dma_addrs, min(ents, DMA_TRACE_MAX_ENTRIES)) + __dynamic_array(unsigned int, lengths, min(ents, DMA_TRACE_MAX_ENTRIES)) __field(enum dma_data_direction, dir) __field(unsigned long, attrs) ), @@ -292,11 +297,16 @@ TRACE_EVENT(dma_map_sg, TP_fast_assign( struct scatterlist *sg; int i; + int traced_nents = min_t(int, nents, DMA_TRACE_MAX_ENTRIES); + int traced_ents = min_t(int, ents, DMA_TRACE_MAX_ENTRIES); __assign_str(device); - for_each_sg(sgl, sg, nents, i) + __entry->full_nents = nents; + __entry->full_ents = ents; + __entry->truncated = (nents > DMA_TRACE_MAX_ENTRIES) || (ents > DMA_TRACE_MAX_ENTRIES); + for_each_sg(sgl, sg, traced_nents, i) ((u64 *)__get_dynamic_array(phys_addrs))[i] = sg_phys(sg); - for_each_sg(sgl, sg, ents, i) { + for_each_sg(sgl, sg, traced_ents, i) { ((u64 *)__get_dynamic_array(dma_addrs))[i] = sg_dma_address(sg); ((unsigned int *)__get_dynamic_array(lengths))[i] = @@ -306,9 +316,12 @@ TRACE_EVENT(dma_map_sg, __entry->attrs = attrs; ), - TP_printk("%s dir=%s dma_addrs=%s sizes=%s phys_addrs=%s attrs=%s", + TP_printk("%s dir=%s nents=%d/%d ents=%d/%d%s dma_addrs=%s sizes=%s phys_addrs=%s attrs=%s", __get_str(device), decode_dma_data_direction(__entry->dir), + min_t(int, __entry->full_nents, DMA_TRACE_MAX_ENTRIES), __entry->full_nents, + min_t(int, __entry->full_ents, DMA_TRACE_MAX_ENTRIES), __entry->full_ents, + __entry->truncated ? " [TRUNCATED]" : "", __print_array(__get_dynamic_array(dma_addrs), __get_dynamic_array_len(dma_addrs) / sizeof(u64), sizeof(u64)), -- cgit v1.2.3 From 2002286e68c964d25826943d77edcf95cf38d6e1 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 3 Feb 2026 19:41:20 +0100 Subject: trace: mptcp: add mptcp_rcvbuf_grow tracepoint Similar to tcp, provide a new tracepoint to better understand mptcp_rcv_space_adjust() behavior, which presents many artifacts. Note that the used format string is so long that I preferred wrap it, contrary to guidance for quoted strings. Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260203-net-next-mptcp-misc-feat-6-20-v1-4-31ec8bfc56d1@kernel.org Signed-off-by: Jakub Kicinski --- include/trace/events/mptcp.h | 80 ++++++++++++++++++++++++++++++++++++++++++++ net/mptcp/protocol.c | 3 ++ 2 files changed, 83 insertions(+) (limited to 'include/trace/events') diff --git a/include/trace/events/mptcp.h b/include/trace/events/mptcp.h index 085b749cdd97..269d949b2025 100644 --- a/include/trace/events/mptcp.h +++ b/include/trace/events/mptcp.h @@ -5,7 +5,13 @@ #if !defined(_TRACE_MPTCP_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_MPTCP_H +#include +#include #include +#include +#include +#include +#include #define show_mapping_status(status) \ __print_symbolic(status, \ @@ -178,6 +184,80 @@ TRACE_EVENT(subflow_check_data_avail, __entry->skb) ); +#include + +TRACE_EVENT(mptcp_rcvbuf_grow, + + TP_PROTO(struct sock *sk, int time), + + TP_ARGS(sk, time), + + TP_STRUCT__entry( + __field(int, time) + __field(__u32, rtt_us) + __field(__u32, copied) + __field(__u32, inq) + __field(__u32, space) + __field(__u32, ooo_space) + __field(__u32, rcvbuf) + __field(__u32, rcv_wnd) + __field(__u8, scaling_ratio) + __field(__u16, sport) + __field(__u16, dport) + __field(__u16, family) + __array(__u8, saddr, 4) + __array(__u8, daddr, 4) + __array(__u8, saddr_v6, 16) + __array(__u8, daddr_v6, 16) + __field(const void *, skaddr) + ), + + TP_fast_assign( + struct mptcp_sock *msk = mptcp_sk(sk); + struct inet_sock *inet = inet_sk(sk); + bool ofo_empty; + __be32 *p32; + + __entry->time = time; + __entry->rtt_us = msk->rcvq_space.rtt_us >> 3; + __entry->copied = msk->rcvq_space.copied; + __entry->inq = mptcp_inq_hint(sk); + __entry->space = msk->rcvq_space.space; + ofo_empty = RB_EMPTY_ROOT(&msk->out_of_order_queue); + __entry->ooo_space = ofo_empty ? 0 : + MPTCP_SKB_CB(msk->ooo_last_skb)->end_seq - + msk->ack_seq; + + __entry->rcvbuf = sk->sk_rcvbuf; + __entry->rcv_wnd = atomic64_read(&msk->rcv_wnd_sent) - + msk->ack_seq; + __entry->scaling_ratio = msk->scaling_ratio; + __entry->sport = ntohs(inet->inet_sport); + __entry->dport = ntohs(inet->inet_dport); + __entry->family = sk->sk_family; + + p32 = (__be32 *)__entry->saddr; + *p32 = inet->inet_saddr; + + p32 = (__be32 *)__entry->daddr; + *p32 = inet->inet_daddr; + + TP_STORE_ADDRS(__entry, inet->inet_saddr, inet->inet_daddr, + sk->sk_v6_rcv_saddr, sk->sk_v6_daddr); + + __entry->skaddr = sk; + ), + + TP_printk("time=%u rtt_us=%u copied=%u inq=%u space=%u ooo=%u scaling_ratio=%u " + "rcvbuf=%u rcv_wnd=%u family=%d sport=%hu dport=%hu saddr=%pI4 " + "daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c skaddr=%p", + __entry->time, __entry->rtt_us, __entry->copied, + __entry->inq, __entry->space, __entry->ooo_space, + __entry->scaling_ratio, __entry->rcvbuf, __entry->rcv_wnd, + __entry->family, __entry->sport, __entry->dport, + __entry->saddr, __entry->daddr, __entry->saddr_v6, + __entry->daddr_v6, __entry->skaddr) +); #endif /* _TRACE_MPTCP_H */ /* This part must be outside protection */ diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 514272179714..197ee169c0bc 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -28,6 +28,8 @@ #include "protocol.h" #include "mib.h" +static unsigned int mptcp_inq_hint(const struct sock *sk); + #define CREATE_TRACE_POINTS #include @@ -2133,6 +2135,7 @@ static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied) if (msk->rcvq_space.copied <= msk->rcvq_space.space) goto new_measure; + trace_mptcp_rcvbuf_grow(sk, time); if (mptcp_rcvbuf_grow(sk, msk->rcvq_space.copied)) { /* Make subflows follow along. If we do not do this, we * get drops at subflow level if skbs can't be moved to -- cgit v1.2.3