pnfs执行truncate失败的BUG解析

 

Bug 名称

Truncate操作失败

Bug 描述

使用fstest工具,先执行create再执行truncate后,服务器会返回EIO,而不是期望的0,过一段时间之后,重复执行truncate则成功。

这是导致truncate测试用例所有失败的唯一原因。具体测试脚本在fstest/tests/truncate目录下。

Bug 重现过程

1.       在客户端挂载目录下,使用fstest创建一个文件

命令:./fstest create aaa 0644

2.       执行truncate操作

命令:./fstest truncate aaa 1234567

3.       会显示EIO

出现bug的系统日志

客户端日志:

服务器端日志:

 

Bug 分析

现象分析    

根据操作不成功的返回值EIO,很难分析出具体原因,因为EIO是经过err_map操作之后的映射值,具体错误的返回值需要在log中定位。

log分析

1.       根据客户端和服务器的错误返回值10038,得出对应的宏定义是ERR_OPENMODE;

2.       通过在服务器端日志的3903行(见上图)分析,定位出错的具体代码函数。

3.       通过步骤1和步骤2,结合阅读代码,得出结论:在函数nfs4_preprocess_stateid_op中,走如下流程:

经过判断,当前stateid是存在 对应的delegation的,然后进入check_delemode()函数,该函数代码如下:

在2295行,经过打printk,断定dp->dl_type的值为NFS4_OPEN_DELEAGATE_READ。

4.       经过阅读PNFS协议,发现在setattr()操作之前进行的open()操作,服务器会尝试授予delegation.

5.       阅读服务器端open()操作代码,发现调用nfs4_open_delegation(current_fh, open, stp)后,入口参数open,中含有op_share_access变量,经过打印输入,其值为NFS4_SHARE_ACCESS_READ。在随后进行的分配delegation操作中,会把分配给它的delegation设置为NFS4_SHARE_ACCESS_READ。当客户端下次持有这个delegation来执行setattr操作时,由于设置了修改size的标志位,导致delegation的类型判断失败,操作失败。

6.       若在shell中执行touch命令创建文件,然后在执行truncate系统调用,则不会出现同样错误,是因为touch触发的系统调用是:open("cc", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666);而测试脚本中的创建命令是open(STR(0), O_CREAT | O_EXCL, NUM(1));两者相比,创建时候的标志位设置不同,导致服务器接收到的open操作,入口参数的op_share_access不同,从而测试脚本的命令可以获得读权限的delegation,而touch命令则无法获取delegation。

7.       对于使用测试脚本创建文件,过一段时间后在执行truncate操作可以成功。

是由于客户端会每60秒执行nfs4_renew_state(struct work_struct *work)操作,此操作会检查是否存在“unreferenced”的delegation,若有则将其释放。

而truncate操作之后,会执行文件的close操作,删除对应dentry,使得open操作得到的delegation变成“unreferenced”,在nfs4_renew_state()操作中被删除。也就是说,文件被创建之后,只要客户端执行了nfs4_renew_state操作,再执行truncate操作,就可以成功。

8.由于nfs4_renew_state()操作是定期执行的,而truncate操作是随机发生的,导致之前的第一次操作失败后,间隔“不确定的时间”,再次成功。

Bug定位

执行修改文件大小的操作时候,客户端持有的delegation类型为NFS4_SHARE_ACCESS_READ。

解决方案

在客户端发送setattr()请求之前,检查修改size的标志位是否设置,若设置,则释放delegation。

在函数nfs_setattr()中,添加如下代码:

 

验证

验证通过。

代码以及文档的提交路径

 

 

 


你可能感兴趣的:(pnfs执行truncate失败的BUG解析)