linux 版本路由器长时间会出现死机问题

1 问题现象:

9344路由器,嵌入式林旭系统,使用 pppoe 上网,pc机连接路由器 ,pc机上网播视频,偶尔会出现路由器死机现象,pc机静态ip ping不通base,插拔网线 led会闪,串口也会有插拔网线时的信息输出,但串口没输入,敲击键盘串口中没反应。

2 问题跟踪过程:

(1)起初问题复现时串口没有任何信息输出,只能重启路由器,没有任何获取log方式。
(2) 后来 发现路由器死机时会响应中断,怀疑是在kernel中死锁或者死循环,所以在gpio 中断处理函数中添加了部分log打印,把current 指针信息(进程上下文信息)打印出来, 但发现每次死机打印的进程不同,没有实质帮助。
(3) 怀疑是死锁或死循环后,尝试打开kernel抢占,但问题仍存在。
(4) 之后尝试打开 kernel debug, 打开 soft lockup 和内核锁相关信息打印。 又在 gpio 中断中 加入 dump_stack 信息,路由器死机时相应log如下:

BUG: soft lockup - CPU#0 stuck for 61s! [df:1714]
Modules linked in: as532h phone_passage usb_passage ath_spi_si32178 ath_si32178 cdc_ether usbnet usb_storage ehci_hcd usbcore ath_pktlog(P) umac ath_dev(P) ath_dfs(P) ath_rate_atheros(P) ath_hal(P) asf(P) adf ipt_REJECT ipt_REDIRECT ipt_MASQUERADE iptable_nat iptable_filter ip_tables nf_nat_proto_gre nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state xt_conntrack nf_conntrack_h323 nf_conntrack_proto_gre nf_conntrack ipt_multiurl xt_time xt_string xt_multiport xt_mac xt_iprange xt_comment xt_TCPMSS xt_MARK xt_tcpudp x_tables athrs_gmac
Cpu 0
$ 0 : 00000000 00000001 00000001 87954274
$ 4 : 00000001 00000001 879554a4 00000001
$ 8 : 00000004 8010708c 00000002 b6597875
$12 : 7d3caa9b ffffffff 00000001 a9702db7
$16 : 87954200 87955000 87955000 00000000
$20 : 80626fae 00000000 00000000 879542b0
$24 : 00000000 8017f6a4
$28 : 86826000 868276a8 87955000 80120890
Hi : 00000000
Lo : 00000000
epc : 80152a10 ppp_asynctty_wakeup+0x70/0xcc
    Tainted: P
ra : 80120890 tty_wakeup+0x4c/0x78
Status: 1100ff03 KERNEL EXL IE
Cause : 40808000
PrId : 0001974c (MIPS 74Kc)

按下gpio 按键后,响应gpio中断后,打印dump_stack 信息:

Call Trace:
[<801e0668>] dump_stack+0x8/0x34
[<8000331c>] jumpstart_irq+0x8c/0x108
[<8004cbb0>] handle_IRQ_event+0x78/0x160
[<8004e5a8>] handle_percpu_irq+0x48/0xa0
[<80003b10>] plat_irq_dispatch+0x340/0x480
[<800063ac>] ret_from_irq+0x0/0x4
[<8001f0e4>] __wake_up+0x38/0x4c
[<80128d38>] pty_write+0x54/0x70
[<80153418>] ppp_async_push+0x12c/0x54c
[<8015388c>] ppp_async_send+0x54/0x6c
[<8014f1d4>] ppp_push+0x80/0x5fc
[<8014fe7c>] ppp_xmit_process+0x478/0x540
[<801500e4>] ppp_start_xmit+0x1a0/0x1dc
[<8017f450>] dev_hard_start_xmit+0x244/0x328
[<8018f00c>] __qdisc_run+0x110/0x26c
[<8017f8e8>] dev_queue_xmit+0x244/0x36c
[<8019adb4>] ip_rcv_finish+0x324/0x358
[<801d6ab0>] br_handle_frame_finish+0x144/0x1a0
[<801d6d4c>] br_handle_frame+0x240/0x274
[<8017e2f4>] netif_receive_skb+0x2b0/0x3cc
[<87965c70>] athr_gmac_recv_packets+0x2b0/0x448 [athrs_gmac]
[<8002bf80>] tasklet_action+0x88/0xdc
[<8002c6e8>] __do_softirq+0xb0/0x148
[<8002c7c8>] do_softirq+0x48/0x6c
[<800063ac>] ret_from_irq+0x0/0x4
[<8010b4d4>] inflate_fast+0x244/0x510
[<8010c4d4>] zlib_inflate+0x930/0x124c
[<800d3f84>] jffs2_zlib_decompress+0x104/0x15c
[<800c6e78>] jffs2_decompress+0xbc/0x134
[<800ca328>] jffs2_read_dnode+0x308/0x3c4
[<800ca4e8>] jffs2_read_inode_range+0x104/0x18c
[<800c894c>] jffs2_do_readpage_nolock+0x60/0x110
[<800c8cec>] jffs2_do_readpage_unlock+0x18/0x3c
[<800c8d48>] jffs2_readpage+0x38/0xa0
[<80059d8c>] __do_page_cache_readahead+0x1d8/0x23c
[<80059e1c>] ra_submit+0x2c/0x38
[<80051a70>] filemap_fault+0x1f8/0x408
[<80061d2c>] __do_fault+0x70/0x44c
[<800634d8>] handle_mm_fault+0x32c/0xb5c  

