162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2008 Oracle.  All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/mm.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/sched.h>
1262306a36Sopenharmony_ci#include <linux/pagemap.h>
1362306a36Sopenharmony_ci#include <linux/bio.h>
1462306a36Sopenharmony_ci#include <linux/lzo.h>
1562306a36Sopenharmony_ci#include <linux/refcount.h>
1662306a36Sopenharmony_ci#include "messages.h"
1762306a36Sopenharmony_ci#include "compression.h"
1862306a36Sopenharmony_ci#include "ctree.h"
1962306a36Sopenharmony_ci#include "super.h"
2062306a36Sopenharmony_ci#include "btrfs_inode.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define LZO_LEN	4
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * Btrfs LZO compression format
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Regular and inlined LZO compressed data extents consist of:
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * 1.  Header
3062306a36Sopenharmony_ci *     Fixed size. LZO_LEN (4) bytes long, LE32.
3162306a36Sopenharmony_ci *     Records the total size (including the header) of compressed data.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * 2.  Segment(s)
3462306a36Sopenharmony_ci *     Variable size. Each segment includes one segment header, followed by data
3562306a36Sopenharmony_ci *     payload.
3662306a36Sopenharmony_ci *     One regular LZO compressed extent can have one or more segments.
3762306a36Sopenharmony_ci *     For inlined LZO compressed extent, only one segment is allowed.
3862306a36Sopenharmony_ci *     One segment represents at most one sector of uncompressed data.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * 2.1 Segment header
4162306a36Sopenharmony_ci *     Fixed size. LZO_LEN (4) bytes long, LE32.
4262306a36Sopenharmony_ci *     Records the total size of the segment (not including the header).
4362306a36Sopenharmony_ci *     Segment header never crosses sector boundary, thus it's possible to
4462306a36Sopenharmony_ci *     have at most 3 padding zeros at the end of the sector.
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * 2.2 Data Payload
4762306a36Sopenharmony_ci *     Variable size. Size up limit should be lzo1x_worst_compress(sectorsize)
4862306a36Sopenharmony_ci *     which is 4419 for a 4KiB sectorsize.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * Example with 4K sectorsize:
5162306a36Sopenharmony_ci * Page 1:
5262306a36Sopenharmony_ci *          0     0x2   0x4   0x6   0x8   0xa   0xc   0xe     0x10
5362306a36Sopenharmony_ci * 0x0000   |  Header   | SegHdr 01 | Data payload 01 ...     |
5462306a36Sopenharmony_ci * ...
5562306a36Sopenharmony_ci * 0x0ff0   | SegHdr  N | Data payload  N     ...          |00|
5662306a36Sopenharmony_ci *                                                          ^^ padding zeros
5762306a36Sopenharmony_ci * Page 2:
5862306a36Sopenharmony_ci * 0x1000   | SegHdr N+1| Data payload N+1 ...                |
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define WORKSPACE_BUF_LENGTH	(lzo1x_worst_compress(PAGE_SIZE))
6262306a36Sopenharmony_ci#define WORKSPACE_CBUF_LENGTH	(lzo1x_worst_compress(PAGE_SIZE))
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct workspace {
6562306a36Sopenharmony_ci	void *mem;
6662306a36Sopenharmony_ci	void *buf;	/* where decompressed data goes */
6762306a36Sopenharmony_ci	void *cbuf;	/* where compressed data goes */
6862306a36Sopenharmony_ci	struct list_head list;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic struct workspace_manager wsm;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_civoid lzo_free_workspace(struct list_head *ws)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct workspace *workspace = list_entry(ws, struct workspace, list);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	kvfree(workspace->buf);
7862306a36Sopenharmony_ci	kvfree(workspace->cbuf);
7962306a36Sopenharmony_ci	kvfree(workspace->mem);
8062306a36Sopenharmony_ci	kfree(workspace);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct list_head *lzo_alloc_workspace(unsigned int level)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct workspace *workspace;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	workspace = kzalloc(sizeof(*workspace), GFP_KERNEL);
8862306a36Sopenharmony_ci	if (!workspace)
8962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	workspace->mem = kvmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL | __GFP_NOWARN);
9262306a36Sopenharmony_ci	workspace->buf = kvmalloc(WORKSPACE_BUF_LENGTH, GFP_KERNEL | __GFP_NOWARN);
9362306a36Sopenharmony_ci	workspace->cbuf = kvmalloc(WORKSPACE_CBUF_LENGTH, GFP_KERNEL | __GFP_NOWARN);
9462306a36Sopenharmony_ci	if (!workspace->mem || !workspace->buf || !workspace->cbuf)
9562306a36Sopenharmony_ci		goto fail;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	INIT_LIST_HEAD(&workspace->list);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return &workspace->list;
10062306a36Sopenharmony_cifail:
10162306a36Sopenharmony_ci	lzo_free_workspace(&workspace->list);
10262306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic inline void write_compress_length(char *buf, size_t len)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	__le32 dlen;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	dlen = cpu_to_le32(len);
11062306a36Sopenharmony_ci	memcpy(buf, &dlen, LZO_LEN);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic inline size_t read_compress_length(const char *buf)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	__le32 dlen;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	memcpy(&dlen, buf, LZO_LEN);
11862306a36Sopenharmony_ci	return le32_to_cpu(dlen);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * Will do:
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * - Write a segment header into the destination
12562306a36Sopenharmony_ci * - Copy the compressed buffer into the destination
12662306a36Sopenharmony_ci * - Make sure we have enough space in the last sector to fit a segment header
12762306a36Sopenharmony_ci *   If not, we will pad at most (LZO_LEN (4)) - 1 bytes of zeros.
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * Will allocate new pages when needed.
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_cistatic int copy_compressed_data_to_page(char *compressed_data,
13262306a36Sopenharmony_ci					size_t compressed_size,
13362306a36Sopenharmony_ci					struct page **out_pages,
13462306a36Sopenharmony_ci					unsigned long max_nr_page,
13562306a36Sopenharmony_ci					u32 *cur_out,
13662306a36Sopenharmony_ci					const u32 sectorsize)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	u32 sector_bytes_left;
13962306a36Sopenharmony_ci	u32 orig_out;
14062306a36Sopenharmony_ci	struct page *cur_page;
14162306a36Sopenharmony_ci	char *kaddr;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if ((*cur_out / PAGE_SIZE) >= max_nr_page)
14462306a36Sopenharmony_ci		return -E2BIG;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/*
14762306a36Sopenharmony_ci	 * We never allow a segment header crossing sector boundary, previous
14862306a36Sopenharmony_ci	 * run should ensure we have enough space left inside the sector.
14962306a36Sopenharmony_ci	 */
15062306a36Sopenharmony_ci	ASSERT((*cur_out / sectorsize) == (*cur_out + LZO_LEN - 1) / sectorsize);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	cur_page = out_pages[*cur_out / PAGE_SIZE];
15362306a36Sopenharmony_ci	/* Allocate a new page */
15462306a36Sopenharmony_ci	if (!cur_page) {
15562306a36Sopenharmony_ci		cur_page = alloc_page(GFP_NOFS);
15662306a36Sopenharmony_ci		if (!cur_page)
15762306a36Sopenharmony_ci			return -ENOMEM;
15862306a36Sopenharmony_ci		out_pages[*cur_out / PAGE_SIZE] = cur_page;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	kaddr = kmap_local_page(cur_page);
16262306a36Sopenharmony_ci	write_compress_length(kaddr + offset_in_page(*cur_out),
16362306a36Sopenharmony_ci			      compressed_size);
16462306a36Sopenharmony_ci	*cur_out += LZO_LEN;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	orig_out = *cur_out;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* Copy compressed data */
16962306a36Sopenharmony_ci	while (*cur_out - orig_out < compressed_size) {
17062306a36Sopenharmony_ci		u32 copy_len = min_t(u32, sectorsize - *cur_out % sectorsize,
17162306a36Sopenharmony_ci				     orig_out + compressed_size - *cur_out);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		kunmap_local(kaddr);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		if ((*cur_out / PAGE_SIZE) >= max_nr_page)
17662306a36Sopenharmony_ci			return -E2BIG;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		cur_page = out_pages[*cur_out / PAGE_SIZE];
17962306a36Sopenharmony_ci		/* Allocate a new page */
18062306a36Sopenharmony_ci		if (!cur_page) {
18162306a36Sopenharmony_ci			cur_page = alloc_page(GFP_NOFS);
18262306a36Sopenharmony_ci			if (!cur_page)
18362306a36Sopenharmony_ci				return -ENOMEM;
18462306a36Sopenharmony_ci			out_pages[*cur_out / PAGE_SIZE] = cur_page;
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci		kaddr = kmap_local_page(cur_page);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		memcpy(kaddr + offset_in_page(*cur_out),
18962306a36Sopenharmony_ci		       compressed_data + *cur_out - orig_out, copy_len);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		*cur_out += copy_len;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/*
19562306a36Sopenharmony_ci	 * Check if we can fit the next segment header into the remaining space
19662306a36Sopenharmony_ci	 * of the sector.
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	sector_bytes_left = round_up(*cur_out, sectorsize) - *cur_out;
19962306a36Sopenharmony_ci	if (sector_bytes_left >= LZO_LEN || sector_bytes_left == 0)
20062306a36Sopenharmony_ci		goto out;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* The remaining size is not enough, pad it with zeros */
20362306a36Sopenharmony_ci	memset(kaddr + offset_in_page(*cur_out), 0,
20462306a36Sopenharmony_ci	       sector_bytes_left);
20562306a36Sopenharmony_ci	*cur_out += sector_bytes_left;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciout:
20862306a36Sopenharmony_ci	kunmap_local(kaddr);
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ciint lzo_compress_pages(struct list_head *ws, struct address_space *mapping,
21362306a36Sopenharmony_ci		u64 start, struct page **pages, unsigned long *out_pages,
21462306a36Sopenharmony_ci		unsigned long *total_in, unsigned long *total_out)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct workspace *workspace = list_entry(ws, struct workspace, list);
21762306a36Sopenharmony_ci	const u32 sectorsize = btrfs_sb(mapping->host->i_sb)->sectorsize;
21862306a36Sopenharmony_ci	struct page *page_in = NULL;
21962306a36Sopenharmony_ci	char *sizes_ptr;
22062306a36Sopenharmony_ci	const unsigned long max_nr_page = *out_pages;
22162306a36Sopenharmony_ci	int ret = 0;
22262306a36Sopenharmony_ci	/* Points to the file offset of input data */
22362306a36Sopenharmony_ci	u64 cur_in = start;
22462306a36Sopenharmony_ci	/* Points to the current output byte */
22562306a36Sopenharmony_ci	u32 cur_out = 0;
22662306a36Sopenharmony_ci	u32 len = *total_out;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	ASSERT(max_nr_page > 0);
22962306a36Sopenharmony_ci	*out_pages = 0;
23062306a36Sopenharmony_ci	*total_out = 0;
23162306a36Sopenharmony_ci	*total_in = 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/*
23462306a36Sopenharmony_ci	 * Skip the header for now, we will later come back and write the total
23562306a36Sopenharmony_ci	 * compressed size
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	cur_out += LZO_LEN;
23862306a36Sopenharmony_ci	while (cur_in < start + len) {
23962306a36Sopenharmony_ci		char *data_in;
24062306a36Sopenharmony_ci		const u32 sectorsize_mask = sectorsize - 1;
24162306a36Sopenharmony_ci		u32 sector_off = (cur_in - start) & sectorsize_mask;
24262306a36Sopenharmony_ci		u32 in_len;
24362306a36Sopenharmony_ci		size_t out_len;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		/* Get the input page first */
24662306a36Sopenharmony_ci		if (!page_in) {
24762306a36Sopenharmony_ci			page_in = find_get_page(mapping, cur_in >> PAGE_SHIFT);
24862306a36Sopenharmony_ci			ASSERT(page_in);
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		/* Compress at most one sector of data each time */
25262306a36Sopenharmony_ci		in_len = min_t(u32, start + len - cur_in, sectorsize - sector_off);
25362306a36Sopenharmony_ci		ASSERT(in_len);
25462306a36Sopenharmony_ci		data_in = kmap_local_page(page_in);
25562306a36Sopenharmony_ci		ret = lzo1x_1_compress(data_in +
25662306a36Sopenharmony_ci				       offset_in_page(cur_in), in_len,
25762306a36Sopenharmony_ci				       workspace->cbuf, &out_len,
25862306a36Sopenharmony_ci				       workspace->mem);
25962306a36Sopenharmony_ci		kunmap_local(data_in);
26062306a36Sopenharmony_ci		if (ret < 0) {
26162306a36Sopenharmony_ci			pr_debug("BTRFS: lzo in loop returned %d\n", ret);
26262306a36Sopenharmony_ci			ret = -EIO;
26362306a36Sopenharmony_ci			goto out;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		ret = copy_compressed_data_to_page(workspace->cbuf, out_len,
26762306a36Sopenharmony_ci						   pages, max_nr_page,
26862306a36Sopenharmony_ci						   &cur_out, sectorsize);
26962306a36Sopenharmony_ci		if (ret < 0)
27062306a36Sopenharmony_ci			goto out;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		cur_in += in_len;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		/*
27562306a36Sopenharmony_ci		 * Check if we're making it bigger after two sectors.  And if
27662306a36Sopenharmony_ci		 * it is so, give up.
27762306a36Sopenharmony_ci		 */
27862306a36Sopenharmony_ci		if (cur_in - start > sectorsize * 2 && cur_in - start < cur_out) {
27962306a36Sopenharmony_ci			ret = -E2BIG;
28062306a36Sopenharmony_ci			goto out;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/* Check if we have reached page boundary */
28462306a36Sopenharmony_ci		if (PAGE_ALIGNED(cur_in)) {
28562306a36Sopenharmony_ci			put_page(page_in);
28662306a36Sopenharmony_ci			page_in = NULL;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Store the size of all chunks of compressed data */
29162306a36Sopenharmony_ci	sizes_ptr = kmap_local_page(pages[0]);
29262306a36Sopenharmony_ci	write_compress_length(sizes_ptr, cur_out);
29362306a36Sopenharmony_ci	kunmap_local(sizes_ptr);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	ret = 0;
29662306a36Sopenharmony_ci	*total_out = cur_out;
29762306a36Sopenharmony_ci	*total_in = cur_in - start;
29862306a36Sopenharmony_ciout:
29962306a36Sopenharmony_ci	if (page_in)
30062306a36Sopenharmony_ci		put_page(page_in);
30162306a36Sopenharmony_ci	*out_pages = DIV_ROUND_UP(cur_out, PAGE_SIZE);
30262306a36Sopenharmony_ci	return ret;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/*
30662306a36Sopenharmony_ci * Copy the compressed segment payload into @dest.
30762306a36Sopenharmony_ci *
30862306a36Sopenharmony_ci * For the payload there will be no padding, just need to do page switching.
30962306a36Sopenharmony_ci */
31062306a36Sopenharmony_cistatic void copy_compressed_segment(struct compressed_bio *cb,
31162306a36Sopenharmony_ci				    char *dest, u32 len, u32 *cur_in)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	u32 orig_in = *cur_in;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	while (*cur_in < orig_in + len) {
31662306a36Sopenharmony_ci		struct page *cur_page;
31762306a36Sopenharmony_ci		u32 copy_len = min_t(u32, PAGE_SIZE - offset_in_page(*cur_in),
31862306a36Sopenharmony_ci					  orig_in + len - *cur_in);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		ASSERT(copy_len);
32162306a36Sopenharmony_ci		cur_page = cb->compressed_pages[*cur_in / PAGE_SIZE];
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		memcpy_from_page(dest + *cur_in - orig_in, cur_page,
32462306a36Sopenharmony_ci				 offset_in_page(*cur_in), copy_len);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		*cur_in += copy_len;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ciint lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct workspace *workspace = list_entry(ws, struct workspace, list);
33362306a36Sopenharmony_ci	const struct btrfs_fs_info *fs_info = cb->bbio.inode->root->fs_info;
33462306a36Sopenharmony_ci	const u32 sectorsize = fs_info->sectorsize;
33562306a36Sopenharmony_ci	char *kaddr;
33662306a36Sopenharmony_ci	int ret;
33762306a36Sopenharmony_ci	/* Compressed data length, can be unaligned */
33862306a36Sopenharmony_ci	u32 len_in;
33962306a36Sopenharmony_ci	/* Offset inside the compressed data */
34062306a36Sopenharmony_ci	u32 cur_in = 0;
34162306a36Sopenharmony_ci	/* Bytes decompressed so far */
34262306a36Sopenharmony_ci	u32 cur_out = 0;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	kaddr = kmap_local_page(cb->compressed_pages[0]);
34562306a36Sopenharmony_ci	len_in = read_compress_length(kaddr);
34662306a36Sopenharmony_ci	kunmap_local(kaddr);
34762306a36Sopenharmony_ci	cur_in += LZO_LEN;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/*
35062306a36Sopenharmony_ci	 * LZO header length check
35162306a36Sopenharmony_ci	 *
35262306a36Sopenharmony_ci	 * The total length should not exceed the maximum extent length,
35362306a36Sopenharmony_ci	 * and all sectors should be used.
35462306a36Sopenharmony_ci	 * If this happens, it means the compressed extent is corrupted.
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci	if (len_in > min_t(size_t, BTRFS_MAX_COMPRESSED, cb->compressed_len) ||
35762306a36Sopenharmony_ci	    round_up(len_in, sectorsize) < cb->compressed_len) {
35862306a36Sopenharmony_ci		btrfs_err(fs_info,
35962306a36Sopenharmony_ci			"invalid lzo header, lzo len %u compressed len %u",
36062306a36Sopenharmony_ci			len_in, cb->compressed_len);
36162306a36Sopenharmony_ci		return -EUCLEAN;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Go through each lzo segment */
36562306a36Sopenharmony_ci	while (cur_in < len_in) {
36662306a36Sopenharmony_ci		struct page *cur_page;
36762306a36Sopenharmony_ci		/* Length of the compressed segment */
36862306a36Sopenharmony_ci		u32 seg_len;
36962306a36Sopenharmony_ci		u32 sector_bytes_left;
37062306a36Sopenharmony_ci		size_t out_len = lzo1x_worst_compress(sectorsize);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		/*
37362306a36Sopenharmony_ci		 * We should always have enough space for one segment header
37462306a36Sopenharmony_ci		 * inside current sector.
37562306a36Sopenharmony_ci		 */
37662306a36Sopenharmony_ci		ASSERT(cur_in / sectorsize ==
37762306a36Sopenharmony_ci		       (cur_in + LZO_LEN - 1) / sectorsize);
37862306a36Sopenharmony_ci		cur_page = cb->compressed_pages[cur_in / PAGE_SIZE];
37962306a36Sopenharmony_ci		ASSERT(cur_page);
38062306a36Sopenharmony_ci		kaddr = kmap_local_page(cur_page);
38162306a36Sopenharmony_ci		seg_len = read_compress_length(kaddr + offset_in_page(cur_in));
38262306a36Sopenharmony_ci		kunmap_local(kaddr);
38362306a36Sopenharmony_ci		cur_in += LZO_LEN;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (seg_len > WORKSPACE_CBUF_LENGTH) {
38662306a36Sopenharmony_ci			/*
38762306a36Sopenharmony_ci			 * seg_len shouldn't be larger than we have allocated
38862306a36Sopenharmony_ci			 * for workspace->cbuf
38962306a36Sopenharmony_ci			 */
39062306a36Sopenharmony_ci			btrfs_err(fs_info, "unexpectedly large lzo segment len %u",
39162306a36Sopenharmony_ci					seg_len);
39262306a36Sopenharmony_ci			return -EIO;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		/* Copy the compressed segment payload into workspace */
39662306a36Sopenharmony_ci		copy_compressed_segment(cb, workspace->cbuf, seg_len, &cur_in);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		/* Decompress the data */
39962306a36Sopenharmony_ci		ret = lzo1x_decompress_safe(workspace->cbuf, seg_len,
40062306a36Sopenharmony_ci					    workspace->buf, &out_len);
40162306a36Sopenharmony_ci		if (ret != LZO_E_OK) {
40262306a36Sopenharmony_ci			btrfs_err(fs_info, "failed to decompress");
40362306a36Sopenharmony_ci			return -EIO;
40462306a36Sopenharmony_ci		}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		/* Copy the data into inode pages */
40762306a36Sopenharmony_ci		ret = btrfs_decompress_buf2page(workspace->buf, out_len, cb, cur_out);
40862306a36Sopenharmony_ci		cur_out += out_len;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		/* All data read, exit */
41162306a36Sopenharmony_ci		if (ret == 0)
41262306a36Sopenharmony_ci			return 0;
41362306a36Sopenharmony_ci		ret = 0;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		/* Check if the sector has enough space for a segment header */
41662306a36Sopenharmony_ci		sector_bytes_left = sectorsize - (cur_in % sectorsize);
41762306a36Sopenharmony_ci		if (sector_bytes_left >= LZO_LEN)
41862306a36Sopenharmony_ci			continue;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		/* Skip the padding zeros */
42162306a36Sopenharmony_ci		cur_in += sector_bytes_left;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return 0;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ciint lzo_decompress(struct list_head *ws, const u8 *data_in,
42862306a36Sopenharmony_ci		struct page *dest_page, unsigned long start_byte, size_t srclen,
42962306a36Sopenharmony_ci		size_t destlen)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct workspace *workspace = list_entry(ws, struct workspace, list);
43262306a36Sopenharmony_ci	size_t in_len;
43362306a36Sopenharmony_ci	size_t out_len;
43462306a36Sopenharmony_ci	size_t max_segment_len = WORKSPACE_BUF_LENGTH;
43562306a36Sopenharmony_ci	int ret = 0;
43662306a36Sopenharmony_ci	char *kaddr;
43762306a36Sopenharmony_ci	unsigned long bytes;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2)
44062306a36Sopenharmony_ci		return -EUCLEAN;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	in_len = read_compress_length(data_in);
44362306a36Sopenharmony_ci	if (in_len != srclen)
44462306a36Sopenharmony_ci		return -EUCLEAN;
44562306a36Sopenharmony_ci	data_in += LZO_LEN;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	in_len = read_compress_length(data_in);
44862306a36Sopenharmony_ci	if (in_len != srclen - LZO_LEN * 2) {
44962306a36Sopenharmony_ci		ret = -EUCLEAN;
45062306a36Sopenharmony_ci		goto out;
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci	data_in += LZO_LEN;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	out_len = PAGE_SIZE;
45562306a36Sopenharmony_ci	ret = lzo1x_decompress_safe(data_in, in_len, workspace->buf, &out_len);
45662306a36Sopenharmony_ci	if (ret != LZO_E_OK) {
45762306a36Sopenharmony_ci		pr_warn("BTRFS: decompress failed!\n");
45862306a36Sopenharmony_ci		ret = -EIO;
45962306a36Sopenharmony_ci		goto out;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if (out_len < start_byte) {
46362306a36Sopenharmony_ci		ret = -EIO;
46462306a36Sopenharmony_ci		goto out;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/*
46862306a36Sopenharmony_ci	 * the caller is already checking against PAGE_SIZE, but lets
46962306a36Sopenharmony_ci	 * move this check closer to the memcpy/memset
47062306a36Sopenharmony_ci	 */
47162306a36Sopenharmony_ci	destlen = min_t(unsigned long, destlen, PAGE_SIZE);
47262306a36Sopenharmony_ci	bytes = min_t(unsigned long, destlen, out_len - start_byte);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	kaddr = kmap_local_page(dest_page);
47562306a36Sopenharmony_ci	memcpy(kaddr, workspace->buf + start_byte, bytes);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/*
47862306a36Sopenharmony_ci	 * btrfs_getblock is doing a zero on the tail of the page too,
47962306a36Sopenharmony_ci	 * but this will cover anything missing from the decompressed
48062306a36Sopenharmony_ci	 * data.
48162306a36Sopenharmony_ci	 */
48262306a36Sopenharmony_ci	if (bytes < destlen)
48362306a36Sopenharmony_ci		memset(kaddr+bytes, 0, destlen-bytes);
48462306a36Sopenharmony_ci	kunmap_local(kaddr);
48562306a36Sopenharmony_ciout:
48662306a36Sopenharmony_ci	return ret;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ciconst struct btrfs_compress_op btrfs_lzo_compress = {
49062306a36Sopenharmony_ci	.workspace_manager	= &wsm,
49162306a36Sopenharmony_ci	.max_level		= 1,
49262306a36Sopenharmony_ci	.default_level		= 1,
49362306a36Sopenharmony_ci};
494