2bbfed98a4d8 nfsd: Fix races between nfsd4_cb_release() and nfsd4_shutdown_callback()
邮件:
nfsd4_shutdown_callback()
时,我们必须确保在所有未完成的回调终止并释放它们的有效负载之前不返回。rpc_shutdown_client()
之后继续存在。然而,它导致了 xfstests 的运行挂起,我还没有弄清楚原因。我会在今天下午花些时间进行研究,并告诉你我找到的东西。__destroy_client()
中肯定存在挂起问题,我认为在版本2中已经修复的引用计数泄漏。generic/013
测试中遇到了挂起的情况。我快速检查了一下日志,没有看到有趣的信息,除此之外我还没有进行详细的调查。./check -nfs generic/013
可以重现。在Wireshark中看到的最后一条信息是一个异步的COPY调用和回复。这意味着可能正在尝试执行 CB_OFFLOAD。嗯。讨论此补丁的相关邮件: nfsd: radix tree warning in nfs4_put_stid and kernel panic
4.19等低版本合入此补丁可能还要合入前置补丁12357f1b2c8e nfsd: minor 4.1 callback cleanup
。
已被回退的后续修复补丁c1ccfcf1a9bf NFSD: Reschedule CB operations when backchannel rpc_clnt is shut down
。
struct nfs4_client
中增加一个字段cl_cb_inflight
表示未完成的回调的个数,注意这个补丁的标题是说竞争发生在两个函数之间。我们先来看一下补丁合入前的竞争,nfsd4_cb_release()
和__destroy_client()
同时调用到radix_tree_node_free()
,导致rcu_head
在被释放之后又被释放了一次。
nfsd4_cb_release// nfsd4_run_cb // 这里补丁合入前后没有变化
// queue_work(callback_wq, &cb->cb_work)
// cb->cb_ops->release
nfsd4_cb_recall_release
nfs4_put_stid
idr_remove
radix_tree_delete_item
__radix_tree_delete
delete_node// __destroy_client中也调用到这里,并发
radix_tree_node_free
__destroy_client
nfsd4_shutdown_callback
free_client
idr_destroy
radix_tree_free_nodes// nfsd4_cb_release也调用到这里,并发 radix_tree_node_free
合入补丁之后,nfsd4_cb_release()
中的radix_tree_node_free()
执行完后调用nfsd41_cb_inflight_end()
唤醒nfsd4_shutdown_callback()
,free_client()
才开始执行,避免了并发的场景。
nfsd4_cb_release// nfsd4_queue_cb // 这里补丁合入前后没有变化
// queue_work(callback_wq, &cb->cb_work)
nfsd41_destroy_cb// cb->cb_ops->release
nfsd4_cb_recall_release
nfs4_put_stid
idr_remove
radix_tree_delete_item
__radix_tree_delete
delete_node// 执行完后调用nfsd41_cb_inflight_end唤醒nfsd4_shutdown_callback
radix_tree_node_free
nfsd41_cb_inflight_end
atomic_dec_and_test// cl_cb_inflight变为0时,唤醒nfsd4_shutdown_callback
wake_up_var
__destroy_client
nfsd4_shutdown_callback// cl_cb_inflight变为0时,唤醒
nfsd41_cb_inflight_wait_complete
free_client
idr_destroy
radix_tree_free_nodes// nfsd41_cb_inflight_wait_complete唤醒后才会执行到这里,没有并发的情况 radix_tree_node_free