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()中,添加如下代码:
| |
验证 | 验证通过。 | |
代码以及文档的提交路径
|
|