xref: /openbmc/linux/fs/nfs/blocklayout/extent_tree.c (revision 8067253c)
18067253cSChristoph Hellwig /*
28067253cSChristoph Hellwig  * Copyright (c) 2014 Christoph Hellwig.
38067253cSChristoph Hellwig  */
48067253cSChristoph Hellwig 
58067253cSChristoph Hellwig #include "blocklayout.h"
68067253cSChristoph Hellwig 
78067253cSChristoph Hellwig #define NFSDBG_FACILITY		NFSDBG_PNFS_LD
88067253cSChristoph Hellwig 
98067253cSChristoph Hellwig static inline struct pnfs_block_extent *
108067253cSChristoph Hellwig ext_node(struct rb_node *node)
118067253cSChristoph Hellwig {
128067253cSChristoph Hellwig 	return rb_entry(node, struct pnfs_block_extent, be_node);
138067253cSChristoph Hellwig }
148067253cSChristoph Hellwig 
158067253cSChristoph Hellwig static struct pnfs_block_extent *
168067253cSChristoph Hellwig ext_tree_first(struct rb_root *root)
178067253cSChristoph Hellwig {
188067253cSChristoph Hellwig 	struct rb_node *node = rb_first(root);
198067253cSChristoph Hellwig 	return node ? ext_node(node) : NULL;
208067253cSChristoph Hellwig }
218067253cSChristoph Hellwig 
228067253cSChristoph Hellwig static struct pnfs_block_extent *
238067253cSChristoph Hellwig ext_tree_prev(struct pnfs_block_extent *be)
248067253cSChristoph Hellwig {
258067253cSChristoph Hellwig 	struct rb_node *node = rb_prev(&be->be_node);
268067253cSChristoph Hellwig 	return node ? ext_node(node) : NULL;
278067253cSChristoph Hellwig }
288067253cSChristoph Hellwig 
298067253cSChristoph Hellwig static struct pnfs_block_extent *
308067253cSChristoph Hellwig ext_tree_next(struct pnfs_block_extent *be)
318067253cSChristoph Hellwig {
328067253cSChristoph Hellwig 	struct rb_node *node = rb_next(&be->be_node);
338067253cSChristoph Hellwig 	return node ? ext_node(node) : NULL;
348067253cSChristoph Hellwig }
358067253cSChristoph Hellwig 
368067253cSChristoph Hellwig static inline sector_t
378067253cSChristoph Hellwig ext_f_end(struct pnfs_block_extent *be)
388067253cSChristoph Hellwig {
398067253cSChristoph Hellwig 	return be->be_f_offset + be->be_length;
408067253cSChristoph Hellwig }
418067253cSChristoph Hellwig 
428067253cSChristoph Hellwig static struct pnfs_block_extent *
438067253cSChristoph Hellwig __ext_tree_search(struct rb_root *root, sector_t start)
448067253cSChristoph Hellwig {
458067253cSChristoph Hellwig 	struct rb_node *node = root->rb_node;
468067253cSChristoph Hellwig 	struct pnfs_block_extent *be = NULL;
478067253cSChristoph Hellwig 
488067253cSChristoph Hellwig 	while (node) {
498067253cSChristoph Hellwig 		be = ext_node(node);
508067253cSChristoph Hellwig 		if (start < be->be_f_offset)
518067253cSChristoph Hellwig 			node = node->rb_left;
528067253cSChristoph Hellwig 		else if (start >= ext_f_end(be))
538067253cSChristoph Hellwig 			node = node->rb_right;
548067253cSChristoph Hellwig 		else
558067253cSChristoph Hellwig 			return be;
568067253cSChristoph Hellwig 	}
578067253cSChristoph Hellwig 
588067253cSChristoph Hellwig 	if (be) {
598067253cSChristoph Hellwig 		if (start < be->be_f_offset)
608067253cSChristoph Hellwig 			return be;
618067253cSChristoph Hellwig 
628067253cSChristoph Hellwig 		if (start >= ext_f_end(be))
638067253cSChristoph Hellwig 			return ext_tree_next(be);
648067253cSChristoph Hellwig 	}
658067253cSChristoph Hellwig 
668067253cSChristoph Hellwig 	return NULL;
678067253cSChristoph Hellwig }
688067253cSChristoph Hellwig 
698067253cSChristoph Hellwig static bool
708067253cSChristoph Hellwig ext_can_merge(struct pnfs_block_extent *be1, struct pnfs_block_extent *be2)
718067253cSChristoph Hellwig {
728067253cSChristoph Hellwig 	if (be1->be_state != be2->be_state)
738067253cSChristoph Hellwig 		return false;
748067253cSChristoph Hellwig 	if (be1->be_mdev != be2->be_mdev)
758067253cSChristoph Hellwig 		return false;
768067253cSChristoph Hellwig 
778067253cSChristoph Hellwig 	if (be1->be_f_offset + be1->be_length != be2->be_f_offset)
788067253cSChristoph Hellwig 		return false;
798067253cSChristoph Hellwig 
808067253cSChristoph Hellwig 	if (be1->be_state != PNFS_BLOCK_NONE_DATA &&
818067253cSChristoph Hellwig 	    (be1->be_v_offset + be1->be_length != be2->be_v_offset))
828067253cSChristoph Hellwig 		return false;
838067253cSChristoph Hellwig 
848067253cSChristoph Hellwig 	if (be1->be_state == PNFS_BLOCK_INVALID_DATA &&
858067253cSChristoph Hellwig 	    be1->be_tag != be2->be_tag)
868067253cSChristoph Hellwig 		return false;
878067253cSChristoph Hellwig 
888067253cSChristoph Hellwig 	return true;
898067253cSChristoph Hellwig }
908067253cSChristoph Hellwig 
918067253cSChristoph Hellwig static struct pnfs_block_extent *
928067253cSChristoph Hellwig ext_try_to_merge_left(struct rb_root *root, struct pnfs_block_extent *be)
938067253cSChristoph Hellwig {
948067253cSChristoph Hellwig 	struct pnfs_block_extent *left = ext_tree_prev(be);
958067253cSChristoph Hellwig 
968067253cSChristoph Hellwig 	if (left && ext_can_merge(left, be)) {
978067253cSChristoph Hellwig 		left->be_length += be->be_length;
988067253cSChristoph Hellwig 		rb_erase(&be->be_node, root);
998067253cSChristoph Hellwig 		kfree(be);
1008067253cSChristoph Hellwig 		return left;
1018067253cSChristoph Hellwig 	}
1028067253cSChristoph Hellwig 
1038067253cSChristoph Hellwig 	return be;
1048067253cSChristoph Hellwig }
1058067253cSChristoph Hellwig 
1068067253cSChristoph Hellwig static struct pnfs_block_extent *
1078067253cSChristoph Hellwig ext_try_to_merge_right(struct rb_root *root, struct pnfs_block_extent *be)
1088067253cSChristoph Hellwig {
1098067253cSChristoph Hellwig 	struct pnfs_block_extent *right = ext_tree_next(be);
1108067253cSChristoph Hellwig 
1118067253cSChristoph Hellwig 	if (right && ext_can_merge(be, right)) {
1128067253cSChristoph Hellwig 		be->be_length += right->be_length;
1138067253cSChristoph Hellwig 		rb_erase(&right->be_node, root);
1148067253cSChristoph Hellwig 		kfree(right);
1158067253cSChristoph Hellwig 	}
1168067253cSChristoph Hellwig 
1178067253cSChristoph Hellwig 	return be;
1188067253cSChristoph Hellwig }
1198067253cSChristoph Hellwig 
1208067253cSChristoph Hellwig static void
1218067253cSChristoph Hellwig __ext_tree_insert(struct rb_root *root,
1228067253cSChristoph Hellwig 		struct pnfs_block_extent *new, bool merge_ok)
1238067253cSChristoph Hellwig {
1248067253cSChristoph Hellwig 	struct rb_node **p = &root->rb_node, *parent = NULL;
1258067253cSChristoph Hellwig 	struct pnfs_block_extent *be;
1268067253cSChristoph Hellwig 
1278067253cSChristoph Hellwig 	while (*p) {
1288067253cSChristoph Hellwig 		parent = *p;
1298067253cSChristoph Hellwig 		be = ext_node(parent);
1308067253cSChristoph Hellwig 
1318067253cSChristoph Hellwig 		if (new->be_f_offset < be->be_f_offset) {
1328067253cSChristoph Hellwig 			if (merge_ok && ext_can_merge(new, be)) {
1338067253cSChristoph Hellwig 				be->be_f_offset = new->be_f_offset;
1348067253cSChristoph Hellwig 				if (be->be_state != PNFS_BLOCK_NONE_DATA)
1358067253cSChristoph Hellwig 					be->be_v_offset = new->be_v_offset;
1368067253cSChristoph Hellwig 				be->be_length += new->be_length;
1378067253cSChristoph Hellwig 				be = ext_try_to_merge_left(root, be);
1388067253cSChristoph Hellwig 				kfree(new);
1398067253cSChristoph Hellwig 				return;
1408067253cSChristoph Hellwig 			}
1418067253cSChristoph Hellwig 			p = &(*p)->rb_left;
1428067253cSChristoph Hellwig 		} else if (new->be_f_offset >= ext_f_end(be)) {
1438067253cSChristoph Hellwig 			if (merge_ok && ext_can_merge(be, new)) {
1448067253cSChristoph Hellwig 				be->be_length += new->be_length;
1458067253cSChristoph Hellwig 				be = ext_try_to_merge_right(root, be);
1468067253cSChristoph Hellwig 				kfree(new);
1478067253cSChristoph Hellwig 				return;
1488067253cSChristoph Hellwig 			}
1498067253cSChristoph Hellwig 			p = &(*p)->rb_right;
1508067253cSChristoph Hellwig 		} else {
1518067253cSChristoph Hellwig 			BUG();
1528067253cSChristoph Hellwig 		}
1538067253cSChristoph Hellwig 	}
1548067253cSChristoph Hellwig 
1558067253cSChristoph Hellwig 	rb_link_node(&new->be_node, parent, p);
1568067253cSChristoph Hellwig 	rb_insert_color(&new->be_node, root);
1578067253cSChristoph Hellwig }
1588067253cSChristoph Hellwig 
1598067253cSChristoph Hellwig static int
1608067253cSChristoph Hellwig __ext_tree_remove(struct rb_root *root, sector_t start, sector_t end)
1618067253cSChristoph Hellwig {
1628067253cSChristoph Hellwig 	struct pnfs_block_extent *be;
1638067253cSChristoph Hellwig 	sector_t len1 = 0, len2 = 0;
1648067253cSChristoph Hellwig 	sector_t orig_f_offset;
1658067253cSChristoph Hellwig 	sector_t orig_v_offset;
1668067253cSChristoph Hellwig 	sector_t orig_len;
1678067253cSChristoph Hellwig 
1688067253cSChristoph Hellwig 	be = __ext_tree_search(root, start);
1698067253cSChristoph Hellwig 	if (!be)
1708067253cSChristoph Hellwig 		return 0;
1718067253cSChristoph Hellwig 	if (be->be_f_offset >= end)
1728067253cSChristoph Hellwig 		return 0;
1738067253cSChristoph Hellwig 
1748067253cSChristoph Hellwig 	orig_f_offset = be->be_f_offset;
1758067253cSChristoph Hellwig 	orig_v_offset = be->be_v_offset;
1768067253cSChristoph Hellwig 	orig_len = be->be_length;
1778067253cSChristoph Hellwig 
1788067253cSChristoph Hellwig 	if (start > be->be_f_offset)
1798067253cSChristoph Hellwig 		len1 = start - be->be_f_offset;
1808067253cSChristoph Hellwig 	if (ext_f_end(be) > end)
1818067253cSChristoph Hellwig 		len2 = ext_f_end(be) - end;
1828067253cSChristoph Hellwig 
1838067253cSChristoph Hellwig 	if (len2 > 0) {
1848067253cSChristoph Hellwig 		if (len1 > 0) {
1858067253cSChristoph Hellwig 			struct pnfs_block_extent *new;
1868067253cSChristoph Hellwig 
1878067253cSChristoph Hellwig 			new = kzalloc(sizeof(*new), GFP_ATOMIC);
1888067253cSChristoph Hellwig 			if (!new)
1898067253cSChristoph Hellwig 				return -ENOMEM;
1908067253cSChristoph Hellwig 
1918067253cSChristoph Hellwig 			be->be_length = len1;
1928067253cSChristoph Hellwig 
1938067253cSChristoph Hellwig 			new->be_f_offset = end;
1948067253cSChristoph Hellwig 			if (be->be_state != PNFS_BLOCK_NONE_DATA) {
1958067253cSChristoph Hellwig 				new->be_v_offset =
1968067253cSChristoph Hellwig 					orig_v_offset + orig_len - len2;
1978067253cSChristoph Hellwig 			}
1988067253cSChristoph Hellwig 			new->be_length = len2;
1998067253cSChristoph Hellwig 			new->be_state = be->be_state;
2008067253cSChristoph Hellwig 			new->be_tag = be->be_tag;
2018067253cSChristoph Hellwig 			new->be_mdev = be->be_mdev;
2028067253cSChristoph Hellwig 			memcpy(&new->be_devid, &be->be_devid,
2038067253cSChristoph Hellwig 				sizeof(struct nfs4_deviceid));
2048067253cSChristoph Hellwig 
2058067253cSChristoph Hellwig 			__ext_tree_insert(root, new, true);
2068067253cSChristoph Hellwig 		} else {
2078067253cSChristoph Hellwig 			be->be_f_offset = end;
2088067253cSChristoph Hellwig 			if (be->be_state != PNFS_BLOCK_NONE_DATA) {
2098067253cSChristoph Hellwig 				be->be_v_offset =
2108067253cSChristoph Hellwig 					orig_v_offset + orig_len - len2;
2118067253cSChristoph Hellwig 			}
2128067253cSChristoph Hellwig 			be->be_length = len2;
2138067253cSChristoph Hellwig 		}
2148067253cSChristoph Hellwig 	} else {
2158067253cSChristoph Hellwig 		if (len1 > 0) {
2168067253cSChristoph Hellwig 			be->be_length = len1;
2178067253cSChristoph Hellwig 			be = ext_tree_next(be);
2188067253cSChristoph Hellwig 		}
2198067253cSChristoph Hellwig 
2208067253cSChristoph Hellwig 		while (be && ext_f_end(be) <= end) {
2218067253cSChristoph Hellwig 			struct pnfs_block_extent *next = ext_tree_next(be);
2228067253cSChristoph Hellwig 
2238067253cSChristoph Hellwig 			rb_erase(&be->be_node, root);
2248067253cSChristoph Hellwig 			kfree(be);
2258067253cSChristoph Hellwig 			be = next;
2268067253cSChristoph Hellwig 		}
2278067253cSChristoph Hellwig 
2288067253cSChristoph Hellwig 		if (be && be->be_f_offset < end) {
2298067253cSChristoph Hellwig 			len1 = ext_f_end(be) - end;
2308067253cSChristoph Hellwig 			be->be_f_offset = end;
2318067253cSChristoph Hellwig 			if (be->be_state != PNFS_BLOCK_NONE_DATA)
2328067253cSChristoph Hellwig 				be->be_v_offset += be->be_length - len1;
2338067253cSChristoph Hellwig 			be->be_length = len1;
2348067253cSChristoph Hellwig 		}
2358067253cSChristoph Hellwig 	}
2368067253cSChristoph Hellwig 
2378067253cSChristoph Hellwig 	return 0;
2388067253cSChristoph Hellwig }
2398067253cSChristoph Hellwig 
2408067253cSChristoph Hellwig int
2418067253cSChristoph Hellwig ext_tree_insert(struct pnfs_block_layout *bl, struct pnfs_block_extent *new)
2428067253cSChristoph Hellwig {
2438067253cSChristoph Hellwig 	struct pnfs_block_extent *be;
2448067253cSChristoph Hellwig 	struct rb_root *root;
2458067253cSChristoph Hellwig 	int err = 0;
2468067253cSChristoph Hellwig 
2478067253cSChristoph Hellwig 	switch (new->be_state) {
2488067253cSChristoph Hellwig 	case PNFS_BLOCK_READWRITE_DATA:
2498067253cSChristoph Hellwig 	case PNFS_BLOCK_INVALID_DATA:
2508067253cSChristoph Hellwig 		root = &bl->bl_ext_rw;
2518067253cSChristoph Hellwig 		break;
2528067253cSChristoph Hellwig 	case PNFS_BLOCK_READ_DATA:
2538067253cSChristoph Hellwig 	case PNFS_BLOCK_NONE_DATA:
2548067253cSChristoph Hellwig 		root = &bl->bl_ext_ro;
2558067253cSChristoph Hellwig 		break;
2568067253cSChristoph Hellwig 	default:
2578067253cSChristoph Hellwig 		dprintk("invalid extent type\n");
2588067253cSChristoph Hellwig 		return -EINVAL;
2598067253cSChristoph Hellwig 	}
2608067253cSChristoph Hellwig 
2618067253cSChristoph Hellwig 	spin_lock(&bl->bl_ext_lock);
2628067253cSChristoph Hellwig retry:
2638067253cSChristoph Hellwig 	be = __ext_tree_search(root, new->be_f_offset);
2648067253cSChristoph Hellwig 	if (!be || be->be_f_offset >= ext_f_end(new)) {
2658067253cSChristoph Hellwig 		__ext_tree_insert(root, new, true);
2668067253cSChristoph Hellwig 	} else if (new->be_f_offset >= be->be_f_offset) {
2678067253cSChristoph Hellwig 		if (ext_f_end(new) <= ext_f_end(be)) {
2688067253cSChristoph Hellwig 			kfree(new);
2698067253cSChristoph Hellwig 		} else {
2708067253cSChristoph Hellwig 			sector_t new_len = ext_f_end(new) - ext_f_end(be);
2718067253cSChristoph Hellwig 			sector_t diff = new->be_length - new_len;
2728067253cSChristoph Hellwig 
2738067253cSChristoph Hellwig 			new->be_f_offset += diff;
2748067253cSChristoph Hellwig 			new->be_v_offset += diff;
2758067253cSChristoph Hellwig 			new->be_length = new_len;
2768067253cSChristoph Hellwig 			goto retry;
2778067253cSChristoph Hellwig 		}
2788067253cSChristoph Hellwig 	} else if (ext_f_end(new) <= ext_f_end(be)) {
2798067253cSChristoph Hellwig 		new->be_length = be->be_f_offset - new->be_f_offset;
2808067253cSChristoph Hellwig 		__ext_tree_insert(root, new, true);
2818067253cSChristoph Hellwig 	} else {
2828067253cSChristoph Hellwig 		struct pnfs_block_extent *split;
2838067253cSChristoph Hellwig 		sector_t new_len = ext_f_end(new) - ext_f_end(be);
2848067253cSChristoph Hellwig 		sector_t diff = new->be_length - new_len;
2858067253cSChristoph Hellwig 
2868067253cSChristoph Hellwig 		split = kmemdup(new, sizeof(*new), GFP_ATOMIC);
2878067253cSChristoph Hellwig 		if (!split) {
2888067253cSChristoph Hellwig 			err = -EINVAL;
2898067253cSChristoph Hellwig 			goto out;
2908067253cSChristoph Hellwig 		}
2918067253cSChristoph Hellwig 
2928067253cSChristoph Hellwig 		split->be_length = be->be_f_offset - split->be_f_offset;
2938067253cSChristoph Hellwig 		__ext_tree_insert(root, split, true);
2948067253cSChristoph Hellwig 
2958067253cSChristoph Hellwig 		new->be_f_offset += diff;
2968067253cSChristoph Hellwig 		new->be_v_offset += diff;
2978067253cSChristoph Hellwig 		new->be_length = new_len;
2988067253cSChristoph Hellwig 		goto retry;
2998067253cSChristoph Hellwig 	}
3008067253cSChristoph Hellwig out:
3018067253cSChristoph Hellwig 	spin_unlock(&bl->bl_ext_lock);
3028067253cSChristoph Hellwig 	return err;
3038067253cSChristoph Hellwig }
3048067253cSChristoph Hellwig 
3058067253cSChristoph Hellwig static bool
3068067253cSChristoph Hellwig __ext_tree_lookup(struct rb_root *root, sector_t isect,
3078067253cSChristoph Hellwig 		struct pnfs_block_extent *ret)
3088067253cSChristoph Hellwig {
3098067253cSChristoph Hellwig 	struct rb_node *node;
3108067253cSChristoph Hellwig 	struct pnfs_block_extent *be;
3118067253cSChristoph Hellwig 
3128067253cSChristoph Hellwig 	node = root->rb_node;
3138067253cSChristoph Hellwig 	while (node) {
3148067253cSChristoph Hellwig 		be = ext_node(node);
3158067253cSChristoph Hellwig 		if (isect < be->be_f_offset)
3168067253cSChristoph Hellwig 			node = node->rb_left;
3178067253cSChristoph Hellwig 		else if (isect >= ext_f_end(be))
3188067253cSChristoph Hellwig 			node = node->rb_right;
3198067253cSChristoph Hellwig 		else {
3208067253cSChristoph Hellwig 			*ret = *be;
3218067253cSChristoph Hellwig 			return true;
3228067253cSChristoph Hellwig 		}
3238067253cSChristoph Hellwig 	}
3248067253cSChristoph Hellwig 
3258067253cSChristoph Hellwig 	return false;
3268067253cSChristoph Hellwig }
3278067253cSChristoph Hellwig 
3288067253cSChristoph Hellwig bool
3298067253cSChristoph Hellwig ext_tree_lookup(struct pnfs_block_layout *bl, sector_t isect,
3308067253cSChristoph Hellwig 	    struct pnfs_block_extent *ret, bool rw)
3318067253cSChristoph Hellwig {
3328067253cSChristoph Hellwig 	bool found = false;
3338067253cSChristoph Hellwig 
3348067253cSChristoph Hellwig 	spin_lock(&bl->bl_ext_lock);
3358067253cSChristoph Hellwig 	if (!rw)
3368067253cSChristoph Hellwig 		found = __ext_tree_lookup(&bl->bl_ext_ro, isect, ret);
3378067253cSChristoph Hellwig 	if (!found)
3388067253cSChristoph Hellwig 		found = __ext_tree_lookup(&bl->bl_ext_rw, isect, ret);
3398067253cSChristoph Hellwig 	spin_unlock(&bl->bl_ext_lock);
3408067253cSChristoph Hellwig 
3418067253cSChristoph Hellwig 	return found;
3428067253cSChristoph Hellwig }
3438067253cSChristoph Hellwig 
3448067253cSChristoph Hellwig int ext_tree_remove(struct pnfs_block_layout *bl, bool rw,
3458067253cSChristoph Hellwig 		sector_t start, sector_t end)
3468067253cSChristoph Hellwig {
3478067253cSChristoph Hellwig 	int err, err2;
3488067253cSChristoph Hellwig 
3498067253cSChristoph Hellwig 	spin_lock(&bl->bl_ext_lock);
3508067253cSChristoph Hellwig 	err = __ext_tree_remove(&bl->bl_ext_ro, start, end);
3518067253cSChristoph Hellwig 	if (rw) {
3528067253cSChristoph Hellwig 		err2 = __ext_tree_remove(&bl->bl_ext_rw, start, end);
3538067253cSChristoph Hellwig 		if (!err)
3548067253cSChristoph Hellwig 			err = err2;
3558067253cSChristoph Hellwig 	}
3568067253cSChristoph Hellwig 	spin_unlock(&bl->bl_ext_lock);
3578067253cSChristoph Hellwig 
3588067253cSChristoph Hellwig 	return err;
3598067253cSChristoph Hellwig }
3608067253cSChristoph Hellwig 
3618067253cSChristoph Hellwig static int
3628067253cSChristoph Hellwig ext_tree_split(struct rb_root *root, struct pnfs_block_extent *be,
3638067253cSChristoph Hellwig 		sector_t split)
3648067253cSChristoph Hellwig {
3658067253cSChristoph Hellwig 	struct pnfs_block_extent *new;
3668067253cSChristoph Hellwig 	sector_t orig_len = be->be_length;
3678067253cSChristoph Hellwig 
3688067253cSChristoph Hellwig 	dprintk("%s: need split for 0x%lx:0x%lx at 0x%lx\n",
3698067253cSChristoph Hellwig 		__func__, be->be_f_offset, ext_f_end(be), split);
3708067253cSChristoph Hellwig 
3718067253cSChristoph Hellwig 	new = kzalloc(sizeof(*new), GFP_ATOMIC);
3728067253cSChristoph Hellwig 	if (!new)
3738067253cSChristoph Hellwig 		return -ENOMEM;
3748067253cSChristoph Hellwig 
3758067253cSChristoph Hellwig 	be->be_length = split - be->be_f_offset;
3768067253cSChristoph Hellwig 
3778067253cSChristoph Hellwig 	new->be_f_offset = split;
3788067253cSChristoph Hellwig 	if (be->be_state != PNFS_BLOCK_NONE_DATA)
3798067253cSChristoph Hellwig 		new->be_v_offset = be->be_v_offset + be->be_length;
3808067253cSChristoph Hellwig 	new->be_length = orig_len - be->be_length;
3818067253cSChristoph Hellwig 	new->be_state = be->be_state;
3828067253cSChristoph Hellwig 	new->be_tag = be->be_tag;
3838067253cSChristoph Hellwig 
3848067253cSChristoph Hellwig 	new->be_mdev = be->be_mdev;
3858067253cSChristoph Hellwig 	memcpy(&new->be_devid, &be->be_devid, sizeof(struct nfs4_deviceid));
3868067253cSChristoph Hellwig 
3878067253cSChristoph Hellwig 	dprintk("%s: got 0x%lx:0x%lx!\n",
3888067253cSChristoph Hellwig 		__func__, be->be_f_offset, ext_f_end(be));
3898067253cSChristoph Hellwig 	dprintk("%s: got 0x%lx:0x%lx!\n",
3908067253cSChristoph Hellwig 		__func__, new->be_f_offset, ext_f_end(new));
3918067253cSChristoph Hellwig 
3928067253cSChristoph Hellwig 	__ext_tree_insert(root, new, false);
3938067253cSChristoph Hellwig 	return 0;
3948067253cSChristoph Hellwig }
3958067253cSChristoph Hellwig 
3968067253cSChristoph Hellwig int
3978067253cSChristoph Hellwig ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start,
3988067253cSChristoph Hellwig 		sector_t len)
3998067253cSChristoph Hellwig {
4008067253cSChristoph Hellwig 	struct rb_root *root = &bl->bl_ext_rw;
4018067253cSChristoph Hellwig 	sector_t end = start + len;
4028067253cSChristoph Hellwig 	struct pnfs_block_extent *be;
4038067253cSChristoph Hellwig 	int err = 0;
4048067253cSChristoph Hellwig 
4058067253cSChristoph Hellwig 	spin_lock(&bl->bl_ext_lock);
4068067253cSChristoph Hellwig 	/*
4078067253cSChristoph Hellwig 	 * First remove all COW extents or holes from written to range.
4088067253cSChristoph Hellwig 	 */
4098067253cSChristoph Hellwig 	err = __ext_tree_remove(&bl->bl_ext_ro, start, end);
4108067253cSChristoph Hellwig 	if (err)
4118067253cSChristoph Hellwig 		goto out;
4128067253cSChristoph Hellwig 
4138067253cSChristoph Hellwig 	/*
4148067253cSChristoph Hellwig 	 * Then mark all invalid extents in the range as written to.
4158067253cSChristoph Hellwig 	 */
4168067253cSChristoph Hellwig 	for (be = __ext_tree_search(root, start); be; be = ext_tree_next(be)) {
4178067253cSChristoph Hellwig 		if (be->be_f_offset >= end)
4188067253cSChristoph Hellwig 			break;
4198067253cSChristoph Hellwig 
4208067253cSChristoph Hellwig 		if (be->be_state != PNFS_BLOCK_INVALID_DATA || be->be_tag)
4218067253cSChristoph Hellwig 			continue;
4228067253cSChristoph Hellwig 
4238067253cSChristoph Hellwig 		if (be->be_f_offset < start) {
4248067253cSChristoph Hellwig 			struct pnfs_block_extent *left = ext_tree_prev(be);
4258067253cSChristoph Hellwig 
4268067253cSChristoph Hellwig 			if (left && ext_can_merge(left, be)) {
4278067253cSChristoph Hellwig 				sector_t diff = start - be->be_f_offset;
4288067253cSChristoph Hellwig 
4298067253cSChristoph Hellwig 				left->be_length += diff;
4308067253cSChristoph Hellwig 
4318067253cSChristoph Hellwig 				be->be_f_offset += diff;
4328067253cSChristoph Hellwig 				be->be_v_offset += diff;
4338067253cSChristoph Hellwig 				be->be_length -= diff;
4348067253cSChristoph Hellwig 			} else {
4358067253cSChristoph Hellwig 				err = ext_tree_split(root, be, start);
4368067253cSChristoph Hellwig 				if (err)
4378067253cSChristoph Hellwig 					goto out;
4388067253cSChristoph Hellwig 			}
4398067253cSChristoph Hellwig 		}
4408067253cSChristoph Hellwig 
4418067253cSChristoph Hellwig 		if (ext_f_end(be) > end) {
4428067253cSChristoph Hellwig 			struct pnfs_block_extent *right = ext_tree_next(be);
4438067253cSChristoph Hellwig 
4448067253cSChristoph Hellwig 			if (right && ext_can_merge(be, right)) {
4458067253cSChristoph Hellwig 				sector_t diff = end - be->be_f_offset;
4468067253cSChristoph Hellwig 
4478067253cSChristoph Hellwig 				be->be_length -= diff;
4488067253cSChristoph Hellwig 
4498067253cSChristoph Hellwig 				right->be_f_offset -= diff;
4508067253cSChristoph Hellwig 				right->be_v_offset -= diff;
4518067253cSChristoph Hellwig 				right->be_length += diff;
4528067253cSChristoph Hellwig 			} else {
4538067253cSChristoph Hellwig 				err = ext_tree_split(root, be, end);
4548067253cSChristoph Hellwig 				if (err)
4558067253cSChristoph Hellwig 					goto out;
4568067253cSChristoph Hellwig 			}
4578067253cSChristoph Hellwig 		}
4588067253cSChristoph Hellwig 
4598067253cSChristoph Hellwig 		if (be->be_f_offset >= start && ext_f_end(be) <= end) {
4608067253cSChristoph Hellwig 			be->be_tag = EXTENT_WRITTEN;
4618067253cSChristoph Hellwig 			be = ext_try_to_merge_left(root, be);
4628067253cSChristoph Hellwig 			be = ext_try_to_merge_right(root, be);
4638067253cSChristoph Hellwig 		}
4648067253cSChristoph Hellwig 	}
4658067253cSChristoph Hellwig out:
4668067253cSChristoph Hellwig 	spin_unlock(&bl->bl_ext_lock);
4678067253cSChristoph Hellwig 	return err;
4688067253cSChristoph Hellwig }
4698067253cSChristoph Hellwig 
4708067253cSChristoph Hellwig int
4718067253cSChristoph Hellwig ext_tree_encode_commit(struct pnfs_block_layout *bl, struct xdr_stream *xdr)
4728067253cSChristoph Hellwig {
4738067253cSChristoph Hellwig 	struct pnfs_block_extent *be;
4748067253cSChristoph Hellwig 	unsigned int count = 0;
4758067253cSChristoph Hellwig 	__be32 *p, *xdr_start;
4768067253cSChristoph Hellwig 	int ret = 0;
4778067253cSChristoph Hellwig 
4788067253cSChristoph Hellwig 	dprintk("%s enter\n", __func__);
4798067253cSChristoph Hellwig 
4808067253cSChristoph Hellwig 	xdr_start = xdr_reserve_space(xdr, 8);
4818067253cSChristoph Hellwig 	if (!xdr_start)
4828067253cSChristoph Hellwig 		return -ENOSPC;
4838067253cSChristoph Hellwig 
4848067253cSChristoph Hellwig 	spin_lock(&bl->bl_ext_lock);
4858067253cSChristoph Hellwig 	for (be = ext_tree_first(&bl->bl_ext_rw); be; be = ext_tree_next(be)) {
4868067253cSChristoph Hellwig 		if (be->be_state != PNFS_BLOCK_INVALID_DATA ||
4878067253cSChristoph Hellwig 		    be->be_tag != EXTENT_WRITTEN)
4888067253cSChristoph Hellwig 			continue;
4898067253cSChristoph Hellwig 
4908067253cSChristoph Hellwig 		p = xdr_reserve_space(xdr, 7 * sizeof(__be32) +
4918067253cSChristoph Hellwig 					NFS4_DEVICEID4_SIZE);
4928067253cSChristoph Hellwig 		if (!p) {
4938067253cSChristoph Hellwig 			printk("%s: out of space for extent list\n", __func__);
4948067253cSChristoph Hellwig 			ret = -ENOSPC;
4958067253cSChristoph Hellwig 			break;
4968067253cSChristoph Hellwig 		}
4978067253cSChristoph Hellwig 
4988067253cSChristoph Hellwig 		p = xdr_encode_opaque_fixed(p, be->be_devid.data,
4998067253cSChristoph Hellwig 				NFS4_DEVICEID4_SIZE);
5008067253cSChristoph Hellwig 		p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT);
5018067253cSChristoph Hellwig 		p = xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT);
5028067253cSChristoph Hellwig 		p = xdr_encode_hyper(p, 0LL);
5038067253cSChristoph Hellwig 		*p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA);
5048067253cSChristoph Hellwig 
5058067253cSChristoph Hellwig 		be->be_tag = EXTENT_COMMITTING;
5068067253cSChristoph Hellwig 		count++;
5078067253cSChristoph Hellwig 	}
5088067253cSChristoph Hellwig 	spin_unlock(&bl->bl_ext_lock);
5098067253cSChristoph Hellwig 
5108067253cSChristoph Hellwig 	xdr_start[0] = cpu_to_be32((xdr->p - xdr_start - 1) * 4);
5118067253cSChristoph Hellwig 	xdr_start[1] = cpu_to_be32(count);
5128067253cSChristoph Hellwig 
5138067253cSChristoph Hellwig 	dprintk("%s found %i ranges\n", __func__, count);
5148067253cSChristoph Hellwig 	return ret;
5158067253cSChristoph Hellwig }
5168067253cSChristoph Hellwig 
5178067253cSChristoph Hellwig void
5188067253cSChristoph Hellwig ext_tree_mark_committed(struct pnfs_block_layout *bl, int status)
5198067253cSChristoph Hellwig {
5208067253cSChristoph Hellwig 	struct rb_root *root = &bl->bl_ext_rw;
5218067253cSChristoph Hellwig 	struct pnfs_block_extent *be;
5228067253cSChristoph Hellwig 
5238067253cSChristoph Hellwig 	dprintk("%s status %d\n", __func__, status);
5248067253cSChristoph Hellwig 
5258067253cSChristoph Hellwig 	spin_lock(&bl->bl_ext_lock);
5268067253cSChristoph Hellwig 	for (be = ext_tree_first(root); be; be = ext_tree_next(be)) {
5278067253cSChristoph Hellwig 		if (be->be_state != PNFS_BLOCK_INVALID_DATA ||
5288067253cSChristoph Hellwig 		    be->be_tag != EXTENT_COMMITTING)
5298067253cSChristoph Hellwig 			continue;
5308067253cSChristoph Hellwig 
5318067253cSChristoph Hellwig 		if (status) {
5328067253cSChristoph Hellwig 			/*
5338067253cSChristoph Hellwig 			 * Mark as written and try again.
5348067253cSChristoph Hellwig 			 *
5358067253cSChristoph Hellwig 			 * XXX: some real error handling here wouldn't hurt..
5368067253cSChristoph Hellwig 			 */
5378067253cSChristoph Hellwig 			be->be_tag = EXTENT_WRITTEN;
5388067253cSChristoph Hellwig 		} else {
5398067253cSChristoph Hellwig 			be->be_state = PNFS_BLOCK_READWRITE_DATA;
5408067253cSChristoph Hellwig 			be->be_tag = 0;
5418067253cSChristoph Hellwig 		}
5428067253cSChristoph Hellwig 
5438067253cSChristoph Hellwig 		be = ext_try_to_merge_left(root, be);
5448067253cSChristoph Hellwig 		be = ext_try_to_merge_right(root, be);
5458067253cSChristoph Hellwig 	}
5468067253cSChristoph Hellwig 	spin_unlock(&bl->bl_ext_lock);
5478067253cSChristoph Hellwig }
548