Skip to content

Commit

Permalink
Btrfs: deal with duplciates during extent_map insertion in btrfs_get_…
Browse files Browse the repository at this point in the history
…extent

When dealing with inline extents, btrfs_get_extent will incorrectly try
to insert a duplicate extent_map.  The dup hits -EEXIST from
add_extent_map, but then we try to merge with the existing one and end
up trying to insert a zero length extent_map.

This actually works most of the time, except when there are extent maps
past the end of the inline extent.  rocksdb will trigger this sometimes
because it preallocates an extent and then truncates down.

Josef made a script to trigger with xfs_io:

	#!/bin/bash

	xfs_io -f -c "pwrite 0 1000" inline
	xfs_io -c "falloc -k 4k 1M" inline
	xfs_io -c "pread 0 1000" -c "fadvise -d 0 1000" -c "pread 0 1000" inline
	xfs_io -c "fadvise -d 0 1000" inline
	cat inline

You'll get EIOs trying to read inline after this because add_extent_map
is returning EEXIST

Signed-off-by: Chris Mason <clm@fb.com>
  • Loading branch information
Chris Mason committed Jun 3, 2016
1 parent f881dd2 commit 8dff9c8
Showing 1 changed file with 12 additions and 1 deletion.
13 changes: 12 additions & 1 deletion fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -6979,7 +6979,18 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
* existing will always be non-NULL, since there must be
* extent causing the -EEXIST.
*/
if (start >= extent_map_end(existing) ||
if (existing->start == em->start &&
extent_map_end(existing) == extent_map_end(em) &&
em->block_start == existing->block_start) {
/*
* these two extents are the same, it happens
* with inlines especially
*/
free_extent_map(em);
em = existing;
err = 0;

} else if (start >= extent_map_end(existing) ||
start <= existing->start) {
/*
* The existing extent map is the one nearest to
Expand Down

0 comments on commit 8dff9c8

Please sign in to comment.