diff options
Diffstat (limited to 'fs/btrfs/disk-io.c')
| -rw-r--r-- | fs/btrfs/disk-io.c | 74 | 
1 files changed, 27 insertions, 47 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index b0ab41da91d1..6d776717d8b3 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -477,9 +477,9 @@ static int btree_read_extent_buffer_pages(struct btrfs_fs_info *fs_info,  	int mirror_num = 0;  	int failed_mirror = 0; -	clear_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);  	io_tree = &BTRFS_I(fs_info->btree_inode)->io_tree;  	while (1) { +		clear_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);  		ret = read_extent_buffer_pages(io_tree, eb, WAIT_COMPLETE,  					       mirror_num);  		if (!ret) { @@ -493,15 +493,6 @@ static int btree_read_extent_buffer_pages(struct btrfs_fs_info *fs_info,  				break;  		} -		/* -		 * This buffer's crc is fine, but its contents are corrupted, so -		 * there is no reason to read the other copies, they won't be -		 * any less wrong. -		 */ -		if (test_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags) || -		    ret == -EUCLEAN) -			break; -  		num_copies = btrfs_num_copies(fs_info,  					      eb->start, eb->len);  		if (num_copies == 1) @@ -1664,9 +1655,8 @@ static int cleaner_kthread(void *arg)  	struct btrfs_root *root = arg;  	struct btrfs_fs_info *fs_info = root->fs_info;  	int again; -	struct btrfs_trans_handle *trans; -	do { +	while (1) {  		again = 0;  		/* Make the cleaner go to sleep early. */ @@ -1715,42 +1705,16 @@ static int cleaner_kthread(void *arg)  		 */  		btrfs_delete_unused_bgs(fs_info);  sleep: +		if (kthread_should_park()) +			kthread_parkme(); +		if (kthread_should_stop()) +			return 0;  		if (!again) {  			set_current_state(TASK_INTERRUPTIBLE); -			if (!kthread_should_stop()) -				schedule(); +			schedule();  			__set_current_state(TASK_RUNNING);  		} -	} while (!kthread_should_stop()); - -	/* -	 * Transaction kthread is stopped before us and wakes us up. -	 * However we might have started a new transaction and COWed some -	 * tree blocks when deleting unused block groups for example. So -	 * make sure we commit the transaction we started to have a clean -	 * shutdown when evicting the btree inode - if it has dirty pages -	 * when we do the final iput() on it, eviction will trigger a -	 * writeback for it which will fail with null pointer dereferences -	 * since work queues and other resources were already released and -	 * destroyed by the time the iput/eviction/writeback is made. -	 */ -	trans = btrfs_attach_transaction(root); -	if (IS_ERR(trans)) { -		if (PTR_ERR(trans) != -ENOENT) -			btrfs_err(fs_info, -				  "cleaner transaction attach returned %ld", -				  PTR_ERR(trans)); -	} else { -		int ret; - -		ret = btrfs_commit_transaction(trans); -		if (ret) -			btrfs_err(fs_info, -				  "cleaner open transaction commit returned %d", -				  ret);  	} - -	return 0;  }  static int transaction_kthread(void *arg) @@ -3931,6 +3895,13 @@ void close_ctree(struct btrfs_fs_info *fs_info)  	int ret;  	set_bit(BTRFS_FS_CLOSING_START, &fs_info->flags); +	/* +	 * We don't want the cleaner to start new transactions, add more delayed +	 * iputs, etc. while we're closing. We can't use kthread_stop() yet +	 * because that frees the task_struct, and the transaction kthread might +	 * still try to wake up the cleaner. +	 */ +	kthread_park(fs_info->cleaner_kthread);  	/* wait for the qgroup rescan worker to stop */  	btrfs_qgroup_wait_for_completion(fs_info, false); @@ -3958,9 +3929,8 @@ void close_ctree(struct btrfs_fs_info *fs_info)  	if (!sb_rdonly(fs_info->sb)) {  		/* -		 * If the cleaner thread is stopped and there are -		 * block groups queued for removal, the deletion will be -		 * skipped when we quit the cleaner thread. +		 * The cleaner kthread is stopped, so do one final pass over +		 * unused block groups.  		 */  		btrfs_delete_unused_bgs(fs_info); @@ -4359,13 +4329,23 @@ static int btrfs_destroy_pinned_extent(struct btrfs_fs_info *fs_info,  	unpin = pinned_extents;  again:  	while (1) { +		/* +		 * The btrfs_finish_extent_commit() may get the same range as +		 * ours between find_first_extent_bit and clear_extent_dirty. +		 * Hence, hold the unused_bg_unpin_mutex to avoid double unpin +		 * the same extent range. +		 */ +		mutex_lock(&fs_info->unused_bg_unpin_mutex);  		ret = find_first_extent_bit(unpin, 0, &start, &end,  					    EXTENT_DIRTY, NULL); -		if (ret) +		if (ret) { +			mutex_unlock(&fs_info->unused_bg_unpin_mutex);  			break; +		}  		clear_extent_dirty(unpin, start, end);  		btrfs_error_unpin_extent_range(fs_info, start, end); +		mutex_unlock(&fs_info->unused_bg_unpin_mutex);  		cond_resched();  	}  |