f2fs: avoid deadlock caused by lock order of page and lock_op
authorJaegeuk Kim <jaegeuk@kernel.org>
Thu, 22 Jun 2017 00:52:39 +0000 (17:52 -0700)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 7 Jul 2017 18:07:59 +0000 (11:07 -0700)
- punch_hole
 - fill_zero
  - f2fs_lock_op
  - get_new_data_page
   - lock_page

- f2fs_write_data_pages
 - lock_page
 - do_write_data_page
  - f2fs_lock_op

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/data.c

index 72fc866cad1955a3391d4baab6904da768cb22a3..7dd5fb647d435f51ea755ef97983eb80a035c1e8 100644 (file)
@@ -1404,8 +1404,9 @@ int do_write_data_page(struct f2fs_io_info *fio)
                }
        }
 
-       if (fio->need_lock == LOCK_REQ)
-               f2fs_lock_op(fio->sbi);
+       /* Deadlock due to between page->lock and f2fs_lock_op */
+       if (fio->need_lock == LOCK_REQ && !f2fs_trylock_op(fio->sbi))
+               return -EAGAIN;
 
        err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
        if (err)
@@ -1667,7 +1668,7 @@ retry:
                        }
 
                        done_index = page->index;
-
+retry_write:
                        lock_page(page);
 
                        if (unlikely(page->mapping != mapping)) {
@@ -1703,6 +1704,15 @@ continue_unlock:
                                        unlock_page(page);
                                        ret = 0;
                                        continue;
+                               } else if (ret == -EAGAIN) {
+                                       ret = 0;
+                                       if (wbc->sync_mode == WB_SYNC_ALL) {
+                                               cond_resched();
+                                               congestion_wait(BLK_RW_ASYNC,
+                                                                       HZ/50);
+                                               goto retry_write;
+                                       }
+                                       continue;
                                }
                                done_index = page->index + 1;
                                done = 1;