]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - fs/btrfs/file.c
Btrfs: fix race leading to BUG_ON when running delalloc for nodatacow
[mirror_ubuntu-artful-kernel.git] / fs / btrfs / file.c
index 381be79f779a2daa9c8f5d2a52c4a0bd6784701a..3009d4536d389386dba9a582cd84a5da7ff20552 100644 (file)
@@ -756,8 +756,16 @@ next_slot:
                }
 
                btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
-               if (key.objectid > ino ||
-                   key.type > BTRFS_EXTENT_DATA_KEY || key.offset >= end)
+
+               if (key.objectid > ino)
+                       break;
+               if (WARN_ON_ONCE(key.objectid < ino) ||
+                   key.type < BTRFS_EXTENT_DATA_KEY) {
+                       ASSERT(del_nr == 0);
+                       path->slots[0]++;
+                       goto next_slot;
+               }
+               if (key.type > BTRFS_EXTENT_DATA_KEY || key.offset >= end)
                        break;
 
                fi = btrfs_item_ptr(leaf, path->slots[0],
@@ -776,8 +784,8 @@ next_slot:
                                btrfs_file_extent_inline_len(leaf,
                                                     path->slots[0], fi);
                } else {
-                       WARN_ON(1);
-                       extent_end = search_start;
+                       /* can't happen */
+                       BUG();
                }
 
                /*
@@ -1604,12 +1612,17 @@ again:
                                BTRFS_I(inode)->outstanding_extents++;
                                spin_unlock(&BTRFS_I(inode)->lock);
                        }
-                       if (only_release_metadata)
+                       if (only_release_metadata) {
                                btrfs_delalloc_release_metadata(inode,
                                                                release_bytes);
-                       else
-                               btrfs_delalloc_release_space(inode, pos,
+                       } else {
+                               u64 __pos;
+
+                               __pos = round_down(pos, root->sectorsize) +
+                                       (dirty_pages << PAGE_CACHE_SHIFT);
+                               btrfs_delalloc_release_space(inode, __pos,
                                                             release_bytes);
+                       }
                }
 
                release_bytes = dirty_pages << PAGE_CACHE_SHIFT;
@@ -2488,6 +2501,19 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
        }
 
        trans->block_rsv = &root->fs_info->trans_block_rsv;
+       /*
+        * If we are using the NO_HOLES feature we might have had already an
+        * hole that overlaps a part of the region [lockstart, lockend] and
+        * ends at (or beyond) lockend. Since we have no file extent items to
+        * represent holes, drop_end can be less than lockend and so we must
+        * make sure we have an extent map representing the existing hole (the
+        * call to __btrfs_drop_extents() might have dropped the existing extent
+        * map representing the existing hole), otherwise the fast fsync path
+        * will not record the existence of the hole region
+        * [existing_hole_start, lockend].
+        */
+       if (drop_end <= lockend)
+               drop_end = lockend + 1;
        /*
         * Don't insert file hole extent item if it's for a range beyond eof
         * (because it's useless) or if it represents a 0 bytes range (when