// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2008 Oracle. All rights reserved. */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/err.h> #include <linux/sched.h> #include <linux/pagemap.h> #include <linux/bio.h> #include <linux/lzo.h> #include <linux/refcount.h> #include "messages.h" #include "compression.h" #include "ctree.h" #include "super.h" #include "btrfs_inode.h" #define LZO_LEN … /* * Btrfs LZO compression format * * Regular and inlined LZO compressed data extents consist of: * * 1. Header * Fixed size. LZO_LEN (4) bytes long, LE32. * Records the total size (including the header) of compressed data. * * 2. Segment(s) * Variable size. Each segment includes one segment header, followed by data * payload. * One regular LZO compressed extent can have one or more segments. * For inlined LZO compressed extent, only one segment is allowed. * One segment represents at most one sector of uncompressed data. * * 2.1 Segment header * Fixed size. LZO_LEN (4) bytes long, LE32. * Records the total size of the segment (not including the header). * Segment header never crosses sector boundary, thus it's possible to * have at most 3 padding zeros at the end of the sector. * * 2.2 Data Payload * Variable size. Size up limit should be lzo1x_worst_compress(sectorsize) * which is 4419 for a 4KiB sectorsize. * * Example with 4K sectorsize: * Page 1: * 0 0x2 0x4 0x6 0x8 0xa 0xc 0xe 0x10 * 0x0000 | Header | SegHdr 01 | Data payload 01 ... | * ... * 0x0ff0 | SegHdr N | Data payload N ... |00| * ^^ padding zeros * Page 2: * 0x1000 | SegHdr N+1| Data payload N+1 ... | */ #define WORKSPACE_BUF_LENGTH … #define WORKSPACE_CBUF_LENGTH … struct workspace { … }; static struct workspace_manager wsm; void lzo_free_workspace(struct list_head *ws) { … } struct list_head *lzo_alloc_workspace(unsigned int level) { … } static inline void write_compress_length(char *buf, size_t len) { … } static inline size_t read_compress_length(const char *buf) { … } /* * Will do: * * - Write a segment header into the destination * - Copy the compressed buffer into the destination * - Make sure we have enough space in the last sector to fit a segment header * If not, we will pad at most (LZO_LEN (4)) - 1 bytes of zeros. * * Will allocate new pages when needed. */ static int copy_compressed_data_to_page(char *compressed_data, size_t compressed_size, struct folio **out_folios, unsigned long max_nr_folio, u32 *cur_out, const u32 sectorsize) { … } int lzo_compress_folios(struct list_head *ws, struct address_space *mapping, u64 start, struct folio **folios, unsigned long *out_folios, unsigned long *total_in, unsigned long *total_out) { … } /* * Copy the compressed segment payload into @dest. * * For the payload there will be no padding, just need to do page switching. */ static void copy_compressed_segment(struct compressed_bio *cb, char *dest, u32 len, u32 *cur_in) { … } int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) { … } int lzo_decompress(struct list_head *ws, const u8 *data_in, struct page *dest_page, unsigned long dest_pgoff, size_t srclen, size_t destlen) { … } const struct btrfs_compress_op btrfs_lzo_compress = …;