3.10 内核 BUG_ON() at xfs_vm_writepage() -> page_buffers()
目录
前言
问题分析
page buffers创建
page buffers丢失
Write-Protect
Dirty Page w/o Buffers
问题解决
前言
这个问题发生在3.10.0-514.el7上,并且在RHEL的知识库中快速找到了对应的案例以及解决方案,但是,理解问题如何发生和解决则着实费了些功夫。
RHEL的链接如下:
RHEL7: kernel crash in xfs_vm_writepage - kernel BUG at fs/xfs/xfs_aops.c:1062! - Red Hat Customer Portal
调用栈为:
[1004630.854317] kernel BUG at fs/xfs/xfs_aops.c:1062!
[1004630.854894] invalid opcode: 0000 [#1] SMP
[1004630.861333] CPU: 6 PID: 56715 Comm: kworker/u48:4 Tainted: G W ------------ 3.10.0-514.el7.x86_64 #1
[1004630.862046] Hardware name: HP ProLiant BL460c Gen9, BIOS I36 12/28/2015
[1004630.862703] Workqueue: writeback bdi_writeback_workfn (flush-253:28)
[1004630.863414] task: ffff881f8436de20 ti: ffff881f23a4c000 task.ti: ffff881f23a4c000
[1004630.864117] RIP: 0010:[<ffffffffa083f2fb>] [<ffffffffa083f2fb>] xfs_vm_writepage+0x58b/0x5d0 [xfs]
[1004630.864860] RSP: 0018:ffff881f23a4f948 EFLAGS: 00010246
[1004630.865749] RAX: 002fffff00040029 RBX: ffff881bedd50308 RCX: 000000000000000c
[1004630.866466] RDX: 0000000000000008 RSI: ffff881f23a4fc40 RDI: ffffea00296b7800
[1004630.867218] RBP: ffff881f23a4f9f0 R08: fffffffffffffffe R09: 000000000001a098
[1004630.867941] R10: ffff88207ffd6000 R11: 0000000000000000 R12: ffff881bedd50308
[1004630.868656] R13: ffff881f23a4fc40 R14: ffff881bedd501b8 R15: ffffea00296b7800
[1004630.869399] FS: 0000000000000000(0000) GS:ffff881fff180000(0000) knlGS:0000000000000000
[1004630.870147] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[1004630.870868] CR2: 0000000000eb3d30 CR3: 0000001ff79dc000 CR4: 00000000001407e0
[1004630.871610] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[1004630.872349] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[1004630.873072] Stack:
[1004630.873749] 0000000000008000 ffff880070b03644 ffff881f23a4fc40 ffff881f23a4fa68
[1004630.874480] ffff881f23a4fa80 ffffea00296b7800 0000000000001000 000000000000000e
[1004630.875223] 0000000000001000 ffffffff81180981 0000000000000000 ffff881bedd50310
[1004630.875957] Call Trace:
[1004630.876665] [<ffffffff81180981>] ? find_get_pages_tag+0xe1/0x1a0
[1004630.877417] [<ffffffff8118b3b3>] __writepage+0x13/0x50
[1004630.878173] [<ffffffff8118bed1>] write_cache_pages+0x251/0x4d0
[1004630.878915] [<ffffffffa00c170a>] ? enqueue_cmd_and_start_io+0x3a/0x40 [hpsa]
[1004630.879626] [<ffffffff8118b3a0>] ? global_dirtyable_memory+0x70/0x70
[1004630.880368] [<ffffffff8118c19d>] generic_writepages+0x4d/0x80
[1004630.881157] [<ffffffffa083e063>] xfs_vm_writepages+0x53/0x90 [xfs]
[1004630.881907] [<ffffffff8118d24e>] do_writepages+0x1e/0x40
[1004630.882643] [<ffffffff81228730>] __writeback_single_inode+0x40/0x210
[1004630.883403] [<ffffffff8122941e>] writeback_sb_inodes+0x25e/0x420
[1004630.884141] [<ffffffff8122967f>] __writeback_inodes_wb+0x9f/0xd0
[1004630.884863] [<ffffffff81229ec3>] wb_writeback+0x263/0x2f0
[1004630.885610] [<ffffffff810ab776>] ? set_worker_desc+0x86/0xb0
[1004630.886378] [<ffffffff8122bd05>] bdi_writeback_workfn+0x115/0x460
[1004630.887142] [<ffffffff810c4cf8>] ? try_to_wake_up+0x1c8/0x330
[1004630.887875] [<ffffffff810a7f3b>] process_one_work+0x17b/0x470
[1004630.888638] [<ffffffff810a8d76>] worker_thread+0x126/0x410
[1004630.889389] [<ffffffff810a8c50>] ? rescuer_thread+0x460/0x460
[1004630.890126] [<ffffffff810b052f>] kthread+0xcf/0xe0
[1004630.890816] [<ffffffff810b0460>] ? kthread_create_on_node+0x140/0x140
[1004630.891521] [<ffffffff81696418>] ret_from_fork+0x58/0x90
[1004630.892229] [<ffffffff810b0460>] ? kthread_create_on_node+0x140/0x140
[1004630.892877] Code: e0 80 3d 4d b4 06 00 00 0f 85 a4 fe ff ff be d7 03 00 00 48 c7 c7 4a e0 88 a0 e8 61 66 84 e0 c6 05 2f b4 06 00 01 e9 87 fe ff ff <0f> 0b 8b 4d a4 e9 e8 fb ff ff 41 b9 01 00 00 00 e9 69 fd ff ff
[1004630.894245] RIP [<ffffffffa083f2fb>] xfs_vm_writepage+0x58b/0x5d0 [xfs]
[1004630.894890] RSP <ffff881f23a4f94
问题发生的位置为:
xfs_vm_writepage()
---...bh = head = page_buffers(page);...
---#define page_buffers(page) \({ \BUG_ON(!PagePrivate(page)); \((struct buffer_head *)page_private(page)); \})
问题分析
page buffers创建
在Linux内核中,buffer head的目的是为了对接在存储子系统和内存子系统的两个基本单位:
- sector,这是存储的基本单位,512字节
- page,这个是内存的基本单位,4096字节
文件缓存,即page cache,是存储子系统和内存子系统的结合部,buffer_head对应的就是page cache中的一个sector;当我们格式化文件系统,把fsblock设置为512、1024、2048字节时,或者操作raw block设备时,每个page就会对应多个buffer_head;
近些年,512字节的fsblock基本被主流文件系统抛弃,虽然还支持,但是都以支持4K为主要的优化方向,xfs甚至抛弃了buffer_head,直接使用page作为IO操作的基本单位;
那么,buffer_head都是在哪些契机被创建呢?
3.10.0-514.el7
【A】
generic_perform_write()-> aops->write_begin()xfs_vm_write_begin()-> grab_cache_page_write_begin()-> __block_write_begin()-> create_page_buffers()
【B】
do_shared_fault()-> __do_fault()-> vma->vm_ops->page_mkwrite()xfs_filemap_page_mkwrite()-> __block_page_mkwrite()-> __block_write_begin()-> create_page_buffers()do_mpage_readpage() -> block_read_full_page() //如果page中各个bh的状态不一致,不如有些map有些unmap,会进入到此路径,分别对bh进行操作-> create_page_buffers()
通常来讲【A】和【B】路径可以保证,在对page做写操作之前,保证page buffers已经创建,它们分别代表是通过系统调用和mmap对文件进行写的场景;
page buffers丢失
那本问题中,被设置了dirty标记的page的buffer是如何丢失的呢?
page buffer被释放的典型场景:
3.10.0-514.el7shrink_page_list()-> try_to_unmap()-> try_to_release_page()-> aops->releasepage()xfs_vm_releasepage()-> try_to_free_buffers()-> __remove_mapping()
在try_to_release_page()之前,try_to_unmap()会被调用,它会清理掉pte,并且检测是否需要page dirty
3.10.0-514.el7try_to_unmap()-> try_to_unmap_file()-> try_to_unmap_one()-> set_page_dirty() //pte_dirty()
这样就可以保证,在执行try_to_release_page()之前,给page及其buffer设置dirty 标记,
3.10.0-514.el7xfs_vm_set_page_dirty()
---spin_lock(&mapping->private_lock);if (page_has_buffers(page)) {struct buffer_head *head = page_buffers(page);struct buffer_head *bh = head;do {if (offset < end_offset)set_buffer_dirty(bh);bh = bh->b_this_page;offset += 1 << inode->i_blkbits;} while (bh != head);}
---try_to_free_buffers()-> drop_buffers()-> buffer_busy()-> atomic_read(&bh->b_count) | (bh->b_state & ((1 << BH_Dirty) | (1 << BH_Lock)))
如果buffer有dirty标记就不会被释放。另外,有truncate和invalidate的场景,也是类似的操作。
但是有一个机器特殊的场景:
3.10.0-514.el7shrink_active_list()
---if (unlikely(buffer_heads_over_limit)) {if (page_has_private(page) && trylock_page(page)) {if (page_has_private(page))try_to_release_page(page, 0);unlock_page(page);}}
---
这里对buffers直接进行释放。
Write-Protect
用对mmap的page的写操作,是通过下面的机器触发的page fault并给page设置dirty的
3.10.0-514.el7generic_writepages()-> clear_page_dirty_for_io()-> page_mkclean()-> page_mkclean_file()-> page_mkclean_one()-> pte_wrprotect()-> pte_mkclean()handle_pte_fault()-> do_wp_page() // pte_present() && !pte_write()-> wp_page_shared()-> do_page_mkwrite()-> xfs_filemap_page_mkwrite()-> __block_page_mkwrite()-> lock_page()-> __block_write_begin()-> block_commit_write()-> set_page_dirty(page);-> wait_for_stable_page(page);-> wp_page_reuse()-> set_page_dirty()-> unlock_page()
在执行writepage之前,在page_lock的保护之下,通过clean_page_dirty_for_io()清除page的dirty flags以及mmap的pte的写权限,将相关page变为write-protect,这样,下次用户写这个page的时候,就会触发pagefault,内核在这里将相关的page设置为dirty,在此期间,会给page创建buffers;这样,就可以保证任何对mmap的写操作,都可以通过page fault提交到writeback子系统中。
write-protect page fault发生时,写操作还没有发生,所以,dirty bit并不会被设置;而一旦写操作发生,那么上面的代码所代表的过程,一定会发生,那么buffer也一定是具备的。
Dirty Page w/o Buffers
这里我们对比下ext4和xfs的writepages操作的调用栈:
ext4_writepages()-> mpage_prepare_extent_to_map()-> pagevec_lookup_tag()-> lock_page()-> wait_on_page_writeback()-> mpage_process_page_bufs()-> mpage_submit_page()-> clear_page_dirty_for_io(page)-> ext4_bio_write_page()-> set_page_writeback()-> io_submit_add_bh()-> clean_buffer_dirty()-> unlock_page()xfs_vm_writepages()-> generic_writepages()-> write_cache_pages()-> pagevec_lookup_tag()-> lock_page()-> xfs_vm_writepage()-> lock_buffer()-> xfs_add_to_ioend()-> xfs_start_page_writeback()-> clear_page_dirty_for_io()-> set_page_writeback()-> unlock_page() <<-----------------------HERE!!!! -> xfs_submit_ioend()-> xfs_start_page_writeback()-> mark_buffer_async_write()-> set_buffer_uptodate()-> clear_buffer_dirty()
在ext4调用栈中,Page Dirty和Buffer Dirty的清理都是在page_lock下进行的;而xfs中,buffer的清理是在page_lock之外,这时,我们如果引入page fault过程中的page_mkwrite调用链,就会产生以下竞态,
writeback workqueue user page fault()
xfs_vm_writepages() xfs_filemap_page_mkwrite()
lock_page() __block_page_mkwrite()
clear_page_dirty_for_io()
unlock_page()lock_page()xfs_vm_set_page_dirty()set_buffer_dirty()TestSetPageDirty()
clear_buffer_dirty()end_page_writeback()
于是这里,我们得到了一个page有dirty flags,但是buffer全是clean的;如果将此场景带入到ext4,就不会有这种问题,因为有page_lock的保护,最终的结果,要么是page buffer全部dirty,要是全是clean。
到这一步,产生了两个关键点:
- page dirty + buffer clean
- dirty bit,因为write-protect page fault已经发生过,所以,写操作已经完成
其中page dirty + buffer clean将继续推进问题的发生;
我们再回到shrink_active_list(),它可能会调用try_to_release_page(),
try_to_free_buffers()-> drop_buffers()-> buffer_busy() // dirty or lock-> cancel_dirty_page()
page dirty + buffer clean,在这里,因为buffer是clean的,所以,它可以被释放,然后page的dirty也被清除了;
但是,此时pte中的dirty bit是存在的,于是在后续的shrink_page_list()中:
tshrink_page_list()-> try_to_unmap()-> try_to_unmap_file()-> try_to_unmap_one()-> set_page_dirty() //pte_dirty()
page被设置dirty,然后回收中止;得到了一个page dirty + no buffers
所以,问题的关键是,xfs_vm_writepage()中,对page dirty和buffer dirty的clean操作并没有在page_lock的保护下。
问题解决
在搜索社区代码和Commit记录之后,该问题在以下commit解决:
commit e10de3723c53378e7cf441529f563c316fdc0dd3
Author: Dave Chinner <dchinner@redhat.com>
Date: Mon Feb 15 17:23:12 2016 +1100xfs: don't chain ioends during writepage submission@@ -565,6 +539,7 @@ xfs_add_to_ioend(bh->b_private = NULL;wpc->ioend->io_size += bh->b_size;wpc->last_block = bh->b_blocknr;
+ xfs_start_buffer_writeback(bh);}
在该修改之后,buffer clean操作也放到了page lock之下。
相关文章:
3.10 内核 BUG_ON() at xfs_vm_writepage() -> page_buffers()
目录 前言 问题分析 page buffers创建 page buffers丢失 Write-Protect Dirty Page w/o Buffers 问题解决 前言 这个问题发生在3.10.0-514.el7上,并且在RHEL的知识库中快速找到了对应的案例以及解决方案,但是,理解问题如何发生和解决…...

