tux3: Add ->leaf_pre_write() callback to btree_write()

For overwrite mode, we don't want to dirty leaf if all extents are
exists already.

But, current btree_write() dirty leaf unconditionally. So, this adds
->leaf_pre_write() callback before dirty leaf.

With this, leaf operations can control whether leaf is dirtied or not.

Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
diff --git a/fs/tux3/btree.c b/fs/tux3/btree.c
index d414efb..04d0655 100644
--- a/fs/tux3/btree.c
+++ b/fs/tux3/btree.c
@@ -1161,6 +1161,12 @@
 	return 0;
 }
 
+int noop_pre_write(struct btree *btree, tuxkey_t key_bottom, tuxkey_t key_limit,
+		   void *leaf, struct btree_key_range *key)
+{
+	return BTREE_DO_DIRTY;
+}
+
 int btree_write(struct cursor *cursor, struct btree_key_range *key)
 {
 	struct btree *btree = cursor->btree;
@@ -1171,34 +1177,46 @@
 	while (key->len > 0) {
 		tuxkey_t bottom, limit;
 		void *leaf;
-		int need_split;
+		int ret;
 
 		err = btree_advance(cursor, key);
 		if (err)
 			return err;	/* FIXME: error handling */
 
-		err = cursor_redirect(cursor);
-		if (err)
-			return err;	/* FIXME: error handling */
-
 		bottom = cursor_this_key(cursor);
 		limit = cursor_next_key(cursor);
-		leaf = bufdata(cursor_leafbuf(cursor));
 		assert(bottom <= key->start && key->start < limit);
-		assert(ops->leaf_sniff(btree, leaf));
 
-		need_split = ops->leaf_write(btree, bottom, limit, leaf, key,
-					     &split_hint);
-		if (need_split < 0)
-			return need_split;
-		else if (!need_split) {
-			mark_buffer_dirty_non(cursor_leafbuf(cursor));
+		leaf = bufdata(cursor_leafbuf(cursor));
+		ret = ops->leaf_pre_write(btree, bottom, limit, leaf, key);
+		assert(ret >= 0);
+		if (ret == BTREE_DO_RETRY)
 			continue;
+
+		if (ret == BTREE_DO_DIRTY) {
+			err = cursor_redirect(cursor);
+			if (err)
+				return err;	/* FIXME: error handling */
+
+			/* Reread leaf after redirect */
+			leaf = bufdata(cursor_leafbuf(cursor));
+			assert(ops->leaf_sniff(btree, leaf));
+
+			ret = ops->leaf_write(btree, bottom, limit, leaf, key,
+					      &split_hint);
+			if (ret < 0)
+				return ret;
+			if (ret == BTREE_DO_RETRY) {
+				mark_buffer_dirty_non(cursor_leafbuf(cursor));
+				continue;
+			}
 		}
 
-		err = btree_leaf_split(cursor, key->start, split_hint);
-		if (err)
-			return err;	/* FIXME: error handling */
+		if (ret == BTREE_DO_SPLIT) {
+			err = btree_leaf_split(cursor, key->start, split_hint);
+			if (err)
+				return err;	/* FIXME: error handling */
+		}
 	}
 
 	return 0;
diff --git a/fs/tux3/dleaf2.c b/fs/tux3/dleaf2.c
index fa424c6..4c40681 100644
--- a/fs/tux3/dleaf2.c
+++ b/fs/tux3/dleaf2.c
@@ -676,7 +676,7 @@
 		goto need_split;
 	}
 
-	return 0;
+	return BTREE_DO_RETRY;
 
 need_split:
 	/* FIXME: do we should split at sentinel when filling hole? */
@@ -690,13 +690,13 @@
 			tux3_dbg("key %Lu bottom %Lu, limit %Lu, hint %Lu",
 				 key->start, key_bottom, key_limit,
 				 *split_hint);
-			return 1;
+			return BTREE_DO_SPLIT;
 		}
 	}
 
 	/* FIXME: use better split position */
 	*split_hint = dleaf2_split_at_center(dleaf);
-	return 1;	/* try to split dleaf2 */
+	return BTREE_DO_SPLIT;
 }
 
 /* Read extents */
