2018-09-05 06:46:30 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2009-04-07 10:01:24 +08:00
|
|
|
/*
|
2021-11-09 10:35:01 +08:00
|
|
|
* NILFS B-tree.
|
2009-04-07 10:01:24 +08:00
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
|
|
|
|
*
|
2016-05-24 07:23:09 +08:00
|
|
|
* Written by Koji Sato.
|
2009-04-07 10:01:24 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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"
|
2009-05-25 01:47:14 +08:00
|
|
|
#include "dat.h"
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2015-02-28 07:51:56 +08:00
|
|
|
static void __nilfs_btree_init(struct nilfs_bmap *bmap);
|
|
|
|
|
2010-04-02 17:36:34 +08:00
|
|
|
static struct nilfs_btree_path *nilfs_btree_alloc_path(void)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-04-02 17:36:34 +08:00
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
int level = NILFS_BTREE_LEVEL_DATA;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-04-02 17:36:34 +08:00
|
|
|
path = kmem_cache_alloc(nilfs_btree_path_cache, GFP_NOFS);
|
|
|
|
if (path == NULL)
|
|
|
|
goto out;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-04-02 17:36:34 +08:00
|
|
|
for (; level < NILFS_BTREE_LEVEL_MAX; level++) {
|
2009-04-07 10:01:24 +08:00
|
|
|
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;
|
|
|
|
}
|
2010-04-02 17:36:34 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2010-04-02 18:35:00 +08:00
|
|
|
static void nilfs_btree_free_path(struct nilfs_btree_path *path)
|
2010-04-02 17:36:34 +08:00
|
|
|
{
|
2010-04-02 18:35:00 +08:00
|
|
|
int level = NILFS_BTREE_LEVEL_DATA;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-04-02 18:35:00 +08:00
|
|
|
for (; level < NILFS_BTREE_LEVEL_MAX; level++)
|
2009-08-15 00:54:59 +08:00
|
|
|
brelse(path[level].bp_bh);
|
2010-04-02 18:35:00 +08:00
|
|
|
|
|
|
|
kmem_cache_free(nilfs_btree_path_cache, path);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* B-tree node operations
|
|
|
|
*/
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_get_new_block(const struct nilfs_bmap *btree,
|
2009-05-22 00:07:13 +08:00
|
|
|
__u64 ptr, struct buffer_head **bhp)
|
|
|
|
{
|
2022-04-02 02:28:18 +08:00
|
|
|
struct inode *btnc_inode = NILFS_BMAP_I(btree)->i_assoc_inode;
|
|
|
|
struct address_space *btnc = btnc_inode->i_mapping;
|
2009-11-13 15:25:19 +08:00
|
|
|
struct buffer_head *bh;
|
2009-05-22 00:07:13 +08:00
|
|
|
|
2009-11-13 15:25:19 +08:00
|
|
|
bh = nilfs_btnode_create_block(btnc, ptr);
|
2024-07-25 13:20:07 +08:00
|
|
|
if (IS_ERR(bh))
|
|
|
|
return PTR_ERR(bh);
|
2009-11-13 15:25:19 +08:00
|
|
|
|
|
|
|
set_buffer_nilfs_volatile(bh);
|
|
|
|
*bhp = bh;
|
|
|
|
return 0;
|
2009-05-22 00:07:13 +08:00
|
|
|
}
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static int nilfs_btree_node_get_flags(const struct nilfs_btree_node *node)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
return node->bn_flags;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static void
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_flags(struct nilfs_btree_node *node, int flags)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
node->bn_flags = flags;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static int nilfs_btree_node_root(const struct nilfs_btree_node *node)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2009-08-15 00:14:10 +08:00
|
|
|
return nilfs_btree_node_get_flags(node) & NILFS_BTREE_NODE_ROOT;
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static int nilfs_btree_node_get_level(const struct nilfs_btree_node *node)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
return node->bn_level;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static void
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_level(struct nilfs_btree_node *node, int level)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
node->bn_level = level;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static int nilfs_btree_node_get_nchildren(const struct nilfs_btree_node *node)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
return le16_to_cpu(node->bn_nchildren);
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static void
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_nchildren(struct nilfs_btree_node *node, int nchildren)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
node->bn_nchildren = cpu_to_le16(nchildren);
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static int nilfs_btree_node_size(const struct nilfs_bmap *btree)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2017-02-28 06:28:38 +08:00
|
|
|
return i_blocksize(btree->b_inode);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
static int nilfs_btree_nchildren_per_block(const struct nilfs_bmap *btree)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-07-13 22:33:54 +08:00
|
|
|
return btree->b_nchildren_per_block;
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static __le64 *
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_dkeys(const struct nilfs_btree_node *node)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
return (__le64 *)((char *)(node + 1) +
|
2009-08-15 00:14:10 +08:00
|
|
|
(nilfs_btree_node_root(node) ?
|
2009-04-07 10:01:24 +08:00
|
|
|
0 : NILFS_BTREE_NODE_EXTRA_PAD_SIZE));
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static __le64 *
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_dptrs(const struct nilfs_btree_node *node, int ncmax)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-07-13 22:33:53 +08:00
|
|
|
return (__le64 *)(nilfs_btree_node_dkeys(node) + ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static __u64
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_key(const struct nilfs_btree_node *node, int index)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-07-10 15:50:41 +08:00
|
|
|
return le64_to_cpu(*(nilfs_btree_node_dkeys(node) + index));
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static void
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_key(struct nilfs_btree_node *node, int index, __u64 key)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-07-10 15:50:41 +08:00
|
|
|
*(nilfs_btree_node_dkeys(node) + index) = cpu_to_le64(key);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static __u64
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_get_ptr(const struct nilfs_btree_node *node, int index,
|
|
|
|
int ncmax)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-07-13 22:33:53 +08:00
|
|
|
return le64_to_cpu(*(nilfs_btree_node_dptrs(node, ncmax) + index));
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static void
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_set_ptr(struct nilfs_btree_node *node, int index, __u64 ptr,
|
|
|
|
int ncmax)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-07-13 22:33:53 +08:00
|
|
|
*(nilfs_btree_node_dptrs(node, ncmax) + index) = cpu_to_le64(ptr);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
static void nilfs_btree_node_init(struct nilfs_btree_node *node, int flags,
|
|
|
|
int level, int nchildren, int ncmax,
|
2009-04-07 10:01:24 +08:00
|
|
|
const __u64 *keys, const __u64 *ptrs)
|
|
|
|
{
|
|
|
|
__le64 *dkeys;
|
|
|
|
__le64 *dptrs;
|
|
|
|
int i;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_flags(node, flags);
|
|
|
|
nilfs_btree_node_set_level(node, level);
|
|
|
|
nilfs_btree_node_set_nchildren(node, nchildren);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
dkeys = nilfs_btree_node_dkeys(node);
|
2010-07-13 22:33:53 +08:00
|
|
|
dptrs = nilfs_btree_node_dptrs(node, ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
for (i = 0; i < nchildren; i++) {
|
2010-07-10 15:50:41 +08:00
|
|
|
dkeys[i] = cpu_to_le64(keys[i]);
|
|
|
|
dptrs[i] = cpu_to_le64(ptrs[i]);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assume the buffer heads corresponding to left and right are locked. */
|
2010-07-13 22:33:53 +08:00
|
|
|
static void nilfs_btree_node_move_left(struct nilfs_btree_node *left,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_node *right,
|
2010-07-13 22:33:53 +08:00
|
|
|
int n, int lncmax, int rncmax)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
__le64 *ldkeys, *rdkeys;
|
|
|
|
__le64 *ldptrs, *rdptrs;
|
|
|
|
int lnchildren, rnchildren;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
ldkeys = nilfs_btree_node_dkeys(left);
|
2010-07-13 22:33:53 +08:00
|
|
|
ldptrs = nilfs_btree_node_dptrs(left, lncmax);
|
2009-08-15 00:14:10 +08:00
|
|
|
lnchildren = nilfs_btree_node_get_nchildren(left);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
rdkeys = nilfs_btree_node_dkeys(right);
|
2010-07-13 22:33:53 +08:00
|
|
|
rdptrs = nilfs_btree_node_dptrs(right, rncmax);
|
2009-08-15 00:14:10 +08:00
|
|
|
rnchildren = nilfs_btree_node_get_nchildren(right);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
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;
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_nchildren(left, lnchildren);
|
|
|
|
nilfs_btree_node_set_nchildren(right, rnchildren);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Assume that the buffer heads corresponding to left and right are locked. */
|
2010-07-13 22:33:53 +08:00
|
|
|
static void nilfs_btree_node_move_right(struct nilfs_btree_node *left,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_node *right,
|
2010-07-13 22:33:53 +08:00
|
|
|
int n, int lncmax, int rncmax)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
__le64 *ldkeys, *rdkeys;
|
|
|
|
__le64 *ldptrs, *rdptrs;
|
|
|
|
int lnchildren, rnchildren;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
ldkeys = nilfs_btree_node_dkeys(left);
|
2010-07-13 22:33:53 +08:00
|
|
|
ldptrs = nilfs_btree_node_dptrs(left, lncmax);
|
2009-08-15 00:14:10 +08:00
|
|
|
lnchildren = nilfs_btree_node_get_nchildren(left);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
rdkeys = nilfs_btree_node_dkeys(right);
|
2010-07-13 22:33:53 +08:00
|
|
|
rdptrs = nilfs_btree_node_dptrs(right, rncmax);
|
2009-08-15 00:14:10 +08:00
|
|
|
rnchildren = nilfs_btree_node_get_nchildren(right);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
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;
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_nchildren(left, lnchildren);
|
|
|
|
nilfs_btree_node_set_nchildren(right, rnchildren);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Assume that the buffer head corresponding to node is locked. */
|
2010-07-13 22:33:53 +08:00
|
|
|
static void nilfs_btree_node_insert(struct nilfs_btree_node *node, int index,
|
|
|
|
__u64 key, __u64 ptr, int ncmax)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
__le64 *dkeys;
|
|
|
|
__le64 *dptrs;
|
|
|
|
int nchildren;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
dkeys = nilfs_btree_node_dkeys(node);
|
2010-07-13 22:33:53 +08:00
|
|
|
dptrs = nilfs_btree_node_dptrs(node, ncmax);
|
2009-08-15 00:14:10 +08:00
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (index < nchildren) {
|
|
|
|
memmove(dkeys + index + 1, dkeys + index,
|
|
|
|
(nchildren - index) * sizeof(*dkeys));
|
|
|
|
memmove(dptrs + index + 1, dptrs + index,
|
|
|
|
(nchildren - index) * sizeof(*dptrs));
|
|
|
|
}
|
2010-07-10 15:50:41 +08:00
|
|
|
dkeys[index] = cpu_to_le64(key);
|
|
|
|
dptrs[index] = cpu_to_le64(ptr);
|
2009-04-07 10:01:24 +08:00
|
|
|
nchildren++;
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_nchildren(node, nchildren);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Assume that the buffer head corresponding to node is locked. */
|
2010-07-13 22:33:53 +08:00
|
|
|
static void nilfs_btree_node_delete(struct nilfs_btree_node *node, int index,
|
|
|
|
__u64 *keyp, __u64 *ptrp, int ncmax)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
__u64 key;
|
|
|
|
__u64 ptr;
|
|
|
|
__le64 *dkeys;
|
|
|
|
__le64 *dptrs;
|
|
|
|
int nchildren;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
dkeys = nilfs_btree_node_dkeys(node);
|
2010-07-13 22:33:53 +08:00
|
|
|
dptrs = nilfs_btree_node_dptrs(node, ncmax);
|
2010-07-10 15:50:41 +08:00
|
|
|
key = le64_to_cpu(dkeys[index]);
|
|
|
|
ptr = le64_to_cpu(dptrs[index]);
|
2009-08-15 00:14:10 +08:00
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
2009-04-07 10:01:24 +08:00
|
|
|
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--;
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_nchildren(node, nchildren);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
static int nilfs_btree_node_lookup(const struct nilfs_btree_node *node,
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 key, int *indexp)
|
|
|
|
{
|
|
|
|
__u64 nkey;
|
|
|
|
int index, low, high, s;
|
|
|
|
|
|
|
|
/* binary search */
|
|
|
|
low = 0;
|
2009-08-15 00:14:10 +08:00
|
|
|
high = nilfs_btree_node_get_nchildren(node) - 1;
|
2009-04-07 10:01:24 +08:00
|
|
|
index = 0;
|
|
|
|
s = 0;
|
|
|
|
while (low <= high) {
|
|
|
|
index = (low + high) / 2;
|
2009-08-15 00:14:10 +08:00
|
|
|
nkey = nilfs_btree_node_get_key(node, index);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (nkey == key) {
|
|
|
|
s = 0;
|
|
|
|
goto out;
|
|
|
|
} else if (nkey < key) {
|
|
|
|
low = index + 1;
|
|
|
|
s = -1;
|
|
|
|
} else {
|
|
|
|
high = index - 1;
|
|
|
|
s = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* adjust index */
|
2009-08-15 00:14:10 +08:00
|
|
|
if (nilfs_btree_node_get_level(node) > NILFS_BTREE_LEVEL_NODE_MIN) {
|
|
|
|
if (s > 0 && index > 0)
|
2009-04-07 10:01:24 +08:00
|
|
|
index--;
|
|
|
|
} else if (s < 0)
|
|
|
|
index++;
|
|
|
|
|
|
|
|
out:
|
|
|
|
*indexp = index;
|
|
|
|
|
|
|
|
return s == 0;
|
|
|
|
}
|
|
|
|
|
2010-07-16 22:52:40 +08:00
|
|
|
/**
|
|
|
|
* nilfs_btree_node_broken - verify consistency of btree node
|
|
|
|
* @node: btree node block to be examined
|
|
|
|
* @size: node size (in bytes)
|
2016-08-03 05:05:10 +08:00
|
|
|
* @inode: host inode of btree
|
2010-07-16 22:52:40 +08:00
|
|
|
* @blocknr: block number
|
|
|
|
*
|
|
|
|
* Return Value: If node is broken, 1 is returned. Otherwise, 0 is returned.
|
|
|
|
*/
|
|
|
|
static int nilfs_btree_node_broken(const struct nilfs_btree_node *node,
|
2016-08-03 05:05:10 +08:00
|
|
|
size_t size, struct inode *inode,
|
|
|
|
sector_t blocknr)
|
2010-07-16 22:52:40 +08:00
|
|
|
{
|
|
|
|
int level, flags, nchildren;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
level = nilfs_btree_node_get_level(node);
|
|
|
|
flags = nilfs_btree_node_get_flags(node);
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
|
|
|
|
|
|
|
if (unlikely(level < NILFS_BTREE_LEVEL_NODE_MIN ||
|
|
|
|
level >= NILFS_BTREE_LEVEL_MAX ||
|
|
|
|
(flags & NILFS_BTREE_NODE_ROOT) ||
|
2024-09-04 16:13:08 +08:00
|
|
|
nchildren <= 0 ||
|
2010-07-16 22:52:40 +08:00
|
|
|
nchildren > NILFS_BTREE_NODE_NCHILDREN_MAX(size))) {
|
2020-08-12 09:35:49 +08:00
|
|
|
nilfs_crit(inode->i_sb,
|
|
|
|
"bad btree node (ino=%lu, blocknr=%llu): level = %d, flags = 0x%x, nchildren = %d",
|
|
|
|
inode->i_ino, (unsigned long long)blocknr, level,
|
|
|
|
flags, nchildren);
|
2010-07-16 22:52:40 +08:00
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-02-28 07:51:56 +08:00
|
|
|
/**
|
|
|
|
* nilfs_btree_root_broken - verify consistency of btree root node
|
|
|
|
* @node: btree root node to be examined
|
2016-08-03 05:05:10 +08:00
|
|
|
* @inode: host inode of btree
|
2015-02-28 07:51:56 +08:00
|
|
|
*
|
|
|
|
* Return Value: If node is broken, 1 is returned. Otherwise, 0 is returned.
|
|
|
|
*/
|
|
|
|
static int nilfs_btree_root_broken(const struct nilfs_btree_node *node,
|
2016-08-03 05:05:10 +08:00
|
|
|
struct inode *inode)
|
2015-02-28 07:51:56 +08:00
|
|
|
{
|
|
|
|
int level, flags, nchildren;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
level = nilfs_btree_node_get_level(node);
|
|
|
|
flags = nilfs_btree_node_get_flags(node);
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
|
|
|
|
|
|
|
if (unlikely(level < NILFS_BTREE_LEVEL_NODE_MIN ||
|
2015-05-06 07:24:00 +08:00
|
|
|
level >= NILFS_BTREE_LEVEL_MAX ||
|
2015-02-28 07:51:56 +08:00
|
|
|
nchildren < 0 ||
|
nilfs2: fix potential null-ptr-deref in nilfs_btree_insert()
[ Upstream commit 9403001ad65ae4f4c5de368bdda3a0636b51d51a ]
Patch series "nilfs2: fix potential issues with empty b-tree nodes".
This series addresses three potential issues with empty b-tree nodes that
can occur with corrupted filesystem images, including one recently
discovered by syzbot.
This patch (of 3):
If a b-tree is broken on the device, and the b-tree height is greater than
2 (the level of the root node is greater than 1) even if the number of
child nodes of the b-tree root is 0, a NULL pointer dereference occurs in
nilfs_btree_prepare_insert(), which is called from nilfs_btree_insert().
This is because, when the number of child nodes of the b-tree root is 0,
nilfs_btree_do_lookup() does not set the block buffer head in any of
path[x].bp_bh, leaving it as the initial value of NULL, but if the level
of the b-tree root node is greater than 1, nilfs_btree_get_nonroot_node(),
which accesses the buffer memory of path[x].bp_bh, is called.
Fix this issue by adding a check to nilfs_btree_root_broken(), which
performs sanity checks when reading the root node from the device, to
detect this inconsistency.
Thanks to Lizhi Xu for trying to solve the bug and clarifying the cause
early on.
Link: https://lkml.kernel.org/r/20240904081401.16682-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20240902084101.138971-1-lizhi.xu@windriver.com
Link: https://lkml.kernel.org/r/20240904081401.16682-2-konishi.ryusuke@gmail.com
Fixes: 17c76b0104e4 ("nilfs2: B-tree based block mapping")
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+9bff4c7b992038a7409f@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9bff4c7b992038a7409f
Cc: Lizhi Xu <lizhi.xu@windriver.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2024-09-04 16:13:07 +08:00
|
|
|
nchildren > NILFS_BTREE_ROOT_NCHILDREN_MAX ||
|
|
|
|
(nchildren == 0 && level > NILFS_BTREE_LEVEL_NODE_MIN))) {
|
2020-08-12 09:35:49 +08:00
|
|
|
nilfs_crit(inode->i_sb,
|
|
|
|
"bad btree root (ino=%lu): level = %d, flags = 0x%x, nchildren = %d",
|
|
|
|
inode->i_ino, level, flags, nchildren);
|
2015-02-28 07:51:56 +08:00
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-16 22:52:40 +08:00
|
|
|
int nilfs_btree_broken_node_block(struct buffer_head *bh)
|
|
|
|
{
|
2016-08-03 05:05:10 +08:00
|
|
|
struct inode *inode;
|
2010-07-18 09:42:25 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (buffer_nilfs_checked(bh))
|
|
|
|
return 0;
|
|
|
|
|
2022-12-16 05:44:00 +08:00
|
|
|
inode = bh->b_folio->mapping->host;
|
2010-07-18 09:42:25 +08:00
|
|
|
ret = nilfs_btree_node_broken((struct nilfs_btree_node *)bh->b_data,
|
2016-08-03 05:05:10 +08:00
|
|
|
bh->b_size, inode, bh->b_blocknr);
|
2010-07-18 09:42:25 +08:00
|
|
|
if (likely(!ret))
|
|
|
|
set_buffer_nilfs_checked(bh);
|
|
|
|
return ret;
|
2010-07-16 22:52:40 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static struct nilfs_btree_node *
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_btree_get_root(const struct nilfs_bmap *btree)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-07-10 18:09:49 +08:00
|
|
|
return (struct nilfs_btree_node *)btree->b_u.u_data;
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static struct nilfs_btree_node *
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_get_nonroot_node(const struct nilfs_btree_path *path, int level)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
return (struct nilfs_btree_node *)path[level].bp_bh->b_data;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static struct nilfs_btree_node *
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_get_sib_node(const struct nilfs_btree_path *path, int level)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
return (struct nilfs_btree_node *)path[level].bp_sib_bh->b_data;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:55 +08:00
|
|
|
static int nilfs_btree_height(const struct nilfs_bmap *btree)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2009-08-15 00:14:10 +08:00
|
|
|
return nilfs_btree_node_get_level(nilfs_btree_get_root(btree)) + 1;
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
static struct nilfs_btree_node *
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_btree_get_node(const struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
const struct nilfs_btree_path *path,
|
2010-07-13 22:33:53 +08:00
|
|
|
int level, int *ncmaxp)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-07-13 22:33:53 +08:00
|
|
|
struct nilfs_btree_node *node;
|
|
|
|
|
|
|
|
if (level == nilfs_btree_height(btree) - 1) {
|
|
|
|
node = nilfs_btree_get_root(btree);
|
|
|
|
*ncmaxp = NILFS_BTREE_ROOT_NCHILDREN_MAX;
|
|
|
|
} else {
|
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
*ncmaxp = nilfs_btree_nchildren_per_block(btree);
|
|
|
|
}
|
|
|
|
return node;
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2016-08-03 05:05:10 +08:00
|
|
|
static int nilfs_btree_bad_node(const struct nilfs_bmap *btree,
|
|
|
|
struct nilfs_btree_node *node, int level)
|
2009-10-10 21:58:10 +08:00
|
|
|
{
|
|
|
|
if (unlikely(nilfs_btree_node_get_level(node) != level)) {
|
|
|
|
dump_stack();
|
2020-08-12 09:35:49 +08:00
|
|
|
nilfs_crit(btree->b_inode->i_sb,
|
|
|
|
"btree level mismatch (ino=%lu): %d != %d",
|
|
|
|
btree->b_inode->i_ino,
|
|
|
|
nilfs_btree_node_get_level(node), level);
|
2009-10-10 21:58:10 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-18 09:42:24 +08:00
|
|
|
struct nilfs_btree_readahead_info {
|
|
|
|
struct nilfs_btree_node *node; /* parent node */
|
|
|
|
int max_ra_blocks; /* max nof blocks to read ahead */
|
|
|
|
int index; /* current index on the parent node */
|
|
|
|
int ncmax; /* nof children in the parent node */
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr,
|
|
|
|
struct buffer_head **bhp,
|
|
|
|
const struct nilfs_btree_readahead_info *ra)
|
|
|
|
{
|
2022-04-02 02:28:18 +08:00
|
|
|
struct inode *btnc_inode = NILFS_BMAP_I(btree)->i_assoc_inode;
|
|
|
|
struct address_space *btnc = btnc_inode->i_mapping;
|
2010-07-18 09:42:24 +08:00
|
|
|
struct buffer_head *bh, *ra_bh;
|
|
|
|
sector_t submit_ptr = 0;
|
|
|
|
int ret;
|
|
|
|
|
2022-07-15 02:07:24 +08:00
|
|
|
ret = nilfs_btnode_submit_block(btnc, ptr, 0, REQ_OP_READ, &bh,
|
2016-06-06 03:31:43 +08:00
|
|
|
&submit_ptr);
|
2010-07-18 09:42:24 +08:00
|
|
|
if (ret) {
|
nilfs2: fix general protection fault in nilfs_btree_insert()
If nilfs2 reads a corrupted disk image and tries to reads a b-tree node
block by calling __nilfs_btree_get_block() against an invalid virtual
block address, it returns -ENOENT because conversion of the virtual block
address to a disk block address fails. However, this return value is the
same as the internal code that b-tree lookup routines return to indicate
that the block being searched does not exist, so functions that operate on
that b-tree may misbehave.
When nilfs_btree_insert() receives this spurious 'not found' code from
nilfs_btree_do_lookup(), it misunderstands that the 'not found' check was
successful and continues the insert operation using incomplete lookup path
data, causing the following crash:
general protection fault, probably for non-canonical address
0xdffffc0000000005: 0000 [#1] PREEMPT SMP KASAN
KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f]
...
RIP: 0010:nilfs_btree_get_nonroot_node fs/nilfs2/btree.c:418 [inline]
RIP: 0010:nilfs_btree_prepare_insert fs/nilfs2/btree.c:1077 [inline]
RIP: 0010:nilfs_btree_insert+0x6d3/0x1c10 fs/nilfs2/btree.c:1238
Code: bc 24 80 00 00 00 4c 89 f8 48 c1 e8 03 42 80 3c 28 00 74 08 4c 89
ff e8 4b 02 92 fe 4d 8b 3f 49 83 c7 28 4c 89 f8 48 c1 e8 03 <42> 80 3c
28 00 74 08 4c 89 ff e8 2e 02 92 fe 4d 8b 3f 49 83 c7 02
...
Call Trace:
<TASK>
nilfs_bmap_do_insert fs/nilfs2/bmap.c:121 [inline]
nilfs_bmap_insert+0x20d/0x360 fs/nilfs2/bmap.c:147
nilfs_get_block+0x414/0x8d0 fs/nilfs2/inode.c:101
__block_write_begin_int+0x54c/0x1a80 fs/buffer.c:1991
__block_write_begin fs/buffer.c:2041 [inline]
block_write_begin+0x93/0x1e0 fs/buffer.c:2102
nilfs_write_begin+0x9c/0x110 fs/nilfs2/inode.c:261
generic_perform_write+0x2e4/0x5e0 mm/filemap.c:3772
__generic_file_write_iter+0x176/0x400 mm/filemap.c:3900
generic_file_write_iter+0xab/0x310 mm/filemap.c:3932
call_write_iter include/linux/fs.h:2186 [inline]
new_sync_write fs/read_write.c:491 [inline]
vfs_write+0x7dc/0xc50 fs/read_write.c:584
ksys_write+0x177/0x2a0 fs/read_write.c:637
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3d/0xb0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
...
</TASK>
This patch fixes the root cause of this problem by replacing the error
code that __nilfs_btree_get_block() returns on block address conversion
failure from -ENOENT to another internal code -EINVAL which means that the
b-tree metadata is corrupted.
By returning -EINVAL, it propagates without glitches, and for all relevant
b-tree operations, functions in the upper bmap layer output an error
message indicating corrupted b-tree metadata via
nilfs_bmap_convert_error(), and code -EIO will be eventually returned as
it should be.
Link: https://lkml.kernel.org/r/000000000000bd89e205f0e38355@google.com
Link: https://lkml.kernel.org/r/20230105055356.8811-1-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+ede796cecd5296353515@syzkaller.appspotmail.com
Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-01-05 13:53:56 +08:00
|
|
|
if (likely(ret == -EEXIST))
|
|
|
|
goto out_check;
|
|
|
|
if (ret == -ENOENT) {
|
|
|
|
/*
|
|
|
|
* Block address translation failed due to invalid
|
|
|
|
* value of 'ptr'. In this case, return internal code
|
|
|
|
* -EINVAL (broken bmap) to notify bmap layer of fatal
|
|
|
|
* metadata corruption.
|
|
|
|
*/
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
return ret;
|
2010-07-18 09:42:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ra) {
|
|
|
|
int i, n;
|
|
|
|
__u64 ptr2;
|
|
|
|
|
|
|
|
/* read ahead sibling nodes */
|
|
|
|
for (n = ra->max_ra_blocks, i = ra->index + 1;
|
|
|
|
n > 0 && i < ra->ncmax; n--, i++) {
|
|
|
|
ptr2 = nilfs_btree_node_get_ptr(ra->node, i, ra->ncmax);
|
|
|
|
|
2016-06-06 03:31:43 +08:00
|
|
|
ret = nilfs_btnode_submit_block(btnc, ptr2, 0,
|
2022-07-15 02:07:24 +08:00
|
|
|
REQ_OP_READ | REQ_RAHEAD,
|
|
|
|
&ra_bh, &submit_ptr);
|
2010-07-18 09:42:24 +08:00
|
|
|
if (likely(!ret || ret == -EEXIST))
|
|
|
|
brelse(ra_bh);
|
|
|
|
else if (ret != -EBUSY)
|
|
|
|
break;
|
|
|
|
if (!buffer_locked(bh))
|
|
|
|
goto out_no_wait;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wait_on_buffer(bh);
|
|
|
|
|
|
|
|
out_no_wait:
|
|
|
|
if (!buffer_uptodate(bh)) {
|
2020-08-12 09:35:49 +08:00
|
|
|
nilfs_err(btree->b_inode->i_sb,
|
2016-08-03 05:05:17 +08:00
|
|
|
"I/O error reading b-tree node block (ino=%lu, blocknr=%llu)",
|
|
|
|
btree->b_inode->i_ino, (unsigned long long)ptr);
|
2010-07-18 09:42:24 +08:00
|
|
|
brelse(bh);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
out_check:
|
|
|
|
if (nilfs_btree_broken_node_block(bh)) {
|
|
|
|
clear_buffer_uptodate(bh);
|
|
|
|
brelse(bh);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*bhp = bh;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr,
|
|
|
|
struct buffer_head **bhp)
|
|
|
|
{
|
|
|
|
return __nilfs_btree_get_block(btree, ptr, bhp, NULL);
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
2010-07-18 09:42:26 +08:00
|
|
|
__u64 key, __u64 *ptrp, int minlevel,
|
|
|
|
int readahead)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node;
|
2010-07-18 09:42:26 +08:00
|
|
|
struct nilfs_btree_readahead_info p, *ra;
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 ptr;
|
2010-07-13 22:33:52 +08:00
|
|
|
int level, index, found, ncmax, ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
node = nilfs_btree_get_root(btree);
|
2009-08-15 00:14:10 +08:00
|
|
|
level = nilfs_btree_node_get_level(node);
|
|
|
|
if (level < minlevel || nilfs_btree_node_get_nchildren(node) <= 0)
|
2009-04-07 10:01:24 +08:00
|
|
|
return -ENOENT;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
found = nilfs_btree_node_lookup(node, key, &index);
|
2010-07-13 22:33:53 +08:00
|
|
|
ptr = nilfs_btree_node_get_ptr(node, index,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_bh = NULL;
|
|
|
|
path[level].bp_index = index;
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
ncmax = nilfs_btree_nchildren_per_block(btree);
|
2010-07-13 22:33:52 +08:00
|
|
|
|
2010-07-18 09:42:26 +08:00
|
|
|
while (--level >= minlevel) {
|
|
|
|
ra = NULL;
|
|
|
|
if (level == NILFS_BTREE_LEVEL_NODE_MIN && readahead) {
|
|
|
|
p.node = nilfs_btree_get_node(btree, path, level + 1,
|
|
|
|
&p.ncmax);
|
|
|
|
p.index = index;
|
|
|
|
p.max_ra_blocks = 7;
|
|
|
|
ra = &p;
|
|
|
|
}
|
|
|
|
ret = __nilfs_btree_get_block(btree, ptr, &path[level].bp_bh,
|
|
|
|
ra);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2010-07-18 09:42:26 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
2016-08-03 05:05:10 +08:00
|
|
|
if (nilfs_btree_bad_node(btree, node, level))
|
2009-10-10 21:58:10 +08:00
|
|
|
return -EINVAL;
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!found)
|
2009-08-15 00:14:10 +08:00
|
|
|
found = nilfs_btree_node_lookup(node, key, &index);
|
2009-04-07 10:01:24 +08:00
|
|
|
else
|
|
|
|
index = 0;
|
2010-07-13 22:33:52 +08:00
|
|
|
if (index < ncmax) {
|
2010-07-13 22:33:53 +08:00
|
|
|
ptr = nilfs_btree_node_get_ptr(node, index, ncmax);
|
2010-07-13 22:33:52 +08:00
|
|
|
} else {
|
2009-04-07 10:01:55 +08:00
|
|
|
WARN_ON(found || level != NILFS_BTREE_LEVEL_NODE_MIN);
|
2009-04-07 10:01:24 +08:00
|
|
|
/* insert */
|
|
|
|
ptr = NILFS_BMAP_INVALID_PTR;
|
|
|
|
}
|
|
|
|
path[level].bp_index = index;
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
if (ptrp != NULL)
|
|
|
|
*ptrp = ptr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_do_lookup_last(const struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
__u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node;
|
|
|
|
__u64 ptr;
|
2010-07-13 22:33:53 +08:00
|
|
|
int index, level, ncmax, ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
node = nilfs_btree_get_root(btree);
|
2009-08-15 00:14:10 +08:00
|
|
|
index = nilfs_btree_node_get_nchildren(node) - 1;
|
2009-04-07 10:01:24 +08:00
|
|
|
if (index < 0)
|
|
|
|
return -ENOENT;
|
2009-08-15 00:14:10 +08:00
|
|
|
level = nilfs_btree_node_get_level(node);
|
2010-07-13 22:33:53 +08:00
|
|
|
ptr = nilfs_btree_node_get_ptr(node, index,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_bh = NULL;
|
|
|
|
path[level].bp_index = index;
|
2010-07-13 22:33:53 +08:00
|
|
|
ncmax = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
for (level--; level > 0; level--) {
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
2016-08-03 05:05:10 +08:00
|
|
|
if (nilfs_btree_bad_node(btree, node, level))
|
2009-10-10 21:58:10 +08:00
|
|
|
return -EINVAL;
|
2009-08-15 00:14:10 +08:00
|
|
|
index = nilfs_btree_node_get_nchildren(node) - 1;
|
2010-07-13 22:33:53 +08:00
|
|
|
ptr = nilfs_btree_node_get_ptr(node, index, ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_index = index;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyp != NULL)
|
2009-08-15 00:14:10 +08:00
|
|
|
*keyp = nilfs_btree_node_get_key(node, index);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ptrp != NULL)
|
|
|
|
*ptrp = ptr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-17 03:46:36 +08:00
|
|
|
/**
|
|
|
|
* nilfs_btree_get_next_key - get next valid key from btree path array
|
|
|
|
* @btree: bmap struct of btree
|
|
|
|
* @path: array of nilfs_btree_path struct
|
|
|
|
* @minlevel: start level
|
|
|
|
* @nextkey: place to store the next valid key
|
|
|
|
*
|
|
|
|
* Return Value: If a next key was found, 0 is returned. Otherwise,
|
|
|
|
* -ENOENT is returned.
|
|
|
|
*/
|
|
|
|
static int nilfs_btree_get_next_key(const struct nilfs_bmap *btree,
|
|
|
|
const struct nilfs_btree_path *path,
|
|
|
|
int minlevel, __u64 *nextkey)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node;
|
|
|
|
int maxlevel = nilfs_btree_height(btree) - 1;
|
|
|
|
int index, next_adj, level;
|
|
|
|
|
|
|
|
/* Next index is already set to bp_index for leaf nodes. */
|
|
|
|
next_adj = 0;
|
|
|
|
for (level = minlevel; level <= maxlevel; level++) {
|
|
|
|
if (level == maxlevel)
|
|
|
|
node = nilfs_btree_get_root(btree);
|
|
|
|
else
|
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
|
|
|
|
index = path[level].bp_index + next_adj;
|
|
|
|
if (index < nilfs_btree_node_get_nchildren(node)) {
|
|
|
|
/* Next key is in this node */
|
|
|
|
*nextkey = nilfs_btree_node_get_key(node, index);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* For non-leaf nodes, next index is stored at bp_index + 1. */
|
|
|
|
next_adj = 1;
|
|
|
|
}
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_lookup(const struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 key, int level, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
int ret;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
path = nilfs_btree_alloc_path();
|
2009-04-07 10:01:24 +08:00
|
|
|
if (path == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2010-07-18 09:42:26 +08:00
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, ptrp, level, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_free_path(path);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree,
|
2016-05-24 07:23:39 +08:00
|
|
|
__u64 key, __u64 *ptrp,
|
|
|
|
unsigned int maxblocks)
|
2009-05-25 01:47:14 +08:00
|
|
|
{
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
struct nilfs_btree_node *node;
|
|
|
|
struct inode *dat = NULL;
|
|
|
|
__u64 ptr, ptr2;
|
|
|
|
sector_t blocknr;
|
|
|
|
int level = NILFS_BTREE_LEVEL_NODE_MIN;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ret, cnt, index, maxlevel, ncmax;
|
2010-07-18 09:42:26 +08:00
|
|
|
struct nilfs_btree_readahead_info p;
|
2009-05-25 01:47:14 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
path = nilfs_btree_alloc_path();
|
2009-05-25 01:47:14 +08:00
|
|
|
if (path == NULL)
|
|
|
|
return -ENOMEM;
|
2010-04-02 17:36:34 +08:00
|
|
|
|
2010-07-18 09:42:26 +08:00
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level, 1);
|
2009-05-25 01:47:14 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
if (NILFS_BMAP_USE_VBN(btree)) {
|
|
|
|
dat = nilfs_bmap_get_dat(btree);
|
2009-05-25 01:47:14 +08:00
|
|
|
ret = nilfs_dat_translate(dat, ptr, &blocknr);
|
|
|
|
if (ret < 0)
|
nilfs2: fix failure to detect DAT corruption in btree and direct mappings
[ Upstream commit f2f26b4a84a0ef41791bd2d70861c8eac748f4ba ]
Patch series "nilfs2: fix kernel bug at submit_bh_wbc()".
This resolves a kernel BUG reported by syzbot. Since there are two
flaws involved, I've made each one a separate patch.
The first patch alone resolves the syzbot-reported bug, but I think
both fixes should be sent to stable, so I've tagged them as such.
This patch (of 2):
Syzbot has reported a kernel bug in submit_bh_wbc() when writing file data
to a nilfs2 file system whose metadata is corrupted.
There are two flaws involved in this issue.
The first flaw is that when nilfs_get_block() locates a data block using
btree or direct mapping, if the disk address translation routine
nilfs_dat_translate() fails with internal code -ENOENT due to DAT metadata
corruption, it can be passed back to nilfs_get_block(). This causes
nilfs_get_block() to misidentify an existing block as non-existent,
causing both data block lookup and insertion to fail inconsistently.
The second flaw is that nilfs_get_block() returns a successful status in
this inconsistent state. This causes the caller __block_write_begin_int()
or others to request a read even though the buffer is not mapped,
resulting in a BUG_ON check for the BH_Mapped flag in submit_bh_wbc()
failing.
This fixes the first issue by changing the return value to code -EINVAL
when a conversion using DAT fails with code -ENOENT, avoiding the
conflicting condition that leads to the kernel bug described above. Here,
code -EINVAL indicates that metadata corruption was detected during the
block lookup, which will be properly handled as a file system error and
converted to -EIO when passing through the nilfs2 bmap layer.
Link: https://lkml.kernel.org/r/20240313105827.5296-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20240313105827.5296-2-konishi.ryusuke@gmail.com
Fixes: c3a7abf06ce7 ("nilfs2: support contiguous lookup of blocks")
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+cfed5b56649bddf80d6e@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=cfed5b56649bddf80d6e
Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2024-03-13 18:58:26 +08:00
|
|
|
goto dat_error;
|
2009-05-25 01:47:14 +08:00
|
|
|
ptr = blocknr;
|
|
|
|
}
|
|
|
|
cnt = 1;
|
|
|
|
if (cnt == maxblocks)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
maxlevel = nilfs_btree_height(btree) - 1;
|
2010-07-13 22:33:53 +08:00
|
|
|
node = nilfs_btree_get_node(btree, path, level, &ncmax);
|
2009-05-25 01:47:14 +08:00
|
|
|
index = path[level].bp_index + 1;
|
|
|
|
for (;;) {
|
2009-08-15 00:14:10 +08:00
|
|
|
while (index < nilfs_btree_node_get_nchildren(node)) {
|
|
|
|
if (nilfs_btree_node_get_key(node, index) !=
|
2009-05-25 01:47:14 +08:00
|
|
|
key + cnt)
|
|
|
|
goto end;
|
2010-07-13 22:33:53 +08:00
|
|
|
ptr2 = nilfs_btree_node_get_ptr(node, index, ncmax);
|
2009-05-25 01:47:14 +08:00
|
|
|
if (dat) {
|
|
|
|
ret = nilfs_dat_translate(dat, ptr2, &blocknr);
|
|
|
|
if (ret < 0)
|
nilfs2: fix failure to detect DAT corruption in btree and direct mappings
[ Upstream commit f2f26b4a84a0ef41791bd2d70861c8eac748f4ba ]
Patch series "nilfs2: fix kernel bug at submit_bh_wbc()".
This resolves a kernel BUG reported by syzbot. Since there are two
flaws involved, I've made each one a separate patch.
The first patch alone resolves the syzbot-reported bug, but I think
both fixes should be sent to stable, so I've tagged them as such.
This patch (of 2):
Syzbot has reported a kernel bug in submit_bh_wbc() when writing file data
to a nilfs2 file system whose metadata is corrupted.
There are two flaws involved in this issue.
The first flaw is that when nilfs_get_block() locates a data block using
btree or direct mapping, if the disk address translation routine
nilfs_dat_translate() fails with internal code -ENOENT due to DAT metadata
corruption, it can be passed back to nilfs_get_block(). This causes
nilfs_get_block() to misidentify an existing block as non-existent,
causing both data block lookup and insertion to fail inconsistently.
The second flaw is that nilfs_get_block() returns a successful status in
this inconsistent state. This causes the caller __block_write_begin_int()
or others to request a read even though the buffer is not mapped,
resulting in a BUG_ON check for the BH_Mapped flag in submit_bh_wbc()
failing.
This fixes the first issue by changing the return value to code -EINVAL
when a conversion using DAT fails with code -ENOENT, avoiding the
conflicting condition that leads to the kernel bug described above. Here,
code -EINVAL indicates that metadata corruption was detected during the
block lookup, which will be properly handled as a file system error and
converted to -EIO when passing through the nilfs2 bmap layer.
Link: https://lkml.kernel.org/r/20240313105827.5296-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20240313105827.5296-2-konishi.ryusuke@gmail.com
Fixes: c3a7abf06ce7 ("nilfs2: support contiguous lookup of blocks")
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+cfed5b56649bddf80d6e@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=cfed5b56649bddf80d6e
Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2024-03-13 18:58:26 +08:00
|
|
|
goto dat_error;
|
2009-05-25 01:47:14 +08:00
|
|
|
ptr2 = blocknr;
|
|
|
|
}
|
|
|
|
if (ptr2 != ptr + cnt || ++cnt == maxblocks)
|
|
|
|
goto end;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
if (level == maxlevel)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* look-up right sibling node */
|
2010-07-18 09:42:26 +08:00
|
|
|
p.node = nilfs_btree_get_node(btree, path, level + 1, &p.ncmax);
|
|
|
|
p.index = path[level + 1].bp_index + 1;
|
|
|
|
p.max_ra_blocks = 7;
|
|
|
|
if (p.index >= nilfs_btree_node_get_nchildren(p.node) ||
|
|
|
|
nilfs_btree_node_get_key(p.node, p.index) != key + cnt)
|
2009-05-25 01:47:14 +08:00
|
|
|
break;
|
2010-07-18 09:42:26 +08:00
|
|
|
ptr2 = nilfs_btree_node_get_ptr(p.node, p.index, p.ncmax);
|
|
|
|
path[level + 1].bp_index = p.index;
|
2009-05-25 01:47:14 +08:00
|
|
|
|
|
|
|
brelse(path[level].bp_bh);
|
|
|
|
path[level].bp_bh = NULL;
|
2010-07-18 09:42:26 +08:00
|
|
|
|
|
|
|
ret = __nilfs_btree_get_block(btree, ptr2, &path[level].bp_bh,
|
|
|
|
&p);
|
2009-05-25 01:47:14 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncmax = nilfs_btree_nchildren_per_block(btree);
|
2009-05-25 01:47:14 +08:00
|
|
|
index = 0;
|
|
|
|
path[level].bp_index = index;
|
|
|
|
}
|
|
|
|
end:
|
|
|
|
*ptrp = ptr;
|
|
|
|
ret = cnt;
|
|
|
|
out:
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_free_path(path);
|
2009-05-25 01:47:14 +08:00
|
|
|
return ret;
|
nilfs2: fix failure to detect DAT corruption in btree and direct mappings
[ Upstream commit f2f26b4a84a0ef41791bd2d70861c8eac748f4ba ]
Patch series "nilfs2: fix kernel bug at submit_bh_wbc()".
This resolves a kernel BUG reported by syzbot. Since there are two
flaws involved, I've made each one a separate patch.
The first patch alone resolves the syzbot-reported bug, but I think
both fixes should be sent to stable, so I've tagged them as such.
This patch (of 2):
Syzbot has reported a kernel bug in submit_bh_wbc() when writing file data
to a nilfs2 file system whose metadata is corrupted.
There are two flaws involved in this issue.
The first flaw is that when nilfs_get_block() locates a data block using
btree or direct mapping, if the disk address translation routine
nilfs_dat_translate() fails with internal code -ENOENT due to DAT metadata
corruption, it can be passed back to nilfs_get_block(). This causes
nilfs_get_block() to misidentify an existing block as non-existent,
causing both data block lookup and insertion to fail inconsistently.
The second flaw is that nilfs_get_block() returns a successful status in
this inconsistent state. This causes the caller __block_write_begin_int()
or others to request a read even though the buffer is not mapped,
resulting in a BUG_ON check for the BH_Mapped flag in submit_bh_wbc()
failing.
This fixes the first issue by changing the return value to code -EINVAL
when a conversion using DAT fails with code -ENOENT, avoiding the
conflicting condition that leads to the kernel bug described above. Here,
code -EINVAL indicates that metadata corruption was detected during the
block lookup, which will be properly handled as a file system error and
converted to -EIO when passing through the nilfs2 bmap layer.
Link: https://lkml.kernel.org/r/20240313105827.5296-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20240313105827.5296-2-konishi.ryusuke@gmail.com
Fixes: c3a7abf06ce7 ("nilfs2: support contiguous lookup of blocks")
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+cfed5b56649bddf80d6e@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=cfed5b56649bddf80d6e
Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2024-03-13 18:58:26 +08:00
|
|
|
|
|
|
|
dat_error:
|
|
|
|
if (ret == -ENOENT)
|
|
|
|
ret = -EINVAL; /* Notify bmap layer of metadata corruption */
|
|
|
|
goto out;
|
2009-05-25 01:47:14 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_promote_key(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 key)
|
|
|
|
{
|
|
|
|
if (level < nilfs_btree_height(btree) - 1) {
|
|
|
|
do {
|
|
|
|
nilfs_btree_node_set_key(
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_get_nonroot_node(path, level),
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_index, key);
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
} while ((path[level].bp_index == 0) &&
|
|
|
|
(++level < nilfs_btree_height(btree) - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* root */
|
|
|
|
if (level == nilfs_btree_height(btree) - 1) {
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_key(nilfs_btree_get_root(btree),
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_index, key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_do_insert(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (level < nilfs_btree_height(btree) - 1) {
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
|
|
|
nilfs_btree_node_insert(node, path[level].bp_index,
|
|
|
|
*keyp, *ptrp, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (path[level].bp_index == 0)
|
|
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_key(node,
|
|
|
|
0));
|
2009-04-07 10:01:24 +08:00
|
|
|
} else {
|
|
|
|
node = nilfs_btree_get_root(btree);
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_insert(node, path[level].bp_index,
|
|
|
|
*keyp, *ptrp,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_carry_left(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node, *left;
|
2010-07-13 22:33:53 +08:00
|
|
|
int nchildren, lnchildren, n, move, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
left = nilfs_btree_get_sib_node(path, level);
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
|
|
|
lnchildren = nilfs_btree_node_get_nchildren(left);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
move = 0;
|
|
|
|
|
|
|
|
n = (nchildren + lnchildren + 1) / 2 - lnchildren;
|
|
|
|
if (n > path[level].bp_index) {
|
|
|
|
/* move insert point */
|
|
|
|
n--;
|
|
|
|
move = 1;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_left(left, node, n, ncblk, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_key(node, 0));
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (move) {
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
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 {
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = NULL;
|
|
|
|
path[level].bp_index -= n;
|
|
|
|
}
|
|
|
|
|
|
|
|
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_carry_right(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node, *right;
|
2010-07-13 22:33:53 +08:00
|
|
|
int nchildren, rnchildren, n, move, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
right = nilfs_btree_get_sib_node(path, level);
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
|
|
|
rnchildren = nilfs_btree_node_get_nchildren(right);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
move = 0;
|
|
|
|
|
|
|
|
n = (nchildren + rnchildren + 1) / 2 - rnchildren;
|
|
|
|
if (n > nchildren - path[level].bp_index) {
|
|
|
|
/* move insert point */
|
|
|
|
n--;
|
|
|
|
move = 1;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_right(node, right, n, ncblk, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
path[level + 1].bp_index++;
|
|
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_key(right, 0));
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level + 1].bp_index--;
|
|
|
|
|
|
|
|
if (move) {
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_bh = path[level].bp_sib_bh;
|
|
|
|
path[level].bp_sib_bh = NULL;
|
2009-08-15 00:14:10 +08:00
|
|
|
path[level].bp_index -= nilfs_btree_node_get_nchildren(node);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level + 1].bp_index++;
|
|
|
|
} else {
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_split(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node, *right;
|
2010-07-13 22:33:53 +08:00
|
|
|
int nchildren, n, move, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
right = nilfs_btree_get_sib_node(path, level);
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
move = 0;
|
|
|
|
|
|
|
|
n = (nchildren + 1) / 2;
|
|
|
|
if (n > nchildren - path[level].bp_index) {
|
|
|
|
n--;
|
|
|
|
move = 1;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_right(node, right, n, ncblk, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (move) {
|
2009-08-15 00:14:10 +08:00
|
|
|
path[level].bp_index -= nilfs_btree_node_get_nchildren(node);
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_insert(right, path[level].bp_index,
|
|
|
|
*keyp, *ptrp, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
*keyp = nilfs_btree_node_get_key(right, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
*ptrp = path[level].bp_newreq.bpr_ptr;
|
|
|
|
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
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);
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
*keyp = nilfs_btree_node_get_key(right, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
*ptrp = path[level].bp_newreq.bpr_ptr;
|
|
|
|
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
path[level + 1].bp_index++;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_grow(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *root, *child;
|
2010-07-13 22:33:53 +08:00
|
|
|
int n, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
root = nilfs_btree_get_root(btree);
|
2009-08-15 00:14:10 +08:00
|
|
|
child = nilfs_btree_get_sib_node(path, level);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
n = nilfs_btree_node_get_nchildren(root);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_right(root, child, n,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX, ncblk);
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_level(root, level + 1);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
path[level].bp_bh = path[level].bp_sib_bh;
|
|
|
|
path[level].bp_sib_bh = NULL;
|
|
|
|
|
|
|
|
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
*keyp = nilfs_btree_node_get_key(child, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
*ptrp = path[level].bp_newreq.bpr_ptr;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static __u64 nilfs_btree_find_near(const struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
const struct nilfs_btree_path *path)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node;
|
2010-07-13 22:33:53 +08:00
|
|
|
int level, ncmax;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (path == NULL)
|
|
|
|
return NILFS_BMAP_INVALID_PTR;
|
|
|
|
|
|
|
|
/* left sibling */
|
|
|
|
level = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
|
|
if (path[level].bp_index > 0) {
|
2010-07-13 22:33:53 +08:00
|
|
|
node = nilfs_btree_get_node(btree, path, level, &ncmax);
|
|
|
|
return nilfs_btree_node_get_ptr(node,
|
|
|
|
path[level].bp_index - 1,
|
|
|
|
ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* parent */
|
|
|
|
level = NILFS_BTREE_LEVEL_NODE_MIN + 1;
|
|
|
|
if (level <= nilfs_btree_height(btree) - 1) {
|
2010-07-13 22:33:53 +08:00
|
|
|
node = nilfs_btree_get_node(btree, path, level, &ncmax);
|
|
|
|
return nilfs_btree_node_get_ptr(node, path[level].bp_index,
|
|
|
|
ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return NILFS_BMAP_INVALID_PTR;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static __u64 nilfs_btree_find_target_v(const struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
const struct nilfs_btree_path *path,
|
|
|
|
__u64 key)
|
|
|
|
{
|
|
|
|
__u64 ptr;
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
ptr = nilfs_bmap_find_target_seq(btree, key);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ptr != NILFS_BMAP_INVALID_PTR)
|
|
|
|
/* sequential access */
|
|
|
|
return ptr;
|
2016-05-24 07:23:42 +08:00
|
|
|
|
|
|
|
ptr = nilfs_btree_find_near(btree, path);
|
|
|
|
if (ptr != NILFS_BMAP_INVALID_PTR)
|
|
|
|
/* near */
|
|
|
|
return ptr;
|
|
|
|
|
2009-04-07 10:01:24 +08:00
|
|
|
/* block group */
|
2010-07-10 18:09:49 +08:00
|
|
|
return nilfs_bmap_find_target_in_group(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
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;
|
2010-07-13 22:33:53 +08:00
|
|
|
int pindex, level, ncmax, ncblk, ret;
|
2009-08-15 14:34:33 +08:00
|
|
|
struct inode *dat = NULL;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
stats->bs_nblocks = 0;
|
|
|
|
level = NILFS_BTREE_LEVEL_DATA;
|
|
|
|
|
|
|
|
/* allocate a new ptr for data block */
|
2010-07-10 18:09:49 +08:00
|
|
|
if (NILFS_BMAP_USE_VBN(btree)) {
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_newreq.bpr_ptr =
|
2009-05-24 17:07:59 +08:00
|
|
|
nilfs_btree_find_target_v(btree, path, key);
|
2010-07-10 18:09:49 +08:00
|
|
|
dat = nilfs_bmap_get_dat(btree);
|
2009-08-15 14:34:33 +08:00
|
|
|
}
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_bmap_prepare_alloc_ptr(btree, &path[level].bp_newreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_data;
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2010-07-13 22:33:52 +08:00
|
|
|
|
2009-04-07 10:01:24 +08:00
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
|
|
level < nilfs_btree_height(btree) - 1;
|
|
|
|
level++) {
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
2010-07-13 22:33:53 +08:00
|
|
|
if (nilfs_btree_node_get_nchildren(node) < ncblk) {
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_op = nilfs_btree_do_insert;
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
pindex = path[level + 1].bp_index;
|
|
|
|
|
|
|
|
/* left sibling */
|
|
|
|
if (pindex > 0) {
|
2010-07-13 22:33:53 +08:00
|
|
|
sibptr = nilfs_btree_node_get_ptr(parent, pindex - 1,
|
|
|
|
ncmax);
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_block(btree, sibptr, &bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_child_node;
|
|
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
2010-07-13 22:33:53 +08:00
|
|
|
if (nilfs_btree_node_get_nchildren(sib) < ncblk) {
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = bh;
|
|
|
|
path[level].bp_op = nilfs_btree_carry_left;
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
goto out;
|
2010-07-13 22:33:53 +08:00
|
|
|
} else {
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(bh);
|
2010-07-13 22:33:53 +08:00
|
|
|
}
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* right sibling */
|
2010-07-13 22:33:53 +08:00
|
|
|
if (pindex < nilfs_btree_node_get_nchildren(parent) - 1) {
|
|
|
|
sibptr = nilfs_btree_node_get_ptr(parent, pindex + 1,
|
|
|
|
ncmax);
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_block(btree, sibptr, &bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_child_node;
|
|
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
2010-07-13 22:33:53 +08:00
|
|
|
if (nilfs_btree_node_get_nchildren(sib) < ncblk) {
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = bh;
|
|
|
|
path[level].bp_op = nilfs_btree_carry_right;
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
goto out;
|
2010-07-13 22:33:53 +08:00
|
|
|
} else {
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(bh);
|
2010-07-13 22:33:53 +08:00
|
|
|
}
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* split */
|
|
|
|
path[level].bp_newreq.bpr_ptr =
|
|
|
|
path[level - 1].bp_newreq.bpr_ptr + 1;
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_bmap_prepare_alloc_ptr(btree,
|
2009-08-15 14:34:33 +08:00
|
|
|
&path[level].bp_newreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_child_node;
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_new_block(btree,
|
|
|
|
path[level].bp_newreq.bpr_ptr,
|
|
|
|
&bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_curr_node;
|
|
|
|
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
|
|
|
nilfs_btree_node_init(sib, 0, level, 0, ncblk, NULL, NULL);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = bh;
|
|
|
|
path[level].bp_op = nilfs_btree_split;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* root */
|
|
|
|
node = nilfs_btree_get_root(btree);
|
2009-08-15 00:14:10 +08:00
|
|
|
if (nilfs_btree_node_get_nchildren(node) <
|
2010-07-13 22:33:52 +08:00
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX) {
|
2009-04-07 10:01:24 +08:00
|
|
|
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;
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_bmap_prepare_alloc_ptr(btree, &path[level].bp_newreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_child_node;
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_new_block(btree, path[level].bp_newreq.bpr_ptr,
|
|
|
|
&bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_curr_node;
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_init((struct nilfs_btree_node *)bh->b_data,
|
|
|
|
0, level, 0, ncblk, NULL, NULL);
|
2009-04-07 10:01:24 +08:00
|
|
|
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:
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
err_out_child_node:
|
|
|
|
for (level--; level > NILFS_BTREE_LEVEL_DATA; level--) {
|
2009-05-21 23:38:56 +08:00
|
|
|
nilfs_btnode_delete(path[level].bp_sib_bh);
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
err_out_data:
|
|
|
|
*levelp = level;
|
|
|
|
stats->bs_nblocks = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_commit_insert(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int maxlevel, __u64 key, __u64 ptr)
|
|
|
|
{
|
2009-08-15 14:34:33 +08:00
|
|
|
struct inode *dat = NULL;
|
2009-04-07 10:01:24 +08:00
|
|
|
int level;
|
|
|
|
|
|
|
|
set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr));
|
|
|
|
ptr = path[NILFS_BTREE_LEVEL_DATA].bp_newreq.bpr_ptr;
|
2010-07-10 18:09:49 +08:00
|
|
|
if (NILFS_BMAP_USE_VBN(btree)) {
|
2010-07-10 21:21:54 +08:00
|
|
|
nilfs_bmap_set_target_v(btree, key, ptr);
|
2010-07-10 18:09:49 +08:00
|
|
|
dat = nilfs_bmap_get_dat(btree);
|
2009-08-15 14:34:33 +08:00
|
|
|
}
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) {
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_commit_alloc_ptr(btree,
|
2009-08-15 14:34:33 +08:00
|
|
|
&path[level - 1].bp_newreq, dat);
|
2009-04-07 10:01:49 +08:00
|
|
|
path[level].bp_op(btree, path, level, &key, &ptr);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
if (!nilfs_bmap_dirty(btree))
|
|
|
|
nilfs_bmap_set_dirty(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_insert(struct nilfs_bmap *btree, __u64 key, __u64 ptr)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
struct nilfs_bmap_stats stats;
|
|
|
|
int level, ret;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
path = nilfs_btree_alloc_path();
|
2009-04-07 10:01:24 +08:00
|
|
|
if (path == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, NULL,
|
2010-07-18 09:42:26 +08:00
|
|
|
NILFS_BTREE_LEVEL_NODE_MIN, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
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);
|
2011-03-04 23:19:32 +08:00
|
|
|
nilfs_inode_add_blocks(btree->b_inode, stats.bs_nblocks);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
out:
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_free_path(path);
|
2009-04-07 10:01:24 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_do_delete(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (level < nilfs_btree_height(btree) - 1) {
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
|
|
|
nilfs_btree_node_delete(node, path[level].bp_index,
|
|
|
|
keyp, ptrp, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (path[level].bp_index == 0)
|
|
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_key(node, 0));
|
2009-04-07 10:01:24 +08:00
|
|
|
} else {
|
|
|
|
node = nilfs_btree_get_root(btree);
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_delete(node, path[level].bp_index,
|
|
|
|
keyp, ptrp,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_borrow_left(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node, *left;
|
2010-07-13 22:33:53 +08:00
|
|
|
int nchildren, lnchildren, n, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
left = nilfs_btree_get_sib_node(path, level);
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
|
|
|
lnchildren = nilfs_btree_node_get_nchildren(left);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
n = (nchildren + lnchildren) / 2 - nchildren;
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_right(left, node, n, ncblk, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_key(node, 0));
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = NULL;
|
|
|
|
path[level].bp_index += n;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_borrow_right(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node, *right;
|
2010-07-13 22:33:53 +08:00
|
|
|
int nchildren, rnchildren, n, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
right = nilfs_btree_get_sib_node(path, level);
|
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
|
|
|
rnchildren = nilfs_btree_node_get_nchildren(right);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
n = (nchildren + rnchildren) / 2 - nchildren;
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_left(node, right, n, ncblk, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
path[level + 1].bp_index++;
|
|
|
|
nilfs_btree_promote_key(btree, path, level + 1,
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_key(right, 0));
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level + 1].bp_index--;
|
|
|
|
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = NULL;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_concat_left(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node, *left;
|
2010-07-13 22:33:53 +08:00
|
|
|
int n, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
left = nilfs_btree_get_sib_node(path, level);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
n = nilfs_btree_node_get_nchildren(node);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_left(left, node, n, ncblk, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (!buffer_dirty(path[level].bp_sib_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-05-21 23:38:56 +08:00
|
|
|
nilfs_btnode_delete(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_bh = path[level].bp_sib_bh;
|
|
|
|
path[level].bp_sib_bh = NULL;
|
2009-08-15 00:14:10 +08:00
|
|
|
path[level].bp_index += nilfs_btree_node_get_nchildren(left);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_concat_right(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node, *right;
|
2010-07-13 22:33:53 +08:00
|
|
|
int n, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
|
|
|
right = nilfs_btree_get_sib_node(path, level);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
n = nilfs_btree_node_get_nchildren(right);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_left(node, right, n, ncblk, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-05-21 23:38:56 +08:00
|
|
|
nilfs_btnode_delete(path[level].bp_sib_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_sib_bh = NULL;
|
|
|
|
path[level + 1].bp_index++;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_shrink(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *root, *child;
|
2010-07-13 22:33:53 +08:00
|
|
|
int n, ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
|
|
|
|
|
|
|
|
root = nilfs_btree_get_root(btree);
|
2009-08-15 00:14:10 +08:00
|
|
|
child = nilfs_btree_get_nonroot_node(path, level);
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_delete(root, 0, NULL, NULL,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_set_level(root, level);
|
|
|
|
n = nilfs_btree_node_get_nchildren(child);
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_move_left(root, child, n,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-05-21 23:38:56 +08:00
|
|
|
nilfs_btnode_delete(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_bh = NULL;
|
|
|
|
}
|
|
|
|
|
2011-05-25 22:00:27 +08:00
|
|
|
static void nilfs_btree_nop(struct nilfs_bmap *btree,
|
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level, __u64 *keyp, __u64 *ptrp)
|
|
|
|
{
|
|
|
|
}
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int *levelp,
|
2009-08-15 14:34:33 +08:00
|
|
|
struct nilfs_bmap_stats *stats,
|
|
|
|
struct inode *dat)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
struct buffer_head *bh;
|
|
|
|
struct nilfs_btree_node *node, *parent, *sib;
|
|
|
|
__u64 sibptr;
|
2011-05-25 22:00:27 +08:00
|
|
|
int pindex, dindex, level, ncmin, ncmax, ncblk, ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
stats->bs_nblocks = 0;
|
2010-07-13 22:33:52 +08:00
|
|
|
ncmin = NILFS_BTREE_NODE_NCHILDREN_MIN(nilfs_btree_node_size(btree));
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
2010-07-13 22:33:52 +08:00
|
|
|
|
2011-05-25 22:00:27 +08:00
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN, dindex = path[level].bp_index;
|
2009-04-07 10:01:24 +08:00
|
|
|
level < nilfs_btree_height(btree) - 1;
|
|
|
|
level++) {
|
2009-08-15 00:14:10 +08:00
|
|
|
node = nilfs_btree_get_nonroot_node(path, level);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_oldreq.bpr_ptr =
|
2011-05-25 22:00:27 +08:00
|
|
|
nilfs_btree_node_get_ptr(node, dindex, ncblk);
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_bmap_prepare_end_ptr(btree,
|
2009-08-15 14:34:33 +08:00
|
|
|
&path[level].bp_oldreq, dat);
|
2009-05-24 02:25:44 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_child_node;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:52 +08:00
|
|
|
if (nilfs_btree_node_get_nchildren(node) > ncmin) {
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_op = nilfs_btree_do_delete;
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
pindex = path[level + 1].bp_index;
|
2011-05-25 22:00:27 +08:00
|
|
|
dindex = pindex;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (pindex > 0) {
|
|
|
|
/* left sibling */
|
2010-07-13 22:33:53 +08:00
|
|
|
sibptr = nilfs_btree_node_get_ptr(parent, pindex - 1,
|
|
|
|
ncmax);
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_block(btree, sibptr, &bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_curr_node;
|
|
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
2010-07-13 22:33:52 +08:00
|
|
|
if (nilfs_btree_node_get_nchildren(sib) > ncmin) {
|
2009-04-07 10:01:24 +08:00
|
|
|
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 <
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_nchildren(parent) - 1) {
|
2009-04-07 10:01:24 +08:00
|
|
|
/* right sibling */
|
2010-07-13 22:33:53 +08:00
|
|
|
sibptr = nilfs_btree_node_get_ptr(parent, pindex + 1,
|
|
|
|
ncmax);
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_block(btree, sibptr, &bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_curr_node;
|
|
|
|
sib = (struct nilfs_btree_node *)bh->b_data;
|
2010-07-13 22:33:52 +08:00
|
|
|
if (nilfs_btree_node_get_nchildren(sib) > ncmin) {
|
2009-04-07 10:01:24 +08:00
|
|
|
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++;
|
2011-05-25 22:00:27 +08:00
|
|
|
/*
|
|
|
|
* When merging right sibling node
|
|
|
|
* into the current node, pointer to
|
|
|
|
* the right sibling node must be
|
|
|
|
* terminated instead. The adjustment
|
|
|
|
* below is required for that.
|
|
|
|
*/
|
|
|
|
dindex = pindex + 1;
|
2009-04-07 10:01:24 +08:00
|
|
|
/* continue; */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* no siblings */
|
|
|
|
/* the only child of the root node */
|
2009-04-07 10:01:55 +08:00
|
|
|
WARN_ON(level != nilfs_btree_height(btree) - 2);
|
2009-08-15 00:14:10 +08:00
|
|
|
if (nilfs_btree_node_get_nchildren(node) - 1 <=
|
2009-04-07 10:01:24 +08:00
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX) {
|
|
|
|
path[level].bp_op = nilfs_btree_shrink;
|
|
|
|
stats->bs_nblocks += 2;
|
2011-05-25 22:00:27 +08:00
|
|
|
level++;
|
|
|
|
path[level].bp_op = nilfs_btree_nop;
|
|
|
|
goto shrink_root_child;
|
2009-04-07 10:01:24 +08:00
|
|
|
} else {
|
|
|
|
path[level].bp_op = nilfs_btree_do_delete;
|
|
|
|
stats->bs_nblocks++;
|
2011-05-25 22:00:27 +08:00
|
|
|
goto out;
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-25 22:00:27 +08:00
|
|
|
/* child of the root node is deleted */
|
|
|
|
path[level].bp_op = nilfs_btree_do_delete;
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
|
|
|
|
shrink_root_child:
|
2009-04-07 10:01:24 +08:00
|
|
|
node = nilfs_btree_get_root(btree);
|
|
|
|
path[level].bp_oldreq.bpr_ptr =
|
2011-05-25 22:00:27 +08:00
|
|
|
nilfs_btree_node_get_ptr(node, dindex,
|
2010-07-13 22:33:53 +08:00
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2009-05-24 02:25:44 +08:00
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_bmap_prepare_end_ptr(btree, &path[level].bp_oldreq, dat);
|
2009-05-24 02:25:44 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_child_node;
|
|
|
|
|
2009-04-07 10:01:24 +08:00
|
|
|
/* success */
|
|
|
|
out:
|
|
|
|
*levelp = level;
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* error */
|
|
|
|
err_out_curr_node:
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_abort_end_ptr(btree, &path[level].bp_oldreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
err_out_child_node:
|
|
|
|
for (level--; level >= NILFS_BTREE_LEVEL_NODE_MIN; level--) {
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(path[level].bp_sib_bh);
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_abort_end_ptr(btree, &path[level].bp_oldreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
*levelp = level;
|
|
|
|
stats->bs_nblocks = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_commit_delete(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
2009-08-15 14:34:33 +08:00
|
|
|
int maxlevel, struct inode *dat)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
int level;
|
|
|
|
|
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) {
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_commit_end_ptr(btree, &path[level].bp_oldreq, dat);
|
2009-04-07 10:01:49 +08:00
|
|
|
path[level].bp_op(btree, path, level, NULL, NULL);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
if (!nilfs_bmap_dirty(btree))
|
|
|
|
nilfs_bmap_set_dirty(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key)
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
{
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
struct nilfs_bmap_stats stats;
|
2009-08-15 14:34:33 +08:00
|
|
|
struct inode *dat;
|
2009-04-07 10:01:24 +08:00
|
|
|
int level, ret;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
path = nilfs_btree_alloc_path();
|
2009-04-07 10:01:24 +08:00
|
|
|
if (path == NULL)
|
|
|
|
return -ENOMEM;
|
2010-04-02 17:36:34 +08:00
|
|
|
|
2009-04-07 10:01:24 +08:00
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, NULL,
|
2010-07-18 09:42:26 +08:00
|
|
|
NILFS_BTREE_LEVEL_NODE_MIN, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2009-08-15 14:34:33 +08:00
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL;
|
2009-08-15 14:34:33 +08:00
|
|
|
|
|
|
|
ret = nilfs_btree_prepare_delete(btree, path, &level, &stats, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_btree_commit_delete(btree, path, level, dat);
|
2011-03-04 23:19:32 +08:00
|
|
|
nilfs_inode_sub_blocks(btree->b_inode, stats.bs_nblocks);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
out:
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_free_path(path);
|
2009-04-07 10:01:24 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-04-17 03:46:36 +08:00
|
|
|
static int nilfs_btree_seek_key(const struct nilfs_bmap *btree, __u64 start,
|
|
|
|
__u64 *keyp)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
const int minlevel = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
path = nilfs_btree_alloc_path();
|
|
|
|
if (!path)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = nilfs_btree_do_lookup(btree, path, start, NULL, minlevel, 0);
|
|
|
|
if (!ret)
|
|
|
|
*keyp = start;
|
|
|
|
else if (ret == -ENOENT)
|
|
|
|
ret = nilfs_btree_get_next_key(btree, path, minlevel, keyp);
|
|
|
|
|
|
|
|
nilfs_btree_free_path(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_last_key(const struct nilfs_bmap *btree, __u64 *keyp)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
int ret;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
path = nilfs_btree_alloc_path();
|
2009-04-07 10:01:24 +08:00
|
|
|
if (path == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = nilfs_btree_do_lookup_last(btree, path, keyp, NULL);
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_free_path(path);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_check_delete(struct nilfs_bmap *btree, __u64 key)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
struct buffer_head *bh;
|
|
|
|
struct nilfs_btree_node *root, *node;
|
|
|
|
__u64 maxkey, nextmaxkey;
|
|
|
|
__u64 ptr;
|
|
|
|
int nchildren, ret;
|
|
|
|
|
|
|
|
root = nilfs_btree_get_root(btree);
|
2024-09-04 16:13:09 +08:00
|
|
|
nchildren = nilfs_btree_node_get_nchildren(root);
|
|
|
|
if (unlikely(nchildren == 0))
|
|
|
|
return 0;
|
|
|
|
|
2009-04-07 10:01:24 +08:00
|
|
|
switch (nilfs_btree_height(btree)) {
|
|
|
|
case 2:
|
|
|
|
bh = NULL;
|
|
|
|
node = root;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (nchildren > 1)
|
|
|
|
return 0;
|
2010-07-13 22:33:53 +08:00
|
|
|
ptr = nilfs_btree_node_get_ptr(root, nchildren - 1,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_block(btree, ptr, &bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
2024-09-04 16:13:09 +08:00
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
2009-04-07 10:01:24 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
maxkey = nilfs_btree_node_get_key(node, nchildren - 1);
|
2009-04-07 10:01:24 +08:00
|
|
|
nextmaxkey = (nchildren > 1) ?
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_node_get_key(node, nchildren - 2) : 0;
|
2022-09-21 11:48:02 +08:00
|
|
|
brelse(bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-05-23 23:09:44 +08:00
|
|
|
return (maxkey == key) && (nextmaxkey < NILFS_BMAP_LARGE_LOW);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_gather_data(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 *keys, __u64 *ptrs, int nitems)
|
|
|
|
{
|
|
|
|
struct buffer_head *bh;
|
|
|
|
struct nilfs_btree_node *node, *root;
|
|
|
|
__le64 *dkeys;
|
|
|
|
__le64 *dptrs;
|
|
|
|
__u64 ptr;
|
2010-07-13 22:33:53 +08:00
|
|
|
int nchildren, ncmax, i, ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
root = nilfs_btree_get_root(btree);
|
|
|
|
switch (nilfs_btree_height(btree)) {
|
|
|
|
case 2:
|
|
|
|
bh = NULL;
|
|
|
|
node = root;
|
2010-07-13 22:33:53 +08:00
|
|
|
ncmax = NILFS_BTREE_ROOT_NCHILDREN_MAX;
|
2009-04-07 10:01:24 +08:00
|
|
|
break;
|
|
|
|
case 3:
|
2009-08-15 00:14:10 +08:00
|
|
|
nchildren = nilfs_btree_node_get_nchildren(root);
|
2009-04-07 10:01:55 +08:00
|
|
|
WARN_ON(nchildren > 1);
|
2010-07-13 22:33:53 +08:00
|
|
|
ptr = nilfs_btree_node_get_ptr(root, nchildren - 1,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_block(btree, ptr, &bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
2010-07-13 22:33:53 +08:00
|
|
|
ncmax = nilfs_btree_nchildren_per_block(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
node = NULL;
|
2009-04-07 10:01:55 +08:00
|
|
|
return -EINVAL;
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
nchildren = nilfs_btree_node_get_nchildren(node);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (nchildren < nitems)
|
|
|
|
nitems = nchildren;
|
2009-08-15 00:14:10 +08:00
|
|
|
dkeys = nilfs_btree_node_dkeys(node);
|
2010-07-13 22:33:53 +08:00
|
|
|
dptrs = nilfs_btree_node_dptrs(node, ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
for (i = 0; i < nitems; i++) {
|
2010-07-10 15:50:41 +08:00
|
|
|
keys[i] = le64_to_cpu(dkeys[i]);
|
|
|
|
ptrs[i] = le64_to_cpu(dptrs[i]);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2022-09-21 11:48:02 +08:00
|
|
|
brelse(bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return nitems;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *btree, __u64 key,
|
2009-04-07 10:01:24 +08:00
|
|
|
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;
|
2009-08-15 14:34:33 +08:00
|
|
|
struct inode *dat = NULL;
|
2009-04-07 10:01:24 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
stats->bs_nblocks = 0;
|
|
|
|
|
|
|
|
/* for data */
|
|
|
|
/* cannot find near ptr */
|
2010-07-10 18:09:49 +08:00
|
|
|
if (NILFS_BMAP_USE_VBN(btree)) {
|
2009-05-24 17:07:59 +08:00
|
|
|
dreq->bpr_ptr = nilfs_btree_find_target_v(btree, NULL, key);
|
2010-07-10 18:09:49 +08:00
|
|
|
dat = nilfs_bmap_get_dat(btree);
|
2009-08-15 14:34:33 +08:00
|
|
|
}
|
2009-05-24 17:07:59 +08:00
|
|
|
|
2022-04-02 02:28:18 +08:00
|
|
|
ret = nilfs_attach_btree_node_cache(&NILFS_BMAP_I(btree)->vfs_inode);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_bmap_prepare_alloc_ptr(btree, dreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*bhp = NULL;
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
if (nreq != NULL) {
|
|
|
|
nreq->bpr_ptr = dreq->bpr_ptr + 1;
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_bmap_prepare_alloc_ptr(btree, nreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_dreq;
|
|
|
|
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_new_block(btree, nreq->bpr_ptr, &bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_out_nreq;
|
|
|
|
|
|
|
|
*bhp = bh;
|
|
|
|
stats->bs_nblocks++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* success */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* error */
|
|
|
|
err_out_nreq:
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_abort_alloc_ptr(btree, nreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
err_out_dreq:
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_abort_alloc_ptr(btree, dreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
stats->bs_nblocks = 0;
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 key, __u64 ptr,
|
|
|
|
const __u64 *keys, const __u64 *ptrs,
|
2009-05-23 23:09:44 +08:00
|
|
|
int n,
|
2009-04-07 10:01:24 +08:00
|
|
|
union nilfs_bmap_ptr_req *dreq,
|
|
|
|
union nilfs_bmap_ptr_req *nreq,
|
|
|
|
struct buffer_head *bh)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node;
|
2009-08-15 14:34:33 +08:00
|
|
|
struct inode *dat;
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 tmpptr;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ncblk;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
/* free resources */
|
2010-07-10 18:09:49 +08:00
|
|
|
if (btree->b_ops->bop_clear != NULL)
|
|
|
|
btree->b_ops->bop_clear(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
/* ptr must be a pointer to a buffer head. */
|
|
|
|
set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr));
|
|
|
|
|
|
|
|
/* convert and insert */
|
2010-07-10 18:09:49 +08:00
|
|
|
dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL;
|
2015-02-28 07:51:56 +08:00
|
|
|
__nilfs_btree_init(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (nreq != NULL) {
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_commit_alloc_ptr(btree, dreq, dat);
|
|
|
|
nilfs_bmap_commit_alloc_ptr(btree, nreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
/* create child node at level 1 */
|
|
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
2010-07-13 22:33:53 +08:00
|
|
|
ncblk = nilfs_btree_nchildren_per_block(btree);
|
|
|
|
nilfs_btree_node_init(node, 0, 1, n, ncblk, keys, ptrs);
|
|
|
|
nilfs_btree_node_insert(node, n, key, dreq->bpr_ptr, ncblk);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_dirty(bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(bh);
|
2010-07-10 18:09:49 +08:00
|
|
|
if (!nilfs_bmap_dirty(btree))
|
|
|
|
nilfs_bmap_set_dirty(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
/* create root node at level 2 */
|
|
|
|
node = nilfs_btree_get_root(btree);
|
|
|
|
tmpptr = nreq->bpr_ptr;
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_init(node, NILFS_BTREE_NODE_ROOT, 2, 1,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX,
|
|
|
|
&keys[0], &tmpptr);
|
2009-04-07 10:01:24 +08:00
|
|
|
} else {
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_bmap_commit_alloc_ptr(btree, dreq, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
/* create root node at level 1 */
|
|
|
|
node = nilfs_btree_get_root(btree);
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_init(node, NILFS_BTREE_NODE_ROOT, 1, n,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX,
|
|
|
|
keys, ptrs);
|
|
|
|
nilfs_btree_node_insert(node, n, key, dreq->bpr_ptr,
|
|
|
|
NILFS_BTREE_ROOT_NCHILDREN_MAX);
|
2010-07-10 18:09:49 +08:00
|
|
|
if (!nilfs_bmap_dirty(btree))
|
|
|
|
nilfs_bmap_set_dirty(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
if (NILFS_BMAP_USE_VBN(btree))
|
2010-07-10 21:21:54 +08:00
|
|
|
nilfs_bmap_set_target_v(btree, key, dreq->bpr_ptr);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nilfs_btree_convert_and_insert -
|
|
|
|
* @bmap:
|
|
|
|
* @key:
|
|
|
|
* @ptr:
|
|
|
|
* @keys:
|
|
|
|
* @ptrs:
|
|
|
|
* @n:
|
|
|
|
*/
|
2010-07-10 18:09:49 +08:00
|
|
|
int nilfs_btree_convert_and_insert(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 key, __u64 ptr,
|
2009-05-23 23:09:44 +08:00
|
|
|
const __u64 *keys, const __u64 *ptrs, int n)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2015-11-07 08:32:16 +08:00
|
|
|
struct buffer_head *bh = NULL;
|
2009-04-07 10:01:24 +08:00
|
|
|
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(
|
2017-02-28 06:28:35 +08:00
|
|
|
nilfs_btree_node_size(btree))) {
|
2009-04-07 10:01:24 +08:00
|
|
|
di = &dreq;
|
|
|
|
ni = &nreq;
|
|
|
|
} else {
|
|
|
|
di = NULL;
|
|
|
|
ni = NULL;
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_btree_prepare_convert_and_insert(btree, key, di, ni, &bh,
|
2009-04-07 10:01:24 +08:00
|
|
|
&stats);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2010-07-10 18:09:49 +08:00
|
|
|
nilfs_btree_commit_convert_and_insert(btree, key, ptr, keys, ptrs, n,
|
2009-05-23 23:09:44 +08:00
|
|
|
di, ni, bh);
|
2011-03-04 23:19:32 +08:00
|
|
|
nilfs_inode_add_blocks(btree->b_inode, stats.bs_nblocks);
|
2009-04-07 10:01:24 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_propagate_p(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level,
|
|
|
|
struct buffer_head *bh)
|
|
|
|
{
|
|
|
|
while ((++level < nilfs_btree_height(btree) - 1) &&
|
|
|
|
!buffer_dirty(path[level].bp_bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(path[level].bp_bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_prepare_update_v(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
2009-08-15 14:34:33 +08:00
|
|
|
int level, struct inode *dat)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
struct nilfs_btree_node *parent;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ncmax, ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_oldreq.bpr_ptr =
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index,
|
|
|
|
ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
path[level].bp_newreq.bpr_ptr = path[level].bp_oldreq.bpr_ptr + 1;
|
2009-08-15 14:34:33 +08:00
|
|
|
ret = nilfs_dat_prepare_update(dat, &path[level].bp_oldreq.bpr_req,
|
|
|
|
&path[level].bp_newreq.bpr_req);
|
2009-04-07 10:01:24 +08:00
|
|
|
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(
|
2022-04-02 02:28:18 +08:00
|
|
|
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
|
2009-04-07 10:01:24 +08:00
|
|
|
&path[level].bp_ctxt);
|
|
|
|
if (ret < 0) {
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_dat_abort_update(dat,
|
|
|
|
&path[level].bp_oldreq.bpr_req,
|
|
|
|
&path[level].bp_newreq.bpr_req);
|
2009-04-07 10:01:24 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_commit_update_v(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
2009-08-15 14:34:33 +08:00
|
|
|
int level, struct inode *dat)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
struct nilfs_btree_node *parent;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ncmax;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_dat_commit_update(dat, &path[level].bp_oldreq.bpr_req,
|
|
|
|
&path[level].bp_newreq.bpr_req,
|
2010-07-10 18:09:49 +08:00
|
|
|
btree->b_ptr_type == NILFS_BMAP_PTR_VS);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
if (buffer_nilfs_node(path[level].bp_bh)) {
|
|
|
|
nilfs_btnode_commit_change_key(
|
2022-04-02 02:28:18 +08:00
|
|
|
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
|
2009-04-07 10:01:24 +08:00
|
|
|
&path[level].bp_ctxt);
|
|
|
|
path[level].bp_bh = path[level].bp_ctxt.bh;
|
|
|
|
}
|
|
|
|
set_buffer_nilfs_volatile(path[level].bp_bh);
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax);
|
|
|
|
nilfs_btree_node_set_ptr(parent, path[level + 1].bp_index,
|
|
|
|
path[level].bp_newreq.bpr_ptr, ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_abort_update_v(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
2009-08-15 14:34:33 +08:00
|
|
|
int level, struct inode *dat)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_dat_abort_update(dat, &path[level].bp_oldreq.bpr_req,
|
|
|
|
&path[level].bp_newreq.bpr_req);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (buffer_nilfs_node(path[level].bp_bh))
|
|
|
|
nilfs_btnode_abort_change_key(
|
2022-04-02 02:28:18 +08:00
|
|
|
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
|
2009-04-07 10:01:24 +08:00
|
|
|
&path[level].bp_ctxt);
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_prepare_propagate_v(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
2009-08-15 14:34:33 +08:00
|
|
|
int minlevel, int *maxlevelp,
|
|
|
|
struct inode *dat)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
int level, ret;
|
|
|
|
|
|
|
|
level = minlevel;
|
|
|
|
if (!buffer_nilfs_volatile(path[level].bp_bh)) {
|
2009-08-15 14:34:33 +08:00
|
|
|
ret = nilfs_btree_prepare_update_v(btree, path, level, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
while ((++level < nilfs_btree_height(btree) - 1) &&
|
|
|
|
!buffer_dirty(path[level].bp_bh)) {
|
|
|
|
|
2009-04-07 10:01:55 +08:00
|
|
|
WARN_ON(buffer_nilfs_volatile(path[level].bp_bh));
|
2009-08-15 14:34:33 +08:00
|
|
|
ret = nilfs_btree_prepare_update_v(btree, path, level, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* success */
|
|
|
|
*maxlevelp = level - 1;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* error */
|
|
|
|
out:
|
|
|
|
while (--level > minlevel)
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_btree_abort_update_v(btree, path, level, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (!buffer_nilfs_volatile(path[level].bp_bh))
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_btree_abort_update_v(btree, path, level, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_commit_propagate_v(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
2009-08-15 14:34:33 +08:00
|
|
|
int minlevel, int maxlevel,
|
|
|
|
struct buffer_head *bh,
|
|
|
|
struct inode *dat)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
int level;
|
|
|
|
|
|
|
|
if (!buffer_nilfs_volatile(path[minlevel].bp_bh))
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_btree_commit_update_v(btree, path, minlevel, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
for (level = minlevel + 1; level <= maxlevel; level++)
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_btree_commit_update_v(btree, path, level, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_propagate_v(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
2009-08-15 14:34:33 +08:00
|
|
|
int level, struct buffer_head *bh)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
2010-04-02 18:40:39 +08:00
|
|
|
int maxlevel = 0, ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_node *parent;
|
2010-07-10 18:09:49 +08:00
|
|
|
struct inode *dat = nilfs_bmap_get_dat(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 ptr;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ncmax;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
get_bh(bh);
|
|
|
|
path[level].bp_bh = bh;
|
2009-08-15 14:34:33 +08:00
|
|
|
ret = nilfs_btree_prepare_propagate_v(btree, path, level, &maxlevel,
|
|
|
|
dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (buffer_nilfs_volatile(path[level].bp_bh)) {
|
2010-07-13 22:33:53 +08:00
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax);
|
|
|
|
ptr = nilfs_btree_node_get_ptr(parent,
|
|
|
|
path[level + 1].bp_index,
|
|
|
|
ncmax);
|
2009-08-15 14:34:33 +08:00
|
|
|
ret = nilfs_dat_mark_dirty(dat, ptr);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_btree_commit_propagate_v(btree, path, level, maxlevel, bh, dat);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
brelse(path[level].bp_bh);
|
|
|
|
path[level].bp_bh = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_propagate(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct buffer_head *bh)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
struct nilfs_btree_node *node;
|
|
|
|
__u64 key;
|
|
|
|
int level, ret;
|
|
|
|
|
2009-04-07 10:01:55 +08:00
|
|
|
WARN_ON(!buffer_dirty(bh));
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
path = nilfs_btree_alloc_path();
|
2009-04-07 10:01:24 +08:00
|
|
|
if (path == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (buffer_nilfs_node(bh)) {
|
|
|
|
node = (struct nilfs_btree_node *)bh->b_data;
|
2009-08-15 00:14:10 +08:00
|
|
|
key = nilfs_btree_node_get_key(node, 0);
|
|
|
|
level = nilfs_btree_node_get_level(node);
|
2009-04-07 10:01:24 +08:00
|
|
|
} else {
|
2010-07-10 18:09:49 +08:00
|
|
|
key = nilfs_bmap_data_get_key(btree, bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
level = NILFS_BTREE_LEVEL_DATA;
|
|
|
|
}
|
|
|
|
|
2010-07-18 09:42:26 +08:00
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0) {
|
2009-04-07 10:01:55 +08:00
|
|
|
if (unlikely(ret == -ENOENT))
|
2020-08-12 09:35:49 +08:00
|
|
|
nilfs_crit(btree->b_inode->i_sb,
|
|
|
|
"writing node/leaf block does not appear in b-tree (ino=%lu) at key=%llu, level=%d",
|
|
|
|
btree->b_inode->i_ino,
|
|
|
|
(unsigned long long)key, level);
|
2009-04-07 10:01:24 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = NILFS_BMAP_USE_VBN(btree) ?
|
2009-05-24 17:07:59 +08:00
|
|
|
nilfs_btree_propagate_v(btree, path, level, bh) :
|
|
|
|
nilfs_btree_propagate_p(btree, path, level, bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
out:
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_free_path(path);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_propagate_gc(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct buffer_head *bh)
|
|
|
|
{
|
2010-07-10 18:09:49 +08:00
|
|
|
return nilfs_dat_mark_dirty(nilfs_bmap_get_dat(btree), bh->b_blocknr);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_add_dirty_buffer(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
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;
|
2009-08-15 00:14:10 +08:00
|
|
|
key = nilfs_btree_node_get_key(node, 0);
|
|
|
|
level = nilfs_btree_node_get_level(node);
|
2010-07-07 16:19:54 +08:00
|
|
|
if (level < NILFS_BTREE_LEVEL_NODE_MIN ||
|
|
|
|
level >= NILFS_BTREE_LEVEL_MAX) {
|
|
|
|
dump_stack();
|
2020-08-12 09:35:49 +08:00
|
|
|
nilfs_warn(btree->b_inode->i_sb,
|
|
|
|
"invalid btree level: %d (key=%llu, ino=%lu, blocknr=%llu)",
|
|
|
|
level, (unsigned long long)key,
|
|
|
|
btree->b_inode->i_ino,
|
|
|
|
(unsigned long long)bh->b_blocknr);
|
2010-07-07 16:19:54 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-04-07 10:01:24 +08:00
|
|
|
list_for_each(head, &lists[level]) {
|
|
|
|
cbh = list_entry(head, struct buffer_head, b_assoc_buffers);
|
|
|
|
cnode = (struct nilfs_btree_node *)cbh->b_data;
|
2009-08-15 00:14:10 +08:00
|
|
|
ckey = nilfs_btree_node_get_key(cnode, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (key < ckey)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
list_add_tail(&bh->b_assoc_buffers, head);
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static void nilfs_btree_lookup_dirty_buffers(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct list_head *listp)
|
|
|
|
{
|
2022-04-02 02:28:18 +08:00
|
|
|
struct inode *btnc_inode = NILFS_BMAP_I(btree)->i_assoc_inode;
|
|
|
|
struct address_space *btcache = btnc_inode->i_mapping;
|
2009-04-07 10:01:24 +08:00
|
|
|
struct list_head lists[NILFS_BTREE_LEVEL_MAX];
|
2023-01-05 05:14:45 +08:00
|
|
|
struct folio_batch fbatch;
|
2009-04-07 10:01:24 +08:00
|
|
|
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]);
|
|
|
|
|
2023-01-05 05:14:45 +08:00
|
|
|
folio_batch_init(&fbatch);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2023-01-05 05:14:45 +08:00
|
|
|
while (filemap_get_folios_tag(btcache, &index, (pgoff_t)-1,
|
|
|
|
PAGECACHE_TAG_DIRTY, &fbatch)) {
|
|
|
|
for (i = 0; i < folio_batch_count(&fbatch); i++) {
|
|
|
|
bh = head = folio_buffers(fbatch.folios[i]);
|
2009-04-07 10:01:24 +08:00
|
|
|
do {
|
|
|
|
if (buffer_dirty(bh))
|
|
|
|
nilfs_btree_add_dirty_buffer(btree,
|
|
|
|
lists, bh);
|
|
|
|
} while ((bh = bh->b_this_page) != head);
|
|
|
|
}
|
2023-01-05 05:14:45 +08:00
|
|
|
folio_batch_release(&fbatch);
|
2009-04-07 10:01:24 +08:00
|
|
|
cond_resched();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (level = NILFS_BTREE_LEVEL_NODE_MIN;
|
|
|
|
level < NILFS_BTREE_LEVEL_MAX;
|
|
|
|
level++)
|
2009-11-29 01:39:11 +08:00
|
|
|
list_splice_tail(&lists[level], listp);
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_assign_p(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
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;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ncmax, ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax);
|
|
|
|
ptr = nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index,
|
|
|
|
ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
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(
|
2022-04-02 02:28:18 +08:00
|
|
|
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
|
2009-04-07 10:01:24 +08:00
|
|
|
&path[level].bp_ctxt);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
nilfs_btnode_commit_change_key(
|
2022-04-02 02:28:18 +08:00
|
|
|
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
|
2009-04-07 10:01:24 +08:00
|
|
|
&path[level].bp_ctxt);
|
|
|
|
*bh = path[level].bp_ctxt.bh;
|
|
|
|
}
|
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
nilfs_btree_node_set_ptr(parent, path[level + 1].bp_index, blocknr,
|
|
|
|
ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index);
|
2009-04-07 10:01:24 +08:00
|
|
|
/* on-disk format */
|
2010-07-10 15:50:41 +08:00
|
|
|
binfo->bi_dat.bi_blkoff = cpu_to_le64(key);
|
2009-04-07 10:01:24 +08:00
|
|
|
binfo->bi_dat.bi_level = level;
|
2023-03-26 23:21:46 +08:00
|
|
|
memset(binfo->bi_dat.bi_pad, 0, sizeof(binfo->bi_dat.bi_pad));
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_assign_v(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct nilfs_btree_path *path,
|
|
|
|
int level,
|
|
|
|
struct buffer_head **bh,
|
|
|
|
sector_t blocknr,
|
|
|
|
union nilfs_binfo *binfo)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *parent;
|
2010-07-10 18:09:49 +08:00
|
|
|
struct inode *dat = nilfs_bmap_get_dat(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
__u64 key;
|
|
|
|
__u64 ptr;
|
|
|
|
union nilfs_bmap_ptr_req req;
|
2010-07-13 22:33:53 +08:00
|
|
|
int ncmax, ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2010-07-13 22:33:53 +08:00
|
|
|
parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax);
|
|
|
|
ptr = nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index,
|
|
|
|
ncmax);
|
2009-04-07 10:01:24 +08:00
|
|
|
req.bpr_ptr = ptr;
|
2009-08-15 14:34:33 +08:00
|
|
|
ret = nilfs_dat_prepare_start(dat, &req.bpr_req);
|
|
|
|
if (ret < 0)
|
2009-04-07 10:01:24 +08:00
|
|
|
return ret;
|
2009-08-15 14:34:33 +08:00
|
|
|
nilfs_dat_commit_start(dat, &req.bpr_req, blocknr);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index);
|
2009-04-07 10:01:24 +08:00
|
|
|
/* on-disk format */
|
2010-07-10 15:50:41 +08:00
|
|
|
binfo->bi_v.bi_vblocknr = cpu_to_le64(ptr);
|
|
|
|
binfo->bi_v.bi_blkoff = cpu_to_le64(key);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_assign(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct buffer_head **bh,
|
|
|
|
sector_t blocknr,
|
|
|
|
union nilfs_binfo *binfo)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
struct nilfs_btree_node *node;
|
|
|
|
__u64 key;
|
|
|
|
int level, ret;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
path = nilfs_btree_alloc_path();
|
2009-04-07 10:01:24 +08:00
|
|
|
if (path == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (buffer_nilfs_node(*bh)) {
|
|
|
|
node = (struct nilfs_btree_node *)(*bh)->b_data;
|
2009-08-15 00:14:10 +08:00
|
|
|
key = nilfs_btree_node_get_key(node, 0);
|
|
|
|
level = nilfs_btree_node_get_level(node);
|
2009-04-07 10:01:24 +08:00
|
|
|
} else {
|
2010-07-10 18:09:49 +08:00
|
|
|
key = nilfs_bmap_data_get_key(btree, *bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
level = NILFS_BTREE_LEVEL_DATA;
|
|
|
|
}
|
|
|
|
|
2010-07-18 09:42:26 +08:00
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0) {
|
2009-04-07 10:01:55 +08:00
|
|
|
WARN_ON(ret == -ENOENT);
|
2009-04-07 10:01:24 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = NILFS_BMAP_USE_VBN(btree) ?
|
2009-05-24 17:07:59 +08:00
|
|
|
nilfs_btree_assign_v(btree, path, level, bh, blocknr, binfo) :
|
|
|
|
nilfs_btree_assign_p(btree, path, level, bh, blocknr, binfo);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
out:
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_free_path(path);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_assign_gc(struct nilfs_bmap *btree,
|
2009-04-07 10:01:24 +08:00
|
|
|
struct buffer_head **bh,
|
|
|
|
sector_t blocknr,
|
|
|
|
union nilfs_binfo *binfo)
|
|
|
|
{
|
|
|
|
struct nilfs_btree_node *node;
|
|
|
|
__u64 key;
|
|
|
|
int ret;
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
ret = nilfs_dat_move(nilfs_bmap_get_dat(btree), (*bh)->b_blocknr,
|
2009-08-15 14:34:33 +08:00
|
|
|
blocknr);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (buffer_nilfs_node(*bh)) {
|
|
|
|
node = (struct nilfs_btree_node *)(*bh)->b_data;
|
2009-08-15 00:14:10 +08:00
|
|
|
key = nilfs_btree_node_get_key(node, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
} else
|
2010-07-10 18:09:49 +08:00
|
|
|
key = nilfs_bmap_data_get_key(btree, *bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
/* on-disk format */
|
|
|
|
binfo->bi_v.bi_vblocknr = cpu_to_le64((*bh)->b_blocknr);
|
2010-07-10 15:50:41 +08:00
|
|
|
binfo->bi_v.bi_blkoff = cpu_to_le64(key);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-10 18:09:49 +08:00
|
|
|
static int nilfs_btree_mark(struct nilfs_bmap *btree, __u64 key, int level)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
struct buffer_head *bh;
|
|
|
|
struct nilfs_btree_path *path;
|
|
|
|
__u64 ptr;
|
|
|
|
int ret;
|
|
|
|
|
2009-08-15 00:14:10 +08:00
|
|
|
path = nilfs_btree_alloc_path();
|
2009-04-07 10:01:24 +08:00
|
|
|
if (path == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2010-07-18 09:42:26 +08:00
|
|
|
ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level + 1, 0);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0) {
|
2009-04-07 10:01:55 +08:00
|
|
|
WARN_ON(ret == -ENOENT);
|
2009-04-07 10:01:24 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2009-05-22 00:07:13 +08:00
|
|
|
ret = nilfs_btree_get_block(btree, ptr, &bh);
|
2009-04-07 10:01:24 +08:00
|
|
|
if (ret < 0) {
|
2009-04-07 10:01:55 +08:00
|
|
|
WARN_ON(ret == -ENOENT);
|
2009-04-07 10:01:24 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!buffer_dirty(bh))
|
2011-05-05 11:56:51 +08:00
|
|
|
mark_buffer_dirty(bh);
|
2009-05-21 23:33:13 +08:00
|
|
|
brelse(bh);
|
2010-07-10 18:09:49 +08:00
|
|
|
if (!nilfs_bmap_dirty(btree))
|
|
|
|
nilfs_bmap_set_dirty(btree);
|
2009-04-07 10:01:24 +08:00
|
|
|
|
|
|
|
out:
|
2009-08-15 00:14:10 +08:00
|
|
|
nilfs_btree_free_path(path);
|
2009-04-07 10:01:24 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct nilfs_bmap_operations nilfs_btree_ops = {
|
|
|
|
.bop_lookup = nilfs_btree_lookup,
|
2009-05-25 01:47:14 +08:00
|
|
|
.bop_lookup_contig = nilfs_btree_lookup_contig,
|
2009-04-07 10:01:24 +08:00
|
|
|
.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,
|
|
|
|
|
2015-04-17 03:46:36 +08:00
|
|
|
.bop_seek_key = nilfs_btree_seek_key,
|
2009-04-07 10:01:24 +08:00
|
|
|
.bop_last_key = nilfs_btree_last_key,
|
2015-04-17 03:46:36 +08:00
|
|
|
|
2009-04-07 10:01:24 +08:00
|
|
|
.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,
|
2009-05-25 01:47:14 +08:00
|
|
|
.bop_lookup_contig = NULL,
|
2009-04-07 10:01:24 +08:00
|
|
|
.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,
|
|
|
|
|
2015-04-17 03:46:36 +08:00
|
|
|
.bop_seek_key = NULL,
|
2009-04-07 10:01:24 +08:00
|
|
|
.bop_last_key = NULL,
|
2015-04-17 03:46:36 +08:00
|
|
|
|
2009-04-07 10:01:24 +08:00
|
|
|
.bop_check_insert = NULL,
|
|
|
|
.bop_check_delete = NULL,
|
|
|
|
.bop_gather_data = NULL,
|
|
|
|
};
|
|
|
|
|
2015-02-28 07:51:56 +08:00
|
|
|
static void __nilfs_btree_init(struct nilfs_bmap *bmap)
|
2009-04-07 10:01:24 +08:00
|
|
|
{
|
|
|
|
bmap->b_ops = &nilfs_btree_ops;
|
2010-07-13 22:33:54 +08:00
|
|
|
bmap->b_nchildren_per_block =
|
|
|
|
NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(bmap));
|
2015-02-28 07:51:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int nilfs_btree_init(struct nilfs_bmap *bmap)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
__nilfs_btree_init(bmap);
|
|
|
|
|
2016-08-03 05:05:10 +08:00
|
|
|
if (nilfs_btree_root_broken(nilfs_btree_get_root(bmap), bmap->b_inode))
|
2015-02-28 07:51:56 +08:00
|
|
|
ret = -EIO;
|
2022-04-02 02:28:18 +08:00
|
|
|
else
|
|
|
|
ret = nilfs_attach_btree_node_cache(
|
|
|
|
&NILFS_BMAP_I(bmap)->vfs_inode);
|
|
|
|
|
2015-02-28 07:51:56 +08:00
|
|
|
return ret;
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void nilfs_btree_init_gc(struct nilfs_bmap *bmap)
|
|
|
|
{
|
|
|
|
bmap->b_ops = &nilfs_btree_ops_gc;
|
2010-07-13 22:33:54 +08:00
|
|
|
bmap->b_nchildren_per_block =
|
|
|
|
NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(bmap));
|
2009-04-07 10:01:24 +08:00
|
|
|
}
|