diff options
Diffstat (limited to 'fs/btrfs/compression.c')
| -rw-r--r-- | fs/btrfs/compression.c | 171 | 
1 files changed, 84 insertions, 87 deletions
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 9a023ae0f98b..7869ad12bc6e 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -172,10 +172,9 @@ static int check_compressed_csum(struct btrfs_inode *inode, struct bio *bio,  		/* Hash through the page sector by sector */  		for (pg_offset = 0; pg_offset < bytes_left;  		     pg_offset += sectorsize) { -			kaddr = kmap_atomic(page); +			kaddr = page_address(page);  			crypto_shash_digest(shash, kaddr + pg_offset,  					    sectorsize, csum); -			kunmap_atomic(kaddr);  			if (memcmp(&csum, cb_sum, csum_size) != 0) {  				btrfs_print_data_csum_error(inode, disk_start, @@ -352,7 +351,7 @@ static void end_compressed_bio_write(struct bio *bio)  	btrfs_record_physical_zoned(inode, cb->start, bio);  	btrfs_writepage_endio_finish_ordered(BTRFS_I(inode), NULL,  			cb->start, cb->start + cb->len - 1, -			bio->bi_status == BLK_STS_OK); +			!cb->errors);  	end_compressed_writeback(inode, cb);  	/* note, our inode could be gone now */ @@ -565,6 +564,16 @@ static noinline int add_ra_bio_pages(struct inode *inode,  	if (isize == 0)  		return 0; +	/* +	 * For current subpage support, we only support 64K page size, +	 * which means maximum compressed extent size (128K) is just 2x page +	 * size. +	 * This makes readahead less effective, so here disable readahead for +	 * subpage for now, until full compressed write is supported. +	 */ +	if (btrfs_sb(inode->i_sb)->sectorsize < PAGE_SIZE) +		return 0; +  	end_index = (i_size_read(inode) - 1) >> PAGE_SHIFT;  	while (last_offset < compressed_end) { @@ -673,6 +682,7 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,  	struct page *page;  	struct bio *comp_bio;  	u64 cur_disk_byte = bio->bi_iter.bi_sector << 9; +	u64 file_offset;  	u64 em_len;  	u64 em_start;  	struct extent_map *em; @@ -682,15 +692,17 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,  	em_tree = &BTRFS_I(inode)->extent_tree; +	file_offset = bio_first_bvec_all(bio)->bv_offset + +		      page_offset(bio_first_page_all(bio)); +  	/* we need the actual starting offset of this extent in the file */  	read_lock(&em_tree->lock); -	em = lookup_extent_mapping(em_tree, -				   page_offset(bio_first_page_all(bio)), -				   fs_info->sectorsize); +	em = lookup_extent_mapping(em_tree, file_offset, fs_info->sectorsize);  	read_unlock(&em_tree->lock);  	if (!em)  		return BLK_STS_IOERR; +	ASSERT(em->compress_type != BTRFS_COMPRESS_NONE);  	compressed_len = em->block_len;  	cb = kmalloc(compressed_bio_size(fs_info, compressed_len), GFP_NOFS);  	if (!cb) @@ -721,8 +733,7 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,  		goto fail1;  	for (pg_index = 0; pg_index < nr_pages; pg_index++) { -		cb->compressed_pages[pg_index] = alloc_page(GFP_NOFS | -							      __GFP_HIGHMEM); +		cb->compressed_pages[pg_index] = alloc_page(GFP_NOFS);  		if (!cb->compressed_pages[pg_index]) {  			faili = pg_index - 1;  			ret = BLK_STS_RESOURCE; @@ -1261,96 +1272,82 @@ void __cold btrfs_exit_compress(void)  }  /* - * Copy uncompressed data from working buffer to pages. + * Copy decompressed data from working buffer to pages. + * + * @buf:		The decompressed data buffer + * @buf_len:		The decompressed data length + * @decompressed:	Number of bytes that are already decompressed inside the + * 			compressed extent + * @cb:			The compressed extent descriptor + * @orig_bio:		The original bio that the caller wants to read for + * + * An easier to understand graph is like below: + * + * 		|<- orig_bio ->|     |<- orig_bio->| + * 	|<-------      full decompressed extent      ----->| + * 	|<-----------    @cb range   ---->| + * 	|			|<-- @buf_len -->| + * 	|<--- @decompressed --->| + * + * Note that, @cb can be a subpage of the full decompressed extent, but + * @cb->start always has the same as the orig_file_offset value of the full + * decompressed extent.   * - * buf_start is the byte offset we're of the start of our workspace buffer. + * When reading compressed extent, we have to read the full compressed extent, + * while @orig_bio may only want part of the range. + * Thus this function will ensure only data covered by @orig_bio will be copied + * to.   * - * total_out is the last byte of the buffer + * Return 0 if we have copied all needed contents for @orig_bio. + * Return >0 if we need continue decompress.   */ -int btrfs_decompress_buf2page(const char *buf, unsigned long buf_start, -			      unsigned long total_out, u64 disk_start, -			      struct bio *bio) +int btrfs_decompress_buf2page(const char *buf, u32 buf_len, +			      struct compressed_bio *cb, u32 decompressed)  { -	unsigned long buf_offset; -	unsigned long current_buf_start; -	unsigned long start_byte; -	unsigned long prev_start_byte; -	unsigned long working_bytes = total_out - buf_start; -	unsigned long bytes; -	struct bio_vec bvec = bio_iter_iovec(bio, bio->bi_iter); - -	/* -	 * start byte is the first byte of the page we're currently -	 * copying into relative to the start of the compressed data. -	 */ -	start_byte = page_offset(bvec.bv_page) - disk_start; - -	/* we haven't yet hit data corresponding to this page */ -	if (total_out <= start_byte) -		return 1; - -	/* -	 * the start of the data we care about is offset into -	 * the middle of our working buffer -	 */ -	if (total_out > start_byte && buf_start < start_byte) { -		buf_offset = start_byte - buf_start; -		working_bytes -= buf_offset; -	} else { -		buf_offset = 0; -	} -	current_buf_start = buf_start; - -	/* copy bytes from the working buffer into the pages */ -	while (working_bytes > 0) { -		bytes = min_t(unsigned long, bvec.bv_len, -				PAGE_SIZE - (buf_offset % PAGE_SIZE)); -		bytes = min(bytes, working_bytes); - -		memcpy_to_page(bvec.bv_page, bvec.bv_offset, buf + buf_offset, -			       bytes); -		flush_dcache_page(bvec.bv_page); +	struct bio *orig_bio = cb->orig_bio; +	/* Offset inside the full decompressed extent */ +	u32 cur_offset; + +	cur_offset = decompressed; +	/* The main loop to do the copy */ +	while (cur_offset < decompressed + buf_len) { +		struct bio_vec bvec; +		size_t copy_len; +		u32 copy_start; +		/* Offset inside the full decompressed extent */ +		u32 bvec_offset; + +		bvec = bio_iter_iovec(orig_bio, orig_bio->bi_iter); +		/* +		 * cb->start may underflow, but subtracting that value can still +		 * give us correct offset inside the full decompressed extent. +		 */ +		bvec_offset = page_offset(bvec.bv_page) + bvec.bv_offset - cb->start; -		buf_offset += bytes; -		working_bytes -= bytes; -		current_buf_start += bytes; +		/* Haven't reached the bvec range, exit */ +		if (decompressed + buf_len <= bvec_offset) +			return 1; -		/* check if we need to pick another page */ -		bio_advance(bio, bytes); -		if (!bio->bi_iter.bi_size) -			return 0; -		bvec = bio_iter_iovec(bio, bio->bi_iter); -		prev_start_byte = start_byte; -		start_byte = page_offset(bvec.bv_page) - disk_start; +		copy_start = max(cur_offset, bvec_offset); +		copy_len = min(bvec_offset + bvec.bv_len, +			       decompressed + buf_len) - copy_start; +		ASSERT(copy_len);  		/* -		 * We need to make sure we're only adjusting -		 * our offset into compression working buffer when -		 * we're switching pages.  Otherwise we can incorrectly -		 * keep copying when we were actually done. +		 * Extra range check to ensure we didn't go beyond +		 * @buf + @buf_len.  		 */ -		if (start_byte != prev_start_byte) { -			/* -			 * make sure our new page is covered by this -			 * working buffer -			 */ -			if (total_out <= start_byte) -				return 1; +		ASSERT(copy_start - decompressed < buf_len); +		memcpy_to_page(bvec.bv_page, bvec.bv_offset, +			       buf + copy_start - decompressed, copy_len); +		flush_dcache_page(bvec.bv_page); +		cur_offset += copy_len; -			/* -			 * the next page in the biovec might not be adjacent -			 * to the last page, but it might still be found -			 * inside this working buffer. bump our offset pointer -			 */ -			if (total_out > start_byte && -			    current_buf_start < start_byte) { -				buf_offset = start_byte - buf_start; -				working_bytes = total_out - start_byte; -				current_buf_start = buf_start + buf_offset; -			} -		} +		bio_advance(orig_bio, copy_len); +		/* Finished the bio */ +		if (!orig_bio->bi_iter.bi_size) +			return 0;  	} -  	return 1;  }  |