免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6122 | 回复: 11
打印 上一主题 下一主题

请教这个Kernel Panic可能的发生原因 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-08-06 16:48 |只看该作者 |倒序浏览
服务器发生Kernel Panic,收集到的dump显示出是在dcache.h:282发生的,这一行是dget()中的
BUG_ON(!atomic_read(&dentry->d_count));        
也就是说,当dget()传入一个未被引用的dentry时会发生。
查看调用代码,是hookmod这个用于Hook kernel syscall的module中的以下代码。
我对kernel还很不了解,只是根据kernel其它的一些相关代码推断是不是因为在smp的机器上某个地方需要加锁?
看到过Dazuko中有提到它用的一个local的__d_path实现是在smp上是不安全的,因为缺少必要的加锁,这里的代码跟__d_path的代码似乎有相似的地方。
请高手指点一下,这个问题出现有哪些可能性,我知道我提供的信息可能还不允分,我只希望能提供一些可能线索。另外,还请高手指点,遇到这样的问题应该如何去分析,有哪些参考书可以阅读和学习?非常感谢!

Boolean path_match_dir(const char *path,
        const struct dentry *dentry, const struct vfsmount *mnt)
{
        const char * cpsMethod = "path_match_dir";
        pid_t cur_pid=0;
        struct nameidata nd;
        struct dentry *ite_dentry = dentry->d_parent;
        struct vfsmount *ite_mnt = (struct vfsmount *)mnt;
        struct dentry *base;
        unsigned int flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT;
        int error = 0;
        cur_pid = current->pid;
        if (path[0] == '/' && path[1] == '\0') {
                return TRUE;
        }
        read_unlock(&kini_lock);
#if LINUX_VERSION_CODE >= 0x20600
        error = PATH_LOOKUP(path, flags, &nd);
        read_lock(&kini_lock);
        if (error) return FALSE;
#else
        error = PATH_LOOKUP(path, flags | LOOKUP_POSITIVE, &nd);
        read_lock(&kini_lock);
        if (error) {
                return FALSE;
        } else if (nd.dentry->d_inode == NULL) {
                read_unlock(&kini_lock);
                path_release(&nd);
                read_lock(&kini_lock);
                return FALSE;
        }
#endif
        base = nd.dentry;
        read_unlock(&kini_lock);
        while (1) {
                if (base == ite_dentry) {
                        path_release(&nd);
                        read_lock(&kini_lock);
                        return TRUE;
                }
                if (IS_ROOT(ite_dentry)) {
                        mntget(ite_mnt);
                        dget(ite_dentry);                                   // 怀疑这里有问题,是不是在mntget和dget前需要加spin_lock?
                        if (follow_up(&ite_mnt, &ite_dentry) == 0) {
                                mntput(ite_mnt);
                                dput(ite_dentry);
                                goto global_root;
                        }
                } else
                        ite_dentry = ite_dentry->d_parent;
        }
global_root:
        path_release(&nd);
        read_lock(&kini_lock);
        return FALSE;
}