CrystalDiskInfo:硬盘健康监测工具简介和下载
原论坛给你更好的阅读体验:CrystalDiskInfo:硬盘健康监测工具简介和下载 | 波波论坛 引言 在日常使用电脑时,硬盘的健康状态对于系统的稳定性和数据的安全性至关重要。硬盘出现故障可能会导致数据丢失,严重时甚至会使整个系统无…...

Flink cdc同步增量数据timestamp字段相差八小时(分析|解决)不是粘贴复制的!
问题 我使用flink cdc同步mysql到mysql遇到了timestamp字段缺少八小时的问题。很少无语,flink ,cdc,debezium时区都设置了,没有任何效果! 分析 问题出现在mysql binlog身上!!! 因为默认mysql会使用UTC来…...

【docker】9. 镜像操作与实战
镜像操作案例 查找镜像 docker search busybox下载镜像 docker pull busybox:1.36.0查看镜像及列表存储位置 rootLAPTOP-H2EI4I6A:~# docker images busybox REPOSITORY TAG IMAGE ID CREATED SIZE busybox latest 517b897a6a83 2 months a…...
js-显示转换(强制转换)与隐式转换,==与===区别
1.显示转换(强制转换)与隐式转换 1.1显示转换 常见的JavaScript强制转换示例。 (1) 一元加号、一元减号- 值是布尔值,true将被转换为1,false将被转换为0。 let a "123"; let b a; // b的值为123,类型为Nu…...

