Alex Lyakas reported a bug where wait_block_group_cache_progress() would wait
forever if a drive failed. This is because we just bail out if there is an
error while trying to cache a block group, we don't update anybody who may be
waiting. So this introduces a new enum for the cache state in case of error and
makes everybody bail out if we have an error. Alex tested and verified this
patch fixed his problem. This fixes bz 59431. Thanks,
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
BTRFS_CACHE_STARTED = 1,
BTRFS_CACHE_FAST = 2,
BTRFS_CACHE_FINISHED = 3,
BTRFS_CACHE_STARTED = 1,
BTRFS_CACHE_FAST = 2,
BTRFS_CACHE_FINISHED = 3,
};
enum btrfs_disk_cache_state {
};
enum btrfs_disk_cache_state {
block_group_cache_done(struct btrfs_block_group_cache *cache)
{
smp_mb();
block_group_cache_done(struct btrfs_block_group_cache *cache)
{
smp_mb();
- return cache->cached == BTRFS_CACHE_FINISHED;
+ return cache->cached == BTRFS_CACHE_FINISHED ||
+ cache->cached == BTRFS_CACHE_ERROR;
}
static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
}
static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
u64 total_found = 0;
u64 last = 0;
u32 nritems;
u64 total_found = 0;
u64 last = 0;
u32 nritems;
caching_ctl = container_of(work, struct btrfs_caching_control, work);
block_group = caching_ctl->block_group;
caching_ctl = container_of(work, struct btrfs_caching_control, work);
block_group = caching_ctl->block_group;
mutex_unlock(&caching_ctl->mutex);
out:
mutex_unlock(&caching_ctl->mutex);
out:
+ if (ret) {
+ spin_lock(&block_group->lock);
+ block_group->caching_ctl = NULL;
+ block_group->cached = BTRFS_CACHE_ERROR;
+ spin_unlock(&block_group->lock);
+ }
wake_up(&caching_ctl->wait);
put_caching_control(caching_ctl);
wake_up(&caching_ctl->wait);
put_caching_control(caching_ctl);
* for our min num_bytes. Another option is to have it go ahead
* and look in the rbtree for a free extent of a given size, but this
* is a good start.
* for our min num_bytes. Another option is to have it go ahead
* and look in the rbtree for a free extent of a given size, but this
* is a good start.
+ *
+ * Callers of this must check if cache->cached == BTRFS_CACHE_ERROR before using
+ * any of the information in this block group.
wait_block_group_cache_progress(struct btrfs_block_group_cache *cache,
u64 num_bytes)
{
wait_block_group_cache_progress(struct btrfs_block_group_cache *cache,
u64 num_bytes)
{
caching_ctl = get_caching_control(cache);
if (!caching_ctl)
caching_ctl = get_caching_control(cache);
if (!caching_ctl)
wait_event(caching_ctl->wait, block_group_cache_done(cache) ||
(cache->free_space_ctl->free_space >= num_bytes));
put_caching_control(caching_ctl);
wait_event(caching_ctl->wait, block_group_cache_done(cache) ||
(cache->free_space_ctl->free_space >= num_bytes));
put_caching_control(caching_ctl);
}
static noinline int
wait_block_group_cache_done(struct btrfs_block_group_cache *cache)
{
struct btrfs_caching_control *caching_ctl;
}
static noinline int
wait_block_group_cache_done(struct btrfs_block_group_cache *cache)
{
struct btrfs_caching_control *caching_ctl;
caching_ctl = get_caching_control(cache);
if (!caching_ctl)
caching_ctl = get_caching_control(cache);
if (!caching_ctl)
+ return (cache->cached == BTRFS_CACHE_ERROR) ? -EIO : 0;
wait_event(caching_ctl->wait, block_group_cache_done(cache));
wait_event(caching_ctl->wait, block_group_cache_done(cache));
+ if (cache->cached == BTRFS_CACHE_ERROR)
+ ret = -EIO;
put_caching_control(caching_ctl);
put_caching_control(caching_ctl);
}
int __get_raid_index(u64 flags)
}
int __get_raid_index(u64 flags)
+ if (unlikely(block_group->cached == BTRFS_CACHE_ERROR))
+ goto loop;
if (unlikely(block_group->ro))
goto loop;
if (unlikely(block_group->ro))
goto loop;
* We haven't cached this block group, which means we could
* possibly have excluded extents on this block group.
*/
* We haven't cached this block group, which means we could
* possibly have excluded extents on this block group.
*/
- if (block_group->cached == BTRFS_CACHE_NO)
+ if (block_group->cached == BTRFS_CACHE_NO ||
+ block_group->cached == BTRFS_CACHE_ERROR)
free_excluded_extents(info->extent_root, block_group);
btrfs_remove_free_space_cache(block_group);
free_excluded_extents(info->extent_root, block_group);
btrfs_remove_free_space_cache(block_group);