[文件系统]块文件系统BUG引起的系统复位问题归零报告

Posted by Yinhj on October 14, 2016

故障现象

着陆时,MCU收集MFL数据并转发给VCM,由VCM存储到IDR记录卡。在MCU开始发送MFL数据9秒左右时,VCM复位。


故障定位

测试发现,通过MCU端手动向双口长时间地周期(2个tick)写MFL数据,必然会导致VCM复位,有时候复位前会打印program和data access错误,有时候直接复位。

分析可能出现的几种复位方式:

  1. 应用监控的任务挂起,引起的手动开门狗复位;
  2. 应用异常导致CPU死机刚好无法喂狗,引起开门狗复位;
  3. 应用执行了非法指令、地址寻址错误、除数为0、负值长度等,导致CPU访问异常,引起CPU热复位。
  • 测试1:在VCM监控任务中,将任务挂起手动停止喂狗功能注释掉。

    结果:VCM还是会出现复位,排除第1种复位。

  • 测试2:在VCM应用中禁掉看门狗。

    结果:VCM有时还是会复位,有时会死机。说明可能是CPU访问异常导致热复位,或异常时刚好无法喂狗导致看门狗复位。

我们知道vxworks5.5操作系统是没有用户态的,用户没有自己的地址空间。所以应用访问异常的地址空间,很可能导致执行非法指令或寻址错误;甚至可能会导致CPU异常而reboot。

从数据流的过程分析可能引起复位的原因:

  1. VCM和MCU的双口通信在数据量增大时是否可能访问数据越界。
  2. VCM从双口取出数据存入本地缓存,写盘时从缓存中取数据,存取数据是否出错。
  3. VCM写盘是否出错。
  • 测试3:在VCM应用中,屏蔽从双口读取MFL数据,以相同的数据流量仿真MFL数据,放入本地的MFL缓存中,VCM按原来的方式从缓存中取数据写盘。

    结果:VCM还是会出现复位,排除MCU和VCM的双口通信问题,同时也将问题定位到了VCM端。

  • 测试4:打印存放MFL的当前扇区的有效字节数oddment(即偏移量)、缓存中取出的数据长度getlen、块数blocknum。取数据和写盘的代码如下:

    blocknum = nbforng/512; //nbforng为本地缓存中数据字节数

    if(blocknum == 0) blocknum = 2;

    ldrs_rawFsGetWrtBuf(fd, WRITE_LEN_MAX, &oddment); //获取块文件系统写数据存放缓冲区

    getlen = getdatafromrngbuf(rngid, semid, buf, (512*blocknum)-oddment); //从缓存中取数据

    … …

    idrs_rawFsWrite(fd, dataidx, buf, getlen); //写盘

    结果:发现打印的当前扇区有效字节数oddment出现大于512的情况,然而一个扇区最大为512字节。所以,当oddment>512,且512<nbforng<1024时,会出现blocknum等于1,(512*blocknum)-oddment)<0的情况,导致取数据的长度<0

  • 测试5:在串口中测试rngBufGet(MFL_ringid, buf, -1),从MFL_ringid缓存中取-1长度的数据,会导致应用程序复位。


故障原因

VCM应用程序为了减少写盘次数,每次写盘都会凑512*blocknum字节的数据量,即凑完整的扇区来写盘。例如,缓存中数据量nbforng为1100字节,扇区偏移量oddment为124,则blocknum为2,算出实际写盘数据量为(512xblocknum)-oddment = 900字节,剩下的1100-900 = 200字节数据等下次写盘时再写。

  • 一种异常情况,当环形缓存中数据量<512字节,即blocknum等于0时,取数据长度(512*blocknum)-oddment)为负值。应用对0值做了处理:

if(blocknum == 0) blocknum = 2;

  • 当环形缓存中数据量在512~1024字节范围,即blocknum等于1时,取数据长度(512*blocknum)-oddment)正常情况下为>=0,但实际oddment出现了>512的情况。因为当数据区通道数>1时,计算扇区偏移量oddment会加上14个字节的帧头,所以oddment的范围变为14~526了。

    当oddment>512,且512<nbforng<1024,即blocknum等于1时,(512*blocknum)-oddment)<0,导致调用vxworks系统函数rngBufGet取数据时长度<0,CPU死机引起VCM应用程序复位。

    那么,其他数据是否会引起同样的故障:

    • LOG的数据区通道数为1且数据量小,oddment不会加14字节的帧头,oddment不会越界>512;
    • 音视频正常数据量大,blocknum = nbforng/512,blocknum>2,而blocknum只有为1才会导致异常,所以基本不会引起异常;
    • 电子战EW/武器BUS/飞参FPA/BM总线数据的正常数据量<512字节,blocknum为0,所以基本不会引起异常;
  • 测试6:同时,测试在vxworks6.8系统下,调用rngBufGet函数取负值长度的数据返回-1,但不会引起CPU死机。而10C使用的是vxworks6.8系统,并且没有采用这种凑512*blocknum字节数据量写盘的方式,所以10C不会出现这样的故障。


解决方案

块文件系统中判断逻辑,当数据区通道数>1时,写盘会增加14个字节,其中8个字节帧头,6个字节帧尾。而应用计算从本地缓存中可取数据量时,不包括这14字节(因为这是文件系统驱动中加的),所以为了留出14个字节,偏移量oddment计算时加上了14个字节:

oddment = 14字节帧头帧尾 + 当前扇区实际偏移量oddment_actual;

为了写扇区时凑完整的扇区,从缓存中取(blocknum*512)-oddment字节的数据,如下:

//从缓存中取数据

getlen = getdatafromrngbuf(rngid, semid, buf, (blocknum*512)-oddment);

因为扇区偏移量oddment并非实际偏移量,其范围为14~526,可能导致(blocknum*512)-oddment为负值。所以,计算oddment时,得出的结果后面加一句:

*oddment = *oddment%512;

使得扇区偏移量范围在0~512字节之间。

例如:缓存中数据量nbforng为600字节,实际偏移量oddment_actual为500字节时,计算oddment = (500+14)%512 = 2,blocknum = nbforng/512 = 1,(blocknum*512)-oddment = 510。从缓存中取510字节,剩下600-510=90字节下次再写。实际写盘数据量为510+14=524字节,从扇区实际偏移量oddment_actual为500开始写,刚好写满下个扇区。