Offering: HULK hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAASLU
--------------------------------
When multiple processes or threads write to the same file concurrently, if a network disruption occurs during the write operation, it may lead to a deadlock situation as follow:
Process 1 (dd) Process 2 (cifsd) Process 3 (cifsiod) cifs_writepages lock_page - [1] wait_on_page_writeback - [2] Waiting for writeback, blocked by [4] wait_on_page_bit cifs_demultiplex_thread cifs_read_from_socket cifs_readv_from_socket - If another process triggers reconnect at this point cifs_reconnect - mid->mid_state updated to MID_RETRY_NEEDED smb2_writev_callback mid_entry->callback() - mid_state leads to wdata->result = -EAGAIN wdata->result = -EAGAIN queue_work(cifsiod_wq, &wdata->work); cifs_writev_complete - work function - Condition satisfied - wdata->result == -EAGAIN cifs_writev_requeue lock_page - [3] Blocked by [1] end_page_writeback - [4] Won't execute, blocked by [3] unlock_page
Mainline refactoring patch d08089f649a0 ("cifs: Change the I/O paths to use an iterator rather than a page list") unlock page while waiting for the writeback to complete, thus avoiding potential deadlocks caused by lock ordering issues during reconnection.
Due to the large refactor of the mainline, the patch cannot be backport directly. Therefore, This patch only uses a part of the idea of the mainline patch to fix deadlock.
Fixes: c28c89fc43e3 ("cifs: add cifs_async_writev") Signed-off-by: Wang Zhaolong wangzhaolong1@huawei.com --- fs/cifs/file.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 875cb44ba573..e346e6c2227a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2056,6 +2056,7 @@ wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages, * back from swapper_space to tmpfs file mapping */
+relock_recheck: if (nr_pages == 0) lock_page(page); else if (!trylock_page(page)) @@ -2078,11 +2079,16 @@ wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages, break; }
- if (wbc->sync_mode != WB_SYNC_NONE) - wait_on_page_writeback(page); + if (PageWriteback(page)) { + unlock_page(page); + if (wbc->sync_mode != WB_SYNC_NONE) { + wait_on_page_writeback(page); + goto relock_recheck; + } + break; + }
- if (PageWriteback(page) || - !clear_page_dirty_for_io(page)) { + if (!clear_page_dirty_for_io(page)) { unlock_page(page); break; }