from:http://blog.donghao.org/2013/03/20/%E4%BF%AE%E5%A4%8Dext4%E6%97%A5%E5%BF%97%EF%BC%88jbd2%EF%BC%89bug/

生产上报来了内核bug:mysql在做reset master时内核整个panic了。

DBA同学非常热心的帮忙找到了重新步骤:就是一个地雷一样的文件,只要open它,再fdatasync,kernel就panic。
从panic的代码位置看,就是 jbd2_journal_commit_transaction() 里的
J_ASSERT(journal->j_running_transaction != NULL);
判断失败触发panic

但是,为什么jbd2在没有running_transaction的时候也会提交事务?那就只能把所有唤醒kjournald2内核线程(里面调用了jbd2_journal_commit_transaction)的地方——即wake_up(&journal->j_wait_commit)处都加上trace,由于重现步骤是现成的,很快就定位到了原因:open一个文件再直接fdatasync的时候,会调用ext4_sync_file ,里面调用jbd2_log_start_commit开始提交jbd2的日志,jbd2_log_start_commit里会加锁然后调用__jbd2_log_start_commit,代码如下:

int __jbd2_log_start_commit(journal_t *journal, tid_t target)
{
/*
* Are we already doing a recent enough commit?
*/
if (!tid_geq(journal->j_commit_request, target)) {
/*
* We want a new commit: OK, mark the request and wakup the
* commit thread. We do _not_ do the commit ourselves.
*/
journal->j_commit_request = target;
jbd_debug(1, "JBD: requesting commit %d/%d\n",
journal->j_commit_request,
journal->j_commit_sequence);
wake_up(&journal->j_wait_commit);
return 1;
}
return 0;
}

从trace的结果看,journal->j_commit_request的值为2177452108,而target的值为0,看上去j_commit_request显然比target小,应该不会走到if判断里面去,但是实际上是走了的,因为tid_geq的实现是:

static inline int tid_geq(tid_t x, tid_t y)
{
int difference = (x - y);
return (difference >= 0);
}

unsigned int型2177452108减去0然后转为int型,猜猜结果是多少?等于 -2117515188 !看上去好像tid_geq的实现又罗嗦又奇怪,于是翻了一下注释,才发现,jbd2给每个transaction一个tid,这个tid是不断增长的,而它又是个unsigned int型,所以容易溢出,于是弄出来这么一个tid_geq,把0看成是比2177452108更“晚”的tid,当commit_request为2177452108而target为0时,意思是:编号2177452108的tid已经提交了,0比2177452108更“晚”,所以有必要把0号transaction给commit一下,于是唤醒kjournald2(那句wake_up)。而这一唤醒,就发现没有running_transaction,于是悲剧了。
从trace看,大部分传入__jbd2_log_start_commit的target值都不是0,看来这个0来得蹊跷,翻了一下upstream的代码,找到了Ted在去年3月份提的一个patch:

commit 688f869ce3bdc892daa993534dc6df18c95df931
Author: Theodore Ts'o
Date: Wed Mar 16 17:16:31 2011 -0400
ext4: Initialize fsync transaction ids in ext4_new_inode()
When allocating a new inode, we need to make sure i_sync_tid and
i_datasync_tid are initialized. Otherwise, one or both of these two
values could be left initialized to zero, which could potentially
result in BUG_ON in jbd2_journal_commit_transaction.
(This could happen by having journal->commit_request getting set to
zero, which could wake up the kjournald process even though there is
no running transaction, which then causes a BUG_ON via the
J_ASSERT(j_ruinning_transaction != NULL) statement.
Signed-off-by: "Theodore Ts'o"
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 2fd3b0e..a679a48 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -1054,6 +1054,11 @@ got:
}
}
+ if (ext4_handle_valid(handle)) {
+ ei->i_sync_tid = handle->h_transaction->t_tid;
+ ei->i_datasync_tid = handle->h_transaction->t_tid;
+ }
+
err = ext4_mark_inode_dirty(handle, inode);
if (err) {
ext4_std_error(sb, err);

啊哈,就是它了,由于i_sync_tid和i_datasync_tid都没有正确赋值,所以带上了默认的0值,一路传给ext4_sync_file,而后面的__jbd2_log_start_commit又误认为0是一个要提交的新事务(其实此时还没有把当前事务挂到running_transaction上去),所以错误了。打上这个patch,再走重现步骤kernel也不panic了。

既然这么容易重现为什么其它机器上没有遇到?原因就是这个commit_request必须是一个很大的值,大到转为int型时会变为负数。我试了一下在ext4上不停的创建空文件并fdatasync之,10分钟左右commit_request才变为一百万,如果要让它到二十亿,至少还需要十四天,而线上的io压力毕竟没有人工压力测试那么大,所以几个月后commit_request才到二十亿,才触发了这个bug。
redhat最新的2.6.32-220内核是有这个问题的,大家多小心。

感谢@元云@希羽两位同学帮忙提供了重现步骤,内核修bug,最难的就是重现,两位却直接把步骤提供出来了,真是太体贴太客气了!

======

本来想用ksplice来不重启升级内核,这样DBA就可以不重启机器修复这个bug,但是研究了一下ksplice,发现它要求加gcc参数 -ffunction-sections -fdata-sections 来编译内核,而这两个参数又和 -pg 参数冲突,而我们的kernel trace需要用到 -pg ,所以....目前无解,还没有办法用ksplice来帮助我们在线升级内核。

转载请注明转自: 斯巴达第二季 , 本文固定链接: 修复ext4日志(jbd2)bug

修复ext4日志(jbd2)bug( Ext4 文件系统有以下 Bug)的更多相关文章

  1. 以芯片直读方式得到的全盘镜像解析及ext4日志区域解析

    之前在centos中分析了/dev/sda1下的结构,但当对象是一块以芯片直读方式作出来的全盘镜像呢? 这次以安卓手机的全盘镜像为对象,尝试按照ext4文件系统结构手动解析,加强对ext4文件系统.E ...

  2. 【原创】修复ios输入框获取焦点时不支持fixed的bug

    前些日子,做了一个手机站的项目,有一个页面是这样的, 有一个固定(position:fixed)的头部和底部导航,中间是一些表单内容,没啥特别的.但是到了ios中,正常滚动页面没有问题,一旦触发了文本 ...

  3. Android Studio 2.0 Beta 5公布,修复几个与即时执行相关的严重BUG.

    Android Studio 2.0 Beta 5公布,修复几个与即时执行相关的严重BUG. This build fixes a couple of important bugs related t ...

  4. 软件测试中Bug的生命周期以及Bug的严重等级

    软件测试中Bug的生命周期以及Bug的严重等级 我猜你们都会,但能说专业且全面不? 1.首先当测试人员接到一个项目或产品准备测试的时候,测试人员会根据测试用例一步步的来执行用例进行简单的功能测试.当测 ...

  5. DK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。

    NIO的epoll空轮询bug - Lost blog - 博客园 https://www.cnblogs.com/JAYIT/p/8241634.html NIO的epoll空轮询bug   JDK ...

  6. 软件测试过程中如何区分什么是功能bug,什么是需求bug,什么是设计bug?

    问题描述: 测试过程中如何区分什么是功能bug,什么是需求bug,什么是设计bug? 精彩答案: 会员 土土的豆豆: 本期问题其实主要是针对不同方面或纬度上对于bug的一个归类和定位. 个人认为,从软 ...

  7. 解Bug之路-串包Bug

    解Bug之路-串包Bug 笔者很热衷于解决Bug,同时比较擅长(网络/协议)部分,所以经常被唤去解决一些网络IO方面的Bug.现在就挑一个案例出来,写出分析思路,以飨读者,希望读者在以后的工作中能够少 ...

  8. ie7,8常见bug,共计257个bug汇总?如何解决ie的历史bug

    ie7.8常见bug,共计257个bug汇总 针对web开发者来说,浏览器的bug,特备是ie的bug是很多人的噩梦,因为ie的更新换代没有ff,chrome,safari,opera那么快,而且ie ...

  9. NTFS(Windows)、ext4(RHEL6)和xfs(RHEL7)文件系统的误删除恢复和备份

    前言 对于误删除文件的设备,要马上停止任何写的操作,防止删除的文件被覆盖,导致数据丢失! 恢复NTFS文件系统下误删的文件 以Windows为例,市面上能恢复的工具不少,例如EasyRecovery. ...

随机推荐

  1. android MediaPlayer API大全已经方法详解(转载)

    通过这张图,我们可以知道一个MediaPlayer对象有以下的状态: 1)当一个MediaPlayer对象被刚刚用new操作符创建或是调用了reset()方法后,它就处于Idle状态.当调用了rele ...

  2. 如何为Swift进行宏定义

    这阵子一直在自学Swift, 因为之前iOS的开发一直用Objective-C, 所以习惯了C语言那种宏定义方式, Swift作为一款更加安全的语言, 放弃了C语言中的宏定义, 有效的防止预编译时代码 ...

  3. 我参加了51CTO博客大赛,求投票!

    我是张传波,也是Fireball(火球). 我参加了51CTO博客大赛,距离网络投票截止没有几天了,求投票!我的参赛链接:http://blog.51cto.com/contest2013/82313 ...

  4. jQuery添加options点击事件并传值

    说明: 根据选择不同店铺选项,上送不同id值,展示不同商品列表   var formStr = "{'supplierId':'供应链企业|%-jm-sprt-%|93794498-3'}& ...

  5. MySql技巧个人笔记

    1.数据null时sum的用法 mysql数据库SUM(A+B)不一定等于SUM(A)+SUM(B),当A或B为NULL时,SUM(A+B)=NULL. 2.or改为in 同一字段,将or改写为in( ...

  6. 按要求编写Java应用程序。 (1)建立一个名叫Cat的类: 属性:姓名、毛色、年龄 行为:显示姓名、喊叫 (2)编写主类: 创建一个对象猫,姓名为“妮妮”,毛色为“灰色”,年龄为2岁,在屏幕上输 出该对象的毛色和年龄,让该对象调用显示姓名和喊叫两个方法。

    package zuoye; public class Cat { String name="妮妮"; String color="灰色"; int age=1 ...

  7. Centos修改DNS重启或者重启network服务后丢失问题处理

    本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 文章是哥(mephisto)写的,SourceLink 阅读目录 介绍 起因 处理 GitHub 本文版 ...

  8. JQuery 世界时间

    根据表格的时间显示表格的时间,本机的时间,韩国的时间 <%@ Page Language="C#" AutoEventWireup ="true" Cod ...

  9. yii2开发后记

    h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...

  10. 论Top与ROW_NUMBER读取第一页的效率问题

    10.29 前一段时间研究关于分页的问题,由于数据库属于百万级的,考虑了关于优化方面的问题.其中一个考虑是:第一页展现的频率肯定是最高的,所以我想第一页就使用Top N来读取. 这个想法本身是没有错, ...