diff options
Diffstat (limited to 'fs/btrfs/ordered-data.c')
| -rw-r--r-- | fs/btrfs/ordered-data.c | 169 | 
1 files changed, 110 insertions, 59 deletions
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 966b413a33b..7187b14faa6 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -67,7 +67,7 @@ static void ordered_data_tree_panic(struct inode *inode, int errno,  {  	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);  	btrfs_panic(fs_info, errno, "Inconsistency in ordered tree at offset " -		    "%llu\n", offset); +		    "%llu", offset);  }  /* @@ -336,22 +336,26 @@ int btrfs_dec_test_first_ordered_pending(struct inode *inode,  		      entry->len);  	*file_offset = dec_end;  	if (dec_start > dec_end) { -		printk(KERN_CRIT "bad ordering dec_start %llu end %llu\n", -		       dec_start, dec_end); +		btrfs_crit(BTRFS_I(inode)->root->fs_info, +			"bad ordering dec_start %llu end %llu", dec_start, dec_end);  	}  	to_dec = dec_end - dec_start;  	if (to_dec > entry->bytes_left) { -		printk(KERN_CRIT "bad ordered accounting left %llu size %llu\n", -		       entry->bytes_left, to_dec); +		btrfs_crit(BTRFS_I(inode)->root->fs_info, +			"bad ordered accounting left %llu size %llu", +			entry->bytes_left, to_dec);  	}  	entry->bytes_left -= to_dec;  	if (!uptodate)  		set_bit(BTRFS_ORDERED_IOERR, &entry->flags); -	if (entry->bytes_left == 0) +	if (entry->bytes_left == 0) {  		ret = test_and_set_bit(BTRFS_ORDERED_IO_DONE, &entry->flags); -	else +		if (waitqueue_active(&entry->wait)) +			wake_up(&entry->wait); +	} else {  		ret = 1; +	}  out:  	if (!ret && cached && entry) {  		*cached = entry; @@ -401,17 +405,21 @@ have_entry:  	}  	if (io_size > entry->bytes_left) { -		printk(KERN_CRIT "bad ordered accounting left %llu size %llu\n", +		btrfs_crit(BTRFS_I(inode)->root->fs_info, +			   "bad ordered accounting left %llu size %llu",  		       entry->bytes_left, io_size);  	}  	entry->bytes_left -= io_size;  	if (!uptodate)  		set_bit(BTRFS_ORDERED_IOERR, &entry->flags); -	if (entry->bytes_left == 0) +	if (entry->bytes_left == 0) {  		ret = test_and_set_bit(BTRFS_ORDERED_IO_DONE, &entry->flags); -	else +		if (waitqueue_active(&entry->wait)) +			wake_up(&entry->wait); +	} else {  		ret = 1; +	}  out:  	if (!ret && cached && entry) {  		*cached = entry; @@ -422,27 +430,48 @@ out:  }  /* Needs to either be called under a log transaction or the log_mutex */ -void btrfs_get_logged_extents(struct btrfs_root *log, struct inode *inode) +void btrfs_get_logged_extents(struct inode *inode, +			      struct list_head *logged_list)  {  	struct btrfs_ordered_inode_tree *tree;  	struct btrfs_ordered_extent *ordered;  	struct rb_node *n; -	int index = log->log_transid % 2;  	tree = &BTRFS_I(inode)->ordered_tree;  	spin_lock_irq(&tree->lock);  	for (n = rb_first(&tree->tree); n; n = rb_next(n)) {  		ordered = rb_entry(n, struct btrfs_ordered_extent, rb_node); -		spin_lock(&log->log_extents_lock[index]); -		if (list_empty(&ordered->log_list)) { -			list_add_tail(&ordered->log_list, &log->logged_list[index]); -			atomic_inc(&ordered->refs); -		} -		spin_unlock(&log->log_extents_lock[index]); +		if (!list_empty(&ordered->log_list)) +			continue; +		list_add_tail(&ordered->log_list, logged_list); +		atomic_inc(&ordered->refs);  	}  	spin_unlock_irq(&tree->lock);  } +void btrfs_put_logged_extents(struct list_head *logged_list) +{ +	struct btrfs_ordered_extent *ordered; + +	while (!list_empty(logged_list)) { +		ordered = list_first_entry(logged_list, +					   struct btrfs_ordered_extent, +					   log_list); +		list_del_init(&ordered->log_list); +		btrfs_put_ordered_extent(ordered); +	} +} + +void btrfs_submit_logged_extents(struct list_head *logged_list, +				 struct btrfs_root *log) +{ +	int index = log->log_transid % 2; + +	spin_lock_irq(&log->log_extents_lock[index]); +	list_splice_tail(logged_list, &log->logged_list[index]); +	spin_unlock_irq(&log->log_extents_lock[index]); +} +  void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid)  {  	struct btrfs_ordered_extent *ordered; @@ -455,8 +484,19 @@ void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid)  					   log_list);  		list_del_init(&ordered->log_list);  		spin_unlock_irq(&log->log_extents_lock[index]); + +		if (!test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags) && +		    !test_bit(BTRFS_ORDERED_DIRECT, &ordered->flags)) { +			struct inode *inode = ordered->inode; +			u64 start = ordered->file_offset; +			u64 end = ordered->file_offset + ordered->len - 1; + +			WARN_ON(!inode); +			filemap_fdatawrite_range(inode->i_mapping, start, end); +		}  		wait_event(ordered->wait, test_bit(BTRFS_ORDERED_IO_DONE,  						   &ordered->flags)); +  		btrfs_put_ordered_extent(ordered);  		spin_lock_irq(&log->log_extents_lock[index]);  	} @@ -520,7 +560,8 @@ void btrfs_remove_ordered_extent(struct inode *inode,  	spin_lock_irq(&tree->lock);  	node = &entry->rb_node;  	rb_erase(node, &tree->tree); -	tree->last = NULL; +	if (tree->last == node) +		tree->last = NULL;  	set_bit(BTRFS_ORDERED_COMPLETE, &entry->flags);  	spin_unlock_irq(&tree->lock); @@ -537,7 +578,9 @@ void btrfs_remove_ordered_extent(struct inode *inode,  	 */  	if (RB_EMPTY_ROOT(&tree->tree) &&  	    !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_DIRTY)) { +		spin_lock(&root->fs_info->ordered_root_lock);  		list_del_init(&BTRFS_I(inode)->ordered_operations); +		spin_unlock(&root->fs_info->ordered_root_lock);  	}  	if (!root->nr_ordered_extents) { @@ -563,72 +606,64 @@ static void btrfs_run_ordered_extent_work(struct btrfs_work *work)   * wait for all the ordered extents in a root.  This is done when balancing   * space between drives.   */ -void btrfs_wait_ordered_extents(struct btrfs_root *root, int delay_iput) +int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr)  {  	struct list_head splice, works;  	struct btrfs_ordered_extent *ordered, *next; -	struct inode *inode; +	int count = 0;  	INIT_LIST_HEAD(&splice);  	INIT_LIST_HEAD(&works); -	mutex_lock(&root->fs_info->ordered_operations_mutex); +	mutex_lock(&root->ordered_extent_mutex);  	spin_lock(&root->ordered_extent_lock);  	list_splice_init(&root->ordered_extents, &splice); -	while (!list_empty(&splice)) { +	while (!list_empty(&splice) && nr) {  		ordered = list_first_entry(&splice, struct btrfs_ordered_extent,  					   root_extent_list);  		list_move_tail(&ordered->root_extent_list,  			       &root->ordered_extents); -		/* -		 * the inode may be getting freed (in sys_unlink path). -		 */ -		inode = igrab(ordered->inode); -		if (!inode) { -			cond_resched_lock(&root->ordered_extent_lock); -			continue; -		} -  		atomic_inc(&ordered->refs);  		spin_unlock(&root->ordered_extent_lock); -		ordered->flush_work.func = btrfs_run_ordered_extent_work; +		btrfs_init_work(&ordered->flush_work, +				btrfs_run_ordered_extent_work, NULL, NULL);  		list_add_tail(&ordered->work_list, &works); -		btrfs_queue_worker(&root->fs_info->flush_workers, -				   &ordered->flush_work); +		btrfs_queue_work(root->fs_info->flush_workers, +				 &ordered->flush_work);  		cond_resched();  		spin_lock(&root->ordered_extent_lock); +		if (nr != -1) +			nr--; +		count++;  	} +	list_splice_tail(&splice, &root->ordered_extents);  	spin_unlock(&root->ordered_extent_lock);  	list_for_each_entry_safe(ordered, next, &works, work_list) {  		list_del_init(&ordered->work_list);  		wait_for_completion(&ordered->completion); - -		inode = ordered->inode;  		btrfs_put_ordered_extent(ordered); -		if (delay_iput) -			btrfs_add_delayed_iput(inode); -		else -			iput(inode); -  		cond_resched();  	} -	mutex_unlock(&root->fs_info->ordered_operations_mutex); +	mutex_unlock(&root->ordered_extent_mutex); + +	return count;  } -void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info, -				    int delay_iput) +void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr)  {  	struct btrfs_root *root;  	struct list_head splice; +	int done;  	INIT_LIST_HEAD(&splice); +	mutex_lock(&fs_info->ordered_operations_mutex);  	spin_lock(&fs_info->ordered_root_lock);  	list_splice_init(&fs_info->ordered_roots, &splice); -	while (!list_empty(&splice)) { +	while (!list_empty(&splice) && nr) {  		root = list_first_entry(&splice, struct btrfs_root,  					ordered_root);  		root = btrfs_grab_fs_root(root); @@ -637,12 +672,18 @@ void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info,  			       &fs_info->ordered_roots);  		spin_unlock(&fs_info->ordered_root_lock); -		btrfs_wait_ordered_extents(root, delay_iput); +		done = btrfs_wait_ordered_extents(root, nr);  		btrfs_put_fs_root(root);  		spin_lock(&fs_info->ordered_root_lock); +		if (nr != -1) { +			nr -= done; +			WARN_ON(nr < 0); +		}  	} +	list_splice_tail(&splice, &fs_info->ordered_roots);  	spin_unlock(&fs_info->ordered_root_lock); +	mutex_unlock(&fs_info->ordered_operations_mutex);  }  /* @@ -704,8 +745,8 @@ int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans,  			goto out;  		}  		list_add_tail(&work->list, &works); -		btrfs_queue_worker(&root->fs_info->flush_workers, -				   &work->work); +		btrfs_queue_work(root->fs_info->flush_workers, +				 &work->work);  		cond_resched();  		spin_lock(&root->fs_info->ordered_root_lock); @@ -752,8 +793,9 @@ void btrfs_start_ordered_extent(struct inode *inode,  /*   * Used to wait on ordered extents across a large range of bytes.   */ -void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) +int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)  { +	int ret = 0;  	u64 end;  	u64 orig_end;  	struct btrfs_ordered_extent *ordered; @@ -769,8 +811,9 @@ void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)  	/* start IO across the range first to instantiate any delalloc  	 * extents  	 */ -	filemap_fdatawrite_range(inode->i_mapping, start, orig_end); - +	ret = filemap_fdatawrite_range(inode->i_mapping, start, orig_end); +	if (ret) +		return ret;  	/*  	 * So with compression we will find and lock a dirty page and clear the  	 * first one as dirty, setup an async extent, and immediately return @@ -786,10 +829,15 @@ void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)  	 * right and you are wrong.  	 */  	if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT, -		     &BTRFS_I(inode)->runtime_flags)) -		filemap_fdatawrite_range(inode->i_mapping, start, orig_end); - -	filemap_fdatawait_range(inode->i_mapping, start, orig_end); +		     &BTRFS_I(inode)->runtime_flags)) { +		ret = filemap_fdatawrite_range(inode->i_mapping, start, +					       orig_end); +		if (ret) +			return ret; +	} +	ret = filemap_fdatawait_range(inode->i_mapping, start, orig_end); +	if (ret) +		return ret;  	end = orig_end;  	while (1) { @@ -800,17 +848,20 @@ void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)  			btrfs_put_ordered_extent(ordered);  			break;  		} -		if (ordered->file_offset + ordered->len < start) { +		if (ordered->file_offset + ordered->len <= start) {  			btrfs_put_ordered_extent(ordered);  			break;  		}  		btrfs_start_ordered_extent(inode, ordered, 1);  		end = ordered->file_offset; +		if (test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)) +			ret = -EIO;  		btrfs_put_ordered_extent(ordered); -		if (end == 0 || end == start) +		if (ret || end == 0 || end == start)  			break;  		end--;  	} +	return ret;  }  /* @@ -1094,7 +1145,7 @@ void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,  	 * if this file hasn't been changed since the last transaction  	 * commit, we can safely return without doing anything  	 */ -	if (last_mod < root->fs_info->last_trans_committed) +	if (last_mod <= root->fs_info->last_trans_committed)  		return;  	spin_lock(&root->fs_info->ordered_root_lock);  | 
