2018-04-03 17:23:33 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 Oracle. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/bio.h>
|
|
|
|
#include <linux/file.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/pagemap.h>
|
2022-08-24 00:40:19 +00:00
|
|
|
#include <linux/pagevec.h>
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
#include <linux/highmem.h>
|
2021-09-20 12:33:13 +00:00
|
|
|
#include <linux/kthread.h>
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/backing-dev.h>
|
|
|
|
#include <linux/writeback.h>
|
2022-09-15 09:41:58 +00:00
|
|
|
#include <linux/psi.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2017-05-31 15:14:56 +00:00
|
|
|
#include <linux/sched/mm.h>
|
2017-10-08 13:11:59 +00:00
|
|
|
#include <linux/log2.h>
|
2019-06-03 14:58:57 +00:00
|
|
|
#include <crypto/hash.h>
|
2019-08-21 16:48:25 +00:00
|
|
|
#include "misc.h"
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
#include "ctree.h"
|
2022-10-19 14:50:51 +00:00
|
|
|
#include "fs.h"
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
#include "disk-io.h"
|
|
|
|
#include "transaction.h"
|
|
|
|
#include "btrfs_inode.h"
|
2022-11-15 09:44:05 +00:00
|
|
|
#include "bio.h"
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
#include "ordered-data.h"
|
|
|
|
#include "compression.h"
|
|
|
|
#include "extent_io.h"
|
|
|
|
#include "extent_map.h"
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
#include "subpage.h"
|
2021-05-18 15:40:28 +00:00
|
|
|
#include "zoned.h"
|
2022-10-26 19:08:27 +00:00
|
|
|
#include "file-item.h"
|
2022-10-26 19:08:40 +00:00
|
|
|
#include "super.h"
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
|
2023-02-10 07:48:34 +00:00
|
|
|
struct bio_set btrfs_compressed_bioset;
|
|
|
|
|
2017-10-31 16:24:26 +00:00
|
|
|
static const char* const btrfs_compress_types[] = { "", "zlib", "lzo", "zstd" };
|
|
|
|
|
|
|
|
const char* btrfs_compress_type2str(enum btrfs_compression_type type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case BTRFS_COMPRESS_ZLIB:
|
|
|
|
case BTRFS_COMPRESS_LZO:
|
|
|
|
case BTRFS_COMPRESS_ZSTD:
|
|
|
|
case BTRFS_COMPRESS_NONE:
|
|
|
|
return btrfs_compress_types[type];
|
2019-10-10 07:59:57 +00:00
|
|
|
default:
|
|
|
|
break;
|
2017-10-31 16:24:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-02-10 07:48:34 +00:00
|
|
|
static inline struct compressed_bio *to_compressed_bio(struct btrfs_bio *bbio)
|
|
|
|
{
|
|
|
|
return container_of(bbio, struct compressed_bio, bbio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct compressed_bio *alloc_compressed_bio(struct btrfs_inode *inode,
|
|
|
|
u64 start, blk_opf_t op,
|
|
|
|
btrfs_bio_end_io_t end_io)
|
|
|
|
{
|
|
|
|
struct btrfs_bio *bbio;
|
|
|
|
|
|
|
|
bbio = btrfs_bio(bio_alloc_bioset(NULL, BTRFS_MAX_COMPRESSED_PAGES, op,
|
|
|
|
GFP_NOFS, &btrfs_compressed_bioset));
|
|
|
|
btrfs_bio_init(bbio, inode, end_io, NULL);
|
|
|
|
bbio->file_offset = start;
|
|
|
|
return to_compressed_bio(bbio);
|
|
|
|
}
|
|
|
|
|
2019-06-06 10:07:15 +00:00
|
|
|
bool btrfs_compress_is_valid_type(const char *str, size_t len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 1; i < ARRAY_SIZE(btrfs_compress_types); i++) {
|
|
|
|
size_t comp_len = strlen(btrfs_compress_types[i]);
|
|
|
|
|
|
|
|
if (len < comp_len)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!strncmp(btrfs_compress_types[i], str, comp_len))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-10-01 22:06:15 +00:00
|
|
|
static int compression_compress_pages(int type, struct list_head *ws,
|
|
|
|
struct address_space *mapping, u64 start, struct page **pages,
|
|
|
|
unsigned long *out_pages, unsigned long *total_in,
|
|
|
|
unsigned long *total_out)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case BTRFS_COMPRESS_ZLIB:
|
|
|
|
return zlib_compress_pages(ws, mapping, start, pages,
|
|
|
|
out_pages, total_in, total_out);
|
|
|
|
case BTRFS_COMPRESS_LZO:
|
|
|
|
return lzo_compress_pages(ws, mapping, start, pages,
|
|
|
|
out_pages, total_in, total_out);
|
|
|
|
case BTRFS_COMPRESS_ZSTD:
|
|
|
|
return zstd_compress_pages(ws, mapping, start, pages,
|
|
|
|
out_pages, total_in, total_out);
|
|
|
|
case BTRFS_COMPRESS_NONE:
|
|
|
|
default:
|
|
|
|
/*
|
btrfs: handle remount to no compress during compression
[BUG]
When running btrfs/071 with inode_need_compress() removed from
compress_file_range(), we got the following crash:
BUG: kernel NULL pointer dereference, address: 0000000000000018
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
Workqueue: btrfs-delalloc btrfs_work_helper [btrfs]
RIP: 0010:compress_file_range+0x476/0x7b0 [btrfs]
Call Trace:
? submit_compressed_extents+0x450/0x450 [btrfs]
async_cow_start+0x16/0x40 [btrfs]
btrfs_work_helper+0xf2/0x3e0 [btrfs]
process_one_work+0x278/0x5e0
worker_thread+0x55/0x400
? process_one_work+0x5e0/0x5e0
kthread+0x168/0x190
? kthread_create_worker_on_cpu+0x70/0x70
ret_from_fork+0x22/0x30
---[ end trace 65faf4eae941fa7d ]---
This is already after the patch "btrfs: inode: fix NULL pointer
dereference if inode doesn't need compression."
[CAUSE]
@pages is firstly created by kcalloc() in compress_file_extent():
pages = kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
Then passed to btrfs_compress_pages() to be utilized there:
ret = btrfs_compress_pages(...
pages,
&nr_pages,
...);
btrfs_compress_pages() will initialize each page as output, in
zlib_compress_pages() we have:
pages[nr_pages] = out_page;
nr_pages++;
Normally this is completely fine, but there is a special case which
is in btrfs_compress_pages() itself:
switch (type) {
default:
return -E2BIG;
}
In this case, we didn't modify @pages nor @out_pages, leaving them
untouched, then when we cleanup pages, the we can hit NULL pointer
dereference again:
if (pages) {
for (i = 0; i < nr_pages; i++) {
WARN_ON(pages[i]->mapping);
put_page(pages[i]);
}
...
}
Since pages[i] are all initialized to zero, and btrfs_compress_pages()
doesn't change them at all, accessing pages[i]->mapping would lead to
NULL pointer dereference.
This is not possible for current kernel, as we check
inode_need_compress() before doing pages allocation.
But if we're going to remove that inode_need_compress() in
compress_file_extent(), then it's going to be a problem.
[FIX]
When btrfs_compress_pages() hits its default case, modify @out_pages to
0 to prevent such problem from happening.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=212331
CC: stable@vger.kernel.org # 5.10+
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2020-08-04 07:25:47 +00:00
|
|
|
* This can happen when compression races with remount setting
|
|
|
|
* it to 'no compress', while caller doesn't call
|
|
|
|
* inode_need_compress() to check if we really need to
|
|
|
|
* compress.
|
|
|
|
*
|
|
|
|
* Not a big deal, just need to inform caller that we
|
|
|
|
* haven't allocated any pages yet.
|
2019-10-01 22:06:15 +00:00
|
|
|
*/
|
btrfs: handle remount to no compress during compression
[BUG]
When running btrfs/071 with inode_need_compress() removed from
compress_file_range(), we got the following crash:
BUG: kernel NULL pointer dereference, address: 0000000000000018
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
Workqueue: btrfs-delalloc btrfs_work_helper [btrfs]
RIP: 0010:compress_file_range+0x476/0x7b0 [btrfs]
Call Trace:
? submit_compressed_extents+0x450/0x450 [btrfs]
async_cow_start+0x16/0x40 [btrfs]
btrfs_work_helper+0xf2/0x3e0 [btrfs]
process_one_work+0x278/0x5e0
worker_thread+0x55/0x400
? process_one_work+0x5e0/0x5e0
kthread+0x168/0x190
? kthread_create_worker_on_cpu+0x70/0x70
ret_from_fork+0x22/0x30
---[ end trace 65faf4eae941fa7d ]---
This is already after the patch "btrfs: inode: fix NULL pointer
dereference if inode doesn't need compression."
[CAUSE]
@pages is firstly created by kcalloc() in compress_file_extent():
pages = kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
Then passed to btrfs_compress_pages() to be utilized there:
ret = btrfs_compress_pages(...
pages,
&nr_pages,
...);
btrfs_compress_pages() will initialize each page as output, in
zlib_compress_pages() we have:
pages[nr_pages] = out_page;
nr_pages++;
Normally this is completely fine, but there is a special case which
is in btrfs_compress_pages() itself:
switch (type) {
default:
return -E2BIG;
}
In this case, we didn't modify @pages nor @out_pages, leaving them
untouched, then when we cleanup pages, the we can hit NULL pointer
dereference again:
if (pages) {
for (i = 0; i < nr_pages; i++) {
WARN_ON(pages[i]->mapping);
put_page(pages[i]);
}
...
}
Since pages[i] are all initialized to zero, and btrfs_compress_pages()
doesn't change them at all, accessing pages[i]->mapping would lead to
NULL pointer dereference.
This is not possible for current kernel, as we check
inode_need_compress() before doing pages allocation.
But if we're going to remove that inode_need_compress() in
compress_file_extent(), then it's going to be a problem.
[FIX]
When btrfs_compress_pages() hits its default case, modify @out_pages to
0 to prevent such problem from happening.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=212331
CC: stable@vger.kernel.org # 5.10+
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2020-08-04 07:25:47 +00:00
|
|
|
*out_pages = 0;
|
2019-10-01 22:06:15 +00:00
|
|
|
return -E2BIG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-27 10:18:39 +00:00
|
|
|
static int compression_decompress_bio(struct list_head *ws,
|
|
|
|
struct compressed_bio *cb)
|
2019-10-01 22:06:15 +00:00
|
|
|
{
|
2021-12-27 10:18:39 +00:00
|
|
|
switch (cb->compress_type) {
|
2019-10-01 22:06:15 +00:00
|
|
|
case BTRFS_COMPRESS_ZLIB: return zlib_decompress_bio(ws, cb);
|
|
|
|
case BTRFS_COMPRESS_LZO: return lzo_decompress_bio(ws, cb);
|
|
|
|
case BTRFS_COMPRESS_ZSTD: return zstd_decompress_bio(ws, cb);
|
|
|
|
case BTRFS_COMPRESS_NONE:
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* This can't happen, the type is validated several times
|
|
|
|
* before we get here.
|
|
|
|
*/
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compression_decompress(int type, struct list_head *ws,
|
2022-11-07 16:30:21 +00:00
|
|
|
const u8 *data_in, struct page *dest_page,
|
2019-10-01 22:06:15 +00:00
|
|
|
unsigned long start_byte, size_t srclen, size_t destlen)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case BTRFS_COMPRESS_ZLIB: return zlib_decompress(ws, data_in, dest_page,
|
|
|
|
start_byte, srclen, destlen);
|
|
|
|
case BTRFS_COMPRESS_LZO: return lzo_decompress(ws, data_in, dest_page,
|
|
|
|
start_byte, srclen, destlen);
|
|
|
|
case BTRFS_COMPRESS_ZSTD: return zstd_decompress(ws, data_in, dest_page,
|
|
|
|
start_byte, srclen, destlen);
|
|
|
|
case BTRFS_COMPRESS_NONE:
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* This can't happen, the type is validated several times
|
|
|
|
* before we get here.
|
|
|
|
*/
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-10 07:48:39 +00:00
|
|
|
static void btrfs_free_compressed_pages(struct compressed_bio *cb)
|
|
|
|
{
|
2023-02-10 07:48:40 +00:00
|
|
|
for (unsigned int i = 0; i < cb->nr_pages; i++)
|
|
|
|
put_page(cb->compressed_pages[i]);
|
2023-02-10 07:48:39 +00:00
|
|
|
kfree(cb->compressed_pages);
|
|
|
|
}
|
|
|
|
|
2017-05-26 07:44:58 +00:00
|
|
|
static int btrfs_decompress_bio(struct compressed_bio *cb);
|
2013-04-25 20:41:01 +00:00
|
|
|
|
2023-01-21 06:50:23 +00:00
|
|
|
static void end_compressed_bio_read(struct btrfs_bio *bbio)
|
2021-09-27 07:21:50 +00:00
|
|
|
{
|
2023-02-10 07:48:34 +00:00
|
|
|
struct compressed_bio *cb = to_compressed_bio(bbio);
|
|
|
|
blk_status_t status = bbio->bio.bi_status;
|
2021-09-27 07:21:50 +00:00
|
|
|
|
2023-02-10 07:48:34 +00:00
|
|
|
if (!status)
|
|
|
|
status = errno_to_blk_status(btrfs_decompress_bio(cb));
|
2022-07-07 05:33:30 +00:00
|
|
|
|
2023-02-10 07:48:39 +00:00
|
|
|
btrfs_free_compressed_pages(cb);
|
2023-03-07 16:39:41 +00:00
|
|
|
btrfs_bio_end_io(cb->orig_bbio, status);
|
2022-08-06 08:03:26 +00:00
|
|
|
bio_put(&bbio->bio);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the writeback bits on all of the file
|
|
|
|
* pages for a compressed write
|
|
|
|
*/
|
2023-02-10 07:48:34 +00:00
|
|
|
static noinline void end_compressed_writeback(const struct compressed_bio *cb)
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
{
|
2023-02-10 07:48:34 +00:00
|
|
|
struct inode *inode = &cb->bbio.inode->vfs_inode;
|
2021-09-27 07:22:01 +00:00
|
|
|
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 12:29:47 +00:00
|
|
|
unsigned long index = cb->start >> PAGE_SHIFT;
|
|
|
|
unsigned long end_index = (cb->start + cb->len - 1) >> PAGE_SHIFT;
|
2022-08-24 00:40:19 +00:00
|
|
|
struct folio_batch fbatch;
|
2023-02-10 07:48:34 +00:00
|
|
|
const int errno = blk_status_to_errno(cb->bbio.bio.bi_status);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
|
2022-02-18 15:03:26 +00:00
|
|
|
if (errno)
|
|
|
|
mapping_set_error(inode->i_mapping, errno);
|
2014-10-07 00:48:26 +00:00
|
|
|
|
2022-08-24 00:40:19 +00:00
|
|
|
folio_batch_init(&fbatch);
|
|
|
|
while (index <= end_index) {
|
|
|
|
ret = filemap_get_folios(inode->i_mapping, &index, end_index,
|
|
|
|
&fbatch);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
return;
|
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
for (i = 0; i < ret; i++) {
|
2022-08-24 00:40:19 +00:00
|
|
|
struct folio *folio = fbatch.folios[i];
|
|
|
|
|
2022-02-18 15:03:26 +00:00
|
|
|
if (errno)
|
2022-08-24 00:40:19 +00:00
|
|
|
folio_set_error(folio);
|
|
|
|
btrfs_page_clamp_clear_writeback(fs_info, &folio->page,
|
2021-09-27 07:22:01 +00:00
|
|
|
cb->start, cb->len);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
}
|
2022-08-24 00:40:19 +00:00
|
|
|
folio_batch_release(&fbatch);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
}
|
|
|
|
/* the inode may be gone now */
|
|
|
|
}
|
|
|
|
|
2023-02-10 07:48:41 +00:00
|
|
|
static void btrfs_finish_compressed_write_work(struct work_struct *work)
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
{
|
2023-02-10 07:48:41 +00:00
|
|
|
struct compressed_bio *cb =
|
|
|
|
container_of(work, struct compressed_bio, write_end_work);
|
|
|
|
|
2021-09-27 07:21:51 +00:00
|
|
|
/*
|
|
|
|
* Ok, we're the last bio for this extent, step one is to call back
|
|
|
|
* into the FS and do all the end_io operations.
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
*/
|
2023-02-10 07:48:34 +00:00
|
|
|
btrfs_writepage_endio_finish_ordered(cb->bbio.inode, NULL,
|
2018-11-08 08:18:08 +00:00
|
|
|
cb->start, cb->start + cb->len - 1,
|
2023-02-10 07:48:34 +00:00
|
|
|
cb->bbio.bio.bi_status == BLK_STS_OK);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
|
2019-08-13 23:00:02 +00:00
|
|
|
if (cb->writeback)
|
2023-02-10 07:48:34 +00:00
|
|
|
end_compressed_writeback(cb);
|
2021-09-27 07:21:51 +00:00
|
|
|
/* Note, our inode could be gone now */
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
|
2023-02-10 07:48:39 +00:00
|
|
|
btrfs_free_compressed_pages(cb);
|
2023-02-10 07:48:34 +00:00
|
|
|
bio_put(&cb->bbio.bio);
|
2021-09-27 07:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the cleanup once all the compressed pages hit the disk. This will clear
|
|
|
|
* writeback on the file pages and free the compressed pages.
|
|
|
|
*
|
|
|
|
* This also calls the writeback end hooks for the file pages so that metadata
|
|
|
|
* and checksums can be updated in the file.
|
|
|
|
*/
|
2022-08-06 08:03:26 +00:00
|
|
|
static void end_compressed_bio_write(struct btrfs_bio *bbio)
|
2021-09-27 07:21:51 +00:00
|
|
|
{
|
2023-02-10 07:48:34 +00:00
|
|
|
struct compressed_bio *cb = to_compressed_bio(bbio);
|
|
|
|
struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
|
2021-09-27 07:21:51 +00:00
|
|
|
|
2023-01-21 06:50:30 +00:00
|
|
|
queue_work(fs_info->compressed_write_workers, &cb->write_end_work);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
}
|
|
|
|
|
2023-03-14 16:51:09 +00:00
|
|
|
static void btrfs_add_compressed_bio_pages(struct compressed_bio *cb)
|
2023-02-10 07:48:38 +00:00
|
|
|
{
|
|
|
|
struct bio *bio = &cb->bbio.bio;
|
2023-03-14 16:51:10 +00:00
|
|
|
u32 offset = 0;
|
2023-02-10 07:48:38 +00:00
|
|
|
|
2023-03-14 16:51:10 +00:00
|
|
|
while (offset < cb->compressed_len) {
|
|
|
|
u32 len = min_t(u32, cb->compressed_len - offset, PAGE_SIZE);
|
2023-02-10 07:48:38 +00:00
|
|
|
|
2023-03-14 16:51:10 +00:00
|
|
|
/* Maximum compressed extent is smaller than bio size limit. */
|
|
|
|
__bio_add_page(bio, cb->compressed_pages[offset >> PAGE_SHIFT],
|
|
|
|
len, 0);
|
|
|
|
offset += len;
|
2023-02-10 07:48:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
/*
|
|
|
|
* worker function to build and submit bios for previously compressed pages.
|
|
|
|
* The corresponding pages in the inode should be marked for writeback
|
|
|
|
* and the compressed pages should have a reference on them for dropping
|
|
|
|
* when the IO is complete.
|
|
|
|
*
|
|
|
|
* This also checksums the file bytes and gets things ready for
|
|
|
|
* the end io hooks.
|
|
|
|
*/
|
2023-02-10 07:48:34 +00:00
|
|
|
void btrfs_submit_compressed_write(struct btrfs_inode *inode, u64 start,
|
2021-05-29 09:48:35 +00:00
|
|
|
unsigned int len, u64 disk_start,
|
|
|
|
unsigned int compressed_len,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
struct page **compressed_pages,
|
2021-05-29 09:48:35 +00:00
|
|
|
unsigned int nr_pages,
|
2022-07-14 18:07:16 +00:00
|
|
|
blk_opf_t write_flags,
|
2019-08-13 23:00:02 +00:00
|
|
|
struct cgroup_subsys_state *blkcg_css,
|
|
|
|
bool writeback)
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
{
|
2020-06-03 05:55:16 +00:00
|
|
|
struct btrfs_fs_info *fs_info = inode->root->fs_info;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
struct compressed_bio *cb;
|
|
|
|
|
2021-09-27 07:22:00 +00:00
|
|
|
ASSERT(IS_ALIGNED(start, fs_info->sectorsize) &&
|
|
|
|
IS_ALIGNED(len, fs_info->sectorsize));
|
2023-02-10 07:48:34 +00:00
|
|
|
|
|
|
|
if (blkcg_css) {
|
|
|
|
kthread_associate_blkcg(blkcg_css);
|
|
|
|
write_flags |= REQ_CGROUP_PUNT;
|
|
|
|
}
|
|
|
|
write_flags |= REQ_BTRFS_ONE_ORDERED;
|
|
|
|
|
|
|
|
cb = alloc_compressed_bio(inode, start, REQ_OP_WRITE | write_flags,
|
|
|
|
end_compressed_bio_write);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
cb->start = start;
|
|
|
|
cb->len = len;
|
|
|
|
cb->compressed_pages = compressed_pages;
|
|
|
|
cb->compressed_len = compressed_len;
|
2019-08-13 23:00:02 +00:00
|
|
|
cb->writeback = writeback;
|
2022-05-26 07:36:38 +00:00
|
|
|
INIT_WORK(&cb->write_end_work, btrfs_finish_compressed_write_work);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
cb->nr_pages = nr_pages;
|
2023-03-14 16:51:09 +00:00
|
|
|
cb->bbio.bio.bi_iter.bi_sector = disk_start >> SECTOR_SHIFT;
|
|
|
|
btrfs_add_compressed_bio_pages(cb);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
|
2023-03-07 16:39:39 +00:00
|
|
|
btrfs_submit_bio(&cb->bbio, 0);
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-08 21:58:54 +00:00
|
|
|
|
2022-03-31 21:58:28 +00:00
|
|
|
if (blkcg_css)
|
|
|
|
kthread_associate_blkcg(NULL);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
}
|
|
|
|
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
/*
|
|
|
|
* Add extra pages in the same compressed file extent so that we don't need to
|
|
|
|
* re-read the same extent again and again.
|
|
|
|
*
|
|
|
|
* NOTE: this won't work well for subpage, as for subpage read, we lock the
|
|
|
|
* full page then submit bio for each compressed/regular extents.
|
|
|
|
*
|
|
|
|
* This means, if we have several sectors in the same page points to the same
|
|
|
|
* on-disk compressed data, we will re-read the same extent many times and
|
|
|
|
* this function can only help for the next page.
|
|
|
|
*/
|
2008-11-07 03:02:51 +00:00
|
|
|
static noinline int add_ra_bio_pages(struct inode *inode,
|
|
|
|
u64 compressed_end,
|
2022-09-15 09:41:58 +00:00
|
|
|
struct compressed_bio *cb,
|
fs: fix leaked psi pressure state
When psi annotations were added to to btrfs compression reads, the psi
state tracking over add_ra_bio_pages and btrfs_submit_compressed_read was
faulty. A pressure state, once entered, is never left. This results in
incorrectly elevated pressure, which triggers OOM kills.
pflags record the *previous* memstall state when we enter a new one. The
code tried to initialize pflags to 1, and then optimize the leave call
when we either didn't enter a memstall, or were already inside a nested
stall. However, there can be multiple PageWorkingset pages in the bio, at
which point it's that path itself that enters repeatedly and overwrites
pflags. This causes us to miss the exit.
Enter the stall only once if needed, then unwind correctly.
erofs has the same problem, fix that up too. And move the memstall exit
past submit_bio() to restore submit accounting originally added by
b8e24a9300b0 ("block: annotate refault stalls from IO submission").
Link: https://lkml.kernel.org/r/Y2UHRqthNUwuIQGS@cmpxchg.org
Fixes: 4088a47e78f9 ("btrfs: add manual PSI accounting for compressed reads")
Fixes: 99486c511f68 ("erofs: add manual PSI accounting for the compressed address space")
Fixes: 118f3663fbc6 ("block: remove PSI accounting from the bio layer")
Link: https://lore.kernel.org/r/d20a0a85-e415-cf78-27f9-77dd7a94bc8d@leemhuis.info/
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Reported-by: Thorsten Leemhuis <linux@leemhuis.info>
Tested-by: Thorsten Leemhuis <linux@leemhuis.info>
Cc: Chao Yu <chao@kernel.org>
Cc: Chris Mason <clm@fb.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: David Sterba <dsterba@suse.com>
Cc: Gao Xiang <xiang@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Josef Bacik <josef@toxicpanda.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2022-11-03 21:34:31 +00:00
|
|
|
int *memstall, unsigned long *pflags)
|
2008-11-07 03:02:51 +00:00
|
|
|
{
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
2008-11-07 03:02:51 +00:00
|
|
|
unsigned long end_index;
|
2023-03-07 16:39:41 +00:00
|
|
|
struct bio *orig_bio = &cb->orig_bbio->bio;
|
|
|
|
u64 cur = cb->orig_bbio->file_offset + orig_bio->bi_iter.bi_size;
|
2008-11-07 03:02:51 +00:00
|
|
|
u64 isize = i_size_read(inode);
|
|
|
|
int ret;
|
|
|
|
struct page *page;
|
|
|
|
struct extent_map *em;
|
|
|
|
struct address_space *mapping = inode->i_mapping;
|
|
|
|
struct extent_map_tree *em_tree;
|
|
|
|
struct extent_io_tree *tree;
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
int sectors_missed = 0;
|
2008-11-07 03:02:51 +00:00
|
|
|
|
|
|
|
em_tree = &BTRFS_I(inode)->extent_tree;
|
|
|
|
tree = &BTRFS_I(inode)->io_tree;
|
|
|
|
|
|
|
|
if (isize == 0)
|
|
|
|
return 0;
|
|
|
|
|
2021-07-26 06:34:52 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 12:29:47 +00:00
|
|
|
end_index = (i_size_read(inode) - 1) >> PAGE_SHIFT;
|
2008-11-07 03:02:51 +00:00
|
|
|
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
while (cur < compressed_end) {
|
|
|
|
u64 page_end;
|
|
|
|
u64 pg_index = cur >> PAGE_SHIFT;
|
|
|
|
u32 add_size;
|
2008-11-07 03:02:51 +00:00
|
|
|
|
2011-04-19 12:29:38 +00:00
|
|
|
if (pg_index > end_index)
|
2008-11-07 03:02:51 +00:00
|
|
|
break;
|
|
|
|
|
2017-12-04 15:37:22 +00:00
|
|
|
page = xa_load(&mapping->i_pages, pg_index);
|
2017-11-03 17:30:42 +00:00
|
|
|
if (page && !xa_is_value(page)) {
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
sectors_missed += (PAGE_SIZE - offset_in_page(cur)) >>
|
|
|
|
fs_info->sectorsize_bits;
|
|
|
|
|
|
|
|
/* Beyond threshold, no need to continue */
|
|
|
|
if (sectors_missed > 4)
|
2008-11-07 03:02:51 +00:00
|
|
|
break;
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Jump to next page start as we already have page for
|
|
|
|
* current offset.
|
|
|
|
*/
|
|
|
|
cur = (pg_index << PAGE_SHIFT) + PAGE_SIZE;
|
|
|
|
continue;
|
2008-11-07 03:02:51 +00:00
|
|
|
}
|
|
|
|
|
2015-11-07 00:28:49 +00:00
|
|
|
page = __page_cache_alloc(mapping_gfp_constraint(mapping,
|
|
|
|
~__GFP_FS));
|
2008-11-07 03:02:51 +00:00
|
|
|
if (!page)
|
|
|
|
break;
|
|
|
|
|
2015-11-07 00:28:49 +00:00
|
|
|
if (add_to_page_cache_lru(page, mapping, pg_index, GFP_NOFS)) {
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 12:29:47 +00:00
|
|
|
put_page(page);
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
/* There is already a page, skip to page end */
|
|
|
|
cur = (pg_index << PAGE_SHIFT) + PAGE_SIZE;
|
|
|
|
continue;
|
2008-11-07 03:02:51 +00:00
|
|
|
}
|
|
|
|
|
fs: fix leaked psi pressure state
When psi annotations were added to to btrfs compression reads, the psi
state tracking over add_ra_bio_pages and btrfs_submit_compressed_read was
faulty. A pressure state, once entered, is never left. This results in
incorrectly elevated pressure, which triggers OOM kills.
pflags record the *previous* memstall state when we enter a new one. The
code tried to initialize pflags to 1, and then optimize the leave call
when we either didn't enter a memstall, or were already inside a nested
stall. However, there can be multiple PageWorkingset pages in the bio, at
which point it's that path itself that enters repeatedly and overwrites
pflags. This causes us to miss the exit.
Enter the stall only once if needed, then unwind correctly.
erofs has the same problem, fix that up too. And move the memstall exit
past submit_bio() to restore submit accounting originally added by
b8e24a9300b0 ("block: annotate refault stalls from IO submission").
Link: https://lkml.kernel.org/r/Y2UHRqthNUwuIQGS@cmpxchg.org
Fixes: 4088a47e78f9 ("btrfs: add manual PSI accounting for compressed reads")
Fixes: 99486c511f68 ("erofs: add manual PSI accounting for the compressed address space")
Fixes: 118f3663fbc6 ("block: remove PSI accounting from the bio layer")
Link: https://lore.kernel.org/r/d20a0a85-e415-cf78-27f9-77dd7a94bc8d@leemhuis.info/
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Reported-by: Thorsten Leemhuis <linux@leemhuis.info>
Tested-by: Thorsten Leemhuis <linux@leemhuis.info>
Cc: Chao Yu <chao@kernel.org>
Cc: Chris Mason <clm@fb.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: David Sterba <dsterba@suse.com>
Cc: Gao Xiang <xiang@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Josef Bacik <josef@toxicpanda.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2022-11-03 21:34:31 +00:00
|
|
|
if (!*memstall && PageWorkingset(page)) {
|
2022-09-15 09:41:58 +00:00
|
|
|
psi_memstall_enter(pflags);
|
fs: fix leaked psi pressure state
When psi annotations were added to to btrfs compression reads, the psi
state tracking over add_ra_bio_pages and btrfs_submit_compressed_read was
faulty. A pressure state, once entered, is never left. This results in
incorrectly elevated pressure, which triggers OOM kills.
pflags record the *previous* memstall state when we enter a new one. The
code tried to initialize pflags to 1, and then optimize the leave call
when we either didn't enter a memstall, or were already inside a nested
stall. However, there can be multiple PageWorkingset pages in the bio, at
which point it's that path itself that enters repeatedly and overwrites
pflags. This causes us to miss the exit.
Enter the stall only once if needed, then unwind correctly.
erofs has the same problem, fix that up too. And move the memstall exit
past submit_bio() to restore submit accounting originally added by
b8e24a9300b0 ("block: annotate refault stalls from IO submission").
Link: https://lkml.kernel.org/r/Y2UHRqthNUwuIQGS@cmpxchg.org
Fixes: 4088a47e78f9 ("btrfs: add manual PSI accounting for compressed reads")
Fixes: 99486c511f68 ("erofs: add manual PSI accounting for the compressed address space")
Fixes: 118f3663fbc6 ("block: remove PSI accounting from the bio layer")
Link: https://lore.kernel.org/r/d20a0a85-e415-cf78-27f9-77dd7a94bc8d@leemhuis.info/
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Reported-by: Thorsten Leemhuis <linux@leemhuis.info>
Tested-by: Thorsten Leemhuis <linux@leemhuis.info>
Cc: Chao Yu <chao@kernel.org>
Cc: Chris Mason <clm@fb.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: David Sterba <dsterba@suse.com>
Cc: Gao Xiang <xiang@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Josef Bacik <josef@toxicpanda.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2022-11-03 21:34:31 +00:00
|
|
|
*memstall = 1;
|
|
|
|
}
|
2022-09-15 09:41:58 +00:00
|
|
|
|
2021-01-26 08:34:00 +00:00
|
|
|
ret = set_page_extent_mapped(page);
|
|
|
|
if (ret < 0) {
|
|
|
|
unlock_page(page);
|
|
|
|
put_page(page);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
page_end = (pg_index << PAGE_SHIFT) + PAGE_SIZE - 1;
|
2022-09-09 21:53:43 +00:00
|
|
|
lock_extent(tree, cur, page_end, NULL);
|
2009-09-02 20:24:52 +00:00
|
|
|
read_lock(&em_tree->lock);
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
em = lookup_extent_mapping(em_tree, cur, page_end + 1 - cur);
|
2009-09-02 20:24:52 +00:00
|
|
|
read_unlock(&em_tree->lock);
|
2008-11-07 03:02:51 +00:00
|
|
|
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
/*
|
|
|
|
* At this point, we have a locked page in the page cache for
|
|
|
|
* these bytes in the file. But, we have to make sure they map
|
|
|
|
* to this compressed extent on disk.
|
|
|
|
*/
|
|
|
|
if (!em || cur < em->start ||
|
|
|
|
(cur + fs_info->sectorsize > extent_map_end(em)) ||
|
2023-03-07 16:39:41 +00:00
|
|
|
(em->block_start >> 9) != orig_bio->bi_iter.bi_sector) {
|
2008-11-07 03:02:51 +00:00
|
|
|
free_extent_map(em);
|
2022-09-09 21:53:43 +00:00
|
|
|
unlock_extent(tree, cur, page_end, NULL);
|
2008-11-07 03:02:51 +00:00
|
|
|
unlock_page(page);
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 12:29:47 +00:00
|
|
|
put_page(page);
|
2008-11-07 03:02:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
free_extent_map(em);
|
|
|
|
|
|
|
|
if (page->index == end_index) {
|
2018-12-05 14:23:03 +00:00
|
|
|
size_t zero_offset = offset_in_page(isize);
|
2008-11-07 03:02:51 +00:00
|
|
|
|
|
|
|
if (zero_offset) {
|
|
|
|
int zeros;
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 12:29:47 +00:00
|
|
|
zeros = PAGE_SIZE - zero_offset;
|
btrfs: use memzero_page() instead of open coded kmap pattern
There are many places where kmap/memset/kunmap patterns occur.
Use the newly lifted memzero_page() to eliminate direct uses of kmap and
leverage the new core functions use of kmap_local_page().
The development of this patch was aided by the following coccinelle
script:
// <smpl>
// SPDX-License-Identifier: GPL-2.0-only
// Find kmap/memset/kunmap pattern and replace with memset*page calls
//
// NOTE: Offsets and other expressions may be more complex than what the script
// will automatically generate. Therefore a catchall rule is provided to find
// the pattern which then must be evaluated by hand.
//
// Confidence: Low
// Copyright: (C) 2021 Intel Corporation
// URL: http://coccinelle.lip6.fr/
// Comments:
// Options:
//
// Then the memset pattern
//
@ memset_rule1 @
expression page, V, L, Off;
identifier ptr;
type VP;
@@
(
-VP ptr = kmap(page);
|
-ptr = kmap(page);
|
-VP ptr = kmap_atomic(page);
|
-ptr = kmap_atomic(page);
)
<+...
(
-memset(ptr, 0, L);
+memzero_page(page, 0, L);
|
-memset(ptr + Off, 0, L);
+memzero_page(page, Off, L);
|
-memset(ptr, V, L);
+memset_page(page, V, 0, L);
|
-memset(ptr + Off, V, L);
+memset_page(page, V, Off, L);
)
...+>
(
-kunmap(page);
|
-kunmap_atomic(ptr);
)
// Remove any pointers left unused
@
depends on memset_rule1
@
identifier memset_rule1.ptr;
type VP, VP1;
@@
-VP ptr;
... when != ptr;
? VP1 ptr;
//
// Catch all
//
@ memset_rule2 @
expression page;
identifier ptr;
expression GenTo, GenSize, GenValue;
type VP;
@@
(
-VP ptr = kmap(page);
|
-ptr = kmap(page);
|
-VP ptr = kmap_atomic(page);
|
-ptr = kmap_atomic(page);
)
<+...
(
//
// Some call sites have complex expressions within the memset/memcpy
// The follow are catch alls which need to be evaluated by hand.
//
-memset(GenTo, 0, GenSize);
+memzero_pageExtra(page, GenTo, GenSize);
|
-memset(GenTo, GenValue, GenSize);
+memset_pageExtra(page, GenValue, GenTo, GenSize);
)
...+>
(
-kunmap(page);
|
-kunmap_atomic(ptr);
)
// Remove any pointers left unused
@
depends on memset_rule2
@
identifier memset_rule2.ptr;
type VP, VP1;
@@
-VP ptr;
... when != ptr;
? VP1 ptr;
// </smpl>
Link: https://lkml.kernel.org/r/20210309212137.2610186-4-ira.weiny@intel.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
Cc: Chris Mason <clm@fb.com>
Cc: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-05-05 01:40:07 +00:00
|
|
|
memzero_page(page, zero_offset, zeros);
|
2008-11-07 03:02:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
add_size = min(em->start + em->len, page_end + 1) - cur;
|
2023-03-07 16:39:41 +00:00
|
|
|
ret = bio_add_page(orig_bio, page, add_size, offset_in_page(cur));
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
if (ret != add_size) {
|
2022-09-09 21:53:43 +00:00
|
|
|
unlock_extent(tree, cur, page_end, NULL);
|
2008-11-07 03:02:51 +00:00
|
|
|
unlock_page(page);
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 12:29:47 +00:00
|
|
|
put_page(page);
|
2008-11-07 03:02:51 +00:00
|
|
|
break;
|
|
|
|
}
|
btrfs: subpage: make add_ra_bio_pages() compatible
[BUG]
If we remove the subpage limitation in add_ra_bio_pages(), then read a
compressed extent which has part of its range in next page, like the
following inode layout:
0 32K 64K 96K 128K
|<--------------|-------------->|
Btrfs will trigger ASSERT() in endio function:
assertion failed: atomic_read(&subpage->readers) >= nbits
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3431!
Internal error: Oops - BUG: 0 [#1] SMP
Workqueue: btrfs-endio btrfs_work_helper [btrfs]
Call trace:
assertfail.constprop.0+0x28/0x2c [btrfs]
btrfs_subpage_end_reader+0x148/0x14c [btrfs]
end_page_read+0x8c/0x100 [btrfs]
end_bio_extent_readpage+0x320/0x6b0 [btrfs]
bio_endio+0x15c/0x1dc
end_workqueue_fn+0x44/0x64 [btrfs]
btrfs_work_helper+0x74/0x250 [btrfs]
process_one_work+0x1d4/0x47c
worker_thread+0x180/0x400
kthread+0x11c/0x120
ret_from_fork+0x10/0x30
---[ end trace c8b7b552d3bb408c ]---
[CAUSE]
When we read the page range [0, 64K), we find it's a compressed extent,
and we will try to add extra pages in add_ra_bio_pages() to avoid
reading the same compressed extent.
But when we add such page into the read bio, it doesn't follow the
behavior of btrfs_do_readpage() to properly set subpage::readers.
This means, for page [64K, 128K), its subpage::readers is still 0.
And when endio is executed on both pages, since page [64K, 128K) has 0
subpage::readers, it triggers above ASSERT()
[FIX]
Function add_ra_bio_pages() is far from subpage compatible, it always
assume PAGE_SIZE == sectorsize, thus when it skip to next range it
always just skip PAGE_SIZE.
Make it subpage compatible by:
- Skip to next page properly when needed
If we find there is already a page cache, we need to skip to next page.
For that case, we shouldn't just skip PAGE_SIZE bytes, but use
@pg_index to calculate the next bytenr and continue.
- Only add the page range covered by current extent map
We need to calculate which range is covered by current extent map and
only add that part into the read bio.
- Update subpage::readers before submitting the bio
- Use proper cursor other than confusing @last_offset
- Calculate the missed threshold based on sector size
It's no longer using missed pages, as for 64K page size, we have at
most 3 pages to skip. (If aligned only 2 pages)
- Add ASSERT() to make sure our bytenr is always aligned
- Add comment for the function
Add a special note for subpage case, as the function won't really
work well for subpage cases.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-09-27 07:21:47 +00:00
|
|
|
/*
|
|
|
|
* If it's subpage, we also need to increase its
|
|
|
|
* subpage::readers number, as at endio we will decrease
|
|
|
|
* subpage::readers and to unlock the page.
|
|
|
|
*/
|
|
|
|
if (fs_info->sectorsize < PAGE_SIZE)
|
|
|
|
btrfs_subpage_start_reader(fs_info, page, cur, add_size);
|
|
|
|
put_page(page);
|
|
|
|
cur += add_size;
|
2008-11-07 03:02:51 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
/*
|
|
|
|
* for a compressed read, the bio we get passed has all the inode pages
|
|
|
|
* in it. We don't actually do IO on those pages but allocate new ones
|
|
|
|
* to hold the compressed pages on disk.
|
|
|
|
*
|
2013-10-11 22:44:27 +00:00
|
|
|
* bio->bi_iter.bi_sector points to the compressed extent on disk
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
* bio->bi_io_vec points to all of the inode pages
|
|
|
|
*
|
|
|
|
* After the compressed pages are read, we copy the bytes into the
|
|
|
|
* bio we were passed and then call the bio end_io calls
|
|
|
|
*/
|
2023-03-07 16:39:40 +00:00
|
|
|
void btrfs_submit_compressed_read(struct btrfs_bio *bbio, int mirror_num)
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
{
|
2023-03-07 16:39:40 +00:00
|
|
|
struct btrfs_inode *inode = bbio->inode;
|
2023-02-10 07:48:34 +00:00
|
|
|
struct btrfs_fs_info *fs_info = inode->root->fs_info;
|
|
|
|
struct extent_map_tree *em_tree = &inode->extent_tree;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
struct compressed_bio *cb;
|
2021-05-29 09:48:34 +00:00
|
|
|
unsigned int compressed_len;
|
2023-03-07 16:39:40 +00:00
|
|
|
u64 file_offset = bbio->file_offset;
|
2008-11-10 16:44:58 +00:00
|
|
|
u64 em_len;
|
|
|
|
u64 em_start;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
struct extent_map *em;
|
fs: fix leaked psi pressure state
When psi annotations were added to to btrfs compression reads, the psi
state tracking over add_ra_bio_pages and btrfs_submit_compressed_read was
faulty. A pressure state, once entered, is never left. This results in
incorrectly elevated pressure, which triggers OOM kills.
pflags record the *previous* memstall state when we enter a new one. The
code tried to initialize pflags to 1, and then optimize the leave call
when we either didn't enter a memstall, or were already inside a nested
stall. However, there can be multiple PageWorkingset pages in the bio, at
which point it's that path itself that enters repeatedly and overwrites
pflags. This causes us to miss the exit.
Enter the stall only once if needed, then unwind correctly.
erofs has the same problem, fix that up too. And move the memstall exit
past submit_bio() to restore submit accounting originally added by
b8e24a9300b0 ("block: annotate refault stalls from IO submission").
Link: https://lkml.kernel.org/r/Y2UHRqthNUwuIQGS@cmpxchg.org
Fixes: 4088a47e78f9 ("btrfs: add manual PSI accounting for compressed reads")
Fixes: 99486c511f68 ("erofs: add manual PSI accounting for the compressed address space")
Fixes: 118f3663fbc6 ("block: remove PSI accounting from the bio layer")
Link: https://lore.kernel.org/r/d20a0a85-e415-cf78-27f9-77dd7a94bc8d@leemhuis.info/
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Reported-by: Thorsten Leemhuis <linux@leemhuis.info>
Tested-by: Thorsten Leemhuis <linux@leemhuis.info>
Cc: Chao Yu <chao@kernel.org>
Cc: Chris Mason <clm@fb.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: David Sterba <dsterba@suse.com>
Cc: Gao Xiang <xiang@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Josef Bacik <josef@toxicpanda.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2022-11-03 21:34:31 +00:00
|
|
|
unsigned long pflags;
|
|
|
|
int memstall = 0;
|
2022-02-18 15:03:27 +00:00
|
|
|
blk_status_t ret;
|
2022-03-30 20:11:22 +00:00
|
|
|
int ret2;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
|
|
|
|
/* we need the actual starting offset of this extent in the file */
|
2009-09-02 20:24:52 +00:00
|
|
|
read_lock(&em_tree->lock);
|
2021-07-05 02:00:56 +00:00
|
|
|
em = lookup_extent_mapping(em_tree, file_offset, fs_info->sectorsize);
|
2009-09-02 20:24:52 +00:00
|
|
|
read_unlock(&em_tree->lock);
|
2022-02-18 15:03:27 +00:00
|
|
|
if (!em) {
|
|
|
|
ret = BLK_STS_IOERR;
|
|
|
|
goto out;
|
|
|
|
}
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
|
2021-07-05 02:00:56 +00:00
|
|
|
ASSERT(em->compress_type != BTRFS_COMPRESS_NONE);
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-08 21:58:54 +00:00
|
|
|
compressed_len = em->block_len;
|
2011-01-26 06:21:39 +00:00
|
|
|
|
2023-02-10 07:48:34 +00:00
|
|
|
cb = alloc_compressed_bio(inode, file_offset, REQ_OP_READ,
|
|
|
|
end_compressed_bio_read);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
|
2008-11-10 12:34:43 +00:00
|
|
|
cb->start = em->orig_start;
|
2008-11-10 16:44:58 +00:00
|
|
|
em_len = em->len;
|
|
|
|
em_start = em->start;
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-08 21:58:54 +00:00
|
|
|
|
2023-03-07 16:39:40 +00:00
|
|
|
cb->len = bbio->bio.bi_iter.bi_size;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
cb->compressed_len = compressed_len;
|
2022-04-26 13:47:34 +00:00
|
|
|
cb->compress_type = em->compress_type;
|
2023-03-07 16:39:41 +00:00
|
|
|
cb->orig_bbio = bbio;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
|
2022-04-26 13:47:34 +00:00
|
|
|
free_extent_map(em);
|
|
|
|
|
2022-03-30 20:11:22 +00:00
|
|
|
cb->nr_pages = DIV_ROUND_UP(compressed_len, PAGE_SIZE);
|
|
|
|
cb->compressed_pages = kcalloc(cb->nr_pages, sizeof(struct page *), GFP_NOFS);
|
2022-02-18 15:03:27 +00:00
|
|
|
if (!cb->compressed_pages) {
|
|
|
|
ret = BLK_STS_RESOURCE;
|
2023-02-10 07:48:34 +00:00
|
|
|
goto out_free_bio;
|
2022-02-18 15:03:27 +00:00
|
|
|
}
|
2011-01-26 06:21:39 +00:00
|
|
|
|
2022-03-30 20:11:22 +00:00
|
|
|
ret2 = btrfs_alloc_page_array(cb->nr_pages, cb->compressed_pages);
|
|
|
|
if (ret2) {
|
|
|
|
ret = BLK_STS_RESOURCE;
|
2023-02-10 07:48:34 +00:00
|
|
|
goto out_free_compressed_pages;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
}
|
|
|
|
|
2023-02-10 07:48:34 +00:00
|
|
|
add_ra_bio_pages(&inode->vfs_inode, em_start + em_len, cb, &memstall,
|
|
|
|
&pflags);
|
2008-11-07 03:02:51 +00:00
|
|
|
|
|
|
|
/* include any pages we added in add_ra-bio_pages */
|
2023-03-07 16:39:40 +00:00
|
|
|
cb->len = bbio->bio.bi_iter.bi_size;
|
2023-03-14 16:51:09 +00:00
|
|
|
cb->bbio.bio.bi_iter.bi_sector = bbio->bio.bi_iter.bi_sector;
|
|
|
|
btrfs_add_compressed_bio_pages(cb);
|
2022-07-07 05:33:27 +00:00
|
|
|
|
fs: fix leaked psi pressure state
When psi annotations were added to to btrfs compression reads, the psi
state tracking over add_ra_bio_pages and btrfs_submit_compressed_read was
faulty. A pressure state, once entered, is never left. This results in
incorrectly elevated pressure, which triggers OOM kills.
pflags record the *previous* memstall state when we enter a new one. The
code tried to initialize pflags to 1, and then optimize the leave call
when we either didn't enter a memstall, or were already inside a nested
stall. However, there can be multiple PageWorkingset pages in the bio, at
which point it's that path itself that enters repeatedly and overwrites
pflags. This causes us to miss the exit.
Enter the stall only once if needed, then unwind correctly.
erofs has the same problem, fix that up too. And move the memstall exit
past submit_bio() to restore submit accounting originally added by
b8e24a9300b0 ("block: annotate refault stalls from IO submission").
Link: https://lkml.kernel.org/r/Y2UHRqthNUwuIQGS@cmpxchg.org
Fixes: 4088a47e78f9 ("btrfs: add manual PSI accounting for compressed reads")
Fixes: 99486c511f68 ("erofs: add manual PSI accounting for the compressed address space")
Fixes: 118f3663fbc6 ("block: remove PSI accounting from the bio layer")
Link: https://lore.kernel.org/r/d20a0a85-e415-cf78-27f9-77dd7a94bc8d@leemhuis.info/
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Reported-by: Thorsten Leemhuis <linux@leemhuis.info>
Tested-by: Thorsten Leemhuis <linux@leemhuis.info>
Cc: Chao Yu <chao@kernel.org>
Cc: Chris Mason <clm@fb.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: David Sterba <dsterba@suse.com>
Cc: Gao Xiang <xiang@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Josef Bacik <josef@toxicpanda.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2022-11-03 21:34:31 +00:00
|
|
|
if (memstall)
|
2022-09-15 09:41:58 +00:00
|
|
|
psi_memstall_leave(&pflags);
|
|
|
|
|
2023-03-07 16:39:39 +00:00
|
|
|
btrfs_submit_bio(&cb->bbio, mirror_num);
|
2022-04-15 14:33:27 +00:00
|
|
|
return;
|
2011-01-26 06:21:39 +00:00
|
|
|
|
2023-02-10 07:48:34 +00:00
|
|
|
out_free_compressed_pages:
|
2011-01-26 06:21:39 +00:00
|
|
|
kfree(cb->compressed_pages);
|
2023-02-10 07:48:34 +00:00
|
|
|
out_free_bio:
|
2023-02-10 07:48:38 +00:00
|
|
|
bio_put(&cb->bbio.bio);
|
2023-02-10 07:48:34 +00:00
|
|
|
out:
|
2023-03-07 16:39:40 +00:00
|
|
|
btrfs_bio_end_io(bbio, ret);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-29 18:49:59 +00:00
|
|
|
}
|
2010-12-17 06:21:50 +00:00
|
|
|
|
2017-09-28 14:33:37 +00:00
|
|
|
/*
|
|
|
|
* Heuristic uses systematic sampling to collect data from the input data
|
|
|
|
* range, the logic can be tuned by the following constants:
|
|
|
|
*
|
|
|
|
* @SAMPLING_READ_SIZE - how many bytes will be copied from for each sample
|
|
|
|
* @SAMPLING_INTERVAL - range from which the sampled data can be collected
|
|
|
|
*/
|
|
|
|
#define SAMPLING_READ_SIZE (16)
|
|
|
|
#define SAMPLING_INTERVAL (256)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For statistical analysis of the input data we consider bytes that form a
|
|
|
|
* Galois Field of 256 objects. Each object has an attribute count, ie. how
|
|
|
|
* many times the object appeared in the sample.
|
|
|
|
*/
|
|
|
|
#define BUCKET_SIZE (256)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The size of the sample is based on a statistical sampling rule of thumb.
|
|
|
|
* The common way is to perform sampling tests as long as the number of
|
|
|
|
* elements in each cell is at least 5.
|
|
|
|
*
|
|
|
|
* Instead of 5, we choose 32 to obtain more accurate results.
|
|
|
|
* If the data contain the maximum number of symbols, which is 256, we obtain a
|
|
|
|
* sample size bound by 8192.
|
|
|
|
*
|
|
|
|
* For a sample of at most 8KB of data per data range: 16 consecutive bytes
|
|
|
|
* from up to 512 locations.
|
|
|
|
*/
|
|
|
|
#define MAX_SAMPLE_SIZE (BTRFS_MAX_UNCOMPRESSED * \
|
|
|
|
SAMPLING_READ_SIZE / SAMPLING_INTERVAL)
|
|
|
|
|
|
|
|
struct bucket_item {
|
|
|
|
u32 count;
|
|
|
|
};
|
2017-09-28 14:33:36 +00:00
|
|
|
|
|
|
|
struct heuristic_ws {
|
2017-09-28 14:33:37 +00:00
|
|
|
/* Partial copy of input data */
|
|
|
|
u8 *sample;
|
2017-09-28 14:33:38 +00:00
|
|
|
u32 sample_size;
|
2017-09-28 14:33:37 +00:00
|
|
|
/* Buckets store counters for each byte value */
|
|
|
|
struct bucket_item *bucket;
|
2017-12-03 21:30:33 +00:00
|
|
|
/* Sorting buffer */
|
|
|
|
struct bucket_item *bucket_b;
|
2017-09-28 14:33:36 +00:00
|
|
|
struct list_head list;
|
|
|
|
};
|
|
|
|
|
2019-02-04 20:20:03 +00:00
|
|
|
static struct workspace_manager heuristic_wsm;
|
|
|
|
|
2017-09-28 14:33:36 +00:00
|
|
|
static void free_heuristic_ws(struct list_head *ws)
|
|
|
|
{
|
|
|
|
struct heuristic_ws *workspace;
|
|
|
|
|
|
|
|
workspace = list_entry(ws, struct heuristic_ws, list);
|
|
|
|
|
2017-09-28 14:33:37 +00:00
|
|
|
kvfree(workspace->sample);
|
|
|
|
kfree(workspace->bucket);
|
2017-12-03 21:30:33 +00:00
|
|
|
kfree(workspace->bucket_b);
|
2017-09-28 14:33:36 +00:00
|
|
|
kfree(workspace);
|
|
|
|
}
|
|
|
|
|
2019-02-04 20:20:04 +00:00
|
|
|
static struct list_head *alloc_heuristic_ws(unsigned int level)
|
2017-09-28 14:33:36 +00:00
|
|
|
{
|
|
|
|
struct heuristic_ws *ws;
|
|
|
|
|
|
|
|
ws = kzalloc(sizeof(*ws), GFP_KERNEL);
|
|
|
|
if (!ws)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2017-09-28 14:33:37 +00:00
|
|
|
ws->sample = kvmalloc(MAX_SAMPLE_SIZE, GFP_KERNEL);
|
|
|
|
if (!ws->sample)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
ws->bucket = kcalloc(BUCKET_SIZE, sizeof(*ws->bucket), GFP_KERNEL);
|
|
|
|
if (!ws->bucket)
|
|
|
|
goto fail;
|
2017-09-28 14:33:36 +00:00
|
|
|
|
2017-12-03 21:30:33 +00:00
|
|
|
ws->bucket_b = kcalloc(BUCKET_SIZE, sizeof(*ws->bucket_b), GFP_KERNEL);
|
|
|
|
if (!ws->bucket_b)
|
|
|
|
goto fail;
|
|
|
|
|
2017-09-28 14:33:37 +00:00
|
|
|
INIT_LIST_HEAD(&ws->list);
|
2017-09-28 14:33:36 +00:00
|
|
|
return &ws->list;
|
2017-09-28 14:33:37 +00:00
|
|
|
fail:
|
|
|
|
free_heuristic_ws(&ws->list);
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2017-09-28 14:33:36 +00:00
|
|
|
}
|
|
|
|
|
2019-02-04 20:19:59 +00:00
|
|
|
const struct btrfs_compress_op btrfs_heuristic_compress = {
|
2019-10-01 22:53:31 +00:00
|
|
|
.workspace_manager = &heuristic_wsm,
|
2019-02-04 20:19:59 +00:00
|
|
|
};
|
|
|
|
|
2015-01-02 17:23:10 +00:00
|
|
|
static const struct btrfs_compress_op * const btrfs_compress_op[] = {
|
2019-02-04 20:19:59 +00:00
|
|
|
/* The heuristic is represented as compression type 0 */
|
|
|
|
&btrfs_heuristic_compress,
|
2010-12-17 06:21:50 +00:00
|
|
|
&btrfs_zlib_compress,
|
2010-10-25 07:12:26 +00:00
|
|
|
&btrfs_lzo_compress,
|
btrfs: Add zstd support
Add zstd compression and decompression support to BtrFS. zstd at its
fastest level compresses almost as well as zlib, while offering much
faster compression and decompression, approaching lzo speeds.
I benchmarked btrfs with zstd compression against no compression, lzo
compression, and zlib compression. I benchmarked two scenarios. Copying
a set of files to btrfs, and then reading the files. Copying a tarball
to btrfs, extracting it to btrfs, and then reading the extracted files.
After every operation, I call `sync` and include the sync time.
Between every pair of operations I unmount and remount the filesystem
to avoid caching. The benchmark files can be found in the upstream
zstd source repository under
`contrib/linux-kernel/{btrfs-benchmark.sh,btrfs-extract-benchmark.sh}`
[1] [2].
I ran the benchmarks on a Ubuntu 14.04 VM with 2 cores and 4 GiB of RAM.
The VM is running on a MacBook Pro with a 3.1 GHz Intel Core i7 processor,
16 GB of RAM, and a SSD.
The first compression benchmark is copying 10 copies of the unzipped
Silesia corpus [3] into a BtrFS filesystem mounted with
`-o compress-force=Method`. The decompression benchmark times how long
it takes to `tar` all 10 copies into `/dev/null`. The compression ratio is
measured by comparing the output of `df` and `du`. See the benchmark file
[1] for details. I benchmarked multiple zstd compression levels, although
the patch uses zstd level 1.
| Method | Ratio | Compression MB/s | Decompression speed |
|---------|-------|------------------|---------------------|
| None | 0.99 | 504 | 686 |
| lzo | 1.66 | 398 | 442 |
| zlib | 2.58 | 65 | 241 |
| zstd 1 | 2.57 | 260 | 383 |
| zstd 3 | 2.71 | 174 | 408 |
| zstd 6 | 2.87 | 70 | 398 |
| zstd 9 | 2.92 | 43 | 406 |
| zstd 12 | 2.93 | 21 | 408 |
| zstd 15 | 3.01 | 11 | 354 |
The next benchmark first copies `linux-4.11.6.tar` [4] to btrfs. Then it
measures the compression ratio, extracts the tar, and deletes the tar.
Then it measures the compression ratio again, and `tar`s the extracted
files into `/dev/null`. See the benchmark file [2] for details.
| Method | Tar Ratio | Extract Ratio | Copy (s) | Extract (s)| Read (s) |
|--------|-----------|---------------|----------|------------|----------|
| None | 0.97 | 0.78 | 0.981 | 5.501 | 8.807 |
| lzo | 2.06 | 1.38 | 1.631 | 8.458 | 8.585 |
| zlib | 3.40 | 1.86 | 7.750 | 21.544 | 11.744 |
| zstd 1 | 3.57 | 1.85 | 2.579 | 11.479 | 9.389 |
[1] https://github.com/facebook/zstd/blob/dev/contrib/linux-kernel/btrfs-benchmark.sh
[2] https://github.com/facebook/zstd/blob/dev/contrib/linux-kernel/btrfs-extract-benchmark.sh
[3] http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia
[4] https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.11.6.tar.xz
zstd source repository: https://github.com/facebook/zstd
Signed-off-by: Nick Terrell <terrelln@fb.com>
Signed-off-by: Chris Mason <clm@fb.com>
2017-08-10 02:39:02 +00:00
|
|
|
&btrfs_zstd_compress,
|
2010-12-17 06:21:50 +00:00
|
|
|
};
|
|
|
|
|
2019-10-04 00:47:39 +00:00
|
|
|
static struct list_head *alloc_workspace(int type, unsigned int level)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case BTRFS_COMPRESS_NONE: return alloc_heuristic_ws(level);
|
|
|
|
case BTRFS_COMPRESS_ZLIB: return zlib_alloc_workspace(level);
|
|
|
|
case BTRFS_COMPRESS_LZO: return lzo_alloc_workspace(level);
|
|
|
|
case BTRFS_COMPRESS_ZSTD: return zstd_alloc_workspace(level);
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* This can't happen, the type is validated several times
|
|
|
|
* before we get here.
|
|
|
|
*/
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-04 00:57:22 +00:00
|
|
|
static void free_workspace(int type, struct list_head *ws)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case BTRFS_COMPRESS_NONE: return free_heuristic_ws(ws);
|
|
|
|
case BTRFS_COMPRESS_ZLIB: return zlib_free_workspace(ws);
|
|
|
|
case BTRFS_COMPRESS_LZO: return lzo_free_workspace(ws);
|
|
|
|
case BTRFS_COMPRESS_ZSTD: return zstd_free_workspace(ws);
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* This can't happen, the type is validated several times
|
|
|
|
* before we get here.
|
|
|
|
*/
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-01 23:08:03 +00:00
|
|
|
static void btrfs_init_workspace_manager(int type)
|
2010-12-17 06:21:50 +00:00
|
|
|
{
|
2019-10-04 01:09:55 +00:00
|
|
|
struct workspace_manager *wsm;
|
2017-09-28 14:33:36 +00:00
|
|
|
struct list_head *workspace;
|
2010-12-17 06:21:50 +00:00
|
|
|
|
2019-10-04 01:09:55 +00:00
|
|
|
wsm = btrfs_compress_op[type]->workspace_manager;
|
2019-02-04 20:20:03 +00:00
|
|
|
INIT_LIST_HEAD(&wsm->idle_ws);
|
|
|
|
spin_lock_init(&wsm->ws_lock);
|
|
|
|
atomic_set(&wsm->total_ws, 0);
|
|
|
|
init_waitqueue_head(&wsm->ws_wait);
|
2016-04-27 00:55:15 +00:00
|
|
|
|
2019-02-04 20:20:01 +00:00
|
|
|
/*
|
|
|
|
* Preallocate one workspace for each compression type so we can
|
|
|
|
* guarantee forward progress in the worst case
|
|
|
|
*/
|
2019-10-04 00:47:39 +00:00
|
|
|
workspace = alloc_workspace(type, 0);
|
2019-02-04 20:20:01 +00:00
|
|
|
if (IS_ERR(workspace)) {
|
|
|
|
pr_warn(
|
|
|
|
"BTRFS: cannot preallocate compression workspace, will try later\n");
|
|
|
|
} else {
|
2019-02-04 20:20:03 +00:00
|
|
|
atomic_set(&wsm->total_ws, 1);
|
|
|
|
wsm->free_ws = 1;
|
|
|
|
list_add(workspace, &wsm->idle_ws);
|
2019-02-04 20:20:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-01 23:08:03 +00:00
|
|
|
static void btrfs_cleanup_workspace_manager(int type)
|
2019-02-04 20:20:01 +00:00
|
|
|
{
|
2019-10-03 23:40:58 +00:00
|
|
|
struct workspace_manager *wsman;
|
2019-02-04 20:20:01 +00:00
|
|
|
struct list_head *ws;
|
|
|
|
|
2019-10-03 23:40:58 +00:00
|
|
|
wsman = btrfs_compress_op[type]->workspace_manager;
|
2019-02-04 20:20:01 +00:00
|
|
|
while (!list_empty(&wsman->idle_ws)) {
|
|
|
|
ws = wsman->idle_ws.next;
|
|
|
|
list_del(ws);
|
2019-10-04 00:57:22 +00:00
|
|
|
free_workspace(type, ws);
|
2019-02-04 20:20:01 +00:00
|
|
|
atomic_dec(&wsman->total_ws);
|
2010-12-17 06:21:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-04-27 00:41:17 +00:00
|
|
|
* This finds an available workspace or allocates a new one.
|
|
|
|
* If it's not possible to allocate a new one, waits until there's one.
|
|
|
|
* Preallocation makes a forward progress guarantees and we do not return
|
|
|
|
* errors.
|
2010-12-17 06:21:50 +00:00
|
|
|
*/
|
2019-10-04 00:50:28 +00:00
|
|
|
struct list_head *btrfs_get_workspace(int type, unsigned int level)
|
2010-12-17 06:21:50 +00:00
|
|
|
{
|
2019-10-04 00:50:28 +00:00
|
|
|
struct workspace_manager *wsm;
|
2010-12-17 06:21:50 +00:00
|
|
|
struct list_head *workspace;
|
|
|
|
int cpus = num_online_cpus();
|
2017-05-31 15:14:56 +00:00
|
|
|
unsigned nofs_flag;
|
2017-09-28 14:33:36 +00:00
|
|
|
struct list_head *idle_ws;
|
|
|
|
spinlock_t *ws_lock;
|
|
|
|
atomic_t *total_ws;
|
|
|
|
wait_queue_head_t *ws_wait;
|
|
|
|
int *free_ws;
|
|
|
|
|
2019-10-04 00:50:28 +00:00
|
|
|
wsm = btrfs_compress_op[type]->workspace_manager;
|
2019-02-04 20:20:03 +00:00
|
|
|
idle_ws = &wsm->idle_ws;
|
|
|
|
ws_lock = &wsm->ws_lock;
|
|
|
|
total_ws = &wsm->total_ws;
|
|
|
|
ws_wait = &wsm->ws_wait;
|
|
|
|
free_ws = &wsm->free_ws;
|
2010-12-17 06:21:50 +00:00
|
|
|
|
|
|
|
again:
|
2015-10-14 05:05:24 +00:00
|
|
|
spin_lock(ws_lock);
|
|
|
|
if (!list_empty(idle_ws)) {
|
|
|
|
workspace = idle_ws->next;
|
2010-12-17 06:21:50 +00:00
|
|
|
list_del(workspace);
|
2016-04-27 00:15:15 +00:00
|
|
|
(*free_ws)--;
|
2015-10-14 05:05:24 +00:00
|
|
|
spin_unlock(ws_lock);
|
2010-12-17 06:21:50 +00:00
|
|
|
return workspace;
|
|
|
|
|
|
|
|
}
|
2016-04-27 00:15:15 +00:00
|
|
|
if (atomic_read(total_ws) > cpus) {
|
2010-12-17 06:21:50 +00:00
|
|
|
DEFINE_WAIT(wait);
|
|
|
|
|
2015-10-14 05:05:24 +00:00
|
|
|
spin_unlock(ws_lock);
|
|
|
|
prepare_to_wait(ws_wait, &wait, TASK_UNINTERRUPTIBLE);
|
2016-04-27 00:15:15 +00:00
|
|
|
if (atomic_read(total_ws) > cpus && !*free_ws)
|
2010-12-17 06:21:50 +00:00
|
|
|
schedule();
|
2015-10-14 05:05:24 +00:00
|
|
|
finish_wait(ws_wait, &wait);
|
2010-12-17 06:21:50 +00:00
|
|
|
goto again;
|
|
|
|
}
|
2016-04-27 00:15:15 +00:00
|
|
|
atomic_inc(total_ws);
|
2015-10-14 05:05:24 +00:00
|
|
|
spin_unlock(ws_lock);
|
2010-12-17 06:21:50 +00:00
|
|
|
|
2017-05-31 15:14:56 +00:00
|
|
|
/*
|
|
|
|
* Allocation helpers call vmalloc that can't use GFP_NOFS, so we have
|
|
|
|
* to turn it off here because we might get called from the restricted
|
|
|
|
* context of btrfs_compress_bio/btrfs_compress_pages
|
|
|
|
*/
|
|
|
|
nofs_flag = memalloc_nofs_save();
|
2019-10-04 00:47:39 +00:00
|
|
|
workspace = alloc_workspace(type, level);
|
2017-05-31 15:14:56 +00:00
|
|
|
memalloc_nofs_restore(nofs_flag);
|
|
|
|
|
2010-12-17 06:21:50 +00:00
|
|
|
if (IS_ERR(workspace)) {
|
2016-04-27 00:15:15 +00:00
|
|
|
atomic_dec(total_ws);
|
2015-10-14 05:05:24 +00:00
|
|
|
wake_up(ws_wait);
|
2016-04-27 00:41:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do not return the error but go back to waiting. There's a
|
|
|
|
* workspace preallocated for each type and the compression
|
|
|
|
* time is bounded so we get to a workspace eventually. This
|
|
|
|
* makes our caller's life easier.
|
2016-04-27 01:07:39 +00:00
|
|
|
*
|
|
|
|
* To prevent silent and low-probability deadlocks (when the
|
|
|
|
* initial preallocation fails), check if there are any
|
|
|
|
* workspaces at all.
|
2016-04-27 00:41:17 +00:00
|
|
|
*/
|
2016-04-27 01:07:39 +00:00
|
|
|
if (atomic_read(total_ws) == 0) {
|
|
|
|
static DEFINE_RATELIMIT_STATE(_rs,
|
|
|
|
/* once per minute */ 60 * HZ,
|
|
|
|
/* no burst */ 1);
|
|
|
|
|
|
|
|
if (__ratelimit(&_rs)) {
|
2016-09-20 14:05:02 +00:00
|
|
|
pr_warn("BTRFS: no compression workspaces, low memory, retrying\n");
|
2016-04-27 01:07:39 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-27 00:41:17 +00:00
|
|
|
goto again;
|
2010-12-17 06:21:50 +00:00
|
|
|
}
|
|
|
|
return workspace;
|
|
|
|
}
|
|
|
|
|
2019-02-04 20:20:04 +00:00
|
|
|
static struct list_head *get_workspace(int type, int level)
|
2019-02-04 20:20:02 +00:00
|
|
|
{
|
2019-10-04 00:36:16 +00:00
|
|
|
switch (type) {
|
2019-10-04 00:50:28 +00:00
|
|
|
case BTRFS_COMPRESS_NONE: return btrfs_get_workspace(type, level);
|
2019-10-04 00:36:16 +00:00
|
|
|
case BTRFS_COMPRESS_ZLIB: return zlib_get_workspace(level);
|
2019-10-04 00:50:28 +00:00
|
|
|
case BTRFS_COMPRESS_LZO: return btrfs_get_workspace(type, level);
|
2019-10-04 00:36:16 +00:00
|
|
|
case BTRFS_COMPRESS_ZSTD: return zstd_get_workspace(level);
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* This can't happen, the type is validated several times
|
|
|
|
* before we get here.
|
|
|
|
*/
|
|
|
|
BUG();
|
|
|
|
}
|
2019-02-04 20:20:02 +00:00
|
|
|
}
|
|
|
|
|
2010-12-17 06:21:50 +00:00
|
|
|
/*
|
|
|
|
* put a workspace struct back on the list or free it if we have enough
|
|
|
|
* idle ones sitting around
|
|
|
|
*/
|
2019-10-04 00:50:28 +00:00
|
|
|
void btrfs_put_workspace(int type, struct list_head *ws)
|
2010-12-17 06:21:50 +00:00
|
|
|
{
|
2019-10-04 00:50:28 +00:00
|
|
|
struct workspace_manager *wsm;
|
2017-09-28 14:33:36 +00:00
|
|
|
struct list_head *idle_ws;
|
|
|
|
spinlock_t *ws_lock;
|
|
|
|
atomic_t *total_ws;
|
|
|
|
wait_queue_head_t *ws_wait;
|
|
|
|
int *free_ws;
|
|
|
|
|
2019-10-04 00:50:28 +00:00
|
|
|
wsm = btrfs_compress_op[type]->workspace_manager;
|
2019-02-04 20:20:03 +00:00
|
|
|
idle_ws = &wsm->idle_ws;
|
|
|
|
ws_lock = &wsm->ws_lock;
|
|
|
|
total_ws = &wsm->total_ws;
|
|
|
|
ws_wait = &wsm->ws_wait;
|
|
|
|
free_ws = &wsm->free_ws;
|
2015-10-14 05:05:24 +00:00
|
|
|
|
|
|
|
spin_lock(ws_lock);
|
btrfs: Keep one more workspace around
find_workspace() allocates up to num_online_cpus() + 1 workspaces.
free_workspace() will only keep num_online_cpus() workspaces. When
(de)compressing we will allocate num_online_cpus() + 1 workspaces, then
free one, and repeat. Instead, we can just keep num_online_cpus() + 1
workspaces around, and never have to allocate/free another workspace in the
common case.
I tested on a Ubuntu 14.04 VM with 2 cores and 4 GiB of RAM. I mounted a
BtrFS partition with -o compress-force={lzo,zlib,zstd} and logged whenever
a workspace was allocated of freed. Then I copied vmlinux (527 MB) to the
partition. Before the patch, during the copy it would allocate and free 5-6
workspaces. After, it only allocated the initial 3. This held true for lzo,
zlib, and zstd. The time it took to execute cp vmlinux /mnt/btrfs && sync
dropped from 1.70s to 1.44s with lzo compression, and from 2.04s to 1.80s
for zstd compression.
Signed-off-by: Nick Terrell <terrelln@fb.com>
Reviewed-by: Omar Sandoval <osandov@fb.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2017-06-29 17:57:26 +00:00
|
|
|
if (*free_ws <= num_online_cpus()) {
|
2019-02-04 20:20:02 +00:00
|
|
|
list_add(ws, idle_ws);
|
2016-04-27 00:15:15 +00:00
|
|
|
(*free_ws)++;
|
2015-10-14 05:05:24 +00:00
|
|
|
spin_unlock(ws_lock);
|
2010-12-17 06:21:50 +00:00
|
|
|
goto wake;
|
|
|
|
}
|
2015-10-14 05:05:24 +00:00
|
|
|
spin_unlock(ws_lock);
|
2010-12-17 06:21:50 +00:00
|
|
|
|
2019-10-04 00:57:22 +00:00
|
|
|
free_workspace(type, ws);
|
2016-04-27 00:15:15 +00:00
|
|
|
atomic_dec(total_ws);
|
2010-12-17 06:21:50 +00:00
|
|
|
wake:
|
2018-02-26 15:15:17 +00:00
|
|
|
cond_wake_up(ws_wait);
|
2010-12-17 06:21:50 +00:00
|
|
|
}
|
|
|
|
|
2019-02-04 20:20:02 +00:00
|
|
|
static void put_workspace(int type, struct list_head *ws)
|
|
|
|
{
|
2019-10-04 00:42:03 +00:00
|
|
|
switch (type) {
|
2019-10-04 00:50:28 +00:00
|
|
|
case BTRFS_COMPRESS_NONE: return btrfs_put_workspace(type, ws);
|
|
|
|
case BTRFS_COMPRESS_ZLIB: return btrfs_put_workspace(type, ws);
|
|
|
|
case BTRFS_COMPRESS_LZO: return btrfs_put_workspace(type, ws);
|
2019-10-04 00:42:03 +00:00
|
|
|
case BTRFS_COMPRESS_ZSTD: return zstd_put_workspace(ws);
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* This can't happen, the type is validated several times
|
|
|
|
* before we get here.
|
|
|
|
*/
|
|
|
|
BUG();
|
|
|
|
}
|
2019-02-04 20:20:02 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 05:37:51 +00:00
|
|
|
/*
|
|
|
|
* Adjust @level according to the limits of the compression algorithm or
|
|
|
|
* fallback to default
|
|
|
|
*/
|
|
|
|
static unsigned int btrfs_compress_set_level(int type, unsigned level)
|
|
|
|
{
|
|
|
|
const struct btrfs_compress_op *ops = btrfs_compress_op[type];
|
|
|
|
|
|
|
|
if (level == 0)
|
|
|
|
level = ops->default_level;
|
|
|
|
else
|
|
|
|
level = min(level, ops->max_level);
|
|
|
|
|
|
|
|
return level;
|
|
|
|
}
|
|
|
|
|
2010-12-17 06:21:50 +00:00
|
|
|
/*
|
2017-02-14 18:04:07 +00:00
|
|
|
* Given an address space and start and length, compress the bytes into @pages
|
|
|
|
* that are allocated on demand.
|
2010-12-17 06:21:50 +00:00
|
|
|
*
|
2017-09-15 15:36:57 +00:00
|
|
|
* @type_level is encoded algorithm and level, where level 0 means whatever
|
|
|
|
* default the algorithm chooses and is opaque here;
|
|
|
|
* - compression algo are 0-3
|
|
|
|
* - the level are bits 4-7
|
|
|
|
*
|
2017-02-14 18:04:07 +00:00
|
|
|
* @out_pages is an in/out parameter, holds maximum number of pages to allocate
|
|
|
|
* and returns number of actually allocated pages
|
2010-12-17 06:21:50 +00:00
|
|
|
*
|
2017-02-14 18:04:07 +00:00
|
|
|
* @total_in is used to return the number of bytes actually read. It
|
|
|
|
* may be smaller than the input length if we had to exit early because we
|
2010-12-17 06:21:50 +00:00
|
|
|
* ran out of room in the pages array or because we cross the
|
|
|
|
* max_out threshold.
|
|
|
|
*
|
2017-02-14 18:04:07 +00:00
|
|
|
* @total_out is an in/out parameter, must be set to the input length and will
|
|
|
|
* be also used to return the total number of compressed bytes
|
2010-12-17 06:21:50 +00:00
|
|
|
*/
|
2017-09-15 15:36:57 +00:00
|
|
|
int btrfs_compress_pages(unsigned int type_level, struct address_space *mapping,
|
2017-02-14 18:04:07 +00:00
|
|
|
u64 start, struct page **pages,
|
2010-12-17 06:21:50 +00:00
|
|
|
unsigned long *out_pages,
|
|
|
|
unsigned long *total_in,
|
2017-02-14 18:45:05 +00:00
|
|
|
unsigned long *total_out)
|
2010-12-17 06:21:50 +00:00
|
|
|
{
|
2019-02-04 20:19:57 +00:00
|
|
|
int type = btrfs_compress_type(type_level);
|
2019-02-04 20:20:04 +00:00
|
|
|
int level = btrfs_compress_level(type_level);
|
2010-12-17 06:21:50 +00:00
|
|
|
struct list_head *workspace;
|
|
|
|
int ret;
|
|
|
|
|
2019-08-09 14:49:06 +00:00
|
|
|
level = btrfs_compress_set_level(type, level);
|
2019-02-04 20:20:04 +00:00
|
|
|
workspace = get_workspace(type, level);
|
2019-10-01 22:06:15 +00:00
|
|
|
ret = compression_compress_pages(type, workspace, mapping, start, pages,
|
|
|
|
out_pages, total_in, total_out);
|
2019-02-04 20:20:02 +00:00
|
|
|
put_workspace(type, workspace);
|
2010-12-17 06:21:50 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-05-26 07:44:58 +00:00
|
|
|
static int btrfs_decompress_bio(struct compressed_bio *cb)
|
2010-12-17 06:21:50 +00:00
|
|
|
{
|
|
|
|
struct list_head *workspace;
|
|
|
|
int ret;
|
2017-05-26 07:44:58 +00:00
|
|
|
int type = cb->compress_type;
|
2010-12-17 06:21:50 +00:00
|
|
|
|
2019-02-04 20:20:04 +00:00
|
|
|
workspace = get_workspace(type, 0);
|
2021-12-27 10:18:39 +00:00
|
|
|
ret = compression_decompress_bio(workspace, cb);
|
2019-02-04 20:20:02 +00:00
|
|
|
put_workspace(type, workspace);
|
2017-05-26 07:44:59 +00:00
|
|
|
|
2023-03-07 16:39:38 +00:00
|
|
|
if (!ret)
|
2023-03-07 16:39:41 +00:00
|
|
|
zero_fill_bio(&cb->orig_bbio->bio);
|
2010-12-17 06:21:50 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* a less complex decompression routine. Our compressed data fits in a
|
|
|
|
* single page, and we want to read a single page out of it.
|
|
|
|
* start_byte tells us the offset into the compressed data we're interested in
|
|
|
|
*/
|
2022-11-07 16:30:21 +00:00
|
|
|
int btrfs_decompress(int type, const u8 *data_in, struct page *dest_page,
|
2010-12-17 06:21:50 +00:00
|
|
|
unsigned long start_byte, size_t srclen, size_t destlen)
|
|
|
|
{
|
|
|
|
struct list_head *workspace;
|
|
|
|
int ret;
|
|
|
|
|
2019-02-04 20:20:04 +00:00
|
|
|
workspace = get_workspace(type, 0);
|
2019-10-01 22:06:15 +00:00
|
|
|
ret = compression_decompress(type, workspace, data_in, dest_page,
|
|
|
|
start_byte, srclen, destlen);
|
2019-02-04 20:20:02 +00:00
|
|
|
put_workspace(type, workspace);
|
2019-02-04 20:20:04 +00:00
|
|
|
|
2010-12-17 06:21:50 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
btrfs: make module init/exit match their sequence
[BACKGROUND]
In theory init_btrfs_fs() and exit_btrfs_fs() should match their
sequence, thus normally they should look like this:
init_btrfs_fs() | exit_btrfs_fs()
----------------------+------------------------
init_A(); |
init_B(); |
init_C(); |
| exit_C();
| exit_B();
| exit_A();
So is for the error path of init_btrfs_fs().
But it's not the case, some exit functions don't match their init
functions sequence in init_btrfs_fs().
Furthermore in init_btrfs_fs(), we need to have a new error label for
each new init function we added. This is not really expandable,
especially recently we may add several new functions to init_btrfs_fs().
[ENHANCEMENT]
The patch will introduce the following things to enhance the situation:
- struct init_sequence
Just a wrapper of init and exit function pointers.
The init function must use int type as return value, thus some init
functions need to be updated to return 0.
The exit function can be NULL, as there are some init sequence just
outputting a message.
- struct mod_init_seq[] array
This is a const array, recording all the initialization we need to do
in init_btrfs_fs(), and the order follows the old init_btrfs_fs().
- bool mod_init_result[] array
This is a bool array, recording if we have initialized one entry in
mod_init_seq[].
The reason to split mod_init_seq[] and mod_init_result[] is to avoid
section mismatch in reference.
All init function are in .init.text, but if mod_init_seq[] records
the @initialized member it can no longer be const, thus will be put
into .data section, and cause modpost warning.
For init_btrfs_fs() we just call all init functions in their order in
mod_init_seq[] array, and after each call, setting corresponding
mod_init_result[] to true.
For exit_btrfs_fs() and error handling path of init_btrfs_fs(), we just
iterate mod_init_seq[] in reverse order, and skip all uninitialized
entry.
With this patch, init_btrfs_fs()/exit_btrfs_fs() will be much easier to
expand and will always follow the strict order.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2022-10-12 09:22:35 +00:00
|
|
|
int __init btrfs_init_compress(void)
|
2019-02-04 20:20:01 +00:00
|
|
|
{
|
2023-02-10 07:48:34 +00:00
|
|
|
if (bioset_init(&btrfs_compressed_bioset, BIO_POOL_SIZE,
|
|
|
|
offsetof(struct compressed_bio, bbio.bio),
|
|
|
|
BIOSET_NEED_BVECS))
|
|
|
|
return -ENOMEM;
|
2019-10-01 23:08:03 +00:00
|
|
|
btrfs_init_workspace_manager(BTRFS_COMPRESS_NONE);
|
|
|
|
btrfs_init_workspace_manager(BTRFS_COMPRESS_ZLIB);
|
|
|
|
btrfs_init_workspace_manager(BTRFS_COMPRESS_LZO);
|
|
|
|
zstd_init_workspace_manager();
|
btrfs: make module init/exit match their sequence
[BACKGROUND]
In theory init_btrfs_fs() and exit_btrfs_fs() should match their
sequence, thus normally they should look like this:
init_btrfs_fs() | exit_btrfs_fs()
----------------------+------------------------
init_A(); |
init_B(); |
init_C(); |
| exit_C();
| exit_B();
| exit_A();
So is for the error path of init_btrfs_fs().
But it's not the case, some exit functions don't match their init
functions sequence in init_btrfs_fs().
Furthermore in init_btrfs_fs(), we need to have a new error label for
each new init function we added. This is not really expandable,
especially recently we may add several new functions to init_btrfs_fs().
[ENHANCEMENT]
The patch will introduce the following things to enhance the situation:
- struct init_sequence
Just a wrapper of init and exit function pointers.
The init function must use int type as return value, thus some init
functions need to be updated to return 0.
The exit function can be NULL, as there are some init sequence just
outputting a message.
- struct mod_init_seq[] array
This is a const array, recording all the initialization we need to do
in init_btrfs_fs(), and the order follows the old init_btrfs_fs().
- bool mod_init_result[] array
This is a bool array, recording if we have initialized one entry in
mod_init_seq[].
The reason to split mod_init_seq[] and mod_init_result[] is to avoid
section mismatch in reference.
All init function are in .init.text, but if mod_init_seq[] records
the @initialized member it can no longer be const, thus will be put
into .data section, and cause modpost warning.
For init_btrfs_fs() we just call all init functions in their order in
mod_init_seq[] array, and after each call, setting corresponding
mod_init_result[] to true.
For exit_btrfs_fs() and error handling path of init_btrfs_fs(), we just
iterate mod_init_seq[] in reverse order, and skip all uninitialized
entry.
With this patch, init_btrfs_fs()/exit_btrfs_fs() will be much easier to
expand and will always follow the strict order.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2022-10-12 09:22:35 +00:00
|
|
|
return 0;
|
2019-02-04 20:20:01 +00:00
|
|
|
}
|
|
|
|
|
2018-02-19 16:24:18 +00:00
|
|
|
void __cold btrfs_exit_compress(void)
|
2010-12-17 06:21:50 +00:00
|
|
|
{
|
2019-10-01 23:08:03 +00:00
|
|
|
btrfs_cleanup_workspace_manager(BTRFS_COMPRESS_NONE);
|
|
|
|
btrfs_cleanup_workspace_manager(BTRFS_COMPRESS_ZLIB);
|
|
|
|
btrfs_cleanup_workspace_manager(BTRFS_COMPRESS_LZO);
|
|
|
|
zstd_cleanup_workspace_manager();
|
2023-02-10 07:48:34 +00:00
|
|
|
bioset_exit(&btrfs_compressed_bioset);
|
2010-12-17 06:21:50 +00:00
|
|
|
}
|
2010-11-08 07:22:19 +00:00
|
|
|
|
|
|
|
/*
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
* Copy decompressed data from working buffer to pages.
|
2010-11-08 07:22:19 +00:00
|
|
|
*
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
* @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
|
2010-11-08 07:22:19 +00:00
|
|
|
*
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Return 0 if we have copied all needed contents for @orig_bio.
|
|
|
|
* Return >0 if we need continue decompress.
|
2010-11-08 07:22:19 +00:00
|
|
|
*/
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
int btrfs_decompress_buf2page(const char *buf, u32 buf_len,
|
|
|
|
struct compressed_bio *cb, u32 decompressed)
|
2010-11-08 07:22:19 +00:00
|
|
|
{
|
2023-03-07 16:39:41 +00:00
|
|
|
struct bio *orig_bio = &cb->orig_bbio->bio;
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
/* 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;
|
2016-11-25 08:07:46 +00:00
|
|
|
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
/* Haven't reached the bvec range, exit */
|
|
|
|
if (decompressed + buf_len <= bvec_offset)
|
|
|
|
return 1;
|
2010-11-08 07:22:19 +00:00
|
|
|
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
copy_start = max(cur_offset, bvec_offset);
|
|
|
|
copy_len = min(bvec_offset + bvec.bv_len,
|
|
|
|
decompressed + buf_len) - copy_start;
|
|
|
|
ASSERT(copy_len);
|
2010-11-08 07:22:19 +00:00
|
|
|
|
2016-11-25 08:07:46 +00:00
|
|
|
/*
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
* Extra range check to ensure we didn't go beyond
|
|
|
|
* @buf + @buf_len.
|
2016-11-25 08:07:46 +00:00
|
|
|
*/
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
ASSERT(copy_start - decompressed < buf_len);
|
|
|
|
memcpy_to_page(bvec.bv_page, bvec.bv_offset,
|
|
|
|
buf + copy_start - decompressed, copy_len);
|
|
|
|
cur_offset += copy_len;
|
2010-11-08 07:22:19 +00:00
|
|
|
|
btrfs: rework btrfs_decompress_buf2page()
There are several bugs inside the function btrfs_decompress_buf2page()
- @start_byte doesn't take bvec.bv_offset into consideration
Thus it can't handle case where the target range is not page aligned.
- Too many helper variables
There are tons of helper variables, @buf_offset, @current_buf_start,
@start_byte, @prev_start_byte, @working_bytes, @bytes.
This hurts anyone who wants to read the function.
- No obvious main cursor for the iteartion
A new problem caused by previous problem.
- Comments for parameter list makes no sense
Like @buf_start is the offset to @buf, or offset inside the full
decompressed extent? (Spoiler alert, the later case)
And @total_out acts more like @buf_start + @size_of_buf.
The worst is @disk_start.
The real meaning of it is the file offset of the full decompressed
extent.
This patch will rework the whole function by:
- Add a proper comment with ASCII art to explain the parameter list
- Rework parameter list
The old @buf_start is renamed to @decompressed, to show how many bytes
are already decompressed inside the full decompressed extent.
The old @total_out is replaced by @buf_len, which is the decompressed
data size.
For old @disk_start and @bio, just pass @compressed_bio in.
- Use single main cursor
The main cursor will be @cur_file_offset, to show what's the current
file offset.
Other helper variables will be declared inside the main loop, and only
minimal amount of helper variables:
* offset_inside_decompressed_buf: The only real helper
* copy_start_file_offset: File offset we start memcpy
* bvec_file_offset: File offset of current bvec
Even with all these extensive comments, the final function is still
smaller than the original function, which is definitely a win.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-07-05 02:00:58 +00:00
|
|
|
bio_advance(orig_bio, copy_len);
|
|
|
|
/* Finished the bio */
|
|
|
|
if (!orig_bio->bi_iter.bi_size)
|
|
|
|
return 0;
|
2010-11-08 07:22:19 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
2017-07-17 13:52:58 +00:00
|
|
|
|
2017-10-08 13:11:59 +00:00
|
|
|
/*
|
|
|
|
* Shannon Entropy calculation
|
|
|
|
*
|
2018-11-28 11:05:13 +00:00
|
|
|
* Pure byte distribution analysis fails to determine compressibility of data.
|
2017-10-08 13:11:59 +00:00
|
|
|
* Try calculating entropy to estimate the average minimum number of bits
|
|
|
|
* needed to encode the sampled data.
|
|
|
|
*
|
|
|
|
* For convenience, return the percentage of needed bits, instead of amount of
|
|
|
|
* bits directly.
|
|
|
|
*
|
|
|
|
* @ENTROPY_LVL_ACEPTABLE - below that threshold, sample has low byte entropy
|
|
|
|
* and can be compressible with high probability
|
|
|
|
*
|
|
|
|
* @ENTROPY_LVL_HIGH - data are not compressible with high probability
|
|
|
|
*
|
|
|
|
* Use of ilog2() decreases precision, we lower the LVL to 5 to compensate.
|
|
|
|
*/
|
|
|
|
#define ENTROPY_LVL_ACEPTABLE (65)
|
|
|
|
#define ENTROPY_LVL_HIGH (80)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For increasead precision in shannon_entropy calculation,
|
|
|
|
* let's do pow(n, M) to save more digits after comma:
|
|
|
|
*
|
|
|
|
* - maximum int bit length is 64
|
|
|
|
* - ilog2(MAX_SAMPLE_SIZE) -> 13
|
|
|
|
* - 13 * 4 = 52 < 64 -> M = 4
|
|
|
|
*
|
|
|
|
* So use pow(n, 4).
|
|
|
|
*/
|
|
|
|
static inline u32 ilog2_w(u64 n)
|
|
|
|
{
|
|
|
|
return ilog2(n * n * n * n);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 shannon_entropy(struct heuristic_ws *ws)
|
|
|
|
{
|
|
|
|
const u32 entropy_max = 8 * ilog2_w(2);
|
|
|
|
u32 entropy_sum = 0;
|
|
|
|
u32 p, p_base, sz_base;
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
sz_base = ilog2_w(ws->sample_size);
|
|
|
|
for (i = 0; i < BUCKET_SIZE && ws->bucket[i].count > 0; i++) {
|
|
|
|
p = ws->bucket[i].count;
|
|
|
|
p_base = ilog2_w(p);
|
|
|
|
entropy_sum += p * (sz_base - p_base);
|
|
|
|
}
|
|
|
|
|
|
|
|
entropy_sum /= ws->sample_size;
|
|
|
|
return entropy_sum * 100 / entropy_max;
|
|
|
|
}
|
|
|
|
|
2017-12-03 21:30:33 +00:00
|
|
|
#define RADIX_BASE 4U
|
|
|
|
#define COUNTERS_SIZE (1U << RADIX_BASE)
|
|
|
|
|
|
|
|
static u8 get4bits(u64 num, int shift) {
|
|
|
|
u8 low4bits;
|
|
|
|
|
|
|
|
num >>= shift;
|
|
|
|
/* Reverse order */
|
|
|
|
low4bits = (COUNTERS_SIZE - 1) - (num % COUNTERS_SIZE);
|
|
|
|
return low4bits;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use 4 bits as radix base
|
2018-11-28 11:05:13 +00:00
|
|
|
* Use 16 u32 counters for calculating new position in buf array
|
2017-12-03 21:30:33 +00:00
|
|
|
*
|
|
|
|
* @array - array that will be sorted
|
|
|
|
* @array_buf - buffer array to store sorting results
|
|
|
|
* must be equal in size to @array
|
|
|
|
* @num - array size
|
|
|
|
*/
|
2017-12-12 19:35:02 +00:00
|
|
|
static void radix_sort(struct bucket_item *array, struct bucket_item *array_buf,
|
2017-12-12 19:35:02 +00:00
|
|
|
int num)
|
2017-09-28 14:33:41 +00:00
|
|
|
{
|
2017-12-03 21:30:33 +00:00
|
|
|
u64 max_num;
|
|
|
|
u64 buf_num;
|
|
|
|
u32 counters[COUNTERS_SIZE];
|
|
|
|
u32 new_addr;
|
|
|
|
u32 addr;
|
|
|
|
int bitlen;
|
|
|
|
int shift;
|
|
|
|
int i;
|
2017-09-28 14:33:41 +00:00
|
|
|
|
2017-12-03 21:30:33 +00:00
|
|
|
/*
|
|
|
|
* Try avoid useless loop iterations for small numbers stored in big
|
|
|
|
* counters. Example: 48 33 4 ... in 64bit array
|
|
|
|
*/
|
2017-12-12 19:35:02 +00:00
|
|
|
max_num = array[0].count;
|
2017-12-03 21:30:33 +00:00
|
|
|
for (i = 1; i < num; i++) {
|
2017-12-12 19:35:02 +00:00
|
|
|
buf_num = array[i].count;
|
2017-12-03 21:30:33 +00:00
|
|
|
if (buf_num > max_num)
|
|
|
|
max_num = buf_num;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf_num = ilog2(max_num);
|
|
|
|
bitlen = ALIGN(buf_num, RADIX_BASE * 2);
|
|
|
|
|
|
|
|
shift = 0;
|
|
|
|
while (shift < bitlen) {
|
|
|
|
memset(counters, 0, sizeof(counters));
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++) {
|
2017-12-12 19:35:02 +00:00
|
|
|
buf_num = array[i].count;
|
2017-12-03 21:30:33 +00:00
|
|
|
addr = get4bits(buf_num, shift);
|
|
|
|
counters[addr]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i < COUNTERS_SIZE; i++)
|
|
|
|
counters[i] += counters[i - 1];
|
|
|
|
|
|
|
|
for (i = num - 1; i >= 0; i--) {
|
2017-12-12 19:35:02 +00:00
|
|
|
buf_num = array[i].count;
|
2017-12-03 21:30:33 +00:00
|
|
|
addr = get4bits(buf_num, shift);
|
|
|
|
counters[addr]--;
|
|
|
|
new_addr = counters[addr];
|
2017-12-12 19:35:02 +00:00
|
|
|
array_buf[new_addr] = array[i];
|
2017-12-03 21:30:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
shift += RADIX_BASE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Normal radix expects to move data from a temporary array, to
|
|
|
|
* the main one. But that requires some CPU time. Avoid that
|
|
|
|
* by doing another sort iteration to original array instead of
|
|
|
|
* memcpy()
|
|
|
|
*/
|
|
|
|
memset(counters, 0, sizeof(counters));
|
|
|
|
|
|
|
|
for (i = 0; i < num; i ++) {
|
2017-12-12 19:35:02 +00:00
|
|
|
buf_num = array_buf[i].count;
|
2017-12-03 21:30:33 +00:00
|
|
|
addr = get4bits(buf_num, shift);
|
|
|
|
counters[addr]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i < COUNTERS_SIZE; i++)
|
|
|
|
counters[i] += counters[i - 1];
|
|
|
|
|
|
|
|
for (i = num - 1; i >= 0; i--) {
|
2017-12-12 19:35:02 +00:00
|
|
|
buf_num = array_buf[i].count;
|
2017-12-03 21:30:33 +00:00
|
|
|
addr = get4bits(buf_num, shift);
|
|
|
|
counters[addr]--;
|
|
|
|
new_addr = counters[addr];
|
2017-12-12 19:35:02 +00:00
|
|
|
array[new_addr] = array_buf[i];
|
2017-12-03 21:30:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
shift += RADIX_BASE;
|
|
|
|
}
|
2017-09-28 14:33:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Size of the core byte set - how many bytes cover 90% of the sample
|
|
|
|
*
|
|
|
|
* There are several types of structured binary data that use nearly all byte
|
|
|
|
* values. The distribution can be uniform and counts in all buckets will be
|
|
|
|
* nearly the same (eg. encrypted data). Unlikely to be compressible.
|
|
|
|
*
|
|
|
|
* Other possibility is normal (Gaussian) distribution, where the data could
|
|
|
|
* be potentially compressible, but we have to take a few more steps to decide
|
|
|
|
* how much.
|
|
|
|
*
|
|
|
|
* @BYTE_CORE_SET_LOW - main part of byte values repeated frequently,
|
|
|
|
* compression algo can easy fix that
|
|
|
|
* @BYTE_CORE_SET_HIGH - data have uniform distribution and with high
|
|
|
|
* probability is not compressible
|
|
|
|
*/
|
|
|
|
#define BYTE_CORE_SET_LOW (64)
|
|
|
|
#define BYTE_CORE_SET_HIGH (200)
|
|
|
|
|
|
|
|
static int byte_core_set_size(struct heuristic_ws *ws)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
u32 coreset_sum = 0;
|
|
|
|
const u32 core_set_threshold = ws->sample_size * 90 / 100;
|
|
|
|
struct bucket_item *bucket = ws->bucket;
|
|
|
|
|
|
|
|
/* Sort in reverse order */
|
2017-12-12 19:35:02 +00:00
|
|
|
radix_sort(ws->bucket, ws->bucket_b, BUCKET_SIZE);
|
2017-09-28 14:33:41 +00:00
|
|
|
|
|
|
|
for (i = 0; i < BYTE_CORE_SET_LOW; i++)
|
|
|
|
coreset_sum += bucket[i].count;
|
|
|
|
|
|
|
|
if (coreset_sum > core_set_threshold)
|
|
|
|
return i;
|
|
|
|
|
|
|
|
for (; i < BYTE_CORE_SET_HIGH && bucket[i].count > 0; i++) {
|
|
|
|
coreset_sum += bucket[i].count;
|
|
|
|
if (coreset_sum > core_set_threshold)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2017-09-28 14:33:40 +00:00
|
|
|
/*
|
|
|
|
* Count byte values in buckets.
|
|
|
|
* This heuristic can detect textual data (configs, xml, json, html, etc).
|
|
|
|
* Because in most text-like data byte set is restricted to limited number of
|
|
|
|
* possible characters, and that restriction in most cases makes data easy to
|
|
|
|
* compress.
|
|
|
|
*
|
|
|
|
* @BYTE_SET_THRESHOLD - consider all data within this byte set size:
|
|
|
|
* less - compressible
|
|
|
|
* more - need additional analysis
|
|
|
|
*/
|
|
|
|
#define BYTE_SET_THRESHOLD (64)
|
|
|
|
|
|
|
|
static u32 byte_set_size(const struct heuristic_ws *ws)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
u32 byte_set_size = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < BYTE_SET_THRESHOLD; i++) {
|
|
|
|
if (ws->bucket[i].count > 0)
|
|
|
|
byte_set_size++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Continue collecting count of byte values in buckets. If the byte
|
|
|
|
* set size is bigger then the threshold, it's pointless to continue,
|
|
|
|
* the detection technique would fail for this type of data.
|
|
|
|
*/
|
|
|
|
for (; i < BUCKET_SIZE; i++) {
|
|
|
|
if (ws->bucket[i].count > 0) {
|
|
|
|
byte_set_size++;
|
|
|
|
if (byte_set_size > BYTE_SET_THRESHOLD)
|
|
|
|
return byte_set_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return byte_set_size;
|
|
|
|
}
|
|
|
|
|
2017-09-28 14:33:39 +00:00
|
|
|
static bool sample_repeated_patterns(struct heuristic_ws *ws)
|
|
|
|
{
|
|
|
|
const u32 half_of_sample = ws->sample_size / 2;
|
|
|
|
const u8 *data = ws->sample;
|
|
|
|
|
|
|
|
return memcmp(&data[0], &data[half_of_sample], half_of_sample) == 0;
|
|
|
|
}
|
|
|
|
|
2017-09-28 14:33:38 +00:00
|
|
|
static void heuristic_collect_sample(struct inode *inode, u64 start, u64 end,
|
|
|
|
struct heuristic_ws *ws)
|
|
|
|
{
|
|
|
|
struct page *page;
|
|
|
|
u64 index, index_end;
|
|
|
|
u32 i, curr_sample_pos;
|
|
|
|
u8 *in_data;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compression handles the input data by chunks of 128KiB
|
|
|
|
* (defined by BTRFS_MAX_UNCOMPRESSED)
|
|
|
|
*
|
|
|
|
* We do the same for the heuristic and loop over the whole range.
|
|
|
|
*
|
|
|
|
* MAX_SAMPLE_SIZE - calculated under assumption that heuristic will
|
|
|
|
* process no more than BTRFS_MAX_UNCOMPRESSED at a time.
|
|
|
|
*/
|
|
|
|
if (end - start > BTRFS_MAX_UNCOMPRESSED)
|
|
|
|
end = start + BTRFS_MAX_UNCOMPRESSED;
|
|
|
|
|
|
|
|
index = start >> PAGE_SHIFT;
|
|
|
|
index_end = end >> PAGE_SHIFT;
|
|
|
|
|
|
|
|
/* Don't miss unaligned end */
|
2023-01-03 05:11:37 +00:00
|
|
|
if (!PAGE_ALIGNED(end))
|
2017-09-28 14:33:38 +00:00
|
|
|
index_end++;
|
|
|
|
|
|
|
|
curr_sample_pos = 0;
|
|
|
|
while (index < index_end) {
|
|
|
|
page = find_get_page(inode->i_mapping, index);
|
2021-02-17 02:48:23 +00:00
|
|
|
in_data = kmap_local_page(page);
|
2017-09-28 14:33:38 +00:00
|
|
|
/* Handle case where the start is not aligned to PAGE_SIZE */
|
|
|
|
i = start % PAGE_SIZE;
|
|
|
|
while (i < PAGE_SIZE - SAMPLING_READ_SIZE) {
|
|
|
|
/* Don't sample any garbage from the last page */
|
|
|
|
if (start > end - SAMPLING_READ_SIZE)
|
|
|
|
break;
|
|
|
|
memcpy(&ws->sample[curr_sample_pos], &in_data[i],
|
|
|
|
SAMPLING_READ_SIZE);
|
|
|
|
i += SAMPLING_INTERVAL;
|
|
|
|
start += SAMPLING_INTERVAL;
|
|
|
|
curr_sample_pos += SAMPLING_READ_SIZE;
|
|
|
|
}
|
2021-02-17 02:48:23 +00:00
|
|
|
kunmap_local(in_data);
|
2017-09-28 14:33:38 +00:00
|
|
|
put_page(page);
|
|
|
|
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ws->sample_size = curr_sample_pos;
|
|
|
|
}
|
|
|
|
|
2017-07-17 13:52:58 +00:00
|
|
|
/*
|
|
|
|
* Compression heuristic.
|
|
|
|
*
|
|
|
|
* For now is's a naive and optimistic 'return true', we'll extend the logic to
|
|
|
|
* quickly (compared to direct compression) detect data characteristics
|
2023-01-17 10:03:21 +00:00
|
|
|
* (compressible/incompressible) to avoid wasting CPU time on incompressible
|
2017-07-17 13:52:58 +00:00
|
|
|
* data.
|
|
|
|
*
|
|
|
|
* The following types of analysis can be performed:
|
|
|
|
* - detect mostly zero data
|
|
|
|
* - detect data with low "byte set" size (text, etc)
|
|
|
|
* - detect data with low/high "core byte" set
|
|
|
|
*
|
|
|
|
* Return non-zero if the compression should be done, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end)
|
|
|
|
{
|
2019-02-04 20:20:04 +00:00
|
|
|
struct list_head *ws_list = get_workspace(0, 0);
|
2017-09-28 14:33:36 +00:00
|
|
|
struct heuristic_ws *ws;
|
2017-09-28 14:33:38 +00:00
|
|
|
u32 i;
|
|
|
|
u8 byte;
|
2017-10-08 13:11:59 +00:00
|
|
|
int ret = 0;
|
2017-07-17 13:52:58 +00:00
|
|
|
|
2017-09-28 14:33:36 +00:00
|
|
|
ws = list_entry(ws_list, struct heuristic_ws, list);
|
|
|
|
|
2017-09-28 14:33:38 +00:00
|
|
|
heuristic_collect_sample(inode, start, end, ws);
|
|
|
|
|
2017-09-28 14:33:39 +00:00
|
|
|
if (sample_repeated_patterns(ws)) {
|
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-09-28 14:33:38 +00:00
|
|
|
memset(ws->bucket, 0, sizeof(*ws->bucket)*BUCKET_SIZE);
|
|
|
|
|
|
|
|
for (i = 0; i < ws->sample_size; i++) {
|
|
|
|
byte = ws->sample[i];
|
|
|
|
ws->bucket[byte].count++;
|
2017-07-17 13:52:58 +00:00
|
|
|
}
|
|
|
|
|
2017-09-28 14:33:40 +00:00
|
|
|
i = byte_set_size(ws);
|
|
|
|
if (i < BYTE_SET_THRESHOLD) {
|
|
|
|
ret = 2;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-09-28 14:33:41 +00:00
|
|
|
i = byte_core_set_size(ws);
|
|
|
|
if (i <= BYTE_CORE_SET_LOW) {
|
|
|
|
ret = 3;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= BYTE_CORE_SET_HIGH) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-10-08 13:11:59 +00:00
|
|
|
i = shannon_entropy(ws);
|
|
|
|
if (i <= ENTROPY_LVL_ACEPTABLE) {
|
|
|
|
ret = 4;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the levels below ENTROPY_LVL_HIGH, additional analysis would be
|
|
|
|
* needed to give green light to compression.
|
|
|
|
*
|
|
|
|
* For now just assume that compression at that level is not worth the
|
|
|
|
* resources because:
|
|
|
|
*
|
|
|
|
* 1. it is possible to defrag the data later
|
|
|
|
*
|
|
|
|
* 2. the data would turn out to be hardly compressible, eg. 150 byte
|
|
|
|
* values, every bucket has counter at level ~54. The heuristic would
|
|
|
|
* be confused. This can happen when data have some internal repeated
|
|
|
|
* patterns like "abbacbbc...". This can be detected by analyzing
|
|
|
|
* pairs of bytes, which is too costly.
|
|
|
|
*/
|
|
|
|
if (i < ENTROPY_LVL_HIGH) {
|
|
|
|
ret = 5;
|
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-09-28 14:33:39 +00:00
|
|
|
out:
|
2019-02-04 20:20:02 +00:00
|
|
|
put_workspace(0, ws_list);
|
2017-07-17 13:52:58 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2017-09-15 15:36:57 +00:00
|
|
|
|
2019-02-04 20:20:05 +00:00
|
|
|
/*
|
|
|
|
* Convert the compression suffix (eg. after "zlib" starting with ":") to
|
|
|
|
* level, unrecognized string will set the default level
|
|
|
|
*/
|
|
|
|
unsigned int btrfs_compress_str2level(unsigned int type, const char *str)
|
2017-09-15 15:36:57 +00:00
|
|
|
{
|
2019-02-04 20:20:05 +00:00
|
|
|
unsigned int level = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!type)
|
2017-09-15 15:36:57 +00:00
|
|
|
return 0;
|
|
|
|
|
2019-02-04 20:20:05 +00:00
|
|
|
if (str[0] == ':') {
|
|
|
|
ret = kstrtouint(str + 1, 10, &level);
|
|
|
|
if (ret)
|
|
|
|
level = 0;
|
|
|
|
}
|
|
|
|
|
2019-08-09 14:49:06 +00:00
|
|
|
level = btrfs_compress_set_level(type, level);
|
|
|
|
|
|
|
|
return level;
|
|
|
|
}
|