【通俗理解】步长和学习率在神经网络中是一回事吗?
【通俗理解】步长和学习率在神经网络中是一回事吗? 【核心结论】 步长(Step Size)和学习率(Learning Rate, LR)在神经网络中并不是同一个概念,但它们都关乎模型训练过程中的参数更新。 【通俗解释&#x…...

【PTA】【数据库】【SQL命令】编程题2
数据库SQL命令测试题2 测试题目录 10-1 查询“李琳”老师所授课程的课程名称10-2 查询成绩比所有课程的平均成绩高的学生的学号及成绩10-3 创建带表达式的视图StuView10-4 从视图PerView中查询数据10-5 查询工资高于在“HR”部门工作的所有员工的工资的员工信息10-6 查询选修的…...
Spring Boot林业产品推荐系统:用户指南
摘 要 网络技术和计算机技术发展至今,已经拥有了深厚的理论基础,并在现实中进行了充分运用,尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代,所以对于信息的宣传和管理就很关键。因此林业产品销售信…...
【Conda 】Conda 配置文件详解:优化你的包管理与环境设置
目录 引言一、什么是 .condarc 文件?二、.condarc 文件的详细解析与优化2.1 SSL 验证2.2 设置 Conda 下载源2.3 设置环境和包存储路径2.4 代理服务器设置2.5 连接超时设置2.6 显示频道 URL2.7 包版本与构建选择2.8 环境依赖性管理2.9 禁用默认包版本2.10 Conda 配置…...