3 问题分析和解决:

(1) 由于当base死机时 会打印出soft lockup 信息,可以断定是kernel中死锁或者死循环
(2)通过dump_stack 可以看到死机时和ppp 相关代码有关。每次死机时dump_stack 信息不太一致,最后调用的几个函数不通,怀疑在最后摸个函数中死循环。
(3) 后来在 一个帖子中找到和该问题相似的问题,那个问题是 kernel中摸个bug ,照着那个解决方式试了试,发现问题解决了。帖子地址:
http://linux.derkeiler.com/Mailing-Lists/Kernel/2009-09/msg07164.html

4 问题复盘:

通过看源码,发现问题可能出在 ppp 和pty (伪终端)、tty中。

(1)
drivers/net/ppp_async.c 中 ppp_async_push 函数 中

 for (; ;) {
     if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))    在ppp_asynctty_wakeup 函数中置位XMIT_WAKEUP,这里条件成立。
          tty_stuffed = 0;
     if (!tty_stuffed && ap->optr < ap->olim) {          如果终端未拥塞并且编码缓冲区有发送数据 ,当tty_stuffed ==0 并且 缓冲区中有数据 条件就成立。
          sent = tty->ops->write(tty, ap->optr, avail);               实际调用 pyt_write  将数据写入到tty buff 中。
          set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 673 avail = ap->olim - ap->optr;
         if (sent < 0)
              goto flush; /* error, e.g. loss of CD */
          ap->optr += sent;
          if (sent < avail)
             tty_stuffed = 1;
          continue;
       }

在这里可以看到有一个明显的 循环 for (;😉。

(2)
drivers/char/pty.c 中

static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
{
    struct tty_struct *to = tty->link;
    if (tty->stopped)
        return 0;
    if (c > 0) {
        /* Stuff the data into the input queue of the other end */
        c = tty_insert_flip_string(to, buf, c);          //这里会申请一块内存放数据。
        /* And shovel */
        if(c){                   // 添加了该判断,问题解决了。
            tty_flip_buffer_push(to);
            tty_wakeup(tty);        这里最终调用了 上面提到的ppp_asynctty_wakeup 函数。 
        }
    }
    return c;
}

tty_insert_flip_string --》 tty_buffer_request_room --》 tty_buffer_find --》 tty_buffer_alloc --》
调用
p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); 申请内存。

(3) 原因分析,
当kmalloc申请内存不成功时, 会返回NULL ,这样 tty_buffer_find返回NULL (通过后来打log 验证了) ,tty_buffer_request_room 返回0,tty_insert_flip_string 返回0, 如果没有 if©{ 判断,tty_wakeup (ppp_asynctty_wakeup)调用, 这样 drivers/net/ppp_async.c 中XMIT_WAKEUP 置位, 同时若 ppp缓冲中还有数据的话
if (!tty_stuffed && ap->optr < ap->olim) { 条件会成立, 之后又调用 pty_write , 此时应该是系统中摸个地方在申请着大内存而有得不到cpu执行机会,释放不了, 所以pty_write 再执行还是申请不到内存而返回0同时wake_up, 这样缓冲中还是有数据,还是会继续调用pty_write. 就这样陷入了死循环。

同时 ppp_async_push 被调用过程为:
ppp_xmit_process --》ppp_push --》 ppp_async_send --》ppp_async_push

在ppp_xmit_process 中调用 ppp_xmit_lock 上锁
ppp_xmit_lock 中会调用preempt_disable关闭内核抢占, 所以 编译内核时即使打开内核抢占,在 此也会被关掉。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

viqjeee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值