forked from Minki/linux
d4b961576d
Previously, the bmap codes of nilfs used three types of function tables. The abuse of indirect function calls decreased source readability and suffered many indirect jumps which would confuse branch prediction of processors. This eliminates one type of the function tables, nilfs_bmap_ptr_operations, which was used to dispatch low level pointer operations of the nilfs bmap. This adds a new integer variable "b_ptr_type" to nilfs_bmap struct, and uses the value to select the pointer operations. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
2258 lines
59 KiB
C
2258 lines
59 KiB
C
/*
|
|
* btree.c - NILFS B-tree.
|
|
*
|
|
* Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
|
|
*
|
|
* 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 of the License, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Written by Koji Sato <koji@osrg.net>.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/pagevec.h>
|
|
#include "nilfs.h"
|
|
#include "page.h"
|
|
#include "btnode.h"
|
|
#include "btree.h"
|
|
#include "alloc.h"
|
|
|
|
/**
|
|
* struct nilfs_btree_path - A path on which B-tree operations are executed
|
|
* @bp_bh: buffer head of node block
|
|
* @bp_sib_bh: buffer head of sibling node block
|
|
* @bp_index: index of child node
|
|
* @bp_oldreq: ptr end request for old ptr
|
|
* @bp_newreq: ptr alloc request for new ptr
|
|
* @bp_op: rebalance operation
|
|
*/
|
|
struct nilfs_btree_path {
|
|
struct buffer_head *bp_bh;
|
|
struct buffer_head *bp_sib_bh;
|
|
int bp_index;
|
|
union nilfs_bmap_ptr_req bp_oldreq;
|
|
union nilfs_bmap_ptr_req bp_newreq;
|
|
struct nilfs_btnode_chkey_ctxt bp_ctxt;
|
|
void (*bp_op)(struct nilfs_btree *, struct nilfs_btree_path *,
|
|
int, __u64 *, __u64 *);
|
|
};
|
|
|
|
/*
|
|
* B-tree path operations
|
|
*/
|
|
|
|
static struct kmem_cache *nilfs_btree_path_cache;
|
|
|
|
int __init nilfs_btree_path_cache_init(void)
|
|
{
|
|
nilfs_btree_path_cache =
|
|
kmem_cache_create("nilfs2_btree_path_cache",
|
|
sizeof(struct nilfs_btree_path) *
|
|
NILFS_BTREE_LEVEL_MAX, 0, 0, NULL);
|
|
return (nilfs_btree_path_cache != NULL) ? 0 : -ENOMEM;
|
|
}
|
|
|
|
void nilfs_btree_path_cache_destroy(void)
|
|
{
|
|
kmem_cache_destroy(nilfs_btree_path_cache);
|
|
}
|
|
|
|
static inline struct nilfs_btree_path *
|
|
nilfs_btree_alloc_path(const struct nilfs_btree *btree)
|
|
{
|
|
return (struct nilfs_btree_path *)
|
|
kmem_cache_alloc(nilfs_btree_path_cache, GFP_NOFS);
|
|
}
|
|
|
|
static inline void nilfs_btree_free_path(const struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path)
|
|
{
|
|
kmem_cache_free(nilfs_btree_path_cache, path);
|
|
}
|
|
|
|
static void nilfs_btree_init_path(const struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path)
|
|
{
|
|
int level;
|
|
|
|
for (level = NILFS_BTREE_LEVEL_DATA;
|
|
level < NILFS_BTREE_LEVEL_MAX;
|
|
level++) {
|
|
path[level].bp_bh = NULL;
|
|
path[level].bp_sib_bh = NULL;
|
|
path[level].bp_index = 0;
|
|
path[level].bp_oldreq.bpr_ptr = NILFS_BMAP_INVALID_PTR;
|
|
path[level].bp_newreq.bpr_ptr = NILFS_BMAP_INVALID_PTR;
|
|
path[level].bp_op = NULL;
|
|
}
|
|
}
|
|
|
|
static void nilfs_btree_clear_path(const struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path)
|
|
{
|
|
int level;
|
|
|
|
for (level = NILFS_BTREE_LEVEL_DATA;
|
|
level < NILFS_BTREE_LEVEL_MAX;
|
|
level++) {
|
|
if (path[level].bp_bh != NULL) {
|
|
brelse(path[level].bp_bh);
|
|
path[level].bp_bh = NULL;
|
|
}
|
|
/* sib_bh is released or deleted by prepare or commit
|
|
* operations. */
|
|
path[level].bp_sib_bh = NULL;
|
|
path[level].bp_index = 0;
|
|
path[level].bp_oldreq.bpr_ptr = NILFS_BMAP_INVALID_PTR;
|
|
path[level].bp_newreq.bpr_ptr = NILFS_BMAP_INVALID_PTR;
|
|
path[level].bp_op = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* B-tree node operations
|
|
*/
|
|
static int nilfs_btree_get_block(const struct nilfs_btree *btree, __u64 ptr,
|
|
struct buffer_head **bhp)
|
|
{
|
|
struct address_space *btnc =
|
|
&NILFS_BMAP_I((struct nilfs_bmap *)btree)->i_btnode_cache;
|
|
return nilfs_btnode_get(btnc, ptr, 0, bhp, 0);
|
|
}
|
|
|
|
static int nilfs_btree_get_new_block(const struct nilfs_btree *btree,
|
|
__u64 ptr, struct buffer_head **bhp)
|
|
{
|
|
struct address_space *btnc =
|
|
&NILFS_BMAP_I((struct nilfs_bmap *)btree)->i_btnode_cache;
|
|
int ret;
|
|
|
|
ret = nilfs_btnode_get(btnc, ptr, 0, bhp, 1);
|
|
if (!ret)
|
|
set_buffer_nilfs_volatile(*bhp);
|
|
return ret;
|
|
}
|
|
|
|
static inline int
|
|
nilfs_btree_node_get_flags(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node)
|
|
{
|
|
return node->bn_flags;
|
|
}
|
|
|
|
static inline void
|
|
nilfs_btree_node_set_flags(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *node,
|
|
int flags)
|
|
{
|
|
node->bn_flags = flags;
|
|
}
|
|
|
|
static inline int nilfs_btree_node_root(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node)
|
|
{
|
|
return nilfs_btree_node_get_flags(btree, node) & NILFS_BTREE_NODE_ROOT;
|
|
}
|
|
|
|
static inline int
|
|
nilfs_btree_node_get_level(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node)
|
|
{
|
|
return node->bn_level;
|
|
}
|
|
|
|
static inline void
|
|
nilfs_btree_node_set_level(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *node,
|
|
int level)
|
|
{
|
|
node->bn_level = level;
|
|
}
|
|
|
|
static inline int
|
|
nilfs_btree_node_get_nchildren(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node)
|
|
{
|
|
return le16_to_cpu(node->bn_nchildren);
|
|
}
|
|
|
|
static inline void
|
|
nilfs_btree_node_set_nchildren(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *node,
|
|
int nchildren)
|
|
{
|
|
node->bn_nchildren = cpu_to_le16(nchildren);
|
|
}
|
|
|
|
static inline int
|
|
nilfs_btree_node_size(const struct nilfs_btree *btree)
|
|
{
|
|
return 1 << btree->bt_bmap.b_inode->i_blkbits;
|
|
}
|
|
|
|
static inline int
|
|
nilfs_btree_node_nchildren_min(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node)
|
|
{
|
|
return nilfs_btree_node_root(btree, node) ?
|
|
NILFS_BTREE_ROOT_NCHILDREN_MIN :
|
|
NILFS_BTREE_NODE_NCHILDREN_MIN(nilfs_btree_node_size(btree));
|
|
}
|
|
|
|
static inline int
|
|
nilfs_btree_node_nchildren_max(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node)
|
|
{
|
|
return nilfs_btree_node_root(btree, node) ?
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX :
|
|
NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree));
|
|
}
|
|
|
|
static inline __le64 *
|
|
nilfs_btree_node_dkeys(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node)
|
|
{
|
|
return (__le64 *)((char *)(node + 1) +
|
|
(nilfs_btree_node_root(btree, node) ?
|
|
0 : NILFS_BTREE_NODE_EXTRA_PAD_SIZE));
|
|
}
|
|
|
|
static inline __le64 *
|
|
nilfs_btree_node_dptrs(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node)
|
|
{
|
|
return (__le64 *)(nilfs_btree_node_dkeys(btree, node) +
|
|
nilfs_btree_node_nchildren_max(btree, node));
|
|
}
|
|
|
|
static inline __u64
|
|
nilfs_btree_node_get_key(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node, int index)
|
|
{
|
|
return nilfs_bmap_dkey_to_key(*(nilfs_btree_node_dkeys(btree, node) +
|
|
index));
|
|
}
|
|
|
|
static inline void
|
|
nilfs_btree_node_set_key(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *node, int index, __u64 key)
|
|
{
|
|
*(nilfs_btree_node_dkeys(btree, node) + index) =
|
|
nilfs_bmap_key_to_dkey(key);
|
|
}
|
|
|
|
static inline __u64
|
|
nilfs_btree_node_get_ptr(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node,
|
|
int index)
|
|
{
|
|
return nilfs_bmap_dptr_to_ptr(*(nilfs_btree_node_dptrs(btree, node) +
|
|
index));
|
|
}
|
|
|
|
static inline void
|
|
nilfs_btree_node_set_ptr(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *node,
|
|
int index,
|
|
__u64 ptr)
|
|
{
|
|
*(nilfs_btree_node_dptrs(btree, node) + index) =
|
|
nilfs_bmap_ptr_to_dptr(ptr);
|
|
}
|
|
|
|
static void nilfs_btree_node_init(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *node,
|
|
int flags, int level, int nchildren,
|
|
const __u64 *keys, const __u64 *ptrs)
|
|
{
|
|
__le64 *dkeys;
|
|
__le64 *dptrs;
|
|
int i;
|
|
|
|
nilfs_btree_node_set_flags(btree, node, flags);
|
|
nilfs_btree_node_set_level(btree, node, level);
|
|
nilfs_btree_node_set_nchildren(btree, node, nchildren);
|
|
|
|
dkeys = nilfs_btree_node_dkeys(btree, node);
|
|
dptrs = nilfs_btree_node_dptrs(btree, node);
|
|
for (i = 0; i < nchildren; i++) {
|
|
dkeys[i] = nilfs_bmap_key_to_dkey(keys[i]);
|
|
dptrs[i] = nilfs_bmap_ptr_to_dptr(ptrs[i]);
|
|
}
|
|
}
|
|
|
|
/* Assume the buffer heads corresponding to left and right are locked. */
|
|
static void nilfs_btree_node_move_left(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *left,
|
|
struct nilfs_btree_node *right,
|
|
int n)
|
|
{
|
|
__le64 *ldkeys, *rdkeys;
|
|
__le64 *ldptrs, *rdptrs;
|
|
int lnchildren, rnchildren;
|
|
|
|
ldkeys = nilfs_btree_node_dkeys(btree, left);
|
|
ldptrs = nilfs_btree_node_dptrs(btree, left);
|
|
lnchildren = nilfs_btree_node_get_nchildren(btree, left);
|
|
|
|
rdkeys = nilfs_btree_node_dkeys(btree, right);
|
|
rdptrs = nilfs_btree_node_dptrs(btree, right);
|
|
rnchildren = nilfs_btree_node_get_nchildren(btree, right);
|
|
|
|
memcpy(ldkeys + lnchildren, rdkeys, n * sizeof(*rdkeys));
|
|
memcpy(ldptrs + lnchildren, rdptrs, n * sizeof(*rdptrs));
|
|
memmove(rdkeys, rdkeys + n, (rnchildren - n) * sizeof(*rdkeys));
|
|
memmove(rdptrs, rdptrs + n, (rnchildren - n) * sizeof(*rdptrs));
|
|
|
|
lnchildren += n;
|
|
rnchildren -= n;
|
|
nilfs_btree_node_set_nchildren(btree, left, lnchildren);
|
|
nilfs_btree_node_set_nchildren(btree, right, rnchildren);
|
|
}
|
|
|
|
/* Assume that the buffer heads corresponding to left and right are locked. */
|
|
static void nilfs_btree_node_move_right(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *left,
|
|
struct nilfs_btree_node *right,
|
|
int n)
|
|
{
|
|
__le64 *ldkeys, *rdkeys;
|
|
__le64 *ldptrs, *rdptrs;
|
|
int lnchildren, rnchildren;
|
|
|
|
ldkeys = nilfs_btree_node_dkeys(btree, left);
|
|
ldptrs = nilfs_btree_node_dptrs(btree, left);
|
|
lnchildren = nilfs_btree_node_get_nchildren(btree, left);
|
|
|
|
rdkeys = nilfs_btree_node_dkeys(btree, right);
|
|
rdptrs = nilfs_btree_node_dptrs(btree, right);
|
|
rnchildren = nilfs_btree_node_get_nchildren(btree, right);
|
|
|
|
memmove(rdkeys + n, rdkeys, rnchildren * sizeof(*rdkeys));
|
|
memmove(rdptrs + n, rdptrs, rnchildren * sizeof(*rdptrs));
|
|
memcpy(rdkeys, ldkeys + lnchildren - n, n * sizeof(*rdkeys));
|
|
memcpy(rdptrs, ldptrs + lnchildren - n, n * sizeof(*rdptrs));
|
|
|
|
lnchildren -= n;
|
|
rnchildren += n;
|
|
nilfs_btree_node_set_nchildren(btree, left, lnchildren);
|
|
nilfs_btree_node_set_nchildren(btree, right, rnchildren);
|
|
}
|
|
|
|
/* Assume that the buffer head corresponding to node is locked. */
|
|
static void nilfs_btree_node_insert(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *node,
|
|
__u64 key, __u64 ptr, int index)
|
|
{
|
|
__le64 *dkeys;
|
|
__le64 *dptrs;
|
|
int nchildren;
|
|
|
|
dkeys = nilfs_btree_node_dkeys(btree, node);
|
|
dptrs = nilfs_btree_node_dptrs(btree, node);
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
if (index < nchildren) {
|
|
memmove(dkeys + index + 1, dkeys + index,
|
|
(nchildren - index) * sizeof(*dkeys));
|
|
memmove(dptrs + index + 1, dptrs + index,
|
|
(nchildren - index) * sizeof(*dptrs));
|
|
}
|
|
dkeys[index] = nilfs_bmap_key_to_dkey(key);
|
|
dptrs[index] = nilfs_bmap_ptr_to_dptr(ptr);
|
|
nchildren++;
|
|
nilfs_btree_node_set_nchildren(btree, node, nchildren);
|
|
}
|
|
|
|
/* Assume that the buffer head corresponding to node is locked. */
|
|
static void nilfs_btree_node_delete(struct nilfs_btree *btree,
|
|
struct nilfs_btree_node *node,
|
|
__u64 *keyp, __u64 *ptrp, int index)
|
|
{
|
|
__u64 key;
|
|
__u64 ptr;
|
|
__le64 *dkeys;
|
|
__le64 *dptrs;
|
|
int nchildren;
|
|
|
|
dkeys = nilfs_btree_node_dkeys(btree, node);
|
|
dptrs = nilfs_btree_node_dptrs(btree, node);
|
|
key = nilfs_bmap_dkey_to_key(dkeys[index]);
|
|
ptr = nilfs_bmap_dptr_to_ptr(dptrs[index]);
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
if (keyp != NULL)
|
|
*keyp = key;
|
|
if (ptrp != NULL)
|
|
*ptrp = ptr;
|
|
|
|
if (index < nchildren - 1) {
|
|
memmove(dkeys + index, dkeys + index + 1,
|
|
(nchildren - index - 1) * sizeof(*dkeys));
|
|
memmove(dptrs + index, dptrs + index + 1,
|
|
(nchildren - index - 1) * sizeof(*dptrs));
|
|
}
|
|
nchildren--;
|
|
nilfs_btree_node_set_nchildren(btree, node, nchildren);
|
|
}
|
|
|
|
static int nilfs_btree_node_lookup(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_node *node,
|
|
__u64 key, int *indexp)
|
|
{
|
|
__u64 nkey;
|
|
int index, low, high, s;
|
|
|
|
/* binary search */
|
|
low = 0;
|
|
high = nilfs_btree_node_get_nchildren(btree, node) - 1;
|
|
index = 0;
|
|
s = 0;
|
|
while (low <= high) {
|
|
index = (low + high) / 2;
|
|
nkey = nilfs_btree_node_get_key(btree, node, index);
|
|
if (nkey == key) {
|
|
s = 0;
|
|
goto out;
|
|
} else if (nkey < key) {
|
|
low = index + 1;
|
|
s = -1;
|
|
} else {
|
|
high = index - 1;
|
|
s = 1;
|
|
}
|
|
}
|
|
|
|
/* adjust index */
|
|
if (nilfs_btree_node_get_level(btree, node) >
|
|
NILFS_BTREE_LEVEL_NODE_MIN) {
|
|
if ((s > 0) && (index > 0))
|
|
index--;
|
|
} else if (s < 0)
|
|
index++;
|
|
|
|
out:
|
|
*indexp = index;
|
|
|
|
return s == 0;
|
|
}
|
|
|
|
static inline struct nilfs_btree_node *
|
|
nilfs_btree_get_root(const struct nilfs_btree *btree)
|
|
{
|
|
return (struct nilfs_btree_node *)btree->bt_bmap.b_u.u_data;
|
|
}
|
|
|
|
static inline struct nilfs_btree_node *
|
|
nilfs_btree_get_nonroot_node(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_path *path,
|
|
int level)
|
|
{
|
|
return (struct nilfs_btree_node *)path[level].bp_bh->b_data;
|
|
}
|
|
|
|
static inline struct nilfs_btree_node *
|
|
nilfs_btree_get_sib_node(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_path *path,
|
|
int level)
|
|
{
|
|
return (struct nilfs_btree_node *)path[level].bp_sib_bh->b_data;
|
|
}
|
|
|
|
static inline int nilfs_btree_height(const struct nilfs_btree *btree)
|
|
{
|
|
return nilfs_btree_node_get_level(btree, nilfs_btree_get_root(btree))
|
|
+ 1;
|
|
}
|
|
|
|
static inline struct nilfs_btree_node *
|
|
nilfs_btree_get_node(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_path *path,
|
|
int level)
|
|
{
|
|
return (level == nilfs_btree_height(btree) - 1) ?
|
|
nilfs_btree_get_root(btree) :
|
|
nilfs_btree_get_nonroot_node(btree, path, level);
|
|
}
|
|
|
|
static int nilfs_btree_do_lookup(const struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
__u64 key, __u64 *ptrp, int minlevel)
|
|
{
|
|
struct nilfs_btree_node *node;
|
|
__u64 ptr;
|
|
int level, index, found, ret;
|
|
|
|
node = nilfs_btree_get_root(btree);
|
|
level = nilfs_btree_node_get_level(btree, node);
|
|
if ((level < minlevel) ||
|
|
(nilfs_btree_node_get_nchildren(btree, node) <= 0))
|
|
return -ENOENT;
|
|
|
|
found = nilfs_btree_node_lookup(btree, node, key, &index);
|
|
ptr = nilfs_btree_node_get_ptr(btree, node, index);
|
|
path[level].bp_bh = NULL;
|
|
path[level].bp_index = index;
|
|
|
|
for (level--; level >= minlevel; level--) {
|
|
ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh);
|
|
if (ret < 0)
|
|
return ret;
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
BUG_ON(level != nilfs_btree_node_get_level(btree, node));
|
|
if (!found)
|
|
found = nilfs_btree_node_lookup(btree, node, key,
|
|
&index);
|
|
else
|
|
index = 0;
|
|
if (index < nilfs_btree_node_nchildren_max(btree, node))
|
|
ptr = nilfs_btree_node_get_ptr(btree, node, index);
|
|
else {
|
|
WARN_ON(found || level != NILFS_BTREE_LEVEL_NODE_MIN);
|
|
/* insert */
|
|
ptr = NILFS_BMAP_INVALID_PTR;
|
|
}
|
|
path[level].bp_index = index;
|
|
}
|
|
if (!found)
|
|
return -ENOENT;
|
|
|
|
if (ptrp != NULL)
|
|
*ptrp = ptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_btree_do_lookup_last(const struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
__u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node;
|
|
__u64 ptr;
|
|
int index, level, ret;
|
|
|
|
node = nilfs_btree_get_root(btree);
|
|
index = nilfs_btree_node_get_nchildren(btree, node) - 1;
|
|
if (index < 0)
|
|
return -ENOENT;
|
|
level = nilfs_btree_node_get_level(btree, node);
|
|
ptr = nilfs_btree_node_get_ptr(btree, node, index);
|
|
path[level].bp_bh = NULL;
|
|
path[level].bp_index = index;
|
|
|
|
for (level--; level > 0; level--) {
|
|
ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh);
|
|
if (ret < 0)
|
|
return ret;
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
BUG_ON(level != nilfs_btree_node_get_level(btree, node));
|
|
index = nilfs_btree_node_get_nchildren(btree, node) - 1;
|
|
ptr = nilfs_btree_node_get_ptr(btree, node, index);
|
|
path[level].bp_index = index;
|
|
}
|
|
|
|
if (keyp != NULL)
|
|
*keyp = nilfs_btree_node_get_key(btree, node, index);
|
|
if (ptrp != NULL)
|
|
*ptrp = ptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_btree_lookup(const struct nilfs_bmap *bmap,
|
|
__u64 key, int level, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_path *path;
|
|
__u64 ptr;
|
|
int ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
path = nilfs_btree_alloc_path(btree);
|
|
if (path == NULL)
|
|
return -ENOMEM;
|
|
nilfs_btree_init_path(btree, path);
|
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level);
|
|
|
|
if (ptrp != NULL)
|
|
*ptrp = ptr;
|
|
|
|
nilfs_btree_clear_path(btree, path);
|
|
nilfs_btree_free_path(btree, path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void nilfs_btree_promote_key(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 key)
|
|
{
|
|
if (level < nilfs_btree_height(btree) - 1) {
|
|
do {
|
|
lock_buffer(path[level].bp_bh);
|
|
nilfs_btree_node_set_key(
|
|
btree,
|
|
nilfs_btree_get_nonroot_node(
|
|
btree, path, level),
|
|
path[level].bp_index, key);
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_bh);
|
|
} while ((path[level].bp_index == 0) &&
|
|
(++level < nilfs_btree_height(btree) - 1));
|
|
}
|
|
|
|
/* root */
|
|
if (level == nilfs_btree_height(btree) - 1) {
|
|
nilfs_btree_node_set_key(btree,
|
|
nilfs_btree_get_root(btree),
|
|
path[level].bp_index, key);
|
|
}
|
|
}
|
|
|
|
static void nilfs_btree_do_insert(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node;
|
|
|
|
if (level < nilfs_btree_height(btree) - 1) {
|
|
lock_buffer(path[level].bp_bh);
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
nilfs_btree_node_insert(btree, node, *keyp, *ptrp,
|
|
path[level].bp_index);
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_bh);
|
|
|
|
if (path[level].bp_index == 0)
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
|
nilfs_btree_node_get_key(
|
|
btree, node, 0));
|
|
} else {
|
|
node = nilfs_btree_get_root(btree);
|
|
nilfs_btree_node_insert(btree, node, *keyp, *ptrp,
|
|
path[level].bp_index);
|
|
}
|
|
}
|
|
|
|
static void nilfs_btree_carry_left(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node, *left;
|
|
int nchildren, lnchildren, n, move;
|
|
|
|
lock_buffer(path[level].bp_bh);
|
|
lock_buffer(path[level].bp_sib_bh);
|
|
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
left = nilfs_btree_get_sib_node(btree, path, level);
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
lnchildren = nilfs_btree_node_get_nchildren(btree, left);
|
|
move = 0;
|
|
|
|
n = (nchildren + lnchildren + 1) / 2 - lnchildren;
|
|
if (n > path[level].bp_index) {
|
|
/* move insert point */
|
|
n--;
|
|
move = 1;
|
|
}
|
|
|
|
nilfs_btree_node_move_left(btree, left, node, n);
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
|
|
|
|
unlock_buffer(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_sib_bh);
|
|
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
|
nilfs_btree_node_get_key(btree, node, 0));
|
|
|
|
if (move) {
|
|
brelse(path[level].bp_bh);
|
|
path[level].bp_bh = path[level].bp_sib_bh;
|
|
path[level].bp_sib_bh = NULL;
|
|
path[level].bp_index += lnchildren;
|
|
path[level + 1].bp_index--;
|
|
} else {
|
|
brelse(path[level].bp_sib_bh);
|
|
path[level].bp_sib_bh = NULL;
|
|
path[level].bp_index -= n;
|
|
}
|
|
|
|
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
|
|
}
|
|
|
|
static void nilfs_btree_carry_right(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node, *right;
|
|
int nchildren, rnchildren, n, move;
|
|
|
|
lock_buffer(path[level].bp_bh);
|
|
lock_buffer(path[level].bp_sib_bh);
|
|
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
right = nilfs_btree_get_sib_node(btree, path, level);
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
rnchildren = nilfs_btree_node_get_nchildren(btree, right);
|
|
move = 0;
|
|
|
|
n = (nchildren + rnchildren + 1) / 2 - rnchildren;
|
|
if (n > nchildren - path[level].bp_index) {
|
|
/* move insert point */
|
|
n--;
|
|
move = 1;
|
|
}
|
|
|
|
nilfs_btree_node_move_right(btree, node, right, n);
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
|
|
|
|
unlock_buffer(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_sib_bh);
|
|
|
|
path[level + 1].bp_index++;
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
|
nilfs_btree_node_get_key(btree, right, 0));
|
|
path[level + 1].bp_index--;
|
|
|
|
if (move) {
|
|
brelse(path[level].bp_bh);
|
|
path[level].bp_bh = path[level].bp_sib_bh;
|
|
path[level].bp_sib_bh = NULL;
|
|
path[level].bp_index -=
|
|
nilfs_btree_node_get_nchildren(btree, node);
|
|
path[level + 1].bp_index++;
|
|
} else {
|
|
brelse(path[level].bp_sib_bh);
|
|
path[level].bp_sib_bh = NULL;
|
|
}
|
|
|
|
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
|
|
}
|
|
|
|
static void nilfs_btree_split(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node, *right;
|
|
__u64 newkey;
|
|
__u64 newptr;
|
|
int nchildren, n, move;
|
|
|
|
lock_buffer(path[level].bp_bh);
|
|
lock_buffer(path[level].bp_sib_bh);
|
|
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
right = nilfs_btree_get_sib_node(btree, path, level);
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
move = 0;
|
|
|
|
n = (nchildren + 1) / 2;
|
|
if (n > nchildren - path[level].bp_index) {
|
|
n--;
|
|
move = 1;
|
|
}
|
|
|
|
nilfs_btree_node_move_right(btree, node, right, n);
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
|
|
|
|
unlock_buffer(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_sib_bh);
|
|
|
|
newkey = nilfs_btree_node_get_key(btree, right, 0);
|
|
newptr = path[level].bp_newreq.bpr_ptr;
|
|
|
|
if (move) {
|
|
path[level].bp_index -=
|
|
nilfs_btree_node_get_nchildren(btree, node);
|
|
nilfs_btree_node_insert(btree, right, *keyp, *ptrp,
|
|
path[level].bp_index);
|
|
|
|
*keyp = nilfs_btree_node_get_key(btree, right, 0);
|
|
*ptrp = path[level].bp_newreq.bpr_ptr;
|
|
|
|
brelse(path[level].bp_bh);
|
|
path[level].bp_bh = path[level].bp_sib_bh;
|
|
path[level].bp_sib_bh = NULL;
|
|
} else {
|
|
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
|
|
|
|
*keyp = nilfs_btree_node_get_key(btree, right, 0);
|
|
*ptrp = path[level].bp_newreq.bpr_ptr;
|
|
|
|
brelse(path[level].bp_sib_bh);
|
|
path[level].bp_sib_bh = NULL;
|
|
}
|
|
|
|
path[level + 1].bp_index++;
|
|
}
|
|
|
|
static void nilfs_btree_grow(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *root, *child;
|
|
int n;
|
|
|
|
lock_buffer(path[level].bp_sib_bh);
|
|
|
|
root = nilfs_btree_get_root(btree);
|
|
child = nilfs_btree_get_sib_node(btree, path, level);
|
|
|
|
n = nilfs_btree_node_get_nchildren(btree, root);
|
|
|
|
nilfs_btree_node_move_right(btree, root, child, n);
|
|
nilfs_btree_node_set_level(btree, root, level + 1);
|
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
|
|
|
|
unlock_buffer(path[level].bp_sib_bh);
|
|
|
|
path[level].bp_bh = path[level].bp_sib_bh;
|
|
path[level].bp_sib_bh = NULL;
|
|
|
|
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
|
|
|
|
*keyp = nilfs_btree_node_get_key(btree, child, 0);
|
|
*ptrp = path[level].bp_newreq.bpr_ptr;
|
|
}
|
|
|
|
static __u64 nilfs_btree_find_near(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_path *path)
|
|
{
|
|
struct nilfs_btree_node *node;
|
|
int level;
|
|
|
|
if (path == NULL)
|
|
return NILFS_BMAP_INVALID_PTR;
|
|
|
|
/* left sibling */
|
|
level = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
if (path[level].bp_index > 0) {
|
|
node = nilfs_btree_get_node(btree, path, level);
|
|
return nilfs_btree_node_get_ptr(btree, node,
|
|
path[level].bp_index - 1);
|
|
}
|
|
|
|
/* parent */
|
|
level = NILFS_BTREE_LEVEL_NODE_MIN + 1;
|
|
if (level <= nilfs_btree_height(btree) - 1) {
|
|
node = nilfs_btree_get_node(btree, path, level);
|
|
return nilfs_btree_node_get_ptr(btree, node,
|
|
path[level].bp_index);
|
|
}
|
|
|
|
return NILFS_BMAP_INVALID_PTR;
|
|
}
|
|
|
|
static __u64 nilfs_btree_find_target_v(const struct nilfs_btree *btree,
|
|
const struct nilfs_btree_path *path,
|
|
__u64 key)
|
|
{
|
|
__u64 ptr;
|
|
|
|
ptr = nilfs_bmap_find_target_seq(&btree->bt_bmap, key);
|
|
if (ptr != NILFS_BMAP_INVALID_PTR)
|
|
/* sequential access */
|
|
return ptr;
|
|
else {
|
|
ptr = nilfs_btree_find_near(btree, path);
|
|
if (ptr != NILFS_BMAP_INVALID_PTR)
|
|
/* near */
|
|
return ptr;
|
|
}
|
|
/* block group */
|
|
return nilfs_bmap_find_target_in_group(&btree->bt_bmap);
|
|
}
|
|
|
|
static void nilfs_btree_set_target_v(struct nilfs_btree *btree, __u64 key,
|
|
__u64 ptr)
|
|
{
|
|
btree->bt_bmap.b_last_allocated_key = key;
|
|
btree->bt_bmap.b_last_allocated_ptr = ptr;
|
|
}
|
|
|
|
static int nilfs_btree_prepare_insert(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int *levelp, __u64 key, __u64 ptr,
|
|
struct nilfs_bmap_stats *stats)
|
|
{
|
|
struct buffer_head *bh;
|
|
struct nilfs_btree_node *node, *parent, *sib;
|
|
__u64 sibptr;
|
|
int pindex, level, ret;
|
|
|
|
stats->bs_nblocks = 0;
|
|
level = NILFS_BTREE_LEVEL_DATA;
|
|
|
|
/* allocate a new ptr for data block */
|
|
if (btree->bt_ops->btop_find_target != NULL)
|
|
path[level].bp_newreq.bpr_ptr =
|
|
btree->bt_ops->btop_find_target(btree, path, key);
|
|
|
|
ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap,
|
|
&path[level].bp_newreq);
|
|
if (ret < 0)
|
|
goto err_out_data;
|
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
level < nilfs_btree_height(btree) - 1;
|
|
level++) {
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
if (nilfs_btree_node_get_nchildren(btree, node) <
|
|
nilfs_btree_node_nchildren_max(btree, node)) {
|
|
path[level].bp_op = nilfs_btree_do_insert;
|
|
stats->bs_nblocks++;
|
|
goto out;
|
|
}
|
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1);
|
|
pindex = path[level + 1].bp_index;
|
|
|
|
/* left sibling */
|
|
if (pindex > 0) {
|
|
sibptr = nilfs_btree_node_get_ptr(btree, parent,
|
|
pindex - 1);
|
|
ret = nilfs_btree_get_block(btree, sibptr, &bh);
|
|
if (ret < 0)
|
|
goto err_out_child_node;
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
|
if (nilfs_btree_node_get_nchildren(btree, sib) <
|
|
nilfs_btree_node_nchildren_max(btree, sib)) {
|
|
path[level].bp_sib_bh = bh;
|
|
path[level].bp_op = nilfs_btree_carry_left;
|
|
stats->bs_nblocks++;
|
|
goto out;
|
|
} else
|
|
brelse(bh);
|
|
}
|
|
|
|
/* right sibling */
|
|
if (pindex <
|
|
nilfs_btree_node_get_nchildren(btree, parent) - 1) {
|
|
sibptr = nilfs_btree_node_get_ptr(btree, parent,
|
|
pindex + 1);
|
|
ret = nilfs_btree_get_block(btree, sibptr, &bh);
|
|
if (ret < 0)
|
|
goto err_out_child_node;
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
|
if (nilfs_btree_node_get_nchildren(btree, sib) <
|
|
nilfs_btree_node_nchildren_max(btree, sib)) {
|
|
path[level].bp_sib_bh = bh;
|
|
path[level].bp_op = nilfs_btree_carry_right;
|
|
stats->bs_nblocks++;
|
|
goto out;
|
|
} else
|
|
brelse(bh);
|
|
}
|
|
|
|
/* split */
|
|
path[level].bp_newreq.bpr_ptr =
|
|
path[level - 1].bp_newreq.bpr_ptr + 1;
|
|
ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap,
|
|
&path[level].bp_newreq);
|
|
if (ret < 0)
|
|
goto err_out_child_node;
|
|
ret = nilfs_btree_get_new_block(btree,
|
|
path[level].bp_newreq.bpr_ptr,
|
|
&bh);
|
|
if (ret < 0)
|
|
goto err_out_curr_node;
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
lock_buffer(bh);
|
|
nilfs_btree_node_init(btree,
|
|
(struct nilfs_btree_node *)bh->b_data,
|
|
0, level, 0, NULL, NULL);
|
|
unlock_buffer(bh);
|
|
path[level].bp_sib_bh = bh;
|
|
path[level].bp_op = nilfs_btree_split;
|
|
}
|
|
|
|
/* root */
|
|
node = nilfs_btree_get_root(btree);
|
|
if (nilfs_btree_node_get_nchildren(btree, node) <
|
|
nilfs_btree_node_nchildren_max(btree, node)) {
|
|
path[level].bp_op = nilfs_btree_do_insert;
|
|
stats->bs_nblocks++;
|
|
goto out;
|
|
}
|
|
|
|
/* grow */
|
|
path[level].bp_newreq.bpr_ptr = path[level - 1].bp_newreq.bpr_ptr + 1;
|
|
ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap,
|
|
&path[level].bp_newreq);
|
|
if (ret < 0)
|
|
goto err_out_child_node;
|
|
ret = nilfs_btree_get_new_block(btree, path[level].bp_newreq.bpr_ptr,
|
|
&bh);
|
|
if (ret < 0)
|
|
goto err_out_curr_node;
|
|
|
|
lock_buffer(bh);
|
|
nilfs_btree_node_init(btree, (struct nilfs_btree_node *)bh->b_data,
|
|
0, level, 0, NULL, NULL);
|
|
unlock_buffer(bh);
|
|
path[level].bp_sib_bh = bh;
|
|
path[level].bp_op = nilfs_btree_grow;
|
|
|
|
level++;
|
|
path[level].bp_op = nilfs_btree_do_insert;
|
|
|
|
/* a newly-created node block and a data block are added */
|
|
stats->bs_nblocks += 2;
|
|
|
|
/* success */
|
|
out:
|
|
*levelp = level;
|
|
return ret;
|
|
|
|
/* error */
|
|
err_out_curr_node:
|
|
nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, &path[level].bp_newreq);
|
|
err_out_child_node:
|
|
for (level--; level > NILFS_BTREE_LEVEL_DATA; level--) {
|
|
nilfs_btnode_delete(path[level].bp_sib_bh);
|
|
nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap,
|
|
&path[level].bp_newreq);
|
|
|
|
}
|
|
|
|
nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, &path[level].bp_newreq);
|
|
err_out_data:
|
|
*levelp = level;
|
|
stats->bs_nblocks = 0;
|
|
return ret;
|
|
}
|
|
|
|
static void nilfs_btree_commit_insert(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int maxlevel, __u64 key, __u64 ptr)
|
|
{
|
|
int level;
|
|
|
|
set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr));
|
|
ptr = path[NILFS_BTREE_LEVEL_DATA].bp_newreq.bpr_ptr;
|
|
if (btree->bt_ops->btop_set_target != NULL)
|
|
btree->bt_ops->btop_set_target(btree, key, ptr);
|
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) {
|
|
nilfs_bmap_commit_alloc_ptr(&btree->bt_bmap,
|
|
&path[level - 1].bp_newreq);
|
|
path[level].bp_op(btree, path, level, &key, &ptr);
|
|
}
|
|
|
|
if (!nilfs_bmap_dirty(&btree->bt_bmap))
|
|
nilfs_bmap_set_dirty(&btree->bt_bmap);
|
|
}
|
|
|
|
static int nilfs_btree_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr)
|
|
{
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_path *path;
|
|
struct nilfs_bmap_stats stats;
|
|
int level, ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
path = nilfs_btree_alloc_path(btree);
|
|
if (path == NULL)
|
|
return -ENOMEM;
|
|
nilfs_btree_init_path(btree, path);
|
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, NULL,
|
|
NILFS_BTREE_LEVEL_NODE_MIN);
|
|
if (ret != -ENOENT) {
|
|
if (ret == 0)
|
|
ret = -EEXIST;
|
|
goto out;
|
|
}
|
|
|
|
ret = nilfs_btree_prepare_insert(btree, path, &level, key, ptr, &stats);
|
|
if (ret < 0)
|
|
goto out;
|
|
nilfs_btree_commit_insert(btree, path, level, key, ptr);
|
|
nilfs_bmap_add_blocks(bmap, stats.bs_nblocks);
|
|
|
|
out:
|
|
nilfs_btree_clear_path(btree, path);
|
|
nilfs_btree_free_path(btree, path);
|
|
return ret;
|
|
}
|
|
|
|
static void nilfs_btree_do_delete(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node;
|
|
|
|
if (level < nilfs_btree_height(btree) - 1) {
|
|
lock_buffer(path[level].bp_bh);
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
nilfs_btree_node_delete(btree, node, keyp, ptrp,
|
|
path[level].bp_index);
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_bh);
|
|
if (path[level].bp_index == 0)
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
|
nilfs_btree_node_get_key(btree, node, 0));
|
|
} else {
|
|
node = nilfs_btree_get_root(btree);
|
|
nilfs_btree_node_delete(btree, node, keyp, ptrp,
|
|
path[level].bp_index);
|
|
}
|
|
}
|
|
|
|
static void nilfs_btree_borrow_left(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node, *left;
|
|
int nchildren, lnchildren, n;
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
lock_buffer(path[level].bp_bh);
|
|
lock_buffer(path[level].bp_sib_bh);
|
|
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
left = nilfs_btree_get_sib_node(btree, path, level);
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
lnchildren = nilfs_btree_node_get_nchildren(btree, left);
|
|
|
|
n = (nchildren + lnchildren) / 2 - nchildren;
|
|
|
|
nilfs_btree_node_move_right(btree, left, node, n);
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
|
|
|
|
unlock_buffer(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_sib_bh);
|
|
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
|
nilfs_btree_node_get_key(btree, node, 0));
|
|
|
|
brelse(path[level].bp_sib_bh);
|
|
path[level].bp_sib_bh = NULL;
|
|
path[level].bp_index += n;
|
|
}
|
|
|
|
static void nilfs_btree_borrow_right(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node, *right;
|
|
int nchildren, rnchildren, n;
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
lock_buffer(path[level].bp_bh);
|
|
lock_buffer(path[level].bp_sib_bh);
|
|
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
right = nilfs_btree_get_sib_node(btree, path, level);
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
rnchildren = nilfs_btree_node_get_nchildren(btree, right);
|
|
|
|
n = (nchildren + rnchildren) / 2 - nchildren;
|
|
|
|
nilfs_btree_node_move_left(btree, node, right, n);
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
|
|
|
|
unlock_buffer(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_sib_bh);
|
|
|
|
path[level + 1].bp_index++;
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
|
nilfs_btree_node_get_key(btree, right, 0));
|
|
path[level + 1].bp_index--;
|
|
|
|
brelse(path[level].bp_sib_bh);
|
|
path[level].bp_sib_bh = NULL;
|
|
}
|
|
|
|
static void nilfs_btree_concat_left(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node, *left;
|
|
int n;
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
lock_buffer(path[level].bp_bh);
|
|
lock_buffer(path[level].bp_sib_bh);
|
|
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
left = nilfs_btree_get_sib_node(btree, path, level);
|
|
|
|
n = nilfs_btree_node_get_nchildren(btree, node);
|
|
|
|
nilfs_btree_node_move_left(btree, left, node, n);
|
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
|
|
|
|
unlock_buffer(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_sib_bh);
|
|
|
|
nilfs_btnode_delete(path[level].bp_bh);
|
|
path[level].bp_bh = path[level].bp_sib_bh;
|
|
path[level].bp_sib_bh = NULL;
|
|
path[level].bp_index += nilfs_btree_node_get_nchildren(btree, left);
|
|
}
|
|
|
|
static void nilfs_btree_concat_right(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *node, *right;
|
|
int n;
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
lock_buffer(path[level].bp_bh);
|
|
lock_buffer(path[level].bp_sib_bh);
|
|
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
right = nilfs_btree_get_sib_node(btree, path, level);
|
|
|
|
n = nilfs_btree_node_get_nchildren(btree, right);
|
|
|
|
nilfs_btree_node_move_left(btree, node, right, n);
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
|
|
unlock_buffer(path[level].bp_bh);
|
|
unlock_buffer(path[level].bp_sib_bh);
|
|
|
|
nilfs_btnode_delete(path[level].bp_sib_bh);
|
|
path[level].bp_sib_bh = NULL;
|
|
path[level + 1].bp_index++;
|
|
}
|
|
|
|
static void nilfs_btree_shrink(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
{
|
|
struct nilfs_btree_node *root, *child;
|
|
int n;
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
lock_buffer(path[level].bp_bh);
|
|
root = nilfs_btree_get_root(btree);
|
|
child = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
|
|
nilfs_btree_node_delete(btree, root, NULL, NULL, 0);
|
|
nilfs_btree_node_set_level(btree, root, level);
|
|
n = nilfs_btree_node_get_nchildren(btree, child);
|
|
nilfs_btree_node_move_left(btree, root, child, n);
|
|
unlock_buffer(path[level].bp_bh);
|
|
|
|
nilfs_btnode_delete(path[level].bp_bh);
|
|
path[level].bp_bh = NULL;
|
|
}
|
|
|
|
|
|
static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int *levelp,
|
|
struct nilfs_bmap_stats *stats)
|
|
{
|
|
struct buffer_head *bh;
|
|
struct nilfs_btree_node *node, *parent, *sib;
|
|
__u64 sibptr;
|
|
int pindex, level, ret;
|
|
|
|
ret = 0;
|
|
stats->bs_nblocks = 0;
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
level < nilfs_btree_height(btree) - 1;
|
|
level++) {
|
|
node = nilfs_btree_get_nonroot_node(btree, path, level);
|
|
path[level].bp_oldreq.bpr_ptr =
|
|
nilfs_btree_node_get_ptr(btree, node,
|
|
path[level].bp_index);
|
|
ret = nilfs_bmap_prepare_end_ptr(&btree->bt_bmap,
|
|
&path[level].bp_oldreq);
|
|
if (ret < 0)
|
|
goto err_out_child_node;
|
|
|
|
if (nilfs_btree_node_get_nchildren(btree, node) >
|
|
nilfs_btree_node_nchildren_min(btree, node)) {
|
|
path[level].bp_op = nilfs_btree_do_delete;
|
|
stats->bs_nblocks++;
|
|
goto out;
|
|
}
|
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1);
|
|
pindex = path[level + 1].bp_index;
|
|
|
|
if (pindex > 0) {
|
|
/* left sibling */
|
|
sibptr = nilfs_btree_node_get_ptr(btree, parent,
|
|
pindex - 1);
|
|
ret = nilfs_btree_get_block(btree, sibptr, &bh);
|
|
if (ret < 0)
|
|
goto err_out_curr_node;
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
|
if (nilfs_btree_node_get_nchildren(btree, sib) >
|
|
nilfs_btree_node_nchildren_min(btree, sib)) {
|
|
path[level].bp_sib_bh = bh;
|
|
path[level].bp_op = nilfs_btree_borrow_left;
|
|
stats->bs_nblocks++;
|
|
goto out;
|
|
} else {
|
|
path[level].bp_sib_bh = bh;
|
|
path[level].bp_op = nilfs_btree_concat_left;
|
|
stats->bs_nblocks++;
|
|
/* continue; */
|
|
}
|
|
} else if (pindex <
|
|
nilfs_btree_node_get_nchildren(btree, parent) - 1) {
|
|
/* right sibling */
|
|
sibptr = nilfs_btree_node_get_ptr(btree, parent,
|
|
pindex + 1);
|
|
ret = nilfs_btree_get_block(btree, sibptr, &bh);
|
|
if (ret < 0)
|
|
goto err_out_curr_node;
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
|
if (nilfs_btree_node_get_nchildren(btree, sib) >
|
|
nilfs_btree_node_nchildren_min(btree, sib)) {
|
|
path[level].bp_sib_bh = bh;
|
|
path[level].bp_op = nilfs_btree_borrow_right;
|
|
stats->bs_nblocks++;
|
|
goto out;
|
|
} else {
|
|
path[level].bp_sib_bh = bh;
|
|
path[level].bp_op = nilfs_btree_concat_right;
|
|
stats->bs_nblocks++;
|
|
/* continue; */
|
|
}
|
|
} else {
|
|
/* no siblings */
|
|
/* the only child of the root node */
|
|
WARN_ON(level != nilfs_btree_height(btree) - 2);
|
|
if (nilfs_btree_node_get_nchildren(btree, node) - 1 <=
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX) {
|
|
path[level].bp_op = nilfs_btree_shrink;
|
|
stats->bs_nblocks += 2;
|
|
} else {
|
|
path[level].bp_op = nilfs_btree_do_delete;
|
|
stats->bs_nblocks++;
|
|
}
|
|
|
|
goto out;
|
|
|
|
}
|
|
}
|
|
|
|
node = nilfs_btree_get_root(btree);
|
|
path[level].bp_oldreq.bpr_ptr =
|
|
nilfs_btree_node_get_ptr(btree, node, path[level].bp_index);
|
|
|
|
ret = nilfs_bmap_prepare_end_ptr(&btree->bt_bmap,
|
|
&path[level].bp_oldreq);
|
|
if (ret < 0)
|
|
goto err_out_child_node;
|
|
|
|
/* child of the root node is deleted */
|
|
path[level].bp_op = nilfs_btree_do_delete;
|
|
stats->bs_nblocks++;
|
|
|
|
/* success */
|
|
out:
|
|
*levelp = level;
|
|
return ret;
|
|
|
|
/* error */
|
|
err_out_curr_node:
|
|
nilfs_bmap_abort_end_ptr(&btree->bt_bmap, &path[level].bp_oldreq);
|
|
err_out_child_node:
|
|
for (level--; level >= NILFS_BTREE_LEVEL_NODE_MIN; level--) {
|
|
brelse(path[level].bp_sib_bh);
|
|
nilfs_bmap_abort_end_ptr(&btree->bt_bmap,
|
|
&path[level].bp_oldreq);
|
|
}
|
|
*levelp = level;
|
|
stats->bs_nblocks = 0;
|
|
return ret;
|
|
}
|
|
|
|
static void nilfs_btree_commit_delete(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int maxlevel)
|
|
{
|
|
int level;
|
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) {
|
|
nilfs_bmap_commit_end_ptr(&btree->bt_bmap,
|
|
&path[level].bp_oldreq);
|
|
path[level].bp_op(btree, path, level, NULL, NULL);
|
|
}
|
|
|
|
if (!nilfs_bmap_dirty(&btree->bt_bmap))
|
|
nilfs_bmap_set_dirty(&btree->bt_bmap);
|
|
}
|
|
|
|
static int nilfs_btree_delete(struct nilfs_bmap *bmap, __u64 key)
|
|
|
|
{
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_path *path;
|
|
struct nilfs_bmap_stats stats;
|
|
int level, ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
path = nilfs_btree_alloc_path(btree);
|
|
if (path == NULL)
|
|
return -ENOMEM;
|
|
nilfs_btree_init_path(btree, path);
|
|
ret = nilfs_btree_do_lookup(btree, path, key, NULL,
|
|
NILFS_BTREE_LEVEL_NODE_MIN);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
ret = nilfs_btree_prepare_delete(btree, path, &level, &stats);
|
|
if (ret < 0)
|
|
goto out;
|
|
nilfs_btree_commit_delete(btree, path, level);
|
|
nilfs_bmap_sub_blocks(bmap, stats.bs_nblocks);
|
|
|
|
out:
|
|
nilfs_btree_clear_path(btree, path);
|
|
nilfs_btree_free_path(btree, path);
|
|
return ret;
|
|
}
|
|
|
|
static int nilfs_btree_last_key(const struct nilfs_bmap *bmap, __u64 *keyp)
|
|
{
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_path *path;
|
|
int ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
path = nilfs_btree_alloc_path(btree);
|
|
if (path == NULL)
|
|
return -ENOMEM;
|
|
nilfs_btree_init_path(btree, path);
|
|
|
|
ret = nilfs_btree_do_lookup_last(btree, path, keyp, NULL);
|
|
|
|
nilfs_btree_clear_path(btree, path);
|
|
nilfs_btree_free_path(btree, path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int nilfs_btree_check_delete(struct nilfs_bmap *bmap, __u64 key)
|
|
{
|
|
struct buffer_head *bh;
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_node *root, *node;
|
|
__u64 maxkey, nextmaxkey;
|
|
__u64 ptr;
|
|
int nchildren, ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
root = nilfs_btree_get_root(btree);
|
|
switch (nilfs_btree_height(btree)) {
|
|
case 2:
|
|
bh = NULL;
|
|
node = root;
|
|
break;
|
|
case 3:
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, root);
|
|
if (nchildren > 1)
|
|
return 0;
|
|
ptr = nilfs_btree_node_get_ptr(btree, root, nchildren - 1);
|
|
ret = nilfs_btree_get_block(btree, ptr, &bh);
|
|
if (ret < 0)
|
|
return ret;
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
maxkey = nilfs_btree_node_get_key(btree, node, nchildren - 1);
|
|
nextmaxkey = (nchildren > 1) ?
|
|
nilfs_btree_node_get_key(btree, node, nchildren - 2) : 0;
|
|
if (bh != NULL)
|
|
brelse(bh);
|
|
|
|
return (maxkey == key) && (nextmaxkey < NILFS_BMAP_LARGE_LOW);
|
|
}
|
|
|
|
static int nilfs_btree_gather_data(struct nilfs_bmap *bmap,
|
|
__u64 *keys, __u64 *ptrs, int nitems)
|
|
{
|
|
struct buffer_head *bh;
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_node *node, *root;
|
|
__le64 *dkeys;
|
|
__le64 *dptrs;
|
|
__u64 ptr;
|
|
int nchildren, i, ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
root = nilfs_btree_get_root(btree);
|
|
switch (nilfs_btree_height(btree)) {
|
|
case 2:
|
|
bh = NULL;
|
|
node = root;
|
|
break;
|
|
case 3:
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, root);
|
|
WARN_ON(nchildren > 1);
|
|
ptr = nilfs_btree_node_get_ptr(btree, root, nchildren - 1);
|
|
ret = nilfs_btree_get_block(btree, ptr, &bh);
|
|
if (ret < 0)
|
|
return ret;
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
|
break;
|
|
default:
|
|
node = NULL;
|
|
return -EINVAL;
|
|
}
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(btree, node);
|
|
if (nchildren < nitems)
|
|
nitems = nchildren;
|
|
dkeys = nilfs_btree_node_dkeys(btree, node);
|
|
dptrs = nilfs_btree_node_dptrs(btree, node);
|
|
for (i = 0; i < nitems; i++) {
|
|
keys[i] = nilfs_bmap_dkey_to_key(dkeys[i]);
|
|
ptrs[i] = nilfs_bmap_dptr_to_ptr(dptrs[i]);
|
|
}
|
|
|
|
if (bh != NULL)
|
|
brelse(bh);
|
|
|
|
return nitems;
|
|
}
|
|
|
|
static int
|
|
nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *bmap, __u64 key,
|
|
union nilfs_bmap_ptr_req *dreq,
|
|
union nilfs_bmap_ptr_req *nreq,
|
|
struct buffer_head **bhp,
|
|
struct nilfs_bmap_stats *stats)
|
|
{
|
|
struct buffer_head *bh;
|
|
struct nilfs_btree *btree;
|
|
int ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
stats->bs_nblocks = 0;
|
|
|
|
/* for data */
|
|
/* cannot find near ptr */
|
|
if (btree->bt_ops->btop_find_target != NULL)
|
|
dreq->bpr_ptr
|
|
= btree->bt_ops->btop_find_target(btree, NULL, key);
|
|
ret = nilfs_bmap_prepare_alloc_ptr(bmap, dreq);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
*bhp = NULL;
|
|
stats->bs_nblocks++;
|
|
if (nreq != NULL) {
|
|
nreq->bpr_ptr = dreq->bpr_ptr + 1;
|
|
ret = nilfs_bmap_prepare_alloc_ptr(bmap, nreq);
|
|
if (ret < 0)
|
|
goto err_out_dreq;
|
|
|
|
ret = nilfs_btree_get_new_block(btree, nreq->bpr_ptr, &bh);
|
|
if (ret < 0)
|
|
goto err_out_nreq;
|
|
|
|
*bhp = bh;
|
|
stats->bs_nblocks++;
|
|
}
|
|
|
|
/* success */
|
|
return 0;
|
|
|
|
/* error */
|
|
err_out_nreq:
|
|
nilfs_bmap_abort_alloc_ptr(bmap, nreq);
|
|
err_out_dreq:
|
|
nilfs_bmap_abort_alloc_ptr(bmap, dreq);
|
|
stats->bs_nblocks = 0;
|
|
return ret;
|
|
|
|
}
|
|
|
|
static void
|
|
nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *bmap,
|
|
__u64 key, __u64 ptr,
|
|
const __u64 *keys, const __u64 *ptrs,
|
|
int n,
|
|
union nilfs_bmap_ptr_req *dreq,
|
|
union nilfs_bmap_ptr_req *nreq,
|
|
struct buffer_head *bh)
|
|
{
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_node *node;
|
|
__u64 tmpptr;
|
|
|
|
/* free resources */
|
|
if (bmap->b_ops->bop_clear != NULL)
|
|
bmap->b_ops->bop_clear(bmap);
|
|
|
|
/* ptr must be a pointer to a buffer head. */
|
|
set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr));
|
|
|
|
/* convert and insert */
|
|
btree = (struct nilfs_btree *)bmap;
|
|
nilfs_btree_init(bmap);
|
|
if (nreq != NULL) {
|
|
nilfs_bmap_commit_alloc_ptr(bmap, dreq);
|
|
nilfs_bmap_commit_alloc_ptr(bmap, nreq);
|
|
|
|
/* create child node at level 1 */
|
|
lock_buffer(bh);
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
|
nilfs_btree_node_init(btree, node, 0, 1, n, keys, ptrs);
|
|
nilfs_btree_node_insert(btree, node,
|
|
key, dreq->bpr_ptr, n);
|
|
if (!buffer_dirty(bh))
|
|
nilfs_btnode_mark_dirty(bh);
|
|
if (!nilfs_bmap_dirty(bmap))
|
|
nilfs_bmap_set_dirty(bmap);
|
|
|
|
unlock_buffer(bh);
|
|
brelse(bh);
|
|
|
|
/* create root node at level 2 */
|
|
node = nilfs_btree_get_root(btree);
|
|
tmpptr = nreq->bpr_ptr;
|
|
nilfs_btree_node_init(btree, node, NILFS_BTREE_NODE_ROOT,
|
|
2, 1, &keys[0], &tmpptr);
|
|
} else {
|
|
nilfs_bmap_commit_alloc_ptr(bmap, dreq);
|
|
|
|
/* create root node at level 1 */
|
|
node = nilfs_btree_get_root(btree);
|
|
nilfs_btree_node_init(btree, node, NILFS_BTREE_NODE_ROOT,
|
|
1, n, keys, ptrs);
|
|
nilfs_btree_node_insert(btree, node,
|
|
key, dreq->bpr_ptr, n);
|
|
if (!nilfs_bmap_dirty(bmap))
|
|
nilfs_bmap_set_dirty(bmap);
|
|
}
|
|
|
|
if (btree->bt_ops->btop_set_target != NULL)
|
|
btree->bt_ops->btop_set_target(btree, key, dreq->bpr_ptr);
|
|
}
|
|
|
|
/**
|
|
* nilfs_btree_convert_and_insert -
|
|
* @bmap:
|
|
* @key:
|
|
* @ptr:
|
|
* @keys:
|
|
* @ptrs:
|
|
* @n:
|
|
*/
|
|
int nilfs_btree_convert_and_insert(struct nilfs_bmap *bmap,
|
|
__u64 key, __u64 ptr,
|
|
const __u64 *keys, const __u64 *ptrs, int n)
|
|
{
|
|
struct buffer_head *bh;
|
|
union nilfs_bmap_ptr_req dreq, nreq, *di, *ni;
|
|
struct nilfs_bmap_stats stats;
|
|
int ret;
|
|
|
|
if (n + 1 <= NILFS_BTREE_ROOT_NCHILDREN_MAX) {
|
|
di = &dreq;
|
|
ni = NULL;
|
|
} else if ((n + 1) <= NILFS_BTREE_NODE_NCHILDREN_MAX(
|
|
1 << bmap->b_inode->i_blkbits)) {
|
|
di = &dreq;
|
|
ni = &nreq;
|
|
} else {
|
|
di = NULL;
|
|
ni = NULL;
|
|
BUG();
|
|
}
|
|
|
|
ret = nilfs_btree_prepare_convert_and_insert(bmap, key, di, ni, &bh,
|
|
&stats);
|
|
if (ret < 0)
|
|
return ret;
|
|
nilfs_btree_commit_convert_and_insert(bmap, key, ptr, keys, ptrs, n,
|
|
di, ni, bh);
|
|
nilfs_bmap_add_blocks(bmap, stats.bs_nblocks);
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_btree_propagate_p(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level,
|
|
struct buffer_head *bh)
|
|
{
|
|
while ((++level < nilfs_btree_height(btree) - 1) &&
|
|
!buffer_dirty(path[level].bp_bh))
|
|
nilfs_btnode_mark_dirty(path[level].bp_bh);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_btree_prepare_update_v(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level)
|
|
{
|
|
struct nilfs_btree_node *parent;
|
|
int ret;
|
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1);
|
|
path[level].bp_oldreq.bpr_ptr =
|
|
nilfs_btree_node_get_ptr(btree, parent,
|
|
path[level + 1].bp_index);
|
|
path[level].bp_newreq.bpr_ptr = path[level].bp_oldreq.bpr_ptr + 1;
|
|
ret = nilfs_bmap_prepare_update_v(&btree->bt_bmap,
|
|
&path[level].bp_oldreq,
|
|
&path[level].bp_newreq);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (buffer_nilfs_node(path[level].bp_bh)) {
|
|
path[level].bp_ctxt.oldkey = path[level].bp_oldreq.bpr_ptr;
|
|
path[level].bp_ctxt.newkey = path[level].bp_newreq.bpr_ptr;
|
|
path[level].bp_ctxt.bh = path[level].bp_bh;
|
|
ret = nilfs_btnode_prepare_change_key(
|
|
&NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache,
|
|
&path[level].bp_ctxt);
|
|
if (ret < 0) {
|
|
nilfs_bmap_abort_update_v(&btree->bt_bmap,
|
|
&path[level].bp_oldreq,
|
|
&path[level].bp_newreq);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nilfs_btree_commit_update_v(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level)
|
|
{
|
|
struct nilfs_btree_node *parent;
|
|
|
|
nilfs_bmap_commit_update_v(&btree->bt_bmap,
|
|
&path[level].bp_oldreq,
|
|
&path[level].bp_newreq);
|
|
|
|
if (buffer_nilfs_node(path[level].bp_bh)) {
|
|
nilfs_btnode_commit_change_key(
|
|
&NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache,
|
|
&path[level].bp_ctxt);
|
|
path[level].bp_bh = path[level].bp_ctxt.bh;
|
|
}
|
|
set_buffer_nilfs_volatile(path[level].bp_bh);
|
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1);
|
|
nilfs_btree_node_set_ptr(btree, parent, path[level + 1].bp_index,
|
|
path[level].bp_newreq.bpr_ptr);
|
|
}
|
|
|
|
static void nilfs_btree_abort_update_v(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level)
|
|
{
|
|
nilfs_bmap_abort_update_v(&btree->bt_bmap,
|
|
&path[level].bp_oldreq,
|
|
&path[level].bp_newreq);
|
|
if (buffer_nilfs_node(path[level].bp_bh))
|
|
nilfs_btnode_abort_change_key(
|
|
&NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache,
|
|
&path[level].bp_ctxt);
|
|
}
|
|
|
|
static int nilfs_btree_prepare_propagate_v(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int minlevel,
|
|
int *maxlevelp)
|
|
{
|
|
int level, ret;
|
|
|
|
level = minlevel;
|
|
if (!buffer_nilfs_volatile(path[level].bp_bh)) {
|
|
ret = nilfs_btree_prepare_update_v(btree, path, level);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
while ((++level < nilfs_btree_height(btree) - 1) &&
|
|
!buffer_dirty(path[level].bp_bh)) {
|
|
|
|
WARN_ON(buffer_nilfs_volatile(path[level].bp_bh));
|
|
ret = nilfs_btree_prepare_update_v(btree, path, level);
|
|
if (ret < 0)
|
|
goto out;
|
|
}
|
|
|
|
/* success */
|
|
*maxlevelp = level - 1;
|
|
return 0;
|
|
|
|
/* error */
|
|
out:
|
|
while (--level > minlevel)
|
|
nilfs_btree_abort_update_v(btree, path, level);
|
|
if (!buffer_nilfs_volatile(path[level].bp_bh))
|
|
nilfs_btree_abort_update_v(btree, path, level);
|
|
return ret;
|
|
}
|
|
|
|
static void nilfs_btree_commit_propagate_v(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int minlevel,
|
|
int maxlevel,
|
|
struct buffer_head *bh)
|
|
{
|
|
int level;
|
|
|
|
if (!buffer_nilfs_volatile(path[minlevel].bp_bh))
|
|
nilfs_btree_commit_update_v(btree, path, minlevel);
|
|
|
|
for (level = minlevel + 1; level <= maxlevel; level++)
|
|
nilfs_btree_commit_update_v(btree, path, level);
|
|
}
|
|
|
|
static int nilfs_btree_propagate_v(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level,
|
|
struct buffer_head *bh)
|
|
{
|
|
int maxlevel, ret;
|
|
struct nilfs_btree_node *parent;
|
|
__u64 ptr;
|
|
|
|
get_bh(bh);
|
|
path[level].bp_bh = bh;
|
|
ret = nilfs_btree_prepare_propagate_v(btree, path, level, &maxlevel);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
if (buffer_nilfs_volatile(path[level].bp_bh)) {
|
|
parent = nilfs_btree_get_node(btree, path, level + 1);
|
|
ptr = nilfs_btree_node_get_ptr(btree, parent,
|
|
path[level + 1].bp_index);
|
|
ret = nilfs_bmap_mark_dirty(&btree->bt_bmap, ptr);
|
|
if (ret < 0)
|
|
goto out;
|
|
}
|
|
|
|
nilfs_btree_commit_propagate_v(btree, path, level, maxlevel, bh);
|
|
|
|
out:
|
|
brelse(path[level].bp_bh);
|
|
path[level].bp_bh = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static int nilfs_btree_propagate(const struct nilfs_bmap *bmap,
|
|
struct buffer_head *bh)
|
|
{
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_path *path;
|
|
struct nilfs_btree_node *node;
|
|
__u64 key;
|
|
int level, ret;
|
|
|
|
WARN_ON(!buffer_dirty(bh));
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
path = nilfs_btree_alloc_path(btree);
|
|
if (path == NULL)
|
|
return -ENOMEM;
|
|
nilfs_btree_init_path(btree, path);
|
|
|
|
if (buffer_nilfs_node(bh)) {
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
|
key = nilfs_btree_node_get_key(btree, node, 0);
|
|
level = nilfs_btree_node_get_level(btree, node);
|
|
} else {
|
|
key = nilfs_bmap_data_get_key(bmap, bh);
|
|
level = NILFS_BTREE_LEVEL_DATA;
|
|
}
|
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1);
|
|
if (ret < 0) {
|
|
if (unlikely(ret == -ENOENT))
|
|
printk(KERN_CRIT "%s: key = %llu, level == %d\n",
|
|
__func__, (unsigned long long)key, level);
|
|
goto out;
|
|
}
|
|
|
|
ret = btree->bt_ops->btop_propagate(btree, path, level, bh);
|
|
|
|
out:
|
|
nilfs_btree_clear_path(btree, path);
|
|
nilfs_btree_free_path(btree, path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int nilfs_btree_propagate_gc(const struct nilfs_bmap *bmap,
|
|
struct buffer_head *bh)
|
|
{
|
|
return nilfs_bmap_mark_dirty(bmap, bh->b_blocknr);
|
|
}
|
|
|
|
static void nilfs_btree_add_dirty_buffer(struct nilfs_btree *btree,
|
|
struct list_head *lists,
|
|
struct buffer_head *bh)
|
|
{
|
|
struct list_head *head;
|
|
struct buffer_head *cbh;
|
|
struct nilfs_btree_node *node, *cnode;
|
|
__u64 key, ckey;
|
|
int level;
|
|
|
|
get_bh(bh);
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
|
key = nilfs_btree_node_get_key(btree, node, 0);
|
|
level = nilfs_btree_node_get_level(btree, node);
|
|
list_for_each(head, &lists[level]) {
|
|
cbh = list_entry(head, struct buffer_head, b_assoc_buffers);
|
|
cnode = (struct nilfs_btree_node *)cbh->b_data;
|
|
ckey = nilfs_btree_node_get_key(btree, cnode, 0);
|
|
if (key < ckey)
|
|
break;
|
|
}
|
|
list_add_tail(&bh->b_assoc_buffers, head);
|
|
}
|
|
|
|
static void nilfs_btree_lookup_dirty_buffers(struct nilfs_bmap *bmap,
|
|
struct list_head *listp)
|
|
{
|
|
struct nilfs_btree *btree = (struct nilfs_btree *)bmap;
|
|
struct address_space *btcache = &NILFS_BMAP_I(bmap)->i_btnode_cache;
|
|
struct list_head lists[NILFS_BTREE_LEVEL_MAX];
|
|
struct pagevec pvec;
|
|
struct buffer_head *bh, *head;
|
|
pgoff_t index = 0;
|
|
int level, i;
|
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
level < NILFS_BTREE_LEVEL_MAX;
|
|
level++)
|
|
INIT_LIST_HEAD(&lists[level]);
|
|
|
|
pagevec_init(&pvec, 0);
|
|
|
|
while (pagevec_lookup_tag(&pvec, btcache, &index, PAGECACHE_TAG_DIRTY,
|
|
PAGEVEC_SIZE)) {
|
|
for (i = 0; i < pagevec_count(&pvec); i++) {
|
|
bh = head = page_buffers(pvec.pages[i]);
|
|
do {
|
|
if (buffer_dirty(bh))
|
|
nilfs_btree_add_dirty_buffer(btree,
|
|
lists, bh);
|
|
} while ((bh = bh->b_this_page) != head);
|
|
}
|
|
pagevec_release(&pvec);
|
|
cond_resched();
|
|
}
|
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
level < NILFS_BTREE_LEVEL_MAX;
|
|
level++)
|
|
list_splice(&lists[level], listp->prev);
|
|
}
|
|
|
|
static int nilfs_btree_assign_p(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level,
|
|
struct buffer_head **bh,
|
|
sector_t blocknr,
|
|
union nilfs_binfo *binfo)
|
|
{
|
|
struct nilfs_btree_node *parent;
|
|
__u64 key;
|
|
__u64 ptr;
|
|
int ret;
|
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1);
|
|
ptr = nilfs_btree_node_get_ptr(btree, parent,
|
|
path[level + 1].bp_index);
|
|
if (buffer_nilfs_node(*bh)) {
|
|
path[level].bp_ctxt.oldkey = ptr;
|
|
path[level].bp_ctxt.newkey = blocknr;
|
|
path[level].bp_ctxt.bh = *bh;
|
|
ret = nilfs_btnode_prepare_change_key(
|
|
&NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache,
|
|
&path[level].bp_ctxt);
|
|
if (ret < 0)
|
|
return ret;
|
|
nilfs_btnode_commit_change_key(
|
|
&NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache,
|
|
&path[level].bp_ctxt);
|
|
*bh = path[level].bp_ctxt.bh;
|
|
}
|
|
|
|
nilfs_btree_node_set_ptr(btree, parent,
|
|
path[level + 1].bp_index, blocknr);
|
|
|
|
key = nilfs_btree_node_get_key(btree, parent,
|
|
path[level + 1].bp_index);
|
|
/* on-disk format */
|
|
binfo->bi_dat.bi_blkoff = nilfs_bmap_key_to_dkey(key);
|
|
binfo->bi_dat.bi_level = level;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_btree_assign_v(struct nilfs_btree *btree,
|
|
struct nilfs_btree_path *path,
|
|
int level,
|
|
struct buffer_head **bh,
|
|
sector_t blocknr,
|
|
union nilfs_binfo *binfo)
|
|
{
|
|
struct nilfs_btree_node *parent;
|
|
__u64 key;
|
|
__u64 ptr;
|
|
union nilfs_bmap_ptr_req req;
|
|
int ret;
|
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1);
|
|
ptr = nilfs_btree_node_get_ptr(btree, parent,
|
|
path[level + 1].bp_index);
|
|
req.bpr_ptr = ptr;
|
|
ret = nilfs_bmap_start_v(&btree->bt_bmap, &req, blocknr);
|
|
if (unlikely(ret < 0))
|
|
return ret;
|
|
|
|
key = nilfs_btree_node_get_key(btree, parent,
|
|
path[level + 1].bp_index);
|
|
/* on-disk format */
|
|
binfo->bi_v.bi_vblocknr = nilfs_bmap_ptr_to_dptr(ptr);
|
|
binfo->bi_v.bi_blkoff = nilfs_bmap_key_to_dkey(key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_btree_assign(struct nilfs_bmap *bmap,
|
|
struct buffer_head **bh,
|
|
sector_t blocknr,
|
|
union nilfs_binfo *binfo)
|
|
{
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_path *path;
|
|
struct nilfs_btree_node *node;
|
|
__u64 key;
|
|
int level, ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
path = nilfs_btree_alloc_path(btree);
|
|
if (path == NULL)
|
|
return -ENOMEM;
|
|
nilfs_btree_init_path(btree, path);
|
|
|
|
if (buffer_nilfs_node(*bh)) {
|
|
node = (struct nilfs_btree_node *)(*bh)->b_data;
|
|
key = nilfs_btree_node_get_key(btree, node, 0);
|
|
level = nilfs_btree_node_get_level(btree, node);
|
|
} else {
|
|
key = nilfs_bmap_data_get_key(bmap, *bh);
|
|
level = NILFS_BTREE_LEVEL_DATA;
|
|
}
|
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1);
|
|
if (ret < 0) {
|
|
WARN_ON(ret == -ENOENT);
|
|
goto out;
|
|
}
|
|
|
|
ret = btree->bt_ops->btop_assign(btree, path, level, bh,
|
|
blocknr, binfo);
|
|
|
|
out:
|
|
nilfs_btree_clear_path(btree, path);
|
|
nilfs_btree_free_path(btree, path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int nilfs_btree_assign_gc(struct nilfs_bmap *bmap,
|
|
struct buffer_head **bh,
|
|
sector_t blocknr,
|
|
union nilfs_binfo *binfo)
|
|
{
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_node *node;
|
|
__u64 key;
|
|
int ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
ret = nilfs_bmap_move_v(bmap, (*bh)->b_blocknr, blocknr);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (buffer_nilfs_node(*bh)) {
|
|
node = (struct nilfs_btree_node *)(*bh)->b_data;
|
|
key = nilfs_btree_node_get_key(btree, node, 0);
|
|
} else
|
|
key = nilfs_bmap_data_get_key(bmap, *bh);
|
|
|
|
/* on-disk format */
|
|
binfo->bi_v.bi_vblocknr = cpu_to_le64((*bh)->b_blocknr);
|
|
binfo->bi_v.bi_blkoff = nilfs_bmap_key_to_dkey(key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_btree_mark(struct nilfs_bmap *bmap, __u64 key, int level)
|
|
{
|
|
struct buffer_head *bh;
|
|
struct nilfs_btree *btree;
|
|
struct nilfs_btree_path *path;
|
|
__u64 ptr;
|
|
int ret;
|
|
|
|
btree = (struct nilfs_btree *)bmap;
|
|
path = nilfs_btree_alloc_path(btree);
|
|
if (path == NULL)
|
|
return -ENOMEM;
|
|
nilfs_btree_init_path(btree, path);
|
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level + 1);
|
|
if (ret < 0) {
|
|
WARN_ON(ret == -ENOENT);
|
|
goto out;
|
|
}
|
|
ret = nilfs_btree_get_block(btree, ptr, &bh);
|
|
if (ret < 0) {
|
|
WARN_ON(ret == -ENOENT);
|
|
goto out;
|
|
}
|
|
|
|
if (!buffer_dirty(bh))
|
|
nilfs_btnode_mark_dirty(bh);
|
|
brelse(bh);
|
|
if (!nilfs_bmap_dirty(&btree->bt_bmap))
|
|
nilfs_bmap_set_dirty(&btree->bt_bmap);
|
|
|
|
out:
|
|
nilfs_btree_clear_path(btree, path);
|
|
nilfs_btree_free_path(btree, path);
|
|
return ret;
|
|
}
|
|
|
|
static const struct nilfs_bmap_operations nilfs_btree_ops = {
|
|
.bop_lookup = nilfs_btree_lookup,
|
|
.bop_insert = nilfs_btree_insert,
|
|
.bop_delete = nilfs_btree_delete,
|
|
.bop_clear = NULL,
|
|
|
|
.bop_propagate = nilfs_btree_propagate,
|
|
|
|
.bop_lookup_dirty_buffers = nilfs_btree_lookup_dirty_buffers,
|
|
|
|
.bop_assign = nilfs_btree_assign,
|
|
.bop_mark = nilfs_btree_mark,
|
|
|
|
.bop_last_key = nilfs_btree_last_key,
|
|
.bop_check_insert = NULL,
|
|
.bop_check_delete = nilfs_btree_check_delete,
|
|
.bop_gather_data = nilfs_btree_gather_data,
|
|
};
|
|
|
|
static const struct nilfs_bmap_operations nilfs_btree_ops_gc = {
|
|
.bop_lookup = NULL,
|
|
.bop_insert = NULL,
|
|
.bop_delete = NULL,
|
|
.bop_clear = NULL,
|
|
|
|
.bop_propagate = nilfs_btree_propagate_gc,
|
|
|
|
.bop_lookup_dirty_buffers = nilfs_btree_lookup_dirty_buffers,
|
|
|
|
.bop_assign = nilfs_btree_assign_gc,
|
|
.bop_mark = NULL,
|
|
|
|
.bop_last_key = NULL,
|
|
.bop_check_insert = NULL,
|
|
.bop_check_delete = NULL,
|
|
.bop_gather_data = NULL,
|
|
};
|
|
|
|
static const struct nilfs_btree_operations nilfs_btree_ops_v = {
|
|
.btop_find_target = nilfs_btree_find_target_v,
|
|
.btop_set_target = nilfs_btree_set_target_v,
|
|
.btop_propagate = nilfs_btree_propagate_v,
|
|
.btop_assign = nilfs_btree_assign_v,
|
|
};
|
|
|
|
static const struct nilfs_btree_operations nilfs_btree_ops_p = {
|
|
.btop_find_target = NULL,
|
|
.btop_set_target = NULL,
|
|
.btop_propagate = nilfs_btree_propagate_p,
|
|
.btop_assign = nilfs_btree_assign_p,
|
|
};
|
|
|
|
int nilfs_btree_init(struct nilfs_bmap *bmap)
|
|
{
|
|
struct nilfs_btree *btree = (struct nilfs_btree *)bmap;
|
|
|
|
bmap->b_ops = &nilfs_btree_ops;
|
|
switch (bmap->b_inode->i_ino) {
|
|
case NILFS_DAT_INO:
|
|
btree->bt_ops = &nilfs_btree_ops_p;
|
|
break;
|
|
default:
|
|
btree->bt_ops = &nilfs_btree_ops_v;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void nilfs_btree_init_gc(struct nilfs_bmap *bmap)
|
|
{
|
|
bmap->b_ops = &nilfs_btree_ops_gc;
|
|
}
|