btrfs: fix race between page release and a fast fsync
[sfrench/cifs-2.6.git] / fs / btrfs / extent_io.c
index 3fbc3769259218f867c3f30bbddc90fa9a2da73c..c049a33ed0df10b819c975eb180759bee0716d71 100644 (file)
@@ -4494,15 +4494,25 @@ int try_release_extent_mapping(struct page *page, gfp_t mask)
                                free_extent_map(em);
                                break;
                        }
-                       if (!test_range_bit(tree, em->start,
-                                           extent_map_end(em) - 1,
-                                           EXTENT_LOCKED, 0, NULL)) {
+                       if (test_range_bit(tree, em->start,
+                                          extent_map_end(em) - 1,
+                                          EXTENT_LOCKED, 0, NULL))
+                               goto next;
+                       /*
+                        * If it's not in the list of modified extents, used
+                        * by a fast fsync, we can remove it. If it's being
+                        * logged we can safely remove it since fsync took an
+                        * extra reference on the em.
+                        */
+                       if (list_empty(&em->list) ||
+                           test_bit(EXTENT_FLAG_LOGGING, &em->flags)) {
                                set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
                                        &btrfs_inode->runtime_flags);
                                remove_extent_mapping(map, em);
                                /* once for the rb tree */
                                free_extent_map(em);
                        }
+next:
                        start = extent_map_end(em);
                        write_unlock(&map->lock);