mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-30 19:15:14 +00:00
Btrfs: initial move to kernel module land
Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
1261ec42b3
commit
2e635a2783
24 changed files with 274 additions and 2911 deletions
|
@ -1,40 +1,20 @@
|
||||||
CC=gcc
|
ifneq ($(KERNELRELEASE),)
|
||||||
CFLAGS = -g -Wall -Werror
|
# kbuild part of makefile
|
||||||
headers = radix-tree.h ctree.h disk-io.h kerncompat.h print-tree.h list.h \
|
|
||||||
transaction.h
|
|
||||||
objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
|
|
||||||
root-tree.o dir-item.o hash.o file-item.o inode-item.o \
|
|
||||||
inode-map.o \
|
|
||||||
|
|
||||||
# if you don't have sparse installed, use ls instead
|
obj-m := btrfs.o
|
||||||
CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
|
btrfs-y := super.o
|
||||||
-Wcontext -Wcast-truncate -Wuninitialized -Wshadow -Wundef
|
|
||||||
check=sparse $(CHECKFLAGS)
|
|
||||||
#check=ls
|
|
||||||
|
|
||||||
.c.o:
|
#btrfs-y := ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
|
||||||
$(check) $<
|
# root-tree.o dir-item.o hash.o file-item.o inode-item.o \
|
||||||
$(CC) $(CFLAGS) -c $<
|
# inode-map.o \
|
||||||
|
|
||||||
all: tester debug-tree quick-test dir-test tags mkfs.btrfs
|
else
|
||||||
|
|
||||||
mkfs.btrfs: $(objects) mkfs.o
|
|
||||||
gcc $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o
|
|
||||||
|
|
||||||
debug-tree: $(objects) debug-tree.o
|
|
||||||
gcc $(CFLAGS) -o debug-tree $(objects) debug-tree.o
|
|
||||||
|
|
||||||
tester: $(objects) random-test.o
|
|
||||||
gcc $(CFLAGS) -o tester $(objects) random-test.o
|
|
||||||
|
|
||||||
dir-test: $(objects) dir-test.o
|
|
||||||
gcc $(CFLAGS) -o dir-test $(objects) dir-test.o
|
|
||||||
quick-test: $(objects) quick-test.o
|
|
||||||
gcc $(CFLAGS) -o quick-test $(objects) quick-test.o
|
|
||||||
|
|
||||||
$(objects): $(headers)
|
|
||||||
|
|
||||||
clean :
|
|
||||||
rm debug-tree tester *.o
|
|
||||||
|
|
||||||
|
# Normal Makefile
|
||||||
|
|
||||||
|
KERNELDIR := /lib/modules/`uname -r`/build
|
||||||
|
all::
|
||||||
|
$(MAKE) -C $(KERNELDIR) M=`pwd` modules
|
||||||
|
clean::
|
||||||
|
rm *.o btrfs.ko
|
||||||
|
endif
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
#include <stdio.h>
|
#include <linux/module.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
#include "print-tree.h"
|
|
||||||
|
|
||||||
static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
|
static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||||
*root, struct btrfs_path *path, int level);
|
*root, struct btrfs_path *path, int level);
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
#ifndef __BTRFS__
|
#ifndef __BTRFS__
|
||||||
#define __BTRFS__
|
#define __BTRFS__
|
||||||
|
|
||||||
#include "list.h"
|
|
||||||
#include "kerncompat.h"
|
|
||||||
|
|
||||||
struct btrfs_trans_handle;
|
struct btrfs_trans_handle;
|
||||||
|
|
||||||
#define BTRFS_MAGIC "_BtRfS_M"
|
#define BTRFS_MAGIC "_BtRfS_M"
|
||||||
|
@ -75,6 +72,7 @@ struct btrfs_super_block {
|
||||||
__le64 root;
|
__le64 root;
|
||||||
__le64 total_blocks;
|
__le64 total_blocks;
|
||||||
__le64 blocks_used;
|
__le64 blocks_used;
|
||||||
|
__le64 root_dir_objectid;
|
||||||
} __attribute__ ((__packed__));
|
} __attribute__ ((__packed__));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -693,6 +691,17 @@ static inline void btrfs_set_super_blocksize(struct btrfs_super_block *s,
|
||||||
s->blocksize = cpu_to_le32(val);
|
s->blocksize = cpu_to_le32(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u64 btrfs_super_root_dir(struct btrfs_super_block *s)
|
||||||
|
{
|
||||||
|
return le64_to_cpu(s->root_dir_objectid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void btrfs_set_super_root_dir(struct btrfs_super_block *s, u64
|
||||||
|
val)
|
||||||
|
{
|
||||||
|
s->root_dir_objectid = cpu_to_le64(val);
|
||||||
|
}
|
||||||
|
|
||||||
static inline u8 *btrfs_leaf_data(struct btrfs_leaf *l)
|
static inline u8 *btrfs_leaf_data(struct btrfs_leaf *l)
|
||||||
{
|
{
|
||||||
return (u8 *)l->items;
|
return (u8 *)l->items;
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
|
||||||
#include "disk-io.h"
|
|
||||||
#include "print-tree.h"
|
|
||||||
#include "transaction.h"
|
|
||||||
|
|
||||||
int main(int ac, char **av) {
|
|
||||||
struct btrfs_super_block super;
|
|
||||||
struct btrfs_root *root;
|
|
||||||
|
|
||||||
if (ac != 2) {
|
|
||||||
fprintf(stderr, "usage: %s device\n", av[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
radix_tree_init();
|
|
||||||
root = open_ctree(av[1], &super);
|
|
||||||
if (!root) {
|
|
||||||
fprintf(stderr, "unable to open %s\n", av[1]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
printf("fs tree\n");
|
|
||||||
btrfs_print_tree(root, root->node);
|
|
||||||
printf("map tree\n");
|
|
||||||
btrfs_print_tree(root->fs_info->extent_root,
|
|
||||||
root->fs_info->extent_root->node);
|
|
||||||
printf("inode tree\n");
|
|
||||||
btrfs_print_tree(root->fs_info->inode_root,
|
|
||||||
root->fs_info->inode_root->node);
|
|
||||||
printf("root tree\n");
|
|
||||||
btrfs_print_tree(root->fs_info->tree_root,
|
|
||||||
root->fs_info->tree_root->node);
|
|
||||||
printf("total blocks %Lu\n", btrfs_super_total_blocks(&super));
|
|
||||||
printf("blocks used %Lu\n", btrfs_super_blocks_used(&super));
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,7 +1,4 @@
|
||||||
#include <stdio.h>
|
#include <linux/module.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
@ -21,7 +18,12 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||||
key.objectid = dir;
|
key.objectid = dir;
|
||||||
key.flags = 0;
|
key.flags = 0;
|
||||||
btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
|
btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
|
||||||
ret = btrfs_name_hash(name, name_len, &key.offset);
|
if (name_len == 1 && *name == '.')
|
||||||
|
key.offset = 1;
|
||||||
|
else if (name_len == 2 && name[0] == '.' && name[1] == '.')
|
||||||
|
key.offset = 2;
|
||||||
|
else
|
||||||
|
ret = btrfs_name_hash(name, name_len, &key.offset);
|
||||||
BUG_ON(ret);
|
BUG_ON(ret);
|
||||||
btrfs_init_path(&path);
|
btrfs_init_path(&path);
|
||||||
data_size = sizeof(*dir_item) + name_len;
|
data_size = sizeof(*dir_item) + name_len;
|
||||||
|
|
|
@ -1,494 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
|
||||||
#include "disk-io.h"
|
|
||||||
#include "print-tree.h"
|
|
||||||
#include "hash.h"
|
|
||||||
#include "transaction.h"
|
|
||||||
|
|
||||||
int keep_running = 1;
|
|
||||||
struct btrfs_super_block super;
|
|
||||||
static u64 dir_oid = 44556;
|
|
||||||
static u64 file_oid = 33778;
|
|
||||||
|
|
||||||
static int find_num(struct radix_tree_root *root, unsigned long *num_ret,
|
|
||||||
int exists)
|
|
||||||
{
|
|
||||||
unsigned long num = rand();
|
|
||||||
unsigned long res[2];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
again:
|
|
||||||
ret = radix_tree_gang_lookup(root, (void **)res, num, 2);
|
|
||||||
if (exists) {
|
|
||||||
if (ret == 0)
|
|
||||||
return -1;
|
|
||||||
num = res[0];
|
|
||||||
} else if (ret != 0 && num == res[0]) {
|
|
||||||
num++;
|
|
||||||
if (ret > 1 && num == res[1]) {
|
|
||||||
num++;
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*num_ret = num;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initial_inode_init(struct btrfs_root *root,
|
|
||||||
struct btrfs_inode_item *inode_item)
|
|
||||||
{
|
|
||||||
memset(inode_item, 0, sizeof(*inode_item));
|
|
||||||
btrfs_set_inode_generation(inode_item, root->fs_info->generation);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
char buf[128];
|
|
||||||
unsigned long oid;
|
|
||||||
u64 objectid;
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key inode_map;
|
|
||||||
struct btrfs_inode_item inode_item;
|
|
||||||
|
|
||||||
find_num(radix, &oid, 0);
|
|
||||||
sprintf(buf, "str-%lu", oid);
|
|
||||||
|
|
||||||
ret = btrfs_find_free_objectid(trans, root, dir_oid + 1, &objectid);
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
inode_map.objectid = objectid;
|
|
||||||
inode_map.flags = 0;
|
|
||||||
inode_map.offset = 0;
|
|
||||||
|
|
||||||
ret = btrfs_insert_inode_map(trans, root, objectid, &inode_map);
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
initial_inode_init(root, &inode_item);
|
|
||||||
ret = btrfs_insert_inode(trans, root, objectid, &inode_item);
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
|
|
||||||
objectid, 1);
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
radix_tree_preload(GFP_KERNEL);
|
|
||||||
ret = radix_tree_insert(radix, oid, (void *)oid);
|
|
||||||
radix_tree_preload_end();
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
return ret;
|
|
||||||
error:
|
|
||||||
if (ret != -EEXIST)
|
|
||||||
goto fatal;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if we got an EEXIST, it may be due to hash collision, double
|
|
||||||
* check
|
|
||||||
*/
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
|
|
||||||
strlen(buf), 0);
|
|
||||||
if (ret)
|
|
||||||
goto fatal_release;
|
|
||||||
if (!btrfs_match_dir_item_name(root, &path, buf, strlen(buf))) {
|
|
||||||
struct btrfs_dir_item *di;
|
|
||||||
char *found;
|
|
||||||
u32 found_len;
|
|
||||||
u64 myhash;
|
|
||||||
u64 foundhash;
|
|
||||||
|
|
||||||
di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
|
|
||||||
struct btrfs_dir_item);
|
|
||||||
found = (char *)(di + 1);
|
|
||||||
found_len = btrfs_dir_name_len(di);
|
|
||||||
btrfs_name_hash(buf, strlen(buf), &myhash);
|
|
||||||
btrfs_name_hash(found, found_len, &foundhash);
|
|
||||||
if (myhash != foundhash)
|
|
||||||
goto fatal_release;
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fatal_release:
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
fatal:
|
|
||||||
printf("failed to insert %lu ret %d\n", oid, ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root
|
|
||||||
*root, struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
char buf[128];
|
|
||||||
unsigned long oid;
|
|
||||||
|
|
||||||
ret = find_num(radix, &oid, 1);
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
sprintf(buf, "str-%lu", oid);
|
|
||||||
|
|
||||||
ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
|
|
||||||
file_oid, 1);
|
|
||||||
if (ret != -EEXIST) {
|
|
||||||
printf("insert on %s gave us %d\n", buf, ret);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int del_dir_item(struct btrfs_trans_handle *trans,
|
|
||||||
struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix,
|
|
||||||
unsigned long radix_index,
|
|
||||||
struct btrfs_path *path)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
unsigned long *ptr;
|
|
||||||
u64 file_objectid;
|
|
||||||
struct btrfs_dir_item *di;
|
|
||||||
|
|
||||||
/* find the inode number of the file */
|
|
||||||
di = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0],
|
|
||||||
struct btrfs_dir_item);
|
|
||||||
file_objectid = btrfs_dir_objectid(di);
|
|
||||||
|
|
||||||
/* delete the directory item */
|
|
||||||
ret = btrfs_del_item(trans, root, path);
|
|
||||||
if (ret)
|
|
||||||
goto out_release;
|
|
||||||
btrfs_release_path(root, path);
|
|
||||||
|
|
||||||
/* delete the inode */
|
|
||||||
btrfs_init_path(path);
|
|
||||||
ret = btrfs_lookup_inode(trans, root, path, file_objectid, -1);
|
|
||||||
if (ret)
|
|
||||||
goto out_release;
|
|
||||||
ret = btrfs_del_item(trans, root, path);
|
|
||||||
if (ret)
|
|
||||||
goto out_release;
|
|
||||||
btrfs_release_path(root, path);
|
|
||||||
|
|
||||||
/* delete the inode mapping */
|
|
||||||
btrfs_init_path(path);
|
|
||||||
ret = btrfs_lookup_inode_map(trans, root, path, file_objectid, -1);
|
|
||||||
if (ret)
|
|
||||||
goto out_release;
|
|
||||||
ret = btrfs_del_item(trans, root->fs_info->inode_root, path);
|
|
||||||
if (ret)
|
|
||||||
goto out_release;
|
|
||||||
|
|
||||||
if (root->fs_info->last_inode_alloc > file_objectid)
|
|
||||||
root->fs_info->last_inode_alloc = file_objectid;
|
|
||||||
btrfs_release_path(root, path);
|
|
||||||
ptr = radix_tree_delete(radix, radix_index);
|
|
||||||
if (!ptr) {
|
|
||||||
ret = -5555;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
out_release:
|
|
||||||
btrfs_release_path(root, path);
|
|
||||||
out:
|
|
||||||
printf("failed to delete %lu %d\n", radix_index, ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
char buf[128];
|
|
||||||
unsigned long oid;
|
|
||||||
struct btrfs_path path;
|
|
||||||
|
|
||||||
ret = find_num(radix, &oid, 1);
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
sprintf(buf, "str-%lu", oid);
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
|
|
||||||
strlen(buf), -1);
|
|
||||||
if (ret)
|
|
||||||
goto out_release;
|
|
||||||
|
|
||||||
ret = del_dir_item(trans, root, radix, oid, &path);
|
|
||||||
if (ret)
|
|
||||||
goto out_release;
|
|
||||||
return ret;
|
|
||||||
out_release:
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
printf("failed to delete %lu %d\n", oid, ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
|
||||||
*root, struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
char buf[128];
|
|
||||||
int ret;
|
|
||||||
unsigned long oid;
|
|
||||||
u64 objectid;
|
|
||||||
struct btrfs_dir_item *di;
|
|
||||||
|
|
||||||
ret = find_num(radix, &oid, 1);
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
sprintf(buf, "str-%lu", oid);
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
|
|
||||||
strlen(buf), 0);
|
|
||||||
if (!ret) {
|
|
||||||
di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
|
|
||||||
struct btrfs_dir_item);
|
|
||||||
objectid = btrfs_dir_objectid(di);
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_lookup_inode_map(trans, root, &path, objectid, 0);
|
|
||||||
}
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
if (ret) {
|
|
||||||
printf("unable to find key %lu\n", oid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root
|
|
||||||
*root, struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
char buf[128];
|
|
||||||
int ret;
|
|
||||||
unsigned long oid;
|
|
||||||
|
|
||||||
ret = find_num(radix, &oid, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
sprintf(buf, "str-%lu", oid);
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
|
|
||||||
strlen(buf), 0);
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
if (!ret) {
|
|
||||||
printf("able to find key that should not exist %lu\n", oid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root
|
|
||||||
*root, struct radix_tree_root *radix, int nr)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key key;
|
|
||||||
unsigned long found = 0;
|
|
||||||
u32 found_len;
|
|
||||||
int ret;
|
|
||||||
int slot;
|
|
||||||
int count = 0;
|
|
||||||
char buf[128];
|
|
||||||
struct btrfs_dir_item *di;
|
|
||||||
|
|
||||||
key.offset = (u64)-1;
|
|
||||||
key.flags = 0;
|
|
||||||
btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
|
|
||||||
key.objectid = dir_oid;
|
|
||||||
while(nr-- >= 0) {
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
|
|
||||||
if (ret < 0) {
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (ret != 0) {
|
|
||||||
if (path.slots[0] == 0) {
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
path.slots[0] -= 1;
|
|
||||||
}
|
|
||||||
slot = path.slots[0];
|
|
||||||
di = btrfs_item_ptr(&path.nodes[0]->leaf, slot,
|
|
||||||
struct btrfs_dir_item);
|
|
||||||
found_len = btrfs_dir_name_len(di);
|
|
||||||
memcpy(buf, (char *)(di + 1), found_len);
|
|
||||||
BUG_ON(found_len > 128);
|
|
||||||
buf[found_len] = '\0';
|
|
||||||
found = atoi(buf + 4);
|
|
||||||
ret = del_dir_item(trans, root, radix, found, &path);
|
|
||||||
count++;
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"failed to remove %lu from tree\n",
|
|
||||||
found);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!keep_running)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
fprintf(stderr, "failed to delete from the radix %lu\n", found);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix, int count)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int ret = 0;
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
ret = ins_one(trans, root, radix);
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr, "fill failed\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (i % 1000 == 0) {
|
|
||||||
ret = btrfs_commit_transaction(trans, root, &super);
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr, "fill commit failed\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i && i % 10000 == 0) {
|
|
||||||
printf("bigfill %d\n", i);
|
|
||||||
}
|
|
||||||
if (!keep_running)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
int nr = rand() % 5000;
|
|
||||||
static int run_nr = 0;
|
|
||||||
|
|
||||||
/* do the bulk op much less frequently */
|
|
||||||
if (run_nr++ % 100)
|
|
||||||
return 0;
|
|
||||||
ret = empty_tree(trans, root, radix, nr);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
ret = fill_tree(trans, root, radix, nr);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int (*ops[])(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct
|
|
||||||
radix_tree_root *radix) =
|
|
||||||
{ ins_one, insert_dup, del_one, lookup_item,
|
|
||||||
lookup_enoent, bulk_op };
|
|
||||||
|
|
||||||
void sigstopper(int ignored)
|
|
||||||
{
|
|
||||||
keep_running = 0;
|
|
||||||
fprintf(stderr, "caught exit signal, stopping\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int print_usage(void)
|
|
||||||
{
|
|
||||||
printf("usage: tester [-ih] [-c count] [-f count]\n");
|
|
||||||
printf("\t -c count -- iteration count after filling\n");
|
|
||||||
printf("\t -f count -- run this many random inserts before starting\n");
|
|
||||||
printf("\t -i -- only do initial fill\n");
|
|
||||||
printf("\t -h -- this help text\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
int main(int ac, char **av)
|
|
||||||
{
|
|
||||||
RADIX_TREE(radix, GFP_KERNEL);
|
|
||||||
struct btrfs_root *root;
|
|
||||||
int i;
|
|
||||||
int ret;
|
|
||||||
int count;
|
|
||||||
int op;
|
|
||||||
int iterations = 20000;
|
|
||||||
int init_fill_count = 800000;
|
|
||||||
int err = 0;
|
|
||||||
int initial_only = 0;
|
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
radix_tree_init();
|
|
||||||
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
trans = btrfs_start_transaction(root, 1);
|
|
||||||
|
|
||||||
signal(SIGTERM, sigstopper);
|
|
||||||
signal(SIGINT, sigstopper);
|
|
||||||
|
|
||||||
for (i = 1 ; i < ac ; i++) {
|
|
||||||
if (strcmp(av[i], "-i") == 0) {
|
|
||||||
initial_only = 1;
|
|
||||||
} else if (strcmp(av[i], "-c") == 0) {
|
|
||||||
iterations = atoi(av[i+1]);
|
|
||||||
i++;
|
|
||||||
} else if (strcmp(av[i], "-f") == 0) {
|
|
||||||
init_fill_count = atoi(av[i+1]);
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
print_usage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("initial fill\n");
|
|
||||||
ret = fill_tree(trans, root, &radix, init_fill_count);
|
|
||||||
printf("starting run\n");
|
|
||||||
if (ret) {
|
|
||||||
err = ret;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (initial_only == 1) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
for (i = 0; i < iterations; i++) {
|
|
||||||
op = rand() % ARRAY_SIZE(ops);
|
|
||||||
count = rand() % 128;
|
|
||||||
if (i % 2000 == 0) {
|
|
||||||
printf("%d\n", i);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
if (i && i % 5000 == 0) {
|
|
||||||
printf("open & close, root level %d nritems %d\n",
|
|
||||||
btrfs_header_level(&root->node->node.header),
|
|
||||||
btrfs_header_nritems(&root->node->node.header));
|
|
||||||
close_ctree(root, &super);
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
}
|
|
||||||
while(count--) {
|
|
||||||
ret = ops[op](trans, root, &radix);
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr, "op %d failed %d:%d\n",
|
|
||||||
op, i, iterations);
|
|
||||||
btrfs_print_tree(root, root->node);
|
|
||||||
fprintf(stderr, "op %d failed %d:%d\n",
|
|
||||||
op, i, iterations);
|
|
||||||
err = ret;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (ops[op] == bulk_op)
|
|
||||||
break;
|
|
||||||
if (keep_running == 0) {
|
|
||||||
err = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
close_ctree(root, &super);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
|
@ -266,20 +266,25 @@ static int find_and_setup_root(struct btrfs_super_block *super,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct btrfs_root *open_ctree(char *filename, struct btrfs_super_block *super)
|
struct btrfs_root *open_ctree(char *filename, struct btrfs_super_block *super)
|
||||||
|
{
|
||||||
|
int fp;
|
||||||
|
|
||||||
|
fp = open(filename, O_CREAT | O_RDWR, 0600);
|
||||||
|
if (fp < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return open_ctree_fd(fp, super);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct btrfs_root *open_ctree_fd(int fp, struct btrfs_super_block *super)
|
||||||
{
|
{
|
||||||
struct btrfs_root *root = malloc(sizeof(struct btrfs_root));
|
struct btrfs_root *root = malloc(sizeof(struct btrfs_root));
|
||||||
struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root));
|
struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root));
|
||||||
struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
|
struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
|
||||||
struct btrfs_root *inode_root = malloc(sizeof(struct btrfs_root));
|
struct btrfs_root *inode_root = malloc(sizeof(struct btrfs_root));
|
||||||
struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
|
struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
|
||||||
int fp;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
fp = open(filename, O_CREAT | O_RDWR, 0600);
|
|
||||||
if (fp < 0) {
|
|
||||||
free(root);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
INIT_RADIX_TREE(&fs_info->cache_radix, GFP_KERNEL);
|
INIT_RADIX_TREE(&fs_info->cache_radix, GFP_KERNEL);
|
||||||
INIT_RADIX_TREE(&fs_info->pinned_radix, GFP_KERNEL);
|
INIT_RADIX_TREE(&fs_info->pinned_radix, GFP_KERNEL);
|
||||||
INIT_LIST_HEAD(&fs_info->trans);
|
INIT_LIST_HEAD(&fs_info->trans);
|
||||||
|
|
|
@ -24,6 +24,7 @@ int clean_tree_block(struct btrfs_trans_handle *trans,
|
||||||
int btrfs_commit_transaction(struct btrfs_trans_handle *trans, struct btrfs_root
|
int btrfs_commit_transaction(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||||
*root, struct btrfs_super_block *s);
|
*root, struct btrfs_super_block *s);
|
||||||
struct btrfs_root *open_ctree(char *filename, struct btrfs_super_block *s);
|
struct btrfs_root *open_ctree(char *filename, struct btrfs_super_block *s);
|
||||||
|
struct btrfs_root *open_ctree_fd(int fp, struct btrfs_super_block *super);
|
||||||
int close_ctree(struct btrfs_root *root, struct btrfs_super_block *s);
|
int close_ctree(struct btrfs_root *root, struct btrfs_super_block *s);
|
||||||
void btrfs_block_release(struct btrfs_root *root, struct btrfs_buffer *buf);
|
void btrfs_block_release(struct btrfs_root *root, struct btrfs_buffer *buf);
|
||||||
int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <stdio.h>
|
#include <linux/module.h>
|
||||||
#include <stdlib.h>
|
#include <linux/radix-tree.h>
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
#include "print-tree.h"
|
#include "print-tree.h"
|
||||||
|
@ -183,9 +181,9 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||||
btrfs_init_path(&path);
|
btrfs_init_path(&path);
|
||||||
ret = btrfs_search_slot(trans, extent_root, &key, &path, -1, 1);
|
ret = btrfs_search_slot(trans, extent_root, &key, &path, -1, 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printf("failed to find %Lu\n", key.objectid);
|
printk("failed to find %Lu\n", key.objectid);
|
||||||
btrfs_print_tree(extent_root, extent_root->node);
|
btrfs_print_tree(extent_root, extent_root->node);
|
||||||
printf("failed to find %Lu\n", key.objectid);
|
printk("failed to find %Lu\n", key.objectid);
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
ei = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
|
ei = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
#include <stdio.h>
|
#include <linux/module.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
|
||||||
int btrfs_create_file(struct btrfs_trans_handle *trans,
|
int btrfs_create_file(struct btrfs_trans_handle *trans,
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
* License.
|
* License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#define DELTA 0x9E3779B9
|
#define DELTA 0x9E3779B9
|
||||||
|
|
||||||
static void TEA_transform(__u32 buf[2], __u32 const in[])
|
static void TEA_transform(__u32 buf[2], __u32 const in[])
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "hash.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
u64 result;
|
|
||||||
int ret;
|
|
||||||
char line[255];
|
|
||||||
char *p;
|
|
||||||
while(1) {
|
|
||||||
p = fgets(line, 255, stdin);
|
|
||||||
if (!p)
|
|
||||||
break;
|
|
||||||
if (strlen(line) == 0)
|
|
||||||
continue;
|
|
||||||
ret = btrfs_name_hash(line, strlen(line), &result);
|
|
||||||
BUG_ON(ret);
|
|
||||||
printf("hash returns %Lu\n", result);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,7 +1,4 @@
|
||||||
#include <stdio.h>
|
#include <linux/module.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
#include <stdio.h>
|
#include <linux/module.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
#ifndef __KERNCOMPAT
|
|
||||||
#define __KERNCOMPAT
|
|
||||||
#define gfp_t int
|
|
||||||
#define get_cpu_var(p) (p)
|
|
||||||
#define __get_cpu_var(p) (p)
|
|
||||||
#define BITS_PER_LONG 64
|
|
||||||
#define __GFP_BITS_SHIFT 20
|
|
||||||
#define __GFP_BITS_MASK ((int)((1 << __GFP_BITS_SHIFT) - 1))
|
|
||||||
#define GFP_KERNEL 0
|
|
||||||
#define __read_mostly
|
|
||||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
||||||
#define PAGE_SHIFT 12
|
|
||||||
#define ULONG_MAX (~0UL)
|
|
||||||
#define BUG() abort()
|
|
||||||
#ifdef __CHECKER__
|
|
||||||
#define __force __attribute__((force))
|
|
||||||
#define __bitwise__ __attribute__((bitwise))
|
|
||||||
#else
|
|
||||||
#define __force
|
|
||||||
#define __bitwise__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef unsigned int u32;
|
|
||||||
typedef u32 __u32;
|
|
||||||
typedef unsigned long long u64;
|
|
||||||
typedef unsigned char u8;
|
|
||||||
typedef unsigned short u16;
|
|
||||||
|
|
||||||
typedef unsigned long pgoff_t;
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct vma_shared { int prio_tree_node; };
|
|
||||||
struct vm_area_struct {
|
|
||||||
unsigned long vm_pgoff;
|
|
||||||
unsigned long vm_start;
|
|
||||||
unsigned long vm_end;
|
|
||||||
struct vma_shared shared;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct page {
|
|
||||||
unsigned long index;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void preempt_enable(void) { do {; } while(0);}
|
|
||||||
static inline void preempt_disable(void) { do {; } while(0);}
|
|
||||||
|
|
||||||
static inline void __set_bit(int bit, unsigned long *map) {
|
|
||||||
unsigned long *p = map + bit / BITS_PER_LONG;
|
|
||||||
bit = bit & (BITS_PER_LONG -1);
|
|
||||||
*p |= 1UL << bit;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int test_bit(int bit, unsigned long *map) {
|
|
||||||
unsigned long *p = map + bit / BITS_PER_LONG;
|
|
||||||
bit = bit & (BITS_PER_LONG -1);
|
|
||||||
return *p & (1UL << bit) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __clear_bit(int bit, unsigned long *map) {
|
|
||||||
unsigned long *p = map + bit / BITS_PER_LONG;
|
|
||||||
bit = bit & (BITS_PER_LONG -1);
|
|
||||||
*p &= ~(1UL << bit);
|
|
||||||
}
|
|
||||||
#define BUG_ON(c) do { if (c) abort(); } while (0)
|
|
||||||
|
|
||||||
#define container_of(ptr, type, member) ({ \
|
|
||||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
|
||||||
(type *)( (char *)__mptr - __builtin_offsetof(type,member) );})
|
|
||||||
|
|
||||||
#define ENOMEM 5
|
|
||||||
#define EEXIST 6
|
|
||||||
|
|
||||||
#define __CHECK_ENDIAN__
|
|
||||||
#ifdef __CHECK_ENDIAN__
|
|
||||||
#define __bitwise __bitwise__
|
|
||||||
#else
|
|
||||||
#define __bitwise
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef u16 __bitwise __le16;
|
|
||||||
typedef u16 __bitwise __be16;
|
|
||||||
typedef u32 __bitwise __le32;
|
|
||||||
typedef u32 __bitwise __be32;
|
|
||||||
typedef u64 __bitwise __le64;
|
|
||||||
typedef u64 __bitwise __be64;
|
|
||||||
|
|
||||||
#define cpu_to_le64(x) ((__force __le64)(u64)(x))
|
|
||||||
#define le64_to_cpu(x) ((__force u64)(__le64)(x))
|
|
||||||
#define cpu_to_le32(x) ((__force __le32)(u32)(x))
|
|
||||||
#define le32_to_cpu(x) ((__force u32)(__le32)(x))
|
|
||||||
#define cpu_to_le16(x) ((__force __le16)(u16)(x))
|
|
||||||
#define le16_to_cpu(x) ((__force u16)(__le16)(x))
|
|
||||||
#endif
|
|
418
fs/btrfs/list.h
418
fs/btrfs/list.h
|
@ -1,418 +0,0 @@
|
||||||
#ifndef _LINUX_LIST_H
|
|
||||||
#define _LINUX_LIST_H
|
|
||||||
|
|
||||||
#define LIST_POISON1 ((void *) 0x00100100)
|
|
||||||
#define LIST_POISON2 ((void *) 0x00200200)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Simple doubly linked list implementation.
|
|
||||||
*
|
|
||||||
* Some of the internal functions ("__xxx") are useful when
|
|
||||||
* manipulating whole lists rather than single entries, as
|
|
||||||
* sometimes we already know the next/prev entries and we can
|
|
||||||
* generate better code by using them directly rather than
|
|
||||||
* using the generic single-entry routines.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct list_head {
|
|
||||||
struct list_head *next, *prev;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
|
||||||
|
|
||||||
#define LIST_HEAD(name) \
|
|
||||||
struct list_head name = LIST_HEAD_INIT(name)
|
|
||||||
|
|
||||||
static inline void INIT_LIST_HEAD(struct list_head *list)
|
|
||||||
{
|
|
||||||
list->next = list;
|
|
||||||
list->prev = list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Insert a new entry between two known consecutive entries.
|
|
||||||
*
|
|
||||||
* This is only for internal list manipulation where we know
|
|
||||||
* the prev/next entries already!
|
|
||||||
*/
|
|
||||||
#ifndef CONFIG_DEBUG_LIST
|
|
||||||
static inline void __list_add(struct list_head *new,
|
|
||||||
struct list_head *prev,
|
|
||||||
struct list_head *next)
|
|
||||||
{
|
|
||||||
next->prev = new;
|
|
||||||
new->next = next;
|
|
||||||
new->prev = prev;
|
|
||||||
prev->next = new;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
extern void __list_add(struct list_head *new,
|
|
||||||
struct list_head *prev,
|
|
||||||
struct list_head *next);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_add - add a new entry
|
|
||||||
* @new: new entry to be added
|
|
||||||
* @head: list head to add it after
|
|
||||||
*
|
|
||||||
* Insert a new entry after the specified head.
|
|
||||||
* This is good for implementing stacks.
|
|
||||||
*/
|
|
||||||
#ifndef CONFIG_DEBUG_LIST
|
|
||||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
|
||||||
{
|
|
||||||
__list_add(new, head, head->next);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
extern void list_add(struct list_head *new, struct list_head *head);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_add_tail - add a new entry
|
|
||||||
* @new: new entry to be added
|
|
||||||
* @head: list head to add it before
|
|
||||||
*
|
|
||||||
* Insert a new entry before the specified head.
|
|
||||||
* This is useful for implementing queues.
|
|
||||||
*/
|
|
||||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
|
||||||
{
|
|
||||||
__list_add(new, head->prev, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Delete a list entry by making the prev/next entries
|
|
||||||
* point to each other.
|
|
||||||
*
|
|
||||||
* This is only for internal list manipulation where we know
|
|
||||||
* the prev/next entries already!
|
|
||||||
*/
|
|
||||||
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
|
||||||
{
|
|
||||||
next->prev = prev;
|
|
||||||
prev->next = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_del - deletes entry from list.
|
|
||||||
* @entry: the element to delete from the list.
|
|
||||||
* Note: list_empty on entry does not return true after this, the entry is
|
|
||||||
* in an undefined state.
|
|
||||||
*/
|
|
||||||
#ifndef CONFIG_DEBUG_LIST
|
|
||||||
static inline void list_del(struct list_head *entry)
|
|
||||||
{
|
|
||||||
__list_del(entry->prev, entry->next);
|
|
||||||
entry->next = LIST_POISON1;
|
|
||||||
entry->prev = LIST_POISON2;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
extern void list_del(struct list_head *entry);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_replace - replace old entry by new one
|
|
||||||
* @old : the element to be replaced
|
|
||||||
* @new : the new element to insert
|
|
||||||
* Note: if 'old' was empty, it will be overwritten.
|
|
||||||
*/
|
|
||||||
static inline void list_replace(struct list_head *old,
|
|
||||||
struct list_head *new)
|
|
||||||
{
|
|
||||||
new->next = old->next;
|
|
||||||
new->next->prev = new;
|
|
||||||
new->prev = old->prev;
|
|
||||||
new->prev->next = new;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void list_replace_init(struct list_head *old,
|
|
||||||
struct list_head *new)
|
|
||||||
{
|
|
||||||
list_replace(old, new);
|
|
||||||
INIT_LIST_HEAD(old);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* list_del_init - deletes entry from list and reinitialize it.
|
|
||||||
* @entry: the element to delete from the list.
|
|
||||||
*/
|
|
||||||
static inline void list_del_init(struct list_head *entry)
|
|
||||||
{
|
|
||||||
__list_del(entry->prev, entry->next);
|
|
||||||
INIT_LIST_HEAD(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_move - delete from one list and add as another's head
|
|
||||||
* @list: the entry to move
|
|
||||||
* @head: the head that will precede our entry
|
|
||||||
*/
|
|
||||||
static inline void list_move(struct list_head *list, struct list_head *head)
|
|
||||||
{
|
|
||||||
__list_del(list->prev, list->next);
|
|
||||||
list_add(list, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_move_tail - delete from one list and add as another's tail
|
|
||||||
* @list: the entry to move
|
|
||||||
* @head: the head that will follow our entry
|
|
||||||
*/
|
|
||||||
static inline void list_move_tail(struct list_head *list,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
__list_del(list->prev, list->next);
|
|
||||||
list_add_tail(list, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_is_last - tests whether @list is the last entry in list @head
|
|
||||||
* @list: the entry to test
|
|
||||||
* @head: the head of the list
|
|
||||||
*/
|
|
||||||
static inline int list_is_last(const struct list_head *list,
|
|
||||||
const struct list_head *head)
|
|
||||||
{
|
|
||||||
return list->next == head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_empty - tests whether a list is empty
|
|
||||||
* @head: the list to test.
|
|
||||||
*/
|
|
||||||
static inline int list_empty(const struct list_head *head)
|
|
||||||
{
|
|
||||||
return head->next == head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_empty_careful - tests whether a list is empty and not being modified
|
|
||||||
* @head: the list to test
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* tests whether a list is empty _and_ checks that no other CPU might be
|
|
||||||
* in the process of modifying either member (next or prev)
|
|
||||||
*
|
|
||||||
* NOTE: using list_empty_careful() without synchronization
|
|
||||||
* can only be safe if the only activity that can happen
|
|
||||||
* to the list entry is list_del_init(). Eg. it cannot be used
|
|
||||||
* if another CPU could re-list_add() it.
|
|
||||||
*/
|
|
||||||
static inline int list_empty_careful(const struct list_head *head)
|
|
||||||
{
|
|
||||||
struct list_head *next = head->next;
|
|
||||||
return (next == head) && (next == head->prev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __list_splice(struct list_head *list,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
struct list_head *first = list->next;
|
|
||||||
struct list_head *last = list->prev;
|
|
||||||
struct list_head *at = head->next;
|
|
||||||
|
|
||||||
first->prev = head;
|
|
||||||
head->next = first;
|
|
||||||
|
|
||||||
last->next = at;
|
|
||||||
at->prev = last;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_splice - join two lists
|
|
||||||
* @list: the new list to add.
|
|
||||||
* @head: the place to add it in the first list.
|
|
||||||
*/
|
|
||||||
static inline void list_splice(struct list_head *list, struct list_head *head)
|
|
||||||
{
|
|
||||||
if (!list_empty(list))
|
|
||||||
__list_splice(list, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_splice_init - join two lists and reinitialise the emptied list.
|
|
||||||
* @list: the new list to add.
|
|
||||||
* @head: the place to add it in the first list.
|
|
||||||
*
|
|
||||||
* The list at @list is reinitialised
|
|
||||||
*/
|
|
||||||
static inline void list_splice_init(struct list_head *list,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
if (!list_empty(list)) {
|
|
||||||
__list_splice(list, head);
|
|
||||||
INIT_LIST_HEAD(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_entry - get the struct for this entry
|
|
||||||
* @ptr: the &struct list_head pointer.
|
|
||||||
* @type: the type of the struct this is embedded in.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*/
|
|
||||||
#define list_entry(ptr, type, member) \
|
|
||||||
container_of(ptr, type, member)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each - iterate over a list
|
|
||||||
* @pos: the &struct list_head to use as a loop cursor.
|
|
||||||
* @head: the head for your list.
|
|
||||||
*/
|
|
||||||
#define list_for_each(pos, head) \
|
|
||||||
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
|
|
||||||
pos = pos->next)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __list_for_each - iterate over a list
|
|
||||||
* @pos: the &struct list_head to use as a loop cursor.
|
|
||||||
* @head: the head for your list.
|
|
||||||
*
|
|
||||||
* This variant differs from list_for_each() in that it's the
|
|
||||||
* simplest possible list iteration code, no prefetching is done.
|
|
||||||
* Use this for code that knows the list to be very short (empty
|
|
||||||
* or 1 entry) most of the time.
|
|
||||||
*/
|
|
||||||
#define __list_for_each(pos, head) \
|
|
||||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_prev - iterate over a list backwards
|
|
||||||
* @pos: the &struct list_head to use as a loop cursor.
|
|
||||||
* @head: the head for your list.
|
|
||||||
*/
|
|
||||||
#define list_for_each_prev(pos, head) \
|
|
||||||
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
|
|
||||||
pos = pos->prev)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
|
||||||
* @pos: the &struct list_head to use as a loop cursor.
|
|
||||||
* @n: another &struct list_head to use as temporary storage
|
|
||||||
* @head: the head for your list.
|
|
||||||
*/
|
|
||||||
#define list_for_each_safe(pos, n, head) \
|
|
||||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
|
||||||
pos = n, n = pos->next)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_entry - iterate over list of given type
|
|
||||||
* @pos: the type * to use as a loop cursor.
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry(pos, head, member) \
|
|
||||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
|
||||||
prefetch(pos->member.next), &pos->member != (head); \
|
|
||||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
|
||||||
* @pos: the type * to use as a loop cursor.
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_reverse(pos, head, member) \
|
|
||||||
for (pos = list_entry((head)->prev, typeof(*pos), member); \
|
|
||||||
prefetch(pos->member.prev), &pos->member != (head); \
|
|
||||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue
|
|
||||||
* @pos: the type * to use as a start point
|
|
||||||
* @head: the head of the list
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*
|
|
||||||
* Prepares a pos entry for use as a start point in list_for_each_entry_continue.
|
|
||||||
*/
|
|
||||||
#define list_prepare_entry(pos, head, member) \
|
|
||||||
((pos) ? : list_entry(head, typeof(*pos), member))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_entry_continue - continue iteration over list of given type
|
|
||||||
* @pos: the type * to use as a loop cursor.
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*
|
|
||||||
* Continue to iterate over list of given type, continuing after
|
|
||||||
* the current position.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_continue(pos, head, member) \
|
|
||||||
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
|
|
||||||
prefetch(pos->member.next), &pos->member != (head); \
|
|
||||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_entry_from - iterate over list of given type from the current point
|
|
||||||
* @pos: the type * to use as a loop cursor.
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*
|
|
||||||
* Iterate over list of given type, continuing from current position.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_from(pos, head, member) \
|
|
||||||
for (; prefetch(pos->member.next), &pos->member != (head); \
|
|
||||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
|
||||||
* @pos: the type * to use as a loop cursor.
|
|
||||||
* @n: another type * to use as temporary storage
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
|
||||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
|
||||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_entry_safe_continue
|
|
||||||
* @pos: the type * to use as a loop cursor.
|
|
||||||
* @n: another type * to use as temporary storage
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*
|
|
||||||
* Iterate over list of given type, continuing after current point,
|
|
||||||
* safe against removal of list entry.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_safe_continue(pos, n, head, member) \
|
|
||||||
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
|
|
||||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_entry_safe_from
|
|
||||||
* @pos: the type * to use as a loop cursor.
|
|
||||||
* @n: another type * to use as temporary storage
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*
|
|
||||||
* Iterate over list of given type from current point, safe against
|
|
||||||
* removal of list entry.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_safe_from(pos, n, head, member) \
|
|
||||||
for (n = list_entry(pos->member.next, typeof(*pos), member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_for_each_entry_safe_reverse
|
|
||||||
* @pos: the type * to use as a loop cursor.
|
|
||||||
* @n: another type * to use as temporary storage
|
|
||||||
* @head: the head for your list.
|
|
||||||
* @member: the name of the list_struct within the struct.
|
|
||||||
*
|
|
||||||
* Iterate backwards over list of given type, safe against removal
|
|
||||||
* of list entry.
|
|
||||||
*/
|
|
||||||
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
|
|
||||||
for (pos = list_entry((head)->prev, typeof(*pos), member), \
|
|
||||||
n = list_entry(pos->member.prev, typeof(*pos), member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
|
|
||||||
|
|
||||||
#endif
|
|
255
fs/btrfs/mkfs.c
255
fs/btrfs/mkfs.c
|
@ -1,255 +0,0 @@
|
||||||
#define _XOPEN_SOURCE 500
|
|
||||||
#ifndef __CHECKER__
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/mount.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
|
||||||
#include "disk-io.h"
|
|
||||||
|
|
||||||
#ifdef __CHECKER__
|
|
||||||
#define BLKGETSIZE64 0
|
|
||||||
static inline int ioctl(int fd, int define, u64 *size) { return 0; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
|
|
||||||
# define BLKGETSIZE64 _IOR(0x12, 114, __u64)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int mkfs(int fd, u64 num_blocks, u32 blocksize)
|
|
||||||
{
|
|
||||||
struct btrfs_super_block super;
|
|
||||||
struct btrfs_leaf *empty_leaf;
|
|
||||||
struct btrfs_root_item root_item;
|
|
||||||
struct btrfs_item item;
|
|
||||||
struct btrfs_extent_item extent_item;
|
|
||||||
char *block;
|
|
||||||
int ret;
|
|
||||||
u32 itemoff;
|
|
||||||
u32 start_block = BTRFS_SUPER_INFO_OFFSET / blocksize;
|
|
||||||
|
|
||||||
btrfs_set_super_blocknr(&super, start_block);
|
|
||||||
btrfs_set_super_root(&super, start_block + 1);
|
|
||||||
strcpy((char *)(&super.magic), BTRFS_MAGIC);
|
|
||||||
btrfs_set_super_blocksize(&super, blocksize);
|
|
||||||
btrfs_set_super_total_blocks(&super, num_blocks);
|
|
||||||
btrfs_set_super_blocks_used(&super, start_block + 5);
|
|
||||||
|
|
||||||
block = malloc(blocksize);
|
|
||||||
memset(block, 0, blocksize);
|
|
||||||
BUG_ON(sizeof(super) > blocksize);
|
|
||||||
memcpy(block, &super, sizeof(super));
|
|
||||||
ret = pwrite(fd, block, blocksize, BTRFS_SUPER_INFO_OFFSET);
|
|
||||||
BUG_ON(ret != blocksize);
|
|
||||||
|
|
||||||
/* create the tree of root objects */
|
|
||||||
empty_leaf = malloc(blocksize);
|
|
||||||
memset(empty_leaf, 0, blocksize);
|
|
||||||
btrfs_set_header_parentid(&empty_leaf->header,
|
|
||||||
BTRFS_ROOT_TREE_OBJECTID);
|
|
||||||
btrfs_set_header_blocknr(&empty_leaf->header, start_block + 1);
|
|
||||||
btrfs_set_header_nritems(&empty_leaf->header, 3);
|
|
||||||
|
|
||||||
/* create the items for the root tree */
|
|
||||||
btrfs_set_root_blocknr(&root_item, start_block + 2);
|
|
||||||
btrfs_set_root_refs(&root_item, 1);
|
|
||||||
itemoff = __BTRFS_LEAF_DATA_SIZE(blocksize) - sizeof(root_item);
|
|
||||||
btrfs_set_item_offset(&item, itemoff);
|
|
||||||
btrfs_set_item_size(&item, sizeof(root_item));
|
|
||||||
btrfs_set_disk_key_objectid(&item.key, BTRFS_EXTENT_TREE_OBJECTID);
|
|
||||||
btrfs_set_disk_key_offset(&item.key, 0);
|
|
||||||
btrfs_set_disk_key_flags(&item.key, 0);
|
|
||||||
btrfs_set_disk_key_type(&item.key, BTRFS_ROOT_ITEM_KEY);
|
|
||||||
memcpy(empty_leaf->items, &item, sizeof(item));
|
|
||||||
memcpy(btrfs_leaf_data(empty_leaf) + itemoff,
|
|
||||||
&root_item, sizeof(root_item));
|
|
||||||
|
|
||||||
btrfs_set_root_blocknr(&root_item, start_block + 3);
|
|
||||||
itemoff = itemoff - sizeof(root_item);
|
|
||||||
btrfs_set_item_offset(&item, itemoff);
|
|
||||||
btrfs_set_disk_key_objectid(&item.key, BTRFS_INODE_MAP_OBJECTID);
|
|
||||||
memcpy(empty_leaf->items + 1, &item, sizeof(item));
|
|
||||||
memcpy(btrfs_leaf_data(empty_leaf) + itemoff,
|
|
||||||
&root_item, sizeof(root_item));
|
|
||||||
|
|
||||||
btrfs_set_root_blocknr(&root_item, start_block + 4);
|
|
||||||
itemoff = itemoff - sizeof(root_item);
|
|
||||||
btrfs_set_item_offset(&item, itemoff);
|
|
||||||
btrfs_set_disk_key_objectid(&item.key, BTRFS_FS_TREE_OBJECTID);
|
|
||||||
memcpy(empty_leaf->items + 2, &item, sizeof(item));
|
|
||||||
memcpy(btrfs_leaf_data(empty_leaf) + itemoff,
|
|
||||||
&root_item, sizeof(root_item));
|
|
||||||
ret = pwrite(fd, empty_leaf, blocksize, (start_block + 1) * blocksize);
|
|
||||||
|
|
||||||
/* create the items for the extent tree */
|
|
||||||
btrfs_set_header_parentid(&empty_leaf->header,
|
|
||||||
BTRFS_EXTENT_TREE_OBJECTID);
|
|
||||||
btrfs_set_header_blocknr(&empty_leaf->header, start_block + 2);
|
|
||||||
btrfs_set_header_nritems(&empty_leaf->header, 5);
|
|
||||||
|
|
||||||
/* item1, reserve blocks 0-16 */
|
|
||||||
btrfs_set_disk_key_objectid(&item.key, 0);
|
|
||||||
btrfs_set_disk_key_offset(&item.key, start_block + 1);
|
|
||||||
btrfs_set_disk_key_flags(&item.key, 0);
|
|
||||||
btrfs_set_disk_key_type(&item.key, BTRFS_EXTENT_ITEM_KEY);
|
|
||||||
itemoff = __BTRFS_LEAF_DATA_SIZE(blocksize) -
|
|
||||||
sizeof(struct btrfs_extent_item);
|
|
||||||
btrfs_set_item_offset(&item, itemoff);
|
|
||||||
btrfs_set_item_size(&item, sizeof(struct btrfs_extent_item));
|
|
||||||
btrfs_set_extent_refs(&extent_item, 1);
|
|
||||||
btrfs_set_extent_owner(&extent_item, 0);
|
|
||||||
memcpy(empty_leaf->items, &item, sizeof(item));
|
|
||||||
memcpy(btrfs_leaf_data(empty_leaf) + btrfs_item_offset(&item),
|
|
||||||
&extent_item, btrfs_item_size(&item));
|
|
||||||
|
|
||||||
/* item2, give block 17 to the root */
|
|
||||||
btrfs_set_disk_key_objectid(&item.key, start_block + 1);
|
|
||||||
btrfs_set_disk_key_offset(&item.key, 1);
|
|
||||||
itemoff = itemoff - sizeof(struct btrfs_extent_item);
|
|
||||||
btrfs_set_item_offset(&item, itemoff);
|
|
||||||
btrfs_set_extent_owner(&extent_item, BTRFS_ROOT_TREE_OBJECTID);
|
|
||||||
memcpy(empty_leaf->items + 1, &item, sizeof(item));
|
|
||||||
memcpy(btrfs_leaf_data(empty_leaf) + btrfs_item_offset(&item),
|
|
||||||
&extent_item, btrfs_item_size(&item));
|
|
||||||
|
|
||||||
/* item3, give block 18 to the extent root */
|
|
||||||
btrfs_set_disk_key_objectid(&item.key, start_block + 2);
|
|
||||||
btrfs_set_disk_key_offset(&item.key, 1);
|
|
||||||
itemoff = itemoff - sizeof(struct btrfs_extent_item);
|
|
||||||
btrfs_set_item_offset(&item, itemoff);
|
|
||||||
btrfs_set_extent_owner(&extent_item, BTRFS_EXTENT_TREE_OBJECTID);
|
|
||||||
memcpy(empty_leaf->items + 2, &item, sizeof(item));
|
|
||||||
memcpy(btrfs_leaf_data(empty_leaf) + btrfs_item_offset(&item),
|
|
||||||
&extent_item, btrfs_item_size(&item));
|
|
||||||
|
|
||||||
/* item4, give block 19 to the inode map */
|
|
||||||
btrfs_set_disk_key_objectid(&item.key, start_block + 3);
|
|
||||||
btrfs_set_disk_key_offset(&item.key, 1);
|
|
||||||
itemoff = itemoff - sizeof(struct btrfs_extent_item);
|
|
||||||
btrfs_set_item_offset(&item, itemoff);
|
|
||||||
btrfs_set_extent_owner(&extent_item, BTRFS_INODE_MAP_OBJECTID);
|
|
||||||
memcpy(empty_leaf->items + 3, &item, sizeof(item));
|
|
||||||
memcpy(btrfs_leaf_data(empty_leaf) + btrfs_item_offset(&item),
|
|
||||||
&extent_item, btrfs_item_size(&item));
|
|
||||||
ret = pwrite(fd, empty_leaf, blocksize, (start_block + 2) * blocksize);
|
|
||||||
if (ret != blocksize)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* item5, give block 20 to the FS root */
|
|
||||||
btrfs_set_disk_key_objectid(&item.key, start_block + 4);
|
|
||||||
btrfs_set_disk_key_offset(&item.key, 1);
|
|
||||||
itemoff = itemoff - sizeof(struct btrfs_extent_item);
|
|
||||||
btrfs_set_item_offset(&item, itemoff);
|
|
||||||
btrfs_set_extent_owner(&extent_item, BTRFS_FS_TREE_OBJECTID);
|
|
||||||
memcpy(empty_leaf->items + 4, &item, sizeof(item));
|
|
||||||
memcpy(btrfs_leaf_data(empty_leaf) + btrfs_item_offset(&item),
|
|
||||||
&extent_item, btrfs_item_size(&item));
|
|
||||||
ret = pwrite(fd, empty_leaf, blocksize, (start_block + 2) * blocksize);
|
|
||||||
if (ret != blocksize)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* create the inode map */
|
|
||||||
btrfs_set_header_parentid(&empty_leaf->header,
|
|
||||||
BTRFS_INODE_MAP_OBJECTID);
|
|
||||||
btrfs_set_header_blocknr(&empty_leaf->header, start_block + 3);
|
|
||||||
btrfs_set_header_nritems(&empty_leaf->header, 0);
|
|
||||||
ret = pwrite(fd, empty_leaf, blocksize, (start_block + 3) * blocksize);
|
|
||||||
if (ret != blocksize)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* finally create the FS root */
|
|
||||||
btrfs_set_header_parentid(&empty_leaf->header, BTRFS_FS_TREE_OBJECTID);
|
|
||||||
btrfs_set_header_blocknr(&empty_leaf->header, start_block + 4);
|
|
||||||
btrfs_set_header_nritems(&empty_leaf->header, 0);
|
|
||||||
ret = pwrite(fd, empty_leaf, blocksize, (start_block + 4) * blocksize);
|
|
||||||
if (ret != blocksize)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 device_size(int fd, struct stat *st)
|
|
||||||
{
|
|
||||||
u64 size;
|
|
||||||
if (S_ISREG(st->st_mode)) {
|
|
||||||
return st->st_size;
|
|
||||||
}
|
|
||||||
if (!S_ISBLK(st->st_mode)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (ioctl(fd, BLKGETSIZE64, &size) >= 0) {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int ac, char **av)
|
|
||||||
{
|
|
||||||
char *file;
|
|
||||||
u64 block_count = 0;
|
|
||||||
int fd;
|
|
||||||
struct stat st;
|
|
||||||
int ret;
|
|
||||||
int i;
|
|
||||||
char *buf = malloc(4096);
|
|
||||||
if (ac >= 2) {
|
|
||||||
file = av[1];
|
|
||||||
if (ac == 3) {
|
|
||||||
block_count = atoi(av[2]);
|
|
||||||
if (!block_count) {
|
|
||||||
fprintf(stderr, "error finding block count\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "usage: mkfs.btrfs file [block count]\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
fd = open(file, O_RDWR);
|
|
||||||
if (fd < 0) {
|
|
||||||
fprintf(stderr, "unable to open %s\n", file);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
ret = fstat(fd, &st);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "unable to stat %s\n", file);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (block_count == 0) {
|
|
||||||
block_count = device_size(fd, &st);
|
|
||||||
if (block_count == 0) {
|
|
||||||
fprintf(stderr, "unable to find %s size\n", file);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
block_count /= 4096;
|
|
||||||
if (block_count < 256) {
|
|
||||||
fprintf(stderr, "device %s is too small\n", file);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
memset(buf, 0, 4096);
|
|
||||||
for(i = 0; i < 6; i++) {
|
|
||||||
ret = write(fd, buf, 4096);
|
|
||||||
if (ret != 4096) {
|
|
||||||
fprintf(stderr, "unable to zero fill device\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = mkfs(fd, block_count, 4096);
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr, "error during mkfs %d\n", ret);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
printf("fs created on %s blocksize %d blocks %Lu\n",
|
|
||||||
file, 4096, block_count);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
#include <stdio.h>
|
#include <linux/module.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
|
|
||||||
|
@ -17,14 +14,14 @@ void btrfs_print_leaf(struct btrfs_root *root, struct btrfs_leaf *l)
|
||||||
struct btrfs_inode_item *ii;
|
struct btrfs_inode_item *ii;
|
||||||
u32 type;
|
u32 type;
|
||||||
|
|
||||||
printf("leaf %Lu total ptrs %d free space %d\n",
|
printk("leaf %Lu total ptrs %d free space %d\n",
|
||||||
btrfs_header_blocknr(&l->header), nr,
|
btrfs_header_blocknr(&l->header), nr,
|
||||||
btrfs_leaf_free_space(root, l));
|
btrfs_leaf_free_space(root, l));
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
for (i = 0 ; i < nr ; i++) {
|
for (i = 0 ; i < nr ; i++) {
|
||||||
item = l->items + i;
|
item = l->items + i;
|
||||||
type = btrfs_disk_key_type(&item->key);
|
type = btrfs_disk_key_type(&item->key);
|
||||||
printf("\titem %d key (%Lu %u %Lu) itemoff %d itemsize %d\n",
|
printk("\titem %d key (%Lu %u %Lu) itemoff %d itemsize %d\n",
|
||||||
i,
|
i,
|
||||||
btrfs_disk_key_objectid(&item->key),
|
btrfs_disk_key_objectid(&item->key),
|
||||||
btrfs_disk_key_flags(&item->key),
|
btrfs_disk_key_flags(&item->key),
|
||||||
|
@ -34,38 +31,39 @@ void btrfs_print_leaf(struct btrfs_root *root, struct btrfs_leaf *l)
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BTRFS_INODE_ITEM_KEY:
|
case BTRFS_INODE_ITEM_KEY:
|
||||||
ii = btrfs_item_ptr(l, i, struct btrfs_inode_item);
|
ii = btrfs_item_ptr(l, i, struct btrfs_inode_item);
|
||||||
printf("\t\tinode generation %Lu size %Lu\n",
|
printk("\t\tinode generation %Lu size %Lu mode %o\n",
|
||||||
btrfs_inode_generation(ii),
|
btrfs_inode_generation(ii),
|
||||||
btrfs_inode_size(ii));
|
btrfs_inode_size(ii),
|
||||||
|
btrfs_inode_mode(ii));
|
||||||
break;
|
break;
|
||||||
case BTRFS_DIR_ITEM_KEY:
|
case BTRFS_DIR_ITEM_KEY:
|
||||||
di = btrfs_item_ptr(l, i, struct btrfs_dir_item);
|
di = btrfs_item_ptr(l, i, struct btrfs_dir_item);
|
||||||
printf("\t\tdir oid %Lu flags %u type %u\n",
|
printk("\t\tdir oid %Lu flags %u type %u\n",
|
||||||
btrfs_dir_objectid(di),
|
btrfs_dir_objectid(di),
|
||||||
btrfs_dir_flags(di),
|
btrfs_dir_flags(di),
|
||||||
btrfs_dir_type(di));
|
btrfs_dir_type(di));
|
||||||
printf("\t\tname %.*s\n",
|
printk("\t\tname %.*s\n",
|
||||||
btrfs_dir_name_len(di),(char *)(di + 1));
|
btrfs_dir_name_len(di),(char *)(di + 1));
|
||||||
break;
|
break;
|
||||||
case BTRFS_ROOT_ITEM_KEY:
|
case BTRFS_ROOT_ITEM_KEY:
|
||||||
ri = btrfs_item_ptr(l, i, struct btrfs_root_item);
|
ri = btrfs_item_ptr(l, i, struct btrfs_root_item);
|
||||||
printf("\t\troot data blocknr %Lu refs %u\n",
|
printk("\t\troot data blocknr %Lu refs %u\n",
|
||||||
btrfs_root_blocknr(ri), btrfs_root_refs(ri));
|
btrfs_root_blocknr(ri), btrfs_root_refs(ri));
|
||||||
break;
|
break;
|
||||||
case BTRFS_EXTENT_ITEM_KEY:
|
case BTRFS_EXTENT_ITEM_KEY:
|
||||||
ei = btrfs_item_ptr(l, i, struct btrfs_extent_item);
|
ei = btrfs_item_ptr(l, i, struct btrfs_extent_item);
|
||||||
printf("\t\textent data refs %u owner %Lu\n",
|
printk("\t\textent data refs %u owner %Lu\n",
|
||||||
btrfs_extent_refs(ei), btrfs_extent_owner(ei));
|
btrfs_extent_refs(ei), btrfs_extent_owner(ei));
|
||||||
break;
|
break;
|
||||||
case BTRFS_INODE_MAP_ITEM_KEY:
|
case BTRFS_INODE_MAP_ITEM_KEY:
|
||||||
mi = btrfs_item_ptr(l, i, struct btrfs_inode_map_item);
|
mi = btrfs_item_ptr(l, i, struct btrfs_inode_map_item);
|
||||||
printf("\t\tinode map key %Lu %u %Lu\n",
|
printk("\t\tinode map key %Lu %u %Lu\n",
|
||||||
btrfs_disk_key_objectid(&mi->key),
|
btrfs_disk_key_objectid(&mi->key),
|
||||||
btrfs_disk_key_flags(&mi->key),
|
btrfs_disk_key_flags(&mi->key),
|
||||||
btrfs_disk_key_offset(&mi->key));
|
btrfs_disk_key_offset(&mi->key));
|
||||||
break;
|
break;
|
||||||
case BTRFS_STRING_ITEM_KEY:
|
case BTRFS_STRING_ITEM_KEY:
|
||||||
printf("\t\titem data %.*s\n", btrfs_item_size(item),
|
printk("\t\titem data %.*s\n", btrfs_item_size(item),
|
||||||
btrfs_leaf_data(l) + btrfs_item_offset(item));
|
btrfs_leaf_data(l) + btrfs_item_offset(item));
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
@ -86,12 +84,12 @@ void btrfs_print_tree(struct btrfs_root *root, struct btrfs_buffer *t)
|
||||||
btrfs_print_leaf(root, (struct btrfs_leaf *)c);
|
btrfs_print_leaf(root, (struct btrfs_leaf *)c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printf("node %Lu level %d total ptrs %d free spc %u\n", t->blocknr,
|
printk("node %Lu level %d total ptrs %d free spc %u\n", t->blocknr,
|
||||||
btrfs_header_level(&c->header), nr,
|
btrfs_header_level(&c->header), nr,
|
||||||
(u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr);
|
(u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
for (i = 0; i < nr; i++) {
|
for (i = 0; i < nr; i++) {
|
||||||
printf("\tkey %d (%Lu %u %Lu) block %Lu\n",
|
printk("\tkey %d (%Lu %u %Lu) block %Lu\n",
|
||||||
i,
|
i,
|
||||||
c->ptrs[i].key.objectid,
|
c->ptrs[i].key.objectid,
|
||||||
c->ptrs[i].key.flags,
|
c->ptrs[i].key.flags,
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
|
||||||
#include "disk-io.h"
|
|
||||||
#include "print-tree.h"
|
|
||||||
#include "transaction.h"
|
|
||||||
|
|
||||||
/* for testing only */
|
|
||||||
int next_key(int i, int max_key) {
|
|
||||||
return rand() % max_key;
|
|
||||||
// return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int ac, char **av) {
|
|
||||||
struct btrfs_key ins;
|
|
||||||
struct btrfs_key last = { (u64)-1, 0, 0};
|
|
||||||
char *buf;
|
|
||||||
int i;
|
|
||||||
int num;
|
|
||||||
int ret;
|
|
||||||
int run_size = 100000;
|
|
||||||
int max_key = 100000000;
|
|
||||||
int tree_size = 0;
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_super_block super;
|
|
||||||
struct btrfs_root *root;
|
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
|
|
||||||
radix_tree_init();
|
|
||||||
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
trans = btrfs_start_transaction(root, 1);
|
|
||||||
srand(55);
|
|
||||||
ins.flags = 0;
|
|
||||||
btrfs_set_key_type(&ins, BTRFS_STRING_ITEM_KEY);
|
|
||||||
for (i = 0; i < run_size; i++) {
|
|
||||||
buf = malloc(64);
|
|
||||||
num = next_key(i, max_key);
|
|
||||||
// num = i;
|
|
||||||
sprintf(buf, "string-%d", num);
|
|
||||||
if (i % 10000 == 0)
|
|
||||||
fprintf(stderr, "insert %d:%d\n", num, i);
|
|
||||||
ins.objectid = num;
|
|
||||||
ins.offset = 0;
|
|
||||||
ret = btrfs_insert_item(trans, root, &ins, buf, strlen(buf));
|
|
||||||
if (!ret)
|
|
||||||
tree_size++;
|
|
||||||
free(buf);
|
|
||||||
if (i == run_size - 5) {
|
|
||||||
btrfs_commit_transaction(trans, root, &super);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
close_ctree(root, &super);
|
|
||||||
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
printf("starting search\n");
|
|
||||||
srand(55);
|
|
||||||
for (i = 0; i < run_size; i++) {
|
|
||||||
num = next_key(i, max_key);
|
|
||||||
ins.objectid = num;
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
if (i % 10000 == 0)
|
|
||||||
fprintf(stderr, "search %d:%d\n", num, i);
|
|
||||||
ret = btrfs_search_slot(trans, root, &ins, &path, 0, 0);
|
|
||||||
if (ret) {
|
|
||||||
btrfs_print_tree(root, root->node);
|
|
||||||
printf("unable to find %d\n", num);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
}
|
|
||||||
close_ctree(root, &super);
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
printf("node %p level %d total ptrs %d free spc %lu\n", root->node,
|
|
||||||
btrfs_header_level(&root->node->node.header),
|
|
||||||
btrfs_header_nritems(&root->node->node.header),
|
|
||||||
BTRFS_NODEPTRS_PER_BLOCK(root) -
|
|
||||||
btrfs_header_nritems(&root->node->node.header));
|
|
||||||
printf("all searches good, deleting some items\n");
|
|
||||||
i = 0;
|
|
||||||
srand(55);
|
|
||||||
for (i = 0 ; i < run_size/4; i++) {
|
|
||||||
num = next_key(i, max_key);
|
|
||||||
ins.objectid = num;
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_search_slot(trans, root, &ins, &path, -1, 1);
|
|
||||||
if (!ret) {
|
|
||||||
if (i % 10000 == 0)
|
|
||||||
fprintf(stderr, "del %d:%d\n", num, i);
|
|
||||||
ret = btrfs_del_item(trans, root, &path);
|
|
||||||
if (ret != 0)
|
|
||||||
BUG();
|
|
||||||
tree_size--;
|
|
||||||
}
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
}
|
|
||||||
close_ctree(root, &super);
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
srand(128);
|
|
||||||
for (i = 0; i < run_size; i++) {
|
|
||||||
buf = malloc(64);
|
|
||||||
num = next_key(i, max_key);
|
|
||||||
sprintf(buf, "string-%d", num);
|
|
||||||
ins.objectid = num;
|
|
||||||
if (i % 10000 == 0)
|
|
||||||
fprintf(stderr, "insert %d:%d\n", num, i);
|
|
||||||
ret = btrfs_insert_item(trans, root, &ins, buf, strlen(buf));
|
|
||||||
if (!ret)
|
|
||||||
tree_size++;
|
|
||||||
free(buf);
|
|
||||||
}
|
|
||||||
close_ctree(root, &super);
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
srand(128);
|
|
||||||
printf("starting search2\n");
|
|
||||||
for (i = 0; i < run_size; i++) {
|
|
||||||
num = next_key(i, max_key);
|
|
||||||
ins.objectid = num;
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
if (i % 10000 == 0)
|
|
||||||
fprintf(stderr, "search %d:%d\n", num, i);
|
|
||||||
ret = btrfs_search_slot(trans, root, &ins, &path, 0, 0);
|
|
||||||
if (ret) {
|
|
||||||
btrfs_print_tree(root, root->node);
|
|
||||||
printf("unable to find %d\n", num);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
}
|
|
||||||
printf("starting big long delete run\n");
|
|
||||||
while(root->node &&
|
|
||||||
btrfs_header_nritems(&root->node->node.header) > 0) {
|
|
||||||
struct btrfs_leaf *leaf;
|
|
||||||
int slot;
|
|
||||||
ins.objectid = (u64)-1;
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_search_slot(trans, root, &ins, &path, -1, 1);
|
|
||||||
if (ret == 0)
|
|
||||||
BUG();
|
|
||||||
|
|
||||||
leaf = &path.nodes[0]->leaf;
|
|
||||||
slot = path.slots[0];
|
|
||||||
if (slot != btrfs_header_nritems(&leaf->header))
|
|
||||||
BUG();
|
|
||||||
while(path.slots[0] > 0) {
|
|
||||||
path.slots[0] -= 1;
|
|
||||||
slot = path.slots[0];
|
|
||||||
leaf = &path.nodes[0]->leaf;
|
|
||||||
|
|
||||||
btrfs_disk_key_to_cpu(&last, &leaf->items[slot].key);
|
|
||||||
if (tree_size % 10000 == 0)
|
|
||||||
printf("big del %d:%d\n", tree_size, i);
|
|
||||||
ret = btrfs_del_item(trans, root, &path);
|
|
||||||
if (ret != 0) {
|
|
||||||
printf("del_item returned %d\n", ret);
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
tree_size--;
|
|
||||||
}
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
printf("previous tree:\n");
|
|
||||||
btrfs_print_tree(root, root->commit_root);
|
|
||||||
printf("map before commit\n");
|
|
||||||
btrfs_print_tree(root->extent_root, root->extent_root->node);
|
|
||||||
*/
|
|
||||||
btrfs_commit_transaction(trans, root, &super);
|
|
||||||
printf("tree size is now %d\n", tree_size);
|
|
||||||
printf("root %p commit root %p\n", root->node, root->commit_root);
|
|
||||||
printf("map tree\n");
|
|
||||||
btrfs_print_tree(root->fs_info->extent_root,
|
|
||||||
root->fs_info->extent_root->node);
|
|
||||||
close_ctree(root, &super);
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,836 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2001 Momchil Velikov
|
|
||||||
* Portions Copyright (C) 2001 Christoph Hellwig
|
|
||||||
* Copyright (C) 2005 SGI, Christoph Lameter <clameter@sgi.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as
|
|
||||||
* published by the Free Software Foundation; either version 2, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#ifdef __KERNEL__
|
|
||||||
#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6)
|
|
||||||
#else
|
|
||||||
#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT)
|
|
||||||
#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1)
|
|
||||||
|
|
||||||
#define RADIX_TREE_TAG_LONGS \
|
|
||||||
((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG)
|
|
||||||
|
|
||||||
struct radix_tree_node {
|
|
||||||
unsigned int count;
|
|
||||||
void *slots[RADIX_TREE_MAP_SIZE];
|
|
||||||
unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct radix_tree_path {
|
|
||||||
struct radix_tree_node *node;
|
|
||||||
int offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long))
|
|
||||||
#define RADIX_TREE_MAX_PATH (RADIX_TREE_INDEX_BITS/RADIX_TREE_MAP_SHIFT + 2)
|
|
||||||
|
|
||||||
static unsigned long height_to_maxindex[RADIX_TREE_MAX_PATH] __read_mostly;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Per-cpu pool of preloaded nodes
|
|
||||||
*/
|
|
||||||
struct radix_tree_preload {
|
|
||||||
int nr;
|
|
||||||
struct radix_tree_node *nodes[RADIX_TREE_MAX_PATH];
|
|
||||||
};
|
|
||||||
struct radix_tree_preload radix_tree_preloads = { 0, };
|
|
||||||
|
|
||||||
static inline gfp_t root_gfp_mask(struct radix_tree_root *root)
|
|
||||||
{
|
|
||||||
return root->gfp_mask & __GFP_BITS_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int internal_nodes = 0;
|
|
||||||
/*
|
|
||||||
* This assumes that the caller has performed appropriate preallocation, and
|
|
||||||
* that the caller has pinned this thread of control to the current CPU.
|
|
||||||
*/
|
|
||||||
static struct radix_tree_node *
|
|
||||||
radix_tree_node_alloc(struct radix_tree_root *root)
|
|
||||||
{
|
|
||||||
struct radix_tree_node *ret;
|
|
||||||
ret = malloc(sizeof(struct radix_tree_node));
|
|
||||||
if (ret) {
|
|
||||||
memset(ret, 0, sizeof(struct radix_tree_node));
|
|
||||||
internal_nodes++;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
radix_tree_node_free(struct radix_tree_node *node)
|
|
||||||
{
|
|
||||||
internal_nodes--;
|
|
||||||
free(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Load up this CPU's radix_tree_node buffer with sufficient objects to
|
|
||||||
* ensure that the addition of a single element in the tree cannot fail. On
|
|
||||||
* success, return zero, with preemption disabled. On error, return -ENOMEM
|
|
||||||
* with preemption not disabled.
|
|
||||||
*/
|
|
||||||
int radix_tree_preload(gfp_t gfp_mask)
|
|
||||||
{
|
|
||||||
struct radix_tree_preload *rtp;
|
|
||||||
struct radix_tree_node *node;
|
|
||||||
int ret = -ENOMEM;
|
|
||||||
|
|
||||||
preempt_disable();
|
|
||||||
rtp = &__get_cpu_var(radix_tree_preloads);
|
|
||||||
while (rtp->nr < ARRAY_SIZE(rtp->nodes)) {
|
|
||||||
preempt_enable();
|
|
||||||
node = radix_tree_node_alloc(NULL);
|
|
||||||
if (node == NULL)
|
|
||||||
goto out;
|
|
||||||
preempt_disable();
|
|
||||||
rtp = &__get_cpu_var(radix_tree_preloads);
|
|
||||||
if (rtp->nr < ARRAY_SIZE(rtp->nodes))
|
|
||||||
rtp->nodes[rtp->nr++] = node;
|
|
||||||
else
|
|
||||||
radix_tree_node_free(node);
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void tag_set(struct radix_tree_node *node, unsigned int tag,
|
|
||||||
int offset)
|
|
||||||
{
|
|
||||||
__set_bit(offset, node->tags[tag]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void tag_clear(struct radix_tree_node *node, unsigned int tag,
|
|
||||||
int offset)
|
|
||||||
{
|
|
||||||
__clear_bit(offset, node->tags[tag]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int tag_get(struct radix_tree_node *node, unsigned int tag,
|
|
||||||
int offset)
|
|
||||||
{
|
|
||||||
return test_bit(offset, node->tags[tag]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void root_tag_set(struct radix_tree_root *root, unsigned int tag)
|
|
||||||
{
|
|
||||||
root->gfp_mask |= (__force gfp_t)(1 << (tag + __GFP_BITS_SHIFT));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void root_tag_clear(struct radix_tree_root *root, unsigned int tag)
|
|
||||||
{
|
|
||||||
root->gfp_mask &= (__force gfp_t)~(1 << (tag + __GFP_BITS_SHIFT));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void root_tag_clear_all(struct radix_tree_root *root)
|
|
||||||
{
|
|
||||||
root->gfp_mask &= __GFP_BITS_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int root_tag_get(struct radix_tree_root *root, unsigned int tag)
|
|
||||||
{
|
|
||||||
return (__force unsigned)root->gfp_mask & (1 << (tag + __GFP_BITS_SHIFT));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns 1 if any slot in the node has this tag set.
|
|
||||||
* Otherwise returns 0.
|
|
||||||
*/
|
|
||||||
static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag)
|
|
||||||
{
|
|
||||||
int idx;
|
|
||||||
for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) {
|
|
||||||
if (node->tags[tag][idx])
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the maximum key which can be store into a
|
|
||||||
* radix tree with height HEIGHT.
|
|
||||||
*/
|
|
||||||
static inline unsigned long radix_tree_maxindex(unsigned int height)
|
|
||||||
{
|
|
||||||
return height_to_maxindex[height];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Extend a radix tree so it can store key @index.
|
|
||||||
*/
|
|
||||||
static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
|
|
||||||
{
|
|
||||||
struct radix_tree_node *node;
|
|
||||||
unsigned int height;
|
|
||||||
int tag;
|
|
||||||
|
|
||||||
/* Figure out what the height should be. */
|
|
||||||
height = root->height + 1;
|
|
||||||
while (index > radix_tree_maxindex(height))
|
|
||||||
height++;
|
|
||||||
|
|
||||||
if (root->rnode == NULL) {
|
|
||||||
root->height = height;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (!(node = radix_tree_node_alloc(root)))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* Increase the height. */
|
|
||||||
node->slots[0] = root->rnode;
|
|
||||||
|
|
||||||
/* Propagate the aggregated tag info into the new root */
|
|
||||||
for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
|
|
||||||
if (root_tag_get(root, tag))
|
|
||||||
tag_set(node, tag, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
node->count = 1;
|
|
||||||
root->rnode = node;
|
|
||||||
root->height++;
|
|
||||||
} while (height > root->height);
|
|
||||||
out:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_insert - insert into a radix tree
|
|
||||||
* @root: radix tree root
|
|
||||||
* @index: index key
|
|
||||||
* @item: item to insert
|
|
||||||
*
|
|
||||||
* Insert an item into the radix tree at position @index.
|
|
||||||
*/
|
|
||||||
int radix_tree_insert(struct radix_tree_root *root,
|
|
||||||
unsigned long index, void *item)
|
|
||||||
{
|
|
||||||
struct radix_tree_node *node = NULL, *slot;
|
|
||||||
unsigned int height, shift;
|
|
||||||
int offset;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
/* Make sure the tree is high enough. */
|
|
||||||
if (index > radix_tree_maxindex(root->height)) {
|
|
||||||
error = radix_tree_extend(root, index);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot = root->rnode;
|
|
||||||
height = root->height;
|
|
||||||
shift = (height-1) * RADIX_TREE_MAP_SHIFT;
|
|
||||||
|
|
||||||
offset = 0; /* uninitialised var warning */
|
|
||||||
while (height > 0) {
|
|
||||||
if (slot == NULL) {
|
|
||||||
/* Have to add a child node. */
|
|
||||||
if (!(slot = radix_tree_node_alloc(root)))
|
|
||||||
return -ENOMEM;
|
|
||||||
if (node) {
|
|
||||||
node->slots[offset] = slot;
|
|
||||||
node->count++;
|
|
||||||
} else
|
|
||||||
root->rnode = slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Go a level down */
|
|
||||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
|
||||||
node = slot;
|
|
||||||
slot = node->slots[offset];
|
|
||||||
shift -= RADIX_TREE_MAP_SHIFT;
|
|
||||||
height--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot != NULL)
|
|
||||||
return -EEXIST;
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
node->count++;
|
|
||||||
node->slots[offset] = item;
|
|
||||||
BUG_ON(tag_get(node, 0, offset));
|
|
||||||
BUG_ON(tag_get(node, 1, offset));
|
|
||||||
} else {
|
|
||||||
root->rnode = item;
|
|
||||||
BUG_ON(root_tag_get(root, 0));
|
|
||||||
BUG_ON(root_tag_get(root, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void **__lookup_slot(struct radix_tree_root *root,
|
|
||||||
unsigned long index)
|
|
||||||
{
|
|
||||||
unsigned int height, shift;
|
|
||||||
struct radix_tree_node **slot;
|
|
||||||
|
|
||||||
height = root->height;
|
|
||||||
|
|
||||||
if (index > radix_tree_maxindex(height))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (height == 0 && root->rnode)
|
|
||||||
return (void **)&root->rnode;
|
|
||||||
|
|
||||||
shift = (height-1) * RADIX_TREE_MAP_SHIFT;
|
|
||||||
slot = &root->rnode;
|
|
||||||
|
|
||||||
while (height > 0) {
|
|
||||||
if (*slot == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
slot = (struct radix_tree_node **)
|
|
||||||
((*slot)->slots +
|
|
||||||
((index >> shift) & RADIX_TREE_MAP_MASK));
|
|
||||||
shift -= RADIX_TREE_MAP_SHIFT;
|
|
||||||
height--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (void **)slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_lookup_slot - lookup a slot in a radix tree
|
|
||||||
* @root: radix tree root
|
|
||||||
* @index: index key
|
|
||||||
*
|
|
||||||
* Lookup the slot corresponding to the position @index in the radix tree
|
|
||||||
* @root. This is useful for update-if-exists operations.
|
|
||||||
*/
|
|
||||||
void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index)
|
|
||||||
{
|
|
||||||
return __lookup_slot(root, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_lookup - perform lookup operation on a radix tree
|
|
||||||
* @root: radix tree root
|
|
||||||
* @index: index key
|
|
||||||
*
|
|
||||||
* Lookup the item at the position @index in the radix tree @root.
|
|
||||||
*/
|
|
||||||
void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
|
|
||||||
{
|
|
||||||
void **slot;
|
|
||||||
|
|
||||||
slot = __lookup_slot(root, index);
|
|
||||||
return slot != NULL ? *slot : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_tag_set - set a tag on a radix tree node
|
|
||||||
* @root: radix tree root
|
|
||||||
* @index: index key
|
|
||||||
* @tag: tag index
|
|
||||||
*
|
|
||||||
* Set the search tag (which must be < RADIX_TREE_MAX_TAGS)
|
|
||||||
* corresponding to @index in the radix tree. From
|
|
||||||
* the root all the way down to the leaf node.
|
|
||||||
*
|
|
||||||
* Returns the address of the tagged item. Setting a tag on a not-present
|
|
||||||
* item is a bug.
|
|
||||||
*/
|
|
||||||
void *radix_tree_tag_set(struct radix_tree_root *root,
|
|
||||||
unsigned long index, unsigned int tag)
|
|
||||||
{
|
|
||||||
unsigned int height, shift;
|
|
||||||
struct radix_tree_node *slot;
|
|
||||||
|
|
||||||
height = root->height;
|
|
||||||
BUG_ON(index > radix_tree_maxindex(height));
|
|
||||||
|
|
||||||
slot = root->rnode;
|
|
||||||
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
|
|
||||||
|
|
||||||
while (height > 0) {
|
|
||||||
int offset;
|
|
||||||
|
|
||||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
|
||||||
if (!tag_get(slot, tag, offset))
|
|
||||||
tag_set(slot, tag, offset);
|
|
||||||
slot = slot->slots[offset];
|
|
||||||
BUG_ON(slot == NULL);
|
|
||||||
shift -= RADIX_TREE_MAP_SHIFT;
|
|
||||||
height--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set the root's tag bit */
|
|
||||||
if (slot && !root_tag_get(root, tag))
|
|
||||||
root_tag_set(root, tag);
|
|
||||||
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_tag_clear - clear a tag on a radix tree node
|
|
||||||
* @root: radix tree root
|
|
||||||
* @index: index key
|
|
||||||
* @tag: tag index
|
|
||||||
*
|
|
||||||
* Clear the search tag (which must be < RADIX_TREE_MAX_TAGS)
|
|
||||||
* corresponding to @index in the radix tree. If
|
|
||||||
* this causes the leaf node to have no tags set then clear the tag in the
|
|
||||||
* next-to-leaf node, etc.
|
|
||||||
*
|
|
||||||
* Returns the address of the tagged item on success, else NULL. ie:
|
|
||||||
* has the same return value and semantics as radix_tree_lookup().
|
|
||||||
*/
|
|
||||||
void *radix_tree_tag_clear(struct radix_tree_root *root,
|
|
||||||
unsigned long index, unsigned int tag)
|
|
||||||
{
|
|
||||||
struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path;
|
|
||||||
struct radix_tree_node *slot = NULL;
|
|
||||||
unsigned int height, shift;
|
|
||||||
|
|
||||||
height = root->height;
|
|
||||||
if (index > radix_tree_maxindex(height))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
|
|
||||||
pathp->node = NULL;
|
|
||||||
slot = root->rnode;
|
|
||||||
|
|
||||||
while (height > 0) {
|
|
||||||
int offset;
|
|
||||||
|
|
||||||
if (slot == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
|
||||||
pathp[1].offset = offset;
|
|
||||||
pathp[1].node = slot;
|
|
||||||
slot = slot->slots[offset];
|
|
||||||
pathp++;
|
|
||||||
shift -= RADIX_TREE_MAP_SHIFT;
|
|
||||||
height--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
while (pathp->node) {
|
|
||||||
if (!tag_get(pathp->node, tag, pathp->offset))
|
|
||||||
goto out;
|
|
||||||
tag_clear(pathp->node, tag, pathp->offset);
|
|
||||||
if (any_tag_set(pathp->node, tag))
|
|
||||||
goto out;
|
|
||||||
pathp--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear the root's tag bit */
|
|
||||||
if (root_tag_get(root, tag))
|
|
||||||
root_tag_clear(root, tag);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef __KERNEL__ /* Only the test harness uses this at present */
|
|
||||||
/**
|
|
||||||
* radix_tree_tag_get - get a tag on a radix tree node
|
|
||||||
* @root: radix tree root
|
|
||||||
* @index: index key
|
|
||||||
* @tag: tag index (< RADIX_TREE_MAX_TAGS)
|
|
||||||
*
|
|
||||||
* Return values:
|
|
||||||
*
|
|
||||||
* 0: tag not present or not set
|
|
||||||
* 1: tag set
|
|
||||||
*/
|
|
||||||
int radix_tree_tag_get(struct radix_tree_root *root,
|
|
||||||
unsigned long index, unsigned int tag)
|
|
||||||
{
|
|
||||||
unsigned int height, shift;
|
|
||||||
struct radix_tree_node *slot;
|
|
||||||
int saw_unset_tag = 0;
|
|
||||||
|
|
||||||
height = root->height;
|
|
||||||
if (index > radix_tree_maxindex(height))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* check the root's tag bit */
|
|
||||||
if (!root_tag_get(root, tag))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (height == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
|
|
||||||
slot = root->rnode;
|
|
||||||
|
|
||||||
for ( ; ; ) {
|
|
||||||
int offset;
|
|
||||||
|
|
||||||
if (slot == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is just a debug check. Later, we can bale as soon as
|
|
||||||
* we see an unset tag.
|
|
||||||
*/
|
|
||||||
if (!tag_get(slot, tag, offset))
|
|
||||||
saw_unset_tag = 1;
|
|
||||||
if (height == 1) {
|
|
||||||
int ret = tag_get(slot, tag, offset);
|
|
||||||
|
|
||||||
BUG_ON(ret && saw_unset_tag);
|
|
||||||
return !!ret;
|
|
||||||
}
|
|
||||||
slot = slot->slots[offset];
|
|
||||||
shift -= RADIX_TREE_MAP_SHIFT;
|
|
||||||
height--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static unsigned int
|
|
||||||
__lookup(struct radix_tree_root *root, void **results, unsigned long index,
|
|
||||||
unsigned int max_items, unsigned long *next_index)
|
|
||||||
{
|
|
||||||
unsigned int nr_found = 0;
|
|
||||||
unsigned int shift, height;
|
|
||||||
struct radix_tree_node *slot;
|
|
||||||
unsigned long i;
|
|
||||||
|
|
||||||
height = root->height;
|
|
||||||
if (height == 0) {
|
|
||||||
if (root->rnode && index == 0)
|
|
||||||
results[nr_found++] = root->rnode;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
shift = (height-1) * RADIX_TREE_MAP_SHIFT;
|
|
||||||
slot = root->rnode;
|
|
||||||
|
|
||||||
for ( ; height > 1; height--) {
|
|
||||||
|
|
||||||
for (i = (index >> shift) & RADIX_TREE_MAP_MASK ;
|
|
||||||
i < RADIX_TREE_MAP_SIZE; i++) {
|
|
||||||
if (slot->slots[i] != NULL)
|
|
||||||
break;
|
|
||||||
index &= ~((1UL << shift) - 1);
|
|
||||||
index += 1UL << shift;
|
|
||||||
if (index == 0)
|
|
||||||
goto out; /* 32-bit wraparound */
|
|
||||||
}
|
|
||||||
if (i == RADIX_TREE_MAP_SIZE)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
shift -= RADIX_TREE_MAP_SHIFT;
|
|
||||||
slot = slot->slots[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bottom level: grab some items */
|
|
||||||
for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) {
|
|
||||||
index++;
|
|
||||||
if (slot->slots[i]) {
|
|
||||||
results[nr_found++] = slot->slots[i];
|
|
||||||
if (nr_found == max_items)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
*next_index = index;
|
|
||||||
return nr_found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_gang_lookup - perform multiple lookup on a radix tree
|
|
||||||
* @root: radix tree root
|
|
||||||
* @results: where the results of the lookup are placed
|
|
||||||
* @first_index: start the lookup from this key
|
|
||||||
* @max_items: place up to this many items at *results
|
|
||||||
*
|
|
||||||
* Performs an index-ascending scan of the tree for present items. Places
|
|
||||||
* them at *@results and returns the number of items which were placed at
|
|
||||||
* *@results.
|
|
||||||
*
|
|
||||||
* The implementation is naive.
|
|
||||||
*/
|
|
||||||
unsigned int
|
|
||||||
radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
|
|
||||||
unsigned long first_index, unsigned int max_items)
|
|
||||||
{
|
|
||||||
const unsigned long max_index = radix_tree_maxindex(root->height);
|
|
||||||
unsigned long cur_index = first_index;
|
|
||||||
unsigned int ret = 0;
|
|
||||||
|
|
||||||
while (ret < max_items) {
|
|
||||||
unsigned int nr_found;
|
|
||||||
unsigned long next_index; /* Index of next search */
|
|
||||||
|
|
||||||
if (cur_index > max_index)
|
|
||||||
break;
|
|
||||||
nr_found = __lookup(root, results + ret, cur_index,
|
|
||||||
max_items - ret, &next_index);
|
|
||||||
ret += nr_found;
|
|
||||||
if (next_index == 0)
|
|
||||||
break;
|
|
||||||
cur_index = next_index;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: the two tag_get()s here should use find_next_bit() instead of
|
|
||||||
* open-coding the search.
|
|
||||||
*/
|
|
||||||
static unsigned int
|
|
||||||
__lookup_tag(struct radix_tree_root *root, void **results, unsigned long index,
|
|
||||||
unsigned int max_items, unsigned long *next_index, unsigned int tag)
|
|
||||||
{
|
|
||||||
unsigned int nr_found = 0;
|
|
||||||
unsigned int shift;
|
|
||||||
unsigned int height = root->height;
|
|
||||||
struct radix_tree_node *slot;
|
|
||||||
|
|
||||||
if (height == 0) {
|
|
||||||
if (root->rnode && index == 0)
|
|
||||||
results[nr_found++] = root->rnode;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
|
|
||||||
slot = root->rnode;
|
|
||||||
|
|
||||||
do {
|
|
||||||
unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;
|
|
||||||
|
|
||||||
for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
|
|
||||||
if (tag_get(slot, tag, i)) {
|
|
||||||
BUG_ON(slot->slots[i] == NULL);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index &= ~((1UL << shift) - 1);
|
|
||||||
index += 1UL << shift;
|
|
||||||
if (index == 0)
|
|
||||||
goto out; /* 32-bit wraparound */
|
|
||||||
}
|
|
||||||
if (i == RADIX_TREE_MAP_SIZE)
|
|
||||||
goto out;
|
|
||||||
height--;
|
|
||||||
if (height == 0) { /* Bottom level: grab some items */
|
|
||||||
unsigned long j = index & RADIX_TREE_MAP_MASK;
|
|
||||||
|
|
||||||
for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
|
|
||||||
index++;
|
|
||||||
if (tag_get(slot, tag, j)) {
|
|
||||||
BUG_ON(slot->slots[j] == NULL);
|
|
||||||
results[nr_found++] = slot->slots[j];
|
|
||||||
if (nr_found == max_items)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shift -= RADIX_TREE_MAP_SHIFT;
|
|
||||||
slot = slot->slots[i];
|
|
||||||
} while (height > 0);
|
|
||||||
out:
|
|
||||||
*next_index = index;
|
|
||||||
return nr_found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_gang_lookup_tag - perform multiple lookup on a radix tree
|
|
||||||
* based on a tag
|
|
||||||
* @root: radix tree root
|
|
||||||
* @results: where the results of the lookup are placed
|
|
||||||
* @first_index: start the lookup from this key
|
|
||||||
* @max_items: place up to this many items at *results
|
|
||||||
* @tag: the tag index (< RADIX_TREE_MAX_TAGS)
|
|
||||||
*
|
|
||||||
* Performs an index-ascending scan of the tree for present items which
|
|
||||||
* have the tag indexed by @tag set. Places the items at *@results and
|
|
||||||
* returns the number of items which were placed at *@results.
|
|
||||||
*/
|
|
||||||
unsigned int
|
|
||||||
radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
|
|
||||||
unsigned long first_index, unsigned int max_items,
|
|
||||||
unsigned int tag)
|
|
||||||
{
|
|
||||||
const unsigned long max_index = radix_tree_maxindex(root->height);
|
|
||||||
unsigned long cur_index = first_index;
|
|
||||||
unsigned int ret = 0;
|
|
||||||
|
|
||||||
/* check the root's tag bit */
|
|
||||||
if (!root_tag_get(root, tag))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
while (ret < max_items) {
|
|
||||||
unsigned int nr_found;
|
|
||||||
unsigned long next_index; /* Index of next search */
|
|
||||||
|
|
||||||
if (cur_index > max_index)
|
|
||||||
break;
|
|
||||||
nr_found = __lookup_tag(root, results + ret, cur_index,
|
|
||||||
max_items - ret, &next_index, tag);
|
|
||||||
ret += nr_found;
|
|
||||||
if (next_index == 0)
|
|
||||||
break;
|
|
||||||
cur_index = next_index;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_shrink - shrink height of a radix tree to minimal
|
|
||||||
* @root radix tree root
|
|
||||||
*/
|
|
||||||
static inline void radix_tree_shrink(struct radix_tree_root *root)
|
|
||||||
{
|
|
||||||
/* try to shrink tree height */
|
|
||||||
while (root->height > 0 &&
|
|
||||||
root->rnode->count == 1 &&
|
|
||||||
root->rnode->slots[0]) {
|
|
||||||
struct radix_tree_node *to_free = root->rnode;
|
|
||||||
|
|
||||||
root->rnode = to_free->slots[0];
|
|
||||||
root->height--;
|
|
||||||
/* must only free zeroed nodes into the slab */
|
|
||||||
tag_clear(to_free, 0, 0);
|
|
||||||
tag_clear(to_free, 1, 0);
|
|
||||||
to_free->slots[0] = NULL;
|
|
||||||
to_free->count = 0;
|
|
||||||
radix_tree_node_free(to_free);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_delete - delete an item from a radix tree
|
|
||||||
* @root: radix tree root
|
|
||||||
* @index: index key
|
|
||||||
*
|
|
||||||
* Remove the item at @index from the radix tree rooted at @root.
|
|
||||||
*
|
|
||||||
* Returns the address of the deleted item, or NULL if it was not present.
|
|
||||||
*/
|
|
||||||
void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
|
|
||||||
{
|
|
||||||
struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path;
|
|
||||||
struct radix_tree_node *slot = NULL;
|
|
||||||
unsigned int height, shift;
|
|
||||||
int tag;
|
|
||||||
int offset;
|
|
||||||
|
|
||||||
height = root->height;
|
|
||||||
if (index > radix_tree_maxindex(height))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
slot = root->rnode;
|
|
||||||
if (height == 0 && root->rnode) {
|
|
||||||
root_tag_clear_all(root);
|
|
||||||
root->rnode = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
|
|
||||||
pathp->node = NULL;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (slot == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
pathp++;
|
|
||||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
|
||||||
pathp->offset = offset;
|
|
||||||
pathp->node = slot;
|
|
||||||
slot = slot->slots[offset];
|
|
||||||
shift -= RADIX_TREE_MAP_SHIFT;
|
|
||||||
height--;
|
|
||||||
} while (height > 0);
|
|
||||||
|
|
||||||
if (slot == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Clear all tags associated with the just-deleted item
|
|
||||||
*/
|
|
||||||
for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
|
|
||||||
if (tag_get(pathp->node, tag, pathp->offset))
|
|
||||||
radix_tree_tag_clear(root, index, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now free the nodes we do not need anymore */
|
|
||||||
while (pathp->node) {
|
|
||||||
pathp->node->slots[pathp->offset] = NULL;
|
|
||||||
pathp->node->count--;
|
|
||||||
|
|
||||||
if (pathp->node->count) {
|
|
||||||
if (pathp->node == root->rnode)
|
|
||||||
radix_tree_shrink(root);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Node with zero slots in use so free it */
|
|
||||||
radix_tree_node_free(pathp->node);
|
|
||||||
|
|
||||||
pathp--;
|
|
||||||
}
|
|
||||||
root_tag_clear_all(root);
|
|
||||||
root->height = 0;
|
|
||||||
root->rnode = NULL;
|
|
||||||
|
|
||||||
out:
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* radix_tree_tagged - test whether any items in the tree are tagged
|
|
||||||
* @root: radix tree root
|
|
||||||
* @tag: tag to test
|
|
||||||
*/
|
|
||||||
int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag)
|
|
||||||
{
|
|
||||||
return root_tag_get(root, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long __maxindex(unsigned int height)
|
|
||||||
{
|
|
||||||
unsigned int tmp = height * RADIX_TREE_MAP_SHIFT;
|
|
||||||
unsigned long index = (~0UL >> (RADIX_TREE_INDEX_BITS - tmp - 1)) >> 1;
|
|
||||||
|
|
||||||
if (tmp >= RADIX_TREE_INDEX_BITS)
|
|
||||||
index = ~0UL;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void radix_tree_init_maxindex(void)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(height_to_maxindex); i++)
|
|
||||||
height_to_maxindex[i] = __maxindex(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void radix_tree_init(void)
|
|
||||||
{
|
|
||||||
radix_tree_init_maxindex();
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2001 Momchil Velikov
|
|
||||||
* Portions Copyright (C) 2001 Christoph Hellwig
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as
|
|
||||||
* published by the Free Software Foundation; either version 2, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
#ifndef _LINUX_RADIX_TREE_H
|
|
||||||
#define _LINUX_RADIX_TREE_H
|
|
||||||
|
|
||||||
#define RADIX_TREE_MAX_TAGS 2
|
|
||||||
|
|
||||||
/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */
|
|
||||||
struct radix_tree_root {
|
|
||||||
unsigned int height;
|
|
||||||
gfp_t gfp_mask;
|
|
||||||
struct radix_tree_node *rnode;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define RADIX_TREE_INIT(mask) { \
|
|
||||||
.height = 0, \
|
|
||||||
.gfp_mask = (mask), \
|
|
||||||
.rnode = NULL, \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RADIX_TREE(name, mask) \
|
|
||||||
struct radix_tree_root name = RADIX_TREE_INIT(mask)
|
|
||||||
|
|
||||||
#define INIT_RADIX_TREE(root, mask) \
|
|
||||||
do { \
|
|
||||||
(root)->height = 0; \
|
|
||||||
(root)->gfp_mask = (mask); \
|
|
||||||
(root)->rnode = NULL; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
|
|
||||||
void *radix_tree_lookup(struct radix_tree_root *, unsigned long);
|
|
||||||
void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long);
|
|
||||||
void *radix_tree_delete(struct radix_tree_root *, unsigned long);
|
|
||||||
unsigned int
|
|
||||||
radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
|
|
||||||
unsigned long first_index, unsigned int max_items);
|
|
||||||
int radix_tree_preload(gfp_t gfp_mask);
|
|
||||||
void radix_tree_init(void);
|
|
||||||
void *radix_tree_tag_set(struct radix_tree_root *root,
|
|
||||||
unsigned long index, unsigned int tag);
|
|
||||||
void *radix_tree_tag_clear(struct radix_tree_root *root,
|
|
||||||
unsigned long index, unsigned int tag);
|
|
||||||
int radix_tree_tag_get(struct radix_tree_root *root,
|
|
||||||
unsigned long index, unsigned int tag);
|
|
||||||
unsigned int
|
|
||||||
radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
|
|
||||||
unsigned long first_index, unsigned int max_items,
|
|
||||||
unsigned int tag);
|
|
||||||
int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag);
|
|
||||||
|
|
||||||
static inline void radix_tree_preload_end(void)
|
|
||||||
{
|
|
||||||
preempt_enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _LINUX_RADIX_TREE_H */
|
|
|
@ -1,405 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
|
||||||
#include "disk-io.h"
|
|
||||||
#include "print-tree.h"
|
|
||||||
#include "transaction.h"
|
|
||||||
|
|
||||||
int keep_running = 1;
|
|
||||||
struct btrfs_super_block super;
|
|
||||||
|
|
||||||
static int setup_key(struct radix_tree_root *root, struct btrfs_key *key,
|
|
||||||
int exists)
|
|
||||||
{
|
|
||||||
int num = rand();
|
|
||||||
unsigned long res[2];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
key->flags = 0;
|
|
||||||
btrfs_set_key_type(key, BTRFS_STRING_ITEM_KEY);
|
|
||||||
key->offset = 0;
|
|
||||||
again:
|
|
||||||
ret = radix_tree_gang_lookup(root, (void **)res, num, 2);
|
|
||||||
if (exists) {
|
|
||||||
if (ret == 0)
|
|
||||||
return -1;
|
|
||||||
num = res[0];
|
|
||||||
} else if (ret != 0 && num == res[0]) {
|
|
||||||
num++;
|
|
||||||
if (ret > 1 && num == res[1]) {
|
|
||||||
num++;
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key->objectid = num;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key key;
|
|
||||||
int ret;
|
|
||||||
char buf[128];
|
|
||||||
unsigned long oid;
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = setup_key(radix, &key, 0);
|
|
||||||
sprintf(buf, "str-%Lu\n", key.objectid);
|
|
||||||
ret = btrfs_insert_item(trans, root, &key, buf, strlen(buf));
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
oid = (unsigned long)key.objectid;
|
|
||||||
radix_tree_preload(GFP_KERNEL);
|
|
||||||
ret = radix_tree_insert(radix, oid, (void *)oid);
|
|
||||||
radix_tree_preload_end();
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
return ret;
|
|
||||||
error:
|
|
||||||
printf("failed to insert %Lu\n", key.objectid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root
|
|
||||||
*root, struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key key;
|
|
||||||
int ret;
|
|
||||||
char buf[128];
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = setup_key(radix, &key, 1);
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
sprintf(buf, "str-%Lu\n", key.objectid);
|
|
||||||
ret = btrfs_insert_item(trans, root, &key, buf, strlen(buf));
|
|
||||||
if (ret != -EEXIST) {
|
|
||||||
printf("insert on %Lu gave us %d\n", key.objectid, ret);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key key;
|
|
||||||
int ret;
|
|
||||||
unsigned long *ptr;
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = setup_key(radix, &key, 1);
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
ret = btrfs_del_item(trans, root, &path);
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
if (ret != 0)
|
|
||||||
goto error;
|
|
||||||
ptr = radix_tree_delete(radix, key.objectid);
|
|
||||||
if (!ptr)
|
|
||||||
goto error;
|
|
||||||
return 0;
|
|
||||||
error:
|
|
||||||
printf("failed to delete %Lu\n", key.objectid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
|
||||||
*root, struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key key;
|
|
||||||
int ret;
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = setup_key(radix, &key, 1);
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
return 0;
|
|
||||||
error:
|
|
||||||
printf("unable to find key %Lu\n", key.objectid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root
|
|
||||||
*root, struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key key;
|
|
||||||
int ret;
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = setup_key(radix, &key, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
ret = btrfs_search_slot(trans, root, &key, &path, 0, 0);
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
if (ret <= 0)
|
|
||||||
goto error;
|
|
||||||
return 0;
|
|
||||||
error:
|
|
||||||
printf("able to find key that should not exist %Lu\n", key.objectid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root
|
|
||||||
*root, struct radix_tree_root *radix, int nr)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key key;
|
|
||||||
unsigned long found = 0;
|
|
||||||
int ret;
|
|
||||||
int slot;
|
|
||||||
int *ptr;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
key.offset = 0;
|
|
||||||
key.flags = 0;
|
|
||||||
btrfs_set_key_type(&key, BTRFS_STRING_ITEM_KEY);
|
|
||||||
key.objectid = (unsigned long)-1;
|
|
||||||
while(nr-- >= 0) {
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
|
|
||||||
if (ret < 0) {
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (ret != 0) {
|
|
||||||
if (path.slots[0] == 0) {
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
path.slots[0] -= 1;
|
|
||||||
}
|
|
||||||
slot = path.slots[0];
|
|
||||||
found = btrfs_disk_key_objectid(
|
|
||||||
&path.nodes[0]->leaf.items[slot].key);
|
|
||||||
ret = btrfs_del_item(trans, root, &path);
|
|
||||||
count++;
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"failed to remove %lu from tree\n",
|
|
||||||
found);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
ptr = radix_tree_delete(radix, found);
|
|
||||||
if (!ptr)
|
|
||||||
goto error;
|
|
||||||
if (!keep_running)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
error:
|
|
||||||
fprintf(stderr, "failed to delete from the radix %lu\n", found);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix, int count)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int ret = 0;
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
ret = ins_one(trans, root, radix);
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr, "fill failed\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (i % 1000 == 0) {
|
|
||||||
ret = btrfs_commit_transaction(trans, root, &super);
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr, "fill commit failed\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i && i % 10000 == 0) {
|
|
||||||
printf("bigfill %d\n", i);
|
|
||||||
}
|
|
||||||
if (!keep_running)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
||||||
struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
int nr = rand() % 5000;
|
|
||||||
static int run_nr = 0;
|
|
||||||
|
|
||||||
/* do the bulk op much less frequently */
|
|
||||||
if (run_nr++ % 100)
|
|
||||||
return 0;
|
|
||||||
ret = empty_tree(trans, root, radix, nr);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
ret = fill_tree(trans, root, radix, nr);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int (*ops[])(struct btrfs_trans_handle *,
|
|
||||||
struct btrfs_root *root, struct radix_tree_root *radix) =
|
|
||||||
{ ins_one, insert_dup, del_one, lookup_item,
|
|
||||||
lookup_enoent, bulk_op };
|
|
||||||
|
|
||||||
static int fill_radix(struct btrfs_root *root, struct radix_tree_root *radix)
|
|
||||||
{
|
|
||||||
struct btrfs_path path;
|
|
||||||
struct btrfs_key key;
|
|
||||||
unsigned long found;
|
|
||||||
int ret;
|
|
||||||
int slot;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
key.offset = 0;
|
|
||||||
key.flags = 0;
|
|
||||||
btrfs_set_key_type(&key, BTRFS_STRING_ITEM_KEY);
|
|
||||||
key.objectid = (unsigned long)-1;
|
|
||||||
while(1) {
|
|
||||||
btrfs_init_path(&path);
|
|
||||||
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
slot = path.slots[0];
|
|
||||||
if (ret != 0) {
|
|
||||||
if (slot == 0) {
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
slot -= 1;
|
|
||||||
}
|
|
||||||
for (i = slot; i >= 0; i--) {
|
|
||||||
found = btrfs_disk_key_objectid(&path.nodes[0]->
|
|
||||||
leaf.items[i].key);
|
|
||||||
radix_tree_preload(GFP_KERNEL);
|
|
||||||
ret = radix_tree_insert(radix, found, (void *)found);
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"failed to insert %lu into radix\n",
|
|
||||||
found);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
radix_tree_preload_end();
|
|
||||||
}
|
|
||||||
btrfs_release_path(root, &path);
|
|
||||||
key.objectid = found - 1;
|
|
||||||
if (key.objectid > found)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
void sigstopper(int ignored)
|
|
||||||
{
|
|
||||||
keep_running = 0;
|
|
||||||
fprintf(stderr, "caught exit signal, stopping\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int print_usage(void)
|
|
||||||
{
|
|
||||||
printf("usage: tester [-ih] [-c count] [-f count]\n");
|
|
||||||
printf("\t -c count -- iteration count after filling\n");
|
|
||||||
printf("\t -f count -- run this many random inserts before starting\n");
|
|
||||||
printf("\t -i -- only do initial fill\n");
|
|
||||||
printf("\t -h -- this help text\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
int main(int ac, char **av)
|
|
||||||
{
|
|
||||||
RADIX_TREE(radix, GFP_KERNEL);
|
|
||||||
struct btrfs_root *root;
|
|
||||||
int i;
|
|
||||||
int ret;
|
|
||||||
int count;
|
|
||||||
int op;
|
|
||||||
int iterations = 20000;
|
|
||||||
int init_fill_count = 800000;
|
|
||||||
int err = 0;
|
|
||||||
int initial_only = 0;
|
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
radix_tree_init();
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
fill_radix(root, &radix);
|
|
||||||
|
|
||||||
signal(SIGTERM, sigstopper);
|
|
||||||
signal(SIGINT, sigstopper);
|
|
||||||
|
|
||||||
for (i = 1 ; i < ac ; i++) {
|
|
||||||
if (strcmp(av[i], "-i") == 0) {
|
|
||||||
initial_only = 1;
|
|
||||||
} else if (strcmp(av[i], "-c") == 0) {
|
|
||||||
iterations = atoi(av[i+1]);
|
|
||||||
i++;
|
|
||||||
} else if (strcmp(av[i], "-f") == 0) {
|
|
||||||
init_fill_count = atoi(av[i+1]);
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
print_usage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("initial fill\n");
|
|
||||||
trans = btrfs_start_transaction(root, 1);
|
|
||||||
ret = fill_tree(trans, root, &radix, init_fill_count);
|
|
||||||
printf("starting run\n");
|
|
||||||
if (ret) {
|
|
||||||
err = ret;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (initial_only == 1) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
for (i = 0; i < iterations; i++) {
|
|
||||||
op = rand() % ARRAY_SIZE(ops);
|
|
||||||
count = rand() % 128;
|
|
||||||
if (i % 2000 == 0) {
|
|
||||||
printf("%d\n", i);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
if (i && i % 5000 == 0) {
|
|
||||||
printf("open & close, root level %d nritems %d\n",
|
|
||||||
btrfs_header_level(&root->node->node.header),
|
|
||||||
btrfs_header_nritems(&root->node->node.header));
|
|
||||||
close_ctree(root, &super);
|
|
||||||
root = open_ctree("dbfile", &super);
|
|
||||||
}
|
|
||||||
while(count--) {
|
|
||||||
ret = ops[op](trans, root, &radix);
|
|
||||||
if (ret) {
|
|
||||||
fprintf(stderr, "op %d failed %d:%d\n",
|
|
||||||
op, i, iterations);
|
|
||||||
btrfs_print_tree(root, root->node);
|
|
||||||
fprintf(stderr, "op %d failed %d:%d\n",
|
|
||||||
op, i, iterations);
|
|
||||||
err = ret;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (ops[op] == bulk_op)
|
|
||||||
break;
|
|
||||||
if (keep_running == 0) {
|
|
||||||
err = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
close_ctree(root, &super);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
#include <stdio.h>
|
#include <linux/module.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include "kerncompat.h"
|
|
||||||
#include "radix-tree.h"
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
#include "print-tree.h"
|
#include "print-tree.h"
|
||||||
|
|
205
fs/btrfs/super.c
Normal file
205
fs/btrfs/super.c
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/smp_lock.h>
|
||||||
|
#include <linux/backing-dev.h>
|
||||||
|
#include "ctree.h"
|
||||||
|
|
||||||
|
#define BTRFS_SUPER_MAGIC 0x9123682E
|
||||||
|
#if 0
|
||||||
|
/* some random number */
|
||||||
|
|
||||||
|
static struct super_operations ramfs_ops;
|
||||||
|
static struct inode_operations ramfs_dir_inode_operations;
|
||||||
|
|
||||||
|
static struct backing_dev_info ramfs_backing_dev_info = {
|
||||||
|
.ra_pages = 0, /* No readahead */
|
||||||
|
.capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK |
|
||||||
|
BDI_CAP_MAP_DIRECT | BDI_CAP_MAP_COPY |
|
||||||
|
BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
|
||||||
|
{
|
||||||
|
struct inode * inode = new_inode(sb);
|
||||||
|
|
||||||
|
if (inode) {
|
||||||
|
inode->i_mode = mode;
|
||||||
|
inode->i_uid = current->fsuid;
|
||||||
|
inode->i_gid = current->fsgid;
|
||||||
|
inode->i_blocks = 0;
|
||||||
|
inode->i_mapping->a_ops = &ramfs_aops;
|
||||||
|
inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;
|
||||||
|
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||||
|
switch (mode & S_IFMT) {
|
||||||
|
default:
|
||||||
|
init_special_inode(inode, mode, dev);
|
||||||
|
break;
|
||||||
|
case S_IFREG:
|
||||||
|
inode->i_op = &ramfs_file_inode_operations;
|
||||||
|
inode->i_fop = &ramfs_file_operations;
|
||||||
|
break;
|
||||||
|
case S_IFDIR:
|
||||||
|
inode->i_op = &ramfs_dir_inode_operations;
|
||||||
|
inode->i_fop = &simple_dir_operations;
|
||||||
|
|
||||||
|
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
||||||
|
inc_nlink(inode);
|
||||||
|
break;
|
||||||
|
case S_IFLNK:
|
||||||
|
inode->i_op = &page_symlink_inode_operations;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File creation. Allocate an inode, and we're done..
|
||||||
|
*/
|
||||||
|
/* SMP-safe */
|
||||||
|
static int
|
||||||
|
ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
|
||||||
|
{
|
||||||
|
struct inode * inode = ramfs_get_inode(dir->i_sb, mode, dev);
|
||||||
|
int error = -ENOSPC;
|
||||||
|
|
||||||
|
if (inode) {
|
||||||
|
if (dir->i_mode & S_ISGID) {
|
||||||
|
inode->i_gid = dir->i_gid;
|
||||||
|
if (S_ISDIR(mode))
|
||||||
|
inode->i_mode |= S_ISGID;
|
||||||
|
}
|
||||||
|
d_instantiate(dentry, inode);
|
||||||
|
dget(dentry); /* Extra count - pin the dentry in core */
|
||||||
|
error = 0;
|
||||||
|
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ramfs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
|
||||||
|
{
|
||||||
|
int retval = ramfs_mknod(dir, dentry, mode | S_IFDIR, 0);
|
||||||
|
if (!retval)
|
||||||
|
inc_nlink(dir);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ramfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
|
||||||
|
{
|
||||||
|
return ramfs_mknod(dir, dentry, mode | S_IFREG, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
|
||||||
|
{
|
||||||
|
struct inode *inode;
|
||||||
|
int error = -ENOSPC;
|
||||||
|
|
||||||
|
inode = ramfs_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
|
||||||
|
if (inode) {
|
||||||
|
int l = strlen(symname)+1;
|
||||||
|
error = page_symlink(inode, symname, l);
|
||||||
|
if (!error) {
|
||||||
|
if (dir->i_mode & S_ISGID)
|
||||||
|
inode->i_gid = dir->i_gid;
|
||||||
|
d_instantiate(dentry, inode);
|
||||||
|
dget(dentry);
|
||||||
|
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||||
|
} else
|
||||||
|
iput(inode);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct inode_operations ramfs_dir_inode_operations = {
|
||||||
|
.create = ramfs_create,
|
||||||
|
.lookup = simple_lookup,
|
||||||
|
.link = simple_link,
|
||||||
|
.unlink = simple_unlink,
|
||||||
|
.symlink = ramfs_symlink,
|
||||||
|
.mkdir = ramfs_mkdir,
|
||||||
|
.rmdir = simple_rmdir,
|
||||||
|
.mknod = ramfs_mknod,
|
||||||
|
.rename = simple_rename,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct inode *btrfs_get_inode(struct super_block *sb, int mode, dev_t dev)
|
||||||
|
{
|
||||||
|
struct inode * inode = new_inode(sb);
|
||||||
|
|
||||||
|
if (inode) {
|
||||||
|
inode->i_mode = mode;
|
||||||
|
inode->i_uid = current->fsuid;
|
||||||
|
inode->i_gid = current->fsgid;
|
||||||
|
inode->i_blocks = 0;
|
||||||
|
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||||
|
}
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct super_operations btrfs_ops = {
|
||||||
|
.statfs = simple_statfs,
|
||||||
|
.drop_inode = generic_delete_inode,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int btrfs_fill_super(struct super_block * sb, void * data, int silent)
|
||||||
|
{
|
||||||
|
struct inode * inode;
|
||||||
|
struct dentry * root;
|
||||||
|
|
||||||
|
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||||
|
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||||
|
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||||
|
sb->s_magic = BTRFS_SUPER_MAGIC;
|
||||||
|
sb->s_op = &btrfs_ops;
|
||||||
|
sb->s_time_gran = 1;
|
||||||
|
inode = btrfs_get_inode(sb, S_IFDIR | 0755, 0);
|
||||||
|
if (!inode)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
root = d_alloc_root(inode);
|
||||||
|
if (!root) {
|
||||||
|
iput(inode);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
sb->s_root = root;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btrfs_get_sb(struct file_system_type *fs_type,
|
||||||
|
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
|
||||||
|
{
|
||||||
|
return get_sb_bdev(fs_type, flags, dev_name, data,
|
||||||
|
btrfs_fill_super, mnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct file_system_type btrfs_fs_type = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "btrfs",
|
||||||
|
.get_sb = btrfs_get_sb,
|
||||||
|
.kill_sb = kill_block_super,
|
||||||
|
.fs_flags = FS_REQUIRES_DEV,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init init_btrfs_fs(void)
|
||||||
|
{
|
||||||
|
printk("btrfs loaded!\n");
|
||||||
|
return register_filesystem(&btrfs_fs_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit exit_btrfs_fs(void)
|
||||||
|
{
|
||||||
|
unregister_filesystem(&btrfs_fs_type);
|
||||||
|
printk("btrfs unloaded\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(init_btrfs_fs)
|
||||||
|
module_exit(exit_btrfs_fs)
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
Loading…
Add table
Reference in a new issue