@@ -774,6 +774,7 @@
 	.leaf_split	= dleaf2_split,
 	.leaf_merge	= dleaf2_merge,
 	.leaf_chop	= dleaf2_chop,
+	.leaf_pre_write	= noop_pre_write,
 	.leaf_write	= dleaf2_write,
 	.leaf_read	= dleaf2_read,
 	.balloc		= balloc,
diff --git a/fs/tux3/ileaf.c b/fs/tux3/ileaf.c
index 6cbbd5b..a3c747c 100644
--- a/fs/tux3/ileaf.c
+++ b/fs/tux3/ileaf.c
@@ -441,7 +441,7 @@
 	if (attrs == NULL) {
 		/* There is no space to store */
 		*split_hint = ileaf_split_hint(btree, ileaf, key->start, size);
-		return 1;	/* need to split */
+		return BTREE_DO_SPLIT;
 	}
 
 	attr_ops->encode(btree, rq->data, attrs, size);
@@ -449,7 +449,7 @@
 	key->start++;
 	key->len--;
 
-	return 0;
+	return BTREE_DO_RETRY;
 }
 
 static int ileaf_read(struct btree *btree, tuxkey_t key_bottom,
@@ -475,6 +475,7 @@
 	.leaf_split	= ileaf_split,
 	.leaf_merge	= ileaf_merge,
 	.leaf_chop	= ileaf_chop,
+	.leaf_pre_write	= noop_pre_write,
 	.leaf_write	= ileaf_write,
 	.leaf_read	= ileaf_read,
 	.balloc		= balloc,
@@ -491,6 +492,7 @@
 	.leaf_split	= ileaf_split,
 	.leaf_merge	= ileaf_merge,
 	.leaf_chop	= ileaf_chop,
+	.leaf_pre_write	= noop_pre_write,
 	.leaf_write	= ileaf_write,
 	.leaf_read	= ileaf_read,
 	.balloc		= balloc,
diff --git a/fs/tux3/tux3.h b/fs/tux3/tux3.h
index 2519115..c073f3b 100644
--- a/fs/tux3/tux3.h
+++ b/fs/tux3/tux3.h
@@ -557,6 +557,12 @@
 	unsigned len;
 };
 
+enum btree_result {
+	BTREE_DO_RETRY = 0,
+	BTREE_DO_DIRTY,
+	BTREE_DO_SPLIT,
+};
+
 struct btree_ops {
 	void (*btree_init)(struct btree *btree);
 	int (*leaf_init)(struct btree *btree, void *leaf);
@@ -565,7 +571,9 @@
 	int (*leaf_chop)(struct btree *btree, tuxkey_t start, u64 len, void *leaf);
 	/* return value: 1 - merged, 0 - couldn't merge */
 	int (*leaf_merge)(struct btree *btree, void *into, void *from);
-	/* return value: 1 - need to split leaf, 0 - success, < 0 - error */
+	/* return value: < 0 - error, 0 >= - btree_result */
+	int (*leaf_pre_write)(struct btree *btree, tuxkey_t key_bottom, tuxkey_t key_limit, void *leaf, struct btree_key_range *key);
+	/* return value: < 0 - error, 0 >= - btree_result */
 	int (*leaf_write)(struct btree *btree, tuxkey_t key_bottom, tuxkey_t key_limit, void *leaf, struct btree_key_range *key, tuxkey_t *split_hint);
 	int (*leaf_read)(struct btree *btree, tuxkey_t key_bottom, tuxkey_t key_limit, void *leaf, struct btree_key_range *key);
 	int (*balloc)(struct sb *sb, unsigned blocks, struct block_segment *seg, int segs);
@@ -721,6 +729,8 @@
 int btree_chop(struct btree *btree, tuxkey_t start, u64 len);
 int btree_insert_leaf(struct cursor *cursor, tuxkey_t key, struct buffer_head *leafbuf);
 void *btree_expand(struct cursor *cursor, tuxkey_t key, unsigned newsize);
+int noop_pre_write(struct btree *btree, tuxkey_t key_bottom, tuxkey_t key_limit,
+		   void *leaf, struct btree_key_range *key);
 int btree_write(struct cursor *cursor, struct btree_key_range *key);
 int btree_read(struct cursor *cursor, struct btree_key_range *key);
 void show_tree_range(struct btree *btree, tuxkey_t start, unsigned count);