win10中使用ffmpeg的filter滤镜
1 给视频加文字水印 1.1 添加播放时间 ffmpeg -i input.mp4 -vf "drawtextfontfileC\\:/Windows/fonts/consola.ttf:fontsize30:fontcolorwhite:timecode00\:00\:00\:00:rate25:textTCR\::boxcolor0x000000AA:box1:x20:y20" -y output.mp4 在视频的x20:y20位置添加t…...

设计模式 外观模式 门面模式
结构性模式-外观模式 门面模式 适用场景:如果你需要一个指向复杂子系统的直接接口, 且该接口的功能有限, 则可以使用外观模式。 不用关心后面的查询具体操作 /*** 聚合查询接口*/ RestController RequestMapping("/search") Slf…...

Prophet时间序列算法总结及python实现案例
目录 一、prophet理论总结二、python导入模块方式三、python实现案例3.1帮助信息3.2 案例 四、参考学习 一、prophet理论总结 prophet模型是facebook开源的一个时间序列预测算法。[1][2],该算法主要为处理具有周期性、趋势变化以及缺失值和异常值的时间序列数据而设…...

远程调用 rpc 、 open feign
在学习黑马 springcloud 视频的时候,看到 open feign 使用, 就是 http 封装。 spring框架三部曲,导入依赖,加配置,使用api。...
Redis的几种持久化方式
Redis 提供了两种主要的持久化方式,它们分别是: 1. RDB(Redis Database Snapshotting) RDB 是 Redis 的一种数据持久化方式,它会在指定的时间间隔内对 Redis 中的数据进行快照并保存到硬盘上。 特点: 触…...

