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