// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2013 Fusion IO. All rights reserved.
*/
#include <linux/pagemap.h>
#include <linux/pagevec.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/sizes.h>
#include "btrfs-tests.h"
#include "../ctree.h"
#include "../extent_io.h"
#include "../disk-io.h"
#include "../btrfs_inode.h"
#define PROCESS_UNLOCK (1 << 0)
#define PROCESS_RELEASE (1 << 1)
#define PROCESS_TEST_LOCKED (1 << 2)
static noinline int process_page_range(struct inode *inode, u64 start, u64 end,
unsigned long flags)
{
int ret;
struct folio_batch fbatch;
unsigned long index = start >> PAGE_SHIFT;
unsigned long end_index = end >> PAGE_SHIFT;
int i;
int count = 0;
int loops = 0;
folio_batch_init(&fbatch);
while (index <= end_index) {
ret = filemap_get_folios_contig(inode->i_mapping, &index,
end_index, &fbatch);
for (i = 0; i < ret; i++) {
struct folio *folio = fbatch.folios[i];
if (flags & PROCESS_TEST_LOCKED &&
!folio_test_locked(folio))
count++;
if (flags & PROCESS_UNLOCK && folio_test_locked(folio))
folio_unlock(folio);
if (flags & PROCESS_RELEASE)
folio_put(folio);
}
folio_batch_release(&fbatch);
cond_resched();
loops++;
if (loops > 100000) {
printk(KERN_ERR
"stuck in a loop, start %llu, end %llu, ret %d\n",
start, end, ret);
break;
}
}
return count;
}
#define STATE_FLAG_STR_LEN 256
#define PRINT_ONE_FLAG(state, dest, cur, name) \
({ \
if (state->state & EXTENT_##name) \
cur += scnprintf(dest + cur, STATE_FLAG_STR_LEN - cur, \
"%s" #name, cur == 0 ? "" : "|"); \
})
static void extent_flag_to_str(const struct extent_state *state, char *dest)
{
int cur = 0;
dest[0] = 0;
PRINT_ONE_FLAG(state, dest, cur, DIRTY);
PRINT_ONE_FLAG(state, dest, cur, UPTODATE);
PRINT_ONE_FLAG(state, dest, cur, LOCKED);
PRINT_ONE_FLAG(state, dest, cur, NEW);
PRINT_ONE_FLAG(state, dest, cur, DELALLOC);
PRINT_ONE_FLAG(state, dest, cur, DEFRAG);
PRINT_ONE_FLAG(state, dest, cur, BOUNDARY);
PRINT_ONE_FLAG(state, dest, cur, NODATASUM);
PRINT_ONE_FLAG(state, dest, cur, CLEAR_META_RESV);
PRINT_ONE_FLAG(state, dest, cur, NEED_WAIT);
PRINT_ONE_FLAG(state, dest, cur, NORESERVE);
PRINT_ONE_FLAG(state, dest, cur, QGROUP_RESERVED);
PRINT_ONE_FLAG(state, dest, cur, CLEAR_DATA_RESV);
}
static void dump_extent_io_tree(const struct extent_io_tree *tree)
{
struct rb_node *node;
char flags_str[STATE_FLAG_STR_LEN];
node = rb_first(&tree->state);
test_msg("io tree content:");
while (node) {
struct extent_state *state;
state = rb_entry(node, struct extent_state, rb_node);
extent_flag_to_str(state, flags_str);
test_msg(" start=%llu len=%llu flags=%s", state->start,
state->end + 1 - state->start, flags_str);
node = rb_next(node);
}
}
static int test_find_delalloc(u32 sectorsize, u32 nodesize)
{
struct btrfs_fs_info *fs_info;
struct btrfs_root *root = NULL;
struct inode *inode = NULL;
struct extent_io_tree *tmp;
struct page *page;
struct page *locked_page = NULL;
unsigned long index = 0;
/* In this test we need at least 2 file extents at its maximum size */
u64 max_bytes = BTRFS_MAX_EXTENT_SIZE;
u64 total_dirty = 2 * max_bytes;
u64 start, end, test_start;
bool found;
int ret = -EINVAL;
test_msg("running find delalloc tests");
fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize);
if (!fs_info) {
test_std_err(TEST_ALLOC_FS_INFO);
return -ENOMEM;
}
root = btrfs_alloc_dummy_root(fs_info);
if (IS_ERR(root)) {
test_std_err(TEST_ALLOC_ROOT);
ret = PTR_ERR(root);
goto out;
}
inode = btrfs_new_test_inode();
if (!inode) {
test_std_err(TEST_ALLOC_INODE);
ret = -ENOMEM;
goto out;
}
tmp = &BTRFS_I(inode)->io_tree;
BTRFS_I(inode)->root = root;
/*
* Passing NULL as we don't have fs_info but tracepoints are not used
* at this point
*/
extent_io_tree_init(NULL, tmp, IO_TREE_SELFTEST);
/*
* First go through and create and mark all of our pages dirty, we pin
* everything to make sure our pages don't get evicted and screw up our
* test.
*/
for (index = 0; index < (total_dirty >> PAGE_SHIFT); index++) {