论文笔记(五十九)A survey of robot manipulation in contact
A survey of robot manipulation in contact 文章概括摘要1. 引言解释柔顺性控制的概念:应用实例: 2. 需要接触操控的任务2.1 环境塑造2.2 工件对齐2.3 关节运动2.4 双臂接触操控 3. 接触操控中的控制3.1 力控制3.2 阻抗控制3.3 顺应控制 4. 接触操控中的…...

c#控制台程序26-30
26.寻找并输出11至999之间的数m,它满足m,m2和m3均为回文数。所谓回文数是指其各位数字左右对称的整数,例如121,676,94249等。满足上述条件的数如m11,m2121,m31331皆为回文数。请编制函数实现此功能,如果是回文数&#…...
环形链表系列导学
问题描述 给定一个单链表,可能存在一个环。我们的目标是找到环的入口节点,即从这个节点开始,链表进入循环。如果没有环,则返回 null。 将链表问题转化为数学问题 状态序列与循环 我们可以将链表节点视为状态,每个节点的 next 指针代表状态转移函数 f f f。从头节点开始,我…...

IDEA2024创建一个spingboot项目
以下是创建一个基本的 Spring Boot 项目的步骤和示例: 初始化一个springboot工程其实有许多方法,笔者这里挑了一个最快捷的方式搭建一个项目。我们直接通过官方平台(start.spring.io)进行配置,然后下载压缩包就可以获取…...

Nginx:ssl
目录 部署ssl前提 nginx部署ssl证书 部署ssl部署建议 部署ssl前提 网站有域名根据域名申请到ssl证书,并下载证书部署到nginx中 部署了ssl证书后,访问的流量是加密的。 nginx部署ssl证书 #80端口跳转到443 server {listen 80;return 302 https://1…...
QT配置文件详解
TEMPLATElib TEMPLATE变量用于指定项目模板类型,其值可以是以下几种: app:建立一个应用程序的makefile,这是默认值。lib:建立一个库的makefile。vcapp:建立一个应用程序的Visual Studio项目文件。vclib&a…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...