Kernel Panic的栈信息:
kernel BUG at include/linux/dcache.h:282!
invalid operand: 0000 [#1]
SMP
Modules linked in: softdog parport_pc lp parport maintedump(U) mainte(U) autofs4 i2c_dev i2c_core sunrpc scsi_dump diskdump
zlib_deflate hookmod dm_mirror dm_mod button battery ac joydev usb_storage md5 ipv6 uhci_hcd ehci_hcd shpchp e1000 bondin
g(U) st ext3 jbd lpfc scsi_transport_fc megaraid_sas aic7xxx mptscsih mptsas mptspi mptfc mptscsi mptbase sd_mod scsi_mod
CPU:    1
EIP:    0060:[<f89ee746>]    Tainted: P      VLI
EFLAGS: 00010246   (2.6.9-42.ELsmp)
EIP is at path_match_dir+0x12b/0x1ac [hookmod]
eax: 00000000   ebx: f737a5a4   ecx: 0000017c   edx: f7f2b89c
esi: 00000000   edi: 00000aa5   ebp: 00000aa5   esp: ce72cd98
ds: 007b   es: 007b   ss: 0068
Process UCXSingle-Linux (pid: 2725, threadinfo=ce72c000 task=f20291b0)
Stack: f7f28780 f7f2b89c f737a5a4 f73cd580 00000000 c016f070 ce72cf20 00000023
       00000001 00000000 f702389c f7f28f80 00000000 00001000 00000010 00000000
       00000001 00000000 45ed2e0e 00000000 45ed2e0e 00000000 f73dce00 f73dcd20
Call Trace:
[<c016f070>] dput+0x34/0x1a7
[<f89eecd9>] in_exclude_dirs+0x8b/0x128 [hookmod]
[<f89ef5d1>] need_to_scan+0x176/0x216 [hookmod]
[<f89ecb7b>] open_hook+0x39b/0x96d [hookmod]
[<c016f070>] dput+0x34/0x1a7
[<c0167430>] link_path_walk+0x94/0xbe
[<c0163126>] cp_new_stat64+0x124/0x139
[<c0163159>] sys_stat64+0x1e/0x23
[<c02d4703>] syscall_call+0x7/0xb
[<c02d007b>] packet_rcv+0x242/0x307
Code: c7 b8 01 00 00 00 e9 9a 00 00 00 8b 50 14 39 d0 75 55 8b 04 24 85 c0 74 04 f0 ff 40 28 8b 54 24 04 85 d2 74 11 8b 02 8
5 c0 75 08 <0f> 0b 1a 01 d8 45 9f f8 f0 ff 02 8d 54 24 04 89 e0 e8 99 7e 77

论坛徽章:
0
2 [报告]
发表于 2007-08-06 17:42 |只看该作者
你的call trace够奇怪的. packet_rcv到syscall_call, cp_new_stat64到link_path_walk.

论坛徽章:
0
3 [报告]
发表于 2007-08-06 20:01 |只看该作者

  1. if (IS_ROOT(ite_dentry)) {
  2.                         mntget(ite_mnt);
  3.                         dget(ite_dentry);  // 怀疑这里有问题,
  4. //是不是在mntget和dget前需要加spin_lock?
  5.                         if (follow_up(&ite_mnt, &ite_dentry) == 0) {
  6.                                 mntput(ite_mnt);
  7.                                 dput(ite_dentry);
  8.                                 goto global_root;
  9.                         }
  10.                 } else
复制代码

既然是 root, 理应不该->ref ==0
怕是module里没处理对,
否则,dentry cache in VFS早就崩了。

论坛徽章:
0
4 [报告]
发表于 2007-08-06 20:09 |只看该作者
path_match_dir(const char *path,
        const struct dentry *dentry, const struct vfsmount *mnt)
中的dentry谁传进去的,能帖上一级的代码吗?

论坛徽章:
0
5 [报告]
发表于 2007-08-07 10:56 |只看该作者
谢谢各位给出的线索。
上层的代码是这样的(都只列出了相关的部分)。
这个Panic的现象不是经常出现的,可能99.9%的情况下这个module都是工作正常的。
请大家继续提供线索,谢谢!
in_exclude_dirs(struct dentry * dentry, struct vfsmount * mnt) 中
list_for_each_entry_safe(ip, nip, &exc_dir_list_head, item_list) {
        if (has_wildcards(ip->path)) {
                if (path_match_wildcard(ip->path, dentry, mnt)) {
                        return TRUE;
                }
        } else {
                if (path_match_dir(ip->path, dentry, mnt)) {
                        return TRUE;
                }            
        }
}
再上层,need_to_scan(struct dentry * dentry, struct vfsmount * mnt, int flags, ino_t inode)
if ((!list_empty(&exc_dir_list_head)  && in_exclude_dirs(dentry, mnt))
        return FALSE;
再上层,openhook(),这就是用来替换系统open的hook函数。
asmlinkage int openHook(const char *filename, int flags, int mode)
{
    // ....
    int inode;
    char * tmp = getname(filename);
    struct stat        statbuf;
    getStat(tmp, &statbuf);
    inode = statbuf.st_ino;
    struct nameidata nd;
    error = PATH_LOOKUP(tmp, lookup_flags(namei_flags), &nd);
    scan_open = need_to_scan(nd.dentry, nd.mnt, flags, inode);
    // ....
}
其中的PATH_LOOKUP和getStat
int PATH_LOOKUP(const char * path, unsigned flags, struct nameidata * nd)
{
#if LINUX_VERSION_CODE >= 0x20600
#if defined (IT_LOOKUP)
        intent_init(&nd->intent, IT_LOOKUP);
#endif
        return path_lookup(path, flags, nd);
#else
        int error = 1;
        if (path_init(path, flags, nd))
                error = path_walk(path, nd);
        return error;
#endif
}
int getStat(char *filename, struct stat *statbuf)
{
        struct nameidata nd;
        int error = 1;

        error = PATH_LOOKUP(filename, LOOKUP_FOLLOW | LOOKUP_NOALT, &nd);
        if (!error) {
                error = cp_new_stat(nd.dentry->d_inode, statbuf);
                path_release(&nd);
        }

        return error;
}

论坛徽章:
0
6 [报告]
发表于 2007-08-07 14:56 |只看该作者
                if (IS_ROOT(ite_dentry)) {
                        mntget(ite_mnt);
                        dget(ite_dentry);                                   // 怀疑这里有问题,是不是在mntget和dget前需要加spin_lock?
                        if (follow_up(&ite_mnt, &ite_dentry) == 0) {
                                mntput(ite_mnt);
                                dput(ite_dentry);
                                goto global_root;
                        }
                }



1.  dget和dput不对称
2.  这样的代码不能处理mount --bind

[ 本帖最后由 daemeon 于 2007-8-7 14:58 编辑 ]

论坛徽章:
0
7 [报告]
发表于 2007-08-07 15:32 |只看该作者
原帖由 daemeon 于 2007-8-7 14:56 发表
1.  dget和dput不对称
2.  这样的代码不能处理mount --bind


谢谢。
“不对称”是指要把dput放到mntput之前吗?实在是刚刚开始看Kernel方面的东西,又没有足够的时间供前期的学习,只能先尽量把问题解决了再回头来学习,还请您多多帮助。
为了降底复杂度,mount --bind的情况就先不考虑了,不考虑会有严重的问题吗?

论坛徽章:
0
8 [报告]
发表于 2007-08-07 15:55 |只看该作者

回复 #7 lifanxi 的帖子

我觉得follow_up返回非0时, 也应该有 mntget(ite_mnt)和dget(ite_dentry);

论坛徽章:
0
9 [报告]
发表于 2007-08-07 16:29 |只看该作者

回复 #8 daemeon 的帖子

get?怎么感觉应该是put?这样改?
                if (IS_ROOT(ite_dentry)) {
                        mntget(ite_mnt);
                        dget(ite_dentry);                                   
                        if (follow_up(&ite_mnt, &ite_dentry) == 0) {
                                dput(ite_dentry);
                                mntput(ite_mnt);
                                goto global_root;
                        }
                        else
                        {
                                dput(ite_dentry);
                                mntput(ite_mnt);
                        }
                }


但是似乎这个与Panic产生的原因并没有直接的联系,漏做了dput只会导到ref变大,应该还是不会出现ref==0的情况吧。

[ 本帖最后由 lifanxi 于 2007-8-7 17:29 编辑 ]

论坛徽章:
0
10 [报告]
发表于 2007-08-07 20:38 |只看该作者

回复 #1 lifanxi 的帖子

I'd suggest you use

struct dentry *ite_dentry = dget_parent(dentry) and dput ite_dentry when exiting

to avoid the case you are suspecting.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP