18067253cSChristoph Hellwig /* 2d9186c03SChristoph Hellwig * Copyright (c) 2014-2016 Christoph Hellwig. 38067253cSChristoph Hellwig */ 48067253cSChristoph Hellwig 5b262b35cSStephen Rothwell #include <linux/vmalloc.h> 6b262b35cSStephen Rothwell 78067253cSChristoph Hellwig #include "blocklayout.h" 88067253cSChristoph Hellwig 98067253cSChristoph Hellwig #define NFSDBG_FACILITY NFSDBG_PNFS_LD 108067253cSChristoph Hellwig 118067253cSChristoph Hellwig static inline struct pnfs_block_extent * 128067253cSChristoph Hellwig ext_node(struct rb_node *node) 138067253cSChristoph Hellwig { 148067253cSChristoph Hellwig return rb_entry(node, struct pnfs_block_extent, be_node); 158067253cSChristoph Hellwig } 168067253cSChristoph Hellwig 178067253cSChristoph Hellwig static struct pnfs_block_extent * 188067253cSChristoph Hellwig ext_tree_first(struct rb_root *root) 198067253cSChristoph Hellwig { 208067253cSChristoph Hellwig struct rb_node *node = rb_first(root); 218067253cSChristoph Hellwig return node ? ext_node(node) : NULL; 228067253cSChristoph Hellwig } 238067253cSChristoph Hellwig 248067253cSChristoph Hellwig static struct pnfs_block_extent * 258067253cSChristoph Hellwig ext_tree_prev(struct pnfs_block_extent *be) 268067253cSChristoph Hellwig { 278067253cSChristoph Hellwig struct rb_node *node = rb_prev(&be->be_node); 288067253cSChristoph Hellwig return node ? ext_node(node) : NULL; 298067253cSChristoph Hellwig } 308067253cSChristoph Hellwig 318067253cSChristoph Hellwig static struct pnfs_block_extent * 328067253cSChristoph Hellwig ext_tree_next(struct pnfs_block_extent *be) 338067253cSChristoph Hellwig { 348067253cSChristoph Hellwig struct rb_node *node = rb_next(&be->be_node); 358067253cSChristoph Hellwig return node ? ext_node(node) : NULL; 368067253cSChristoph Hellwig } 378067253cSChristoph Hellwig 388067253cSChristoph Hellwig static inline sector_t 398067253cSChristoph Hellwig ext_f_end(struct pnfs_block_extent *be) 408067253cSChristoph Hellwig { 418067253cSChristoph Hellwig return be->be_f_offset + be->be_length; 428067253cSChristoph Hellwig } 438067253cSChristoph Hellwig 448067253cSChristoph Hellwig static struct pnfs_block_extent * 458067253cSChristoph Hellwig __ext_tree_search(struct rb_root *root, sector_t start) 468067253cSChristoph Hellwig { 478067253cSChristoph Hellwig struct rb_node *node = root->rb_node; 488067253cSChristoph Hellwig struct pnfs_block_extent *be = NULL; 498067253cSChristoph Hellwig 508067253cSChristoph Hellwig while (node) { 518067253cSChristoph Hellwig be = ext_node(node); 528067253cSChristoph Hellwig if (start < be->be_f_offset) 538067253cSChristoph Hellwig node = node->rb_left; 548067253cSChristoph Hellwig else if (start >= ext_f_end(be)) 558067253cSChristoph Hellwig node = node->rb_right; 568067253cSChristoph Hellwig else 578067253cSChristoph Hellwig return be; 588067253cSChristoph Hellwig } 598067253cSChristoph Hellwig 608067253cSChristoph Hellwig if (be) { 618067253cSChristoph Hellwig if (start < be->be_f_offset) 628067253cSChristoph Hellwig return be; 638067253cSChristoph Hellwig 648067253cSChristoph Hellwig if (start >= ext_f_end(be)) 658067253cSChristoph Hellwig return ext_tree_next(be); 668067253cSChristoph Hellwig } 678067253cSChristoph Hellwig 688067253cSChristoph Hellwig return NULL; 698067253cSChristoph Hellwig } 708067253cSChristoph Hellwig 718067253cSChristoph Hellwig static bool 728067253cSChristoph Hellwig ext_can_merge(struct pnfs_block_extent *be1, struct pnfs_block_extent *be2) 738067253cSChristoph Hellwig { 748067253cSChristoph Hellwig if (be1->be_state != be2->be_state) 758067253cSChristoph Hellwig return false; 7620d655d6SChristoph Hellwig if (be1->be_device != be2->be_device) 778067253cSChristoph Hellwig return false; 788067253cSChristoph Hellwig 798067253cSChristoph Hellwig if (be1->be_f_offset + be1->be_length != be2->be_f_offset) 808067253cSChristoph Hellwig return false; 818067253cSChristoph Hellwig 828067253cSChristoph Hellwig if (be1->be_state != PNFS_BLOCK_NONE_DATA && 838067253cSChristoph Hellwig (be1->be_v_offset + be1->be_length != be2->be_v_offset)) 848067253cSChristoph Hellwig return false; 858067253cSChristoph Hellwig 868067253cSChristoph Hellwig if (be1->be_state == PNFS_BLOCK_INVALID_DATA && 878067253cSChristoph Hellwig be1->be_tag != be2->be_tag) 888067253cSChristoph Hellwig return false; 898067253cSChristoph Hellwig 908067253cSChristoph Hellwig return true; 918067253cSChristoph Hellwig } 928067253cSChristoph Hellwig 938067253cSChristoph Hellwig static struct pnfs_block_extent * 948067253cSChristoph Hellwig ext_try_to_merge_left(struct rb_root *root, struct pnfs_block_extent *be) 958067253cSChristoph Hellwig { 968067253cSChristoph Hellwig struct pnfs_block_extent *left = ext_tree_prev(be); 978067253cSChristoph Hellwig 988067253cSChristoph Hellwig if (left && ext_can_merge(left, be)) { 998067253cSChristoph Hellwig left->be_length += be->be_length; 1008067253cSChristoph Hellwig rb_erase(&be->be_node, root); 10120d655d6SChristoph Hellwig nfs4_put_deviceid_node(be->be_device); 1028067253cSChristoph Hellwig kfree(be); 1038067253cSChristoph Hellwig return left; 1048067253cSChristoph Hellwig } 1058067253cSChristoph Hellwig 1068067253cSChristoph Hellwig return be; 1078067253cSChristoph Hellwig } 1088067253cSChristoph Hellwig 1098067253cSChristoph Hellwig static struct pnfs_block_extent * 1108067253cSChristoph Hellwig ext_try_to_merge_right(struct rb_root *root, struct pnfs_block_extent *be) 1118067253cSChristoph Hellwig { 1128067253cSChristoph Hellwig struct pnfs_block_extent *right = ext_tree_next(be); 1138067253cSChristoph Hellwig 1148067253cSChristoph Hellwig if (right && ext_can_merge(be, right)) { 1158067253cSChristoph Hellwig be->be_length += right->be_length; 1168067253cSChristoph Hellwig rb_erase(&right->be_node, root); 11720d655d6SChristoph Hellwig nfs4_put_deviceid_node(right->be_device); 1188067253cSChristoph Hellwig kfree(right); 1198067253cSChristoph Hellwig } 1208067253cSChristoph Hellwig 1218067253cSChristoph Hellwig return be; 1228067253cSChristoph Hellwig } 1238067253cSChristoph Hellwig 124d9c0ce0eSBenjamin Coddington static void __ext_put_deviceids(struct list_head *head) 125d9c0ce0eSBenjamin Coddington { 126d9c0ce0eSBenjamin Coddington struct pnfs_block_extent *be, *tmp; 127d9c0ce0eSBenjamin Coddington 128d9c0ce0eSBenjamin Coddington list_for_each_entry_safe(be, tmp, head, be_list) { 129d9c0ce0eSBenjamin Coddington nfs4_put_deviceid_node(be->be_device); 130d9c0ce0eSBenjamin Coddington kfree(be); 131d9c0ce0eSBenjamin Coddington } 132d9c0ce0eSBenjamin Coddington } 133d9c0ce0eSBenjamin Coddington 1348067253cSChristoph Hellwig static void 1358067253cSChristoph Hellwig __ext_tree_insert(struct rb_root *root, 1368067253cSChristoph Hellwig struct pnfs_block_extent *new, bool merge_ok) 1378067253cSChristoph Hellwig { 1388067253cSChristoph Hellwig struct rb_node **p = &root->rb_node, *parent = NULL; 1398067253cSChristoph Hellwig struct pnfs_block_extent *be; 1408067253cSChristoph Hellwig 1418067253cSChristoph Hellwig while (*p) { 1428067253cSChristoph Hellwig parent = *p; 1438067253cSChristoph Hellwig be = ext_node(parent); 1448067253cSChristoph Hellwig 1458067253cSChristoph Hellwig if (new->be_f_offset < be->be_f_offset) { 1468067253cSChristoph Hellwig if (merge_ok && ext_can_merge(new, be)) { 1478067253cSChristoph Hellwig be->be_f_offset = new->be_f_offset; 1488067253cSChristoph Hellwig if (be->be_state != PNFS_BLOCK_NONE_DATA) 1498067253cSChristoph Hellwig be->be_v_offset = new->be_v_offset; 1508067253cSChristoph Hellwig be->be_length += new->be_length; 1518067253cSChristoph Hellwig be = ext_try_to_merge_left(root, be); 15220d655d6SChristoph Hellwig goto free_new; 1538067253cSChristoph Hellwig } 1548067253cSChristoph Hellwig p = &(*p)->rb_left; 1558067253cSChristoph Hellwig } else if (new->be_f_offset >= ext_f_end(be)) { 1568067253cSChristoph Hellwig if (merge_ok && ext_can_merge(be, new)) { 1578067253cSChristoph Hellwig be->be_length += new->be_length; 1588067253cSChristoph Hellwig be = ext_try_to_merge_right(root, be); 15920d655d6SChristoph Hellwig goto free_new; 1608067253cSChristoph Hellwig } 1618067253cSChristoph Hellwig p = &(*p)->rb_right; 1628067253cSChristoph Hellwig } else { 1638067253cSChristoph Hellwig BUG(); 1648067253cSChristoph Hellwig } 1658067253cSChristoph Hellwig } 1668067253cSChristoph Hellwig 1678067253cSChristoph Hellwig rb_link_node(&new->be_node, parent, p); 1688067253cSChristoph Hellwig rb_insert_color(&new->be_node, root); 16920d655d6SChristoph Hellwig return; 17020d655d6SChristoph Hellwig free_new: 17120d655d6SChristoph Hellwig nfs4_put_deviceid_node(new->be_device); 17220d655d6SChristoph Hellwig kfree(new); 1738067253cSChristoph Hellwig } 1748067253cSChristoph Hellwig 1758067253cSChristoph Hellwig static int 176d9c0ce0eSBenjamin Coddington __ext_tree_remove(struct rb_root *root, 177d9c0ce0eSBenjamin Coddington sector_t start, sector_t end, struct list_head *tmp) 1788067253cSChristoph Hellwig { 1798067253cSChristoph Hellwig struct pnfs_block_extent *be; 1808067253cSChristoph Hellwig sector_t len1 = 0, len2 = 0; 1818067253cSChristoph Hellwig sector_t orig_v_offset; 1828067253cSChristoph Hellwig sector_t orig_len; 1838067253cSChristoph Hellwig 1848067253cSChristoph Hellwig be = __ext_tree_search(root, start); 1858067253cSChristoph Hellwig if (!be) 1868067253cSChristoph Hellwig return 0; 1878067253cSChristoph Hellwig if (be->be_f_offset >= end) 1888067253cSChristoph Hellwig return 0; 1898067253cSChristoph Hellwig 1908067253cSChristoph Hellwig orig_v_offset = be->be_v_offset; 1918067253cSChristoph Hellwig orig_len = be->be_length; 1928067253cSChristoph Hellwig 1938067253cSChristoph Hellwig if (start > be->be_f_offset) 1948067253cSChristoph Hellwig len1 = start - be->be_f_offset; 1958067253cSChristoph Hellwig if (ext_f_end(be) > end) 1968067253cSChristoph Hellwig len2 = ext_f_end(be) - end; 1978067253cSChristoph Hellwig 1988067253cSChristoph Hellwig if (len2 > 0) { 1998067253cSChristoph Hellwig if (len1 > 0) { 2008067253cSChristoph Hellwig struct pnfs_block_extent *new; 2018067253cSChristoph Hellwig 2028067253cSChristoph Hellwig new = kzalloc(sizeof(*new), GFP_ATOMIC); 2038067253cSChristoph Hellwig if (!new) 2048067253cSChristoph Hellwig return -ENOMEM; 2058067253cSChristoph Hellwig 2068067253cSChristoph Hellwig be->be_length = len1; 2078067253cSChristoph Hellwig 2088067253cSChristoph Hellwig new->be_f_offset = end; 2098067253cSChristoph Hellwig if (be->be_state != PNFS_BLOCK_NONE_DATA) { 2108067253cSChristoph Hellwig new->be_v_offset = 2118067253cSChristoph Hellwig orig_v_offset + orig_len - len2; 2128067253cSChristoph Hellwig } 2138067253cSChristoph Hellwig new->be_length = len2; 2148067253cSChristoph Hellwig new->be_state = be->be_state; 2158067253cSChristoph Hellwig new->be_tag = be->be_tag; 21620d655d6SChristoph Hellwig new->be_device = nfs4_get_deviceid(be->be_device); 2178067253cSChristoph Hellwig 2188067253cSChristoph Hellwig __ext_tree_insert(root, new, true); 2198067253cSChristoph Hellwig } else { 2208067253cSChristoph Hellwig be->be_f_offset = end; 2218067253cSChristoph Hellwig if (be->be_state != PNFS_BLOCK_NONE_DATA) { 2228067253cSChristoph Hellwig be->be_v_offset = 2238067253cSChristoph Hellwig orig_v_offset + orig_len - len2; 2248067253cSChristoph Hellwig } 2258067253cSChristoph Hellwig be->be_length = len2; 2268067253cSChristoph Hellwig } 2278067253cSChristoph Hellwig } else { 2288067253cSChristoph Hellwig if (len1 > 0) { 2298067253cSChristoph Hellwig be->be_length = len1; 2308067253cSChristoph Hellwig be = ext_tree_next(be); 2318067253cSChristoph Hellwig } 2328067253cSChristoph Hellwig 2338067253cSChristoph Hellwig while (be && ext_f_end(be) <= end) { 2348067253cSChristoph Hellwig struct pnfs_block_extent *next = ext_tree_next(be); 2358067253cSChristoph Hellwig 2368067253cSChristoph Hellwig rb_erase(&be->be_node, root); 237d9c0ce0eSBenjamin Coddington list_add_tail(&be->be_list, tmp); 2388067253cSChristoph Hellwig be = next; 2398067253cSChristoph Hellwig } 2408067253cSChristoph Hellwig 2418067253cSChristoph Hellwig if (be && be->be_f_offset < end) { 2428067253cSChristoph Hellwig len1 = ext_f_end(be) - end; 2438067253cSChristoph Hellwig be->be_f_offset = end; 2448067253cSChristoph Hellwig if (be->be_state != PNFS_BLOCK_NONE_DATA) 2458067253cSChristoph Hellwig be->be_v_offset += be->be_length - len1; 2468067253cSChristoph Hellwig be->be_length = len1; 2478067253cSChristoph Hellwig } 2488067253cSChristoph Hellwig } 2498067253cSChristoph Hellwig 2508067253cSChristoph Hellwig return 0; 2518067253cSChristoph Hellwig } 2528067253cSChristoph Hellwig 2538067253cSChristoph Hellwig int 2548067253cSChristoph Hellwig ext_tree_insert(struct pnfs_block_layout *bl, struct pnfs_block_extent *new) 2558067253cSChristoph Hellwig { 2568067253cSChristoph Hellwig struct pnfs_block_extent *be; 2578067253cSChristoph Hellwig struct rb_root *root; 2588067253cSChristoph Hellwig int err = 0; 2598067253cSChristoph Hellwig 2608067253cSChristoph Hellwig switch (new->be_state) { 2618067253cSChristoph Hellwig case PNFS_BLOCK_READWRITE_DATA: 2628067253cSChristoph Hellwig case PNFS_BLOCK_INVALID_DATA: 2638067253cSChristoph Hellwig root = &bl->bl_ext_rw; 2648067253cSChristoph Hellwig break; 2658067253cSChristoph Hellwig case PNFS_BLOCK_READ_DATA: 2668067253cSChristoph Hellwig case PNFS_BLOCK_NONE_DATA: 2678067253cSChristoph Hellwig root = &bl->bl_ext_ro; 2688067253cSChristoph Hellwig break; 2698067253cSChristoph Hellwig default: 2708067253cSChristoph Hellwig dprintk("invalid extent type\n"); 2718067253cSChristoph Hellwig return -EINVAL; 2728067253cSChristoph Hellwig } 2738067253cSChristoph Hellwig 2748067253cSChristoph Hellwig spin_lock(&bl->bl_ext_lock); 2758067253cSChristoph Hellwig retry: 2768067253cSChristoph Hellwig be = __ext_tree_search(root, new->be_f_offset); 2778067253cSChristoph Hellwig if (!be || be->be_f_offset >= ext_f_end(new)) { 2788067253cSChristoph Hellwig __ext_tree_insert(root, new, true); 2798067253cSChristoph Hellwig } else if (new->be_f_offset >= be->be_f_offset) { 2808067253cSChristoph Hellwig if (ext_f_end(new) <= ext_f_end(be)) { 28120d655d6SChristoph Hellwig nfs4_put_deviceid_node(new->be_device); 2828067253cSChristoph Hellwig kfree(new); 2838067253cSChristoph Hellwig } else { 2848067253cSChristoph Hellwig sector_t new_len = ext_f_end(new) - ext_f_end(be); 2858067253cSChristoph Hellwig sector_t diff = new->be_length - new_len; 2868067253cSChristoph Hellwig 2878067253cSChristoph Hellwig new->be_f_offset += diff; 2888067253cSChristoph Hellwig new->be_v_offset += diff; 2898067253cSChristoph Hellwig new->be_length = new_len; 2908067253cSChristoph Hellwig goto retry; 2918067253cSChristoph Hellwig } 2928067253cSChristoph Hellwig } else if (ext_f_end(new) <= ext_f_end(be)) { 2938067253cSChristoph Hellwig new->be_length = be->be_f_offset - new->be_f_offset; 2948067253cSChristoph Hellwig __ext_tree_insert(root, new, true); 2958067253cSChristoph Hellwig } else { 2968067253cSChristoph Hellwig struct pnfs_block_extent *split; 2978067253cSChristoph Hellwig sector_t new_len = ext_f_end(new) - ext_f_end(be); 2988067253cSChristoph Hellwig sector_t diff = new->be_length - new_len; 2998067253cSChristoph Hellwig 3008067253cSChristoph Hellwig split = kmemdup(new, sizeof(*new), GFP_ATOMIC); 3018067253cSChristoph Hellwig if (!split) { 3028067253cSChristoph Hellwig err = -EINVAL; 3038067253cSChristoph Hellwig goto out; 3048067253cSChristoph Hellwig } 3058067253cSChristoph Hellwig 3068067253cSChristoph Hellwig split->be_length = be->be_f_offset - split->be_f_offset; 30720d655d6SChristoph Hellwig split->be_device = nfs4_get_deviceid(new->be_device); 3088067253cSChristoph Hellwig __ext_tree_insert(root, split, true); 3098067253cSChristoph Hellwig 3108067253cSChristoph Hellwig new->be_f_offset += diff; 3118067253cSChristoph Hellwig new->be_v_offset += diff; 3128067253cSChristoph Hellwig new->be_length = new_len; 3138067253cSChristoph Hellwig goto retry; 3148067253cSChristoph Hellwig } 3158067253cSChristoph Hellwig out: 3168067253cSChristoph Hellwig spin_unlock(&bl->bl_ext_lock); 3178067253cSChristoph Hellwig return err; 3188067253cSChristoph Hellwig } 3198067253cSChristoph Hellwig 3208067253cSChristoph Hellwig static bool 3218067253cSChristoph Hellwig __ext_tree_lookup(struct rb_root *root, sector_t isect, 3228067253cSChristoph Hellwig struct pnfs_block_extent *ret) 3238067253cSChristoph Hellwig { 3248067253cSChristoph Hellwig struct rb_node *node; 3258067253cSChristoph Hellwig struct pnfs_block_extent *be; 3268067253cSChristoph Hellwig 3278067253cSChristoph Hellwig node = root->rb_node; 3288067253cSChristoph Hellwig while (node) { 3298067253cSChristoph Hellwig be = ext_node(node); 3308067253cSChristoph Hellwig if (isect < be->be_f_offset) 3318067253cSChristoph Hellwig node = node->rb_left; 3328067253cSChristoph Hellwig else if (isect >= ext_f_end(be)) 3338067253cSChristoph Hellwig node = node->rb_right; 3348067253cSChristoph Hellwig else { 3358067253cSChristoph Hellwig *ret = *be; 3368067253cSChristoph Hellwig return true; 3378067253cSChristoph Hellwig } 3388067253cSChristoph Hellwig } 3398067253cSChristoph Hellwig 3408067253cSChristoph Hellwig return false; 3418067253cSChristoph Hellwig } 3428067253cSChristoph Hellwig 3438067253cSChristoph Hellwig bool 3448067253cSChristoph Hellwig ext_tree_lookup(struct pnfs_block_layout *bl, sector_t isect, 3458067253cSChristoph Hellwig struct pnfs_block_extent *ret, bool rw) 3468067253cSChristoph Hellwig { 3478067253cSChristoph Hellwig bool found = false; 3488067253cSChristoph Hellwig 3498067253cSChristoph Hellwig spin_lock(&bl->bl_ext_lock); 3508067253cSChristoph Hellwig if (!rw) 3518067253cSChristoph Hellwig found = __ext_tree_lookup(&bl->bl_ext_ro, isect, ret); 3528067253cSChristoph Hellwig if (!found) 3538067253cSChristoph Hellwig found = __ext_tree_lookup(&bl->bl_ext_rw, isect, ret); 3548067253cSChristoph Hellwig spin_unlock(&bl->bl_ext_lock); 3558067253cSChristoph Hellwig 3568067253cSChristoph Hellwig return found; 3578067253cSChristoph Hellwig } 3588067253cSChristoph Hellwig 3598067253cSChristoph Hellwig int ext_tree_remove(struct pnfs_block_layout *bl, bool rw, 3608067253cSChristoph Hellwig sector_t start, sector_t end) 3618067253cSChristoph Hellwig { 3628067253cSChristoph Hellwig int err, err2; 363d9c0ce0eSBenjamin Coddington LIST_HEAD(tmp); 3648067253cSChristoph Hellwig 3658067253cSChristoph Hellwig spin_lock(&bl->bl_ext_lock); 366d9c0ce0eSBenjamin Coddington err = __ext_tree_remove(&bl->bl_ext_ro, start, end, &tmp); 3678067253cSChristoph Hellwig if (rw) { 368d9c0ce0eSBenjamin Coddington err2 = __ext_tree_remove(&bl->bl_ext_rw, start, end, &tmp); 3698067253cSChristoph Hellwig if (!err) 3708067253cSChristoph Hellwig err = err2; 3718067253cSChristoph Hellwig } 3728067253cSChristoph Hellwig spin_unlock(&bl->bl_ext_lock); 3738067253cSChristoph Hellwig 374d9c0ce0eSBenjamin Coddington __ext_put_deviceids(&tmp); 3758067253cSChristoph Hellwig return err; 3768067253cSChristoph Hellwig } 3778067253cSChristoph Hellwig 3788067253cSChristoph Hellwig static int 3798067253cSChristoph Hellwig ext_tree_split(struct rb_root *root, struct pnfs_block_extent *be, 3808067253cSChristoph Hellwig sector_t split) 3818067253cSChristoph Hellwig { 3828067253cSChristoph Hellwig struct pnfs_block_extent *new; 3838067253cSChristoph Hellwig sector_t orig_len = be->be_length; 3848067253cSChristoph Hellwig 3858067253cSChristoph Hellwig new = kzalloc(sizeof(*new), GFP_ATOMIC); 3868067253cSChristoph Hellwig if (!new) 3878067253cSChristoph Hellwig return -ENOMEM; 3888067253cSChristoph Hellwig 3898067253cSChristoph Hellwig be->be_length = split - be->be_f_offset; 3908067253cSChristoph Hellwig 3918067253cSChristoph Hellwig new->be_f_offset = split; 3928067253cSChristoph Hellwig if (be->be_state != PNFS_BLOCK_NONE_DATA) 3938067253cSChristoph Hellwig new->be_v_offset = be->be_v_offset + be->be_length; 3948067253cSChristoph Hellwig new->be_length = orig_len - be->be_length; 3958067253cSChristoph Hellwig new->be_state = be->be_state; 3968067253cSChristoph Hellwig new->be_tag = be->be_tag; 39720d655d6SChristoph Hellwig new->be_device = nfs4_get_deviceid(be->be_device); 3988067253cSChristoph Hellwig 3998067253cSChristoph Hellwig __ext_tree_insert(root, new, false); 4008067253cSChristoph Hellwig return 0; 4018067253cSChristoph Hellwig } 4028067253cSChristoph Hellwig 4038067253cSChristoph Hellwig int 4048067253cSChristoph Hellwig ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start, 4058067253cSChristoph Hellwig sector_t len) 4068067253cSChristoph Hellwig { 4078067253cSChristoph Hellwig struct rb_root *root = &bl->bl_ext_rw; 4088067253cSChristoph Hellwig sector_t end = start + len; 4098067253cSChristoph Hellwig struct pnfs_block_extent *be; 4108067253cSChristoph Hellwig int err = 0; 411d9c0ce0eSBenjamin Coddington LIST_HEAD(tmp); 4128067253cSChristoph Hellwig 4138067253cSChristoph Hellwig spin_lock(&bl->bl_ext_lock); 4148067253cSChristoph Hellwig /* 4158067253cSChristoph Hellwig * First remove all COW extents or holes from written to range. 4168067253cSChristoph Hellwig */ 417d9c0ce0eSBenjamin Coddington err = __ext_tree_remove(&bl->bl_ext_ro, start, end, &tmp); 4188067253cSChristoph Hellwig if (err) 4198067253cSChristoph Hellwig goto out; 4208067253cSChristoph Hellwig 4218067253cSChristoph Hellwig /* 4228067253cSChristoph Hellwig * Then mark all invalid extents in the range as written to. 4238067253cSChristoph Hellwig */ 4248067253cSChristoph Hellwig for (be = __ext_tree_search(root, start); be; be = ext_tree_next(be)) { 4258067253cSChristoph Hellwig if (be->be_f_offset >= end) 4268067253cSChristoph Hellwig break; 4278067253cSChristoph Hellwig 4288067253cSChristoph Hellwig if (be->be_state != PNFS_BLOCK_INVALID_DATA || be->be_tag) 4298067253cSChristoph Hellwig continue; 4308067253cSChristoph Hellwig 4318067253cSChristoph Hellwig if (be->be_f_offset < start) { 4328067253cSChristoph Hellwig struct pnfs_block_extent *left = ext_tree_prev(be); 4338067253cSChristoph Hellwig 4348067253cSChristoph Hellwig if (left && ext_can_merge(left, be)) { 4358067253cSChristoph Hellwig sector_t diff = start - be->be_f_offset; 4368067253cSChristoph Hellwig 4378067253cSChristoph Hellwig left->be_length += diff; 4388067253cSChristoph Hellwig 4398067253cSChristoph Hellwig be->be_f_offset += diff; 4408067253cSChristoph Hellwig be->be_v_offset += diff; 4418067253cSChristoph Hellwig be->be_length -= diff; 4428067253cSChristoph Hellwig } else { 4438067253cSChristoph Hellwig err = ext_tree_split(root, be, start); 4448067253cSChristoph Hellwig if (err) 4458067253cSChristoph Hellwig goto out; 4468067253cSChristoph Hellwig } 4478067253cSChristoph Hellwig } 4488067253cSChristoph Hellwig 4498067253cSChristoph Hellwig if (ext_f_end(be) > end) { 4508067253cSChristoph Hellwig struct pnfs_block_extent *right = ext_tree_next(be); 4518067253cSChristoph Hellwig 4528067253cSChristoph Hellwig if (right && ext_can_merge(be, right)) { 4538067253cSChristoph Hellwig sector_t diff = end - be->be_f_offset; 4548067253cSChristoph Hellwig 4558067253cSChristoph Hellwig be->be_length -= diff; 4568067253cSChristoph Hellwig 4578067253cSChristoph Hellwig right->be_f_offset -= diff; 4588067253cSChristoph Hellwig right->be_v_offset -= diff; 4598067253cSChristoph Hellwig right->be_length += diff; 4608067253cSChristoph Hellwig } else { 4618067253cSChristoph Hellwig err = ext_tree_split(root, be, end); 4628067253cSChristoph Hellwig if (err) 4638067253cSChristoph Hellwig goto out; 4648067253cSChristoph Hellwig } 4658067253cSChristoph Hellwig } 4668067253cSChristoph Hellwig 4678067253cSChristoph Hellwig if (be->be_f_offset >= start && ext_f_end(be) <= end) { 4688067253cSChristoph Hellwig be->be_tag = EXTENT_WRITTEN; 4698067253cSChristoph Hellwig be = ext_try_to_merge_left(root, be); 4708067253cSChristoph Hellwig be = ext_try_to_merge_right(root, be); 4718067253cSChristoph Hellwig } 4728067253cSChristoph Hellwig } 4738067253cSChristoph Hellwig out: 4748067253cSChristoph Hellwig spin_unlock(&bl->bl_ext_lock); 475d9c0ce0eSBenjamin Coddington 476d9c0ce0eSBenjamin Coddington __ext_put_deviceids(&tmp); 4778067253cSChristoph Hellwig return err; 4788067253cSChristoph Hellwig } 4798067253cSChristoph Hellwig 480d9186c03SChristoph Hellwig static size_t ext_tree_layoutupdate_size(struct pnfs_block_layout *bl, size_t count) 48129662fa6SChristoph Hellwig { 482d9186c03SChristoph Hellwig if (bl->bl_scsi_layout) 483d9186c03SChristoph Hellwig return sizeof(__be32) + PNFS_SCSI_RANGE_SIZE * count; 484d9186c03SChristoph Hellwig else 485d9186c03SChristoph Hellwig return sizeof(__be32) + PNFS_BLOCK_EXTENT_SIZE * count; 48629662fa6SChristoph Hellwig } 48729662fa6SChristoph Hellwig 48834dc93c2SChristoph Hellwig static void ext_tree_free_commitdata(struct nfs4_layoutcommit_args *arg, 48934dc93c2SChristoph Hellwig size_t buffer_size) 49034dc93c2SChristoph Hellwig { 49134dc93c2SChristoph Hellwig if (arg->layoutupdate_pages != &arg->layoutupdate_page) { 49234dc93c2SChristoph Hellwig int nr_pages = DIV_ROUND_UP(buffer_size, PAGE_SIZE), i; 49334dc93c2SChristoph Hellwig 49434dc93c2SChristoph Hellwig for (i = 0; i < nr_pages; i++) 49534dc93c2SChristoph Hellwig put_page(arg->layoutupdate_pages[i]); 496c8975706SKinglong Mee vfree(arg->start_p); 49734dc93c2SChristoph Hellwig kfree(arg->layoutupdate_pages); 49834dc93c2SChristoph Hellwig } else { 49934dc93c2SChristoph Hellwig put_page(arg->layoutupdate_page); 50034dc93c2SChristoph Hellwig } 50134dc93c2SChristoph Hellwig } 50234dc93c2SChristoph Hellwig 503d9186c03SChristoph Hellwig static __be32 *encode_block_extent(struct pnfs_block_extent *be, __be32 *p) 504d9186c03SChristoph Hellwig { 505d9186c03SChristoph Hellwig p = xdr_encode_opaque_fixed(p, be->be_device->deviceid.data, 506d9186c03SChristoph Hellwig NFS4_DEVICEID4_SIZE); 507d9186c03SChristoph Hellwig p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT); 508d9186c03SChristoph Hellwig p = xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT); 509d9186c03SChristoph Hellwig p = xdr_encode_hyper(p, 0LL); 510d9186c03SChristoph Hellwig *p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA); 511d9186c03SChristoph Hellwig return p; 512d9186c03SChristoph Hellwig } 513d9186c03SChristoph Hellwig 514d9186c03SChristoph Hellwig static __be32 *encode_scsi_range(struct pnfs_block_extent *be, __be32 *p) 515d9186c03SChristoph Hellwig { 516d9186c03SChristoph Hellwig p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT); 517d9186c03SChristoph Hellwig return xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT); 518d9186c03SChristoph Hellwig } 519d9186c03SChristoph Hellwig 52034dc93c2SChristoph Hellwig static int ext_tree_encode_commit(struct pnfs_block_layout *bl, __be32 *p, 52134dc93c2SChristoph Hellwig size_t buffer_size, size_t *count) 5228067253cSChristoph Hellwig { 5238067253cSChristoph Hellwig struct pnfs_block_extent *be; 5248067253cSChristoph Hellwig int ret = 0; 5258067253cSChristoph Hellwig 5268067253cSChristoph Hellwig spin_lock(&bl->bl_ext_lock); 5278067253cSChristoph Hellwig for (be = ext_tree_first(&bl->bl_ext_rw); be; be = ext_tree_next(be)) { 5288067253cSChristoph Hellwig if (be->be_state != PNFS_BLOCK_INVALID_DATA || 5298067253cSChristoph Hellwig be->be_tag != EXTENT_WRITTEN) 5308067253cSChristoph Hellwig continue; 5318067253cSChristoph Hellwig 53234dc93c2SChristoph Hellwig (*count)++; 533d9186c03SChristoph Hellwig if (ext_tree_layoutupdate_size(bl, *count) > buffer_size) { 53434dc93c2SChristoph Hellwig /* keep counting.. */ 5358067253cSChristoph Hellwig ret = -ENOSPC; 53634dc93c2SChristoph Hellwig continue; 5378067253cSChristoph Hellwig } 5388067253cSChristoph Hellwig 539d9186c03SChristoph Hellwig if (bl->bl_scsi_layout) 540d9186c03SChristoph Hellwig p = encode_scsi_range(be, p); 541d9186c03SChristoph Hellwig else 542d9186c03SChristoph Hellwig p = encode_block_extent(be, p); 5438067253cSChristoph Hellwig be->be_tag = EXTENT_COMMITTING; 5448067253cSChristoph Hellwig } 5458067253cSChristoph Hellwig spin_unlock(&bl->bl_ext_lock); 5468067253cSChristoph Hellwig 5478067253cSChristoph Hellwig return ret; 5488067253cSChristoph Hellwig } 5498067253cSChristoph Hellwig 55034dc93c2SChristoph Hellwig int 55134dc93c2SChristoph Hellwig ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg) 5528067253cSChristoph Hellwig { 55334dc93c2SChristoph Hellwig struct pnfs_block_layout *bl = BLK_LO2EXT(NFS_I(arg->inode)->layout); 55434dc93c2SChristoph Hellwig size_t count = 0, buffer_size = PAGE_SIZE; 55534dc93c2SChristoph Hellwig __be32 *start_p; 55634dc93c2SChristoph Hellwig int ret; 55734dc93c2SChristoph Hellwig 55834dc93c2SChristoph Hellwig dprintk("%s enter\n", __func__); 55934dc93c2SChristoph Hellwig 56034dc93c2SChristoph Hellwig arg->layoutupdate_page = alloc_page(GFP_NOFS); 56134dc93c2SChristoph Hellwig if (!arg->layoutupdate_page) 56234dc93c2SChristoph Hellwig return -ENOMEM; 56334dc93c2SChristoph Hellwig start_p = page_address(arg->layoutupdate_page); 56434dc93c2SChristoph Hellwig arg->layoutupdate_pages = &arg->layoutupdate_page; 56534dc93c2SChristoph Hellwig 56634dc93c2SChristoph Hellwig retry: 56734dc93c2SChristoph Hellwig ret = ext_tree_encode_commit(bl, start_p + 1, buffer_size, &count); 56834dc93c2SChristoph Hellwig if (unlikely(ret)) { 56934dc93c2SChristoph Hellwig ext_tree_free_commitdata(arg, buffer_size); 57034dc93c2SChristoph Hellwig 571d9186c03SChristoph Hellwig buffer_size = ext_tree_layoutupdate_size(bl, count); 57234dc93c2SChristoph Hellwig count = 0; 57334dc93c2SChristoph Hellwig 57434dc93c2SChristoph Hellwig arg->layoutupdate_pages = 57534dc93c2SChristoph Hellwig kcalloc(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 57634dc93c2SChristoph Hellwig sizeof(struct page *), GFP_NOFS); 57734dc93c2SChristoph Hellwig if (!arg->layoutupdate_pages) 57834dc93c2SChristoph Hellwig return -ENOMEM; 57934dc93c2SChristoph Hellwig 58034dc93c2SChristoph Hellwig start_p = __vmalloc(buffer_size, GFP_NOFS, PAGE_KERNEL); 58134dc93c2SChristoph Hellwig if (!start_p) { 58234dc93c2SChristoph Hellwig kfree(arg->layoutupdate_pages); 58334dc93c2SChristoph Hellwig return -ENOMEM; 58434dc93c2SChristoph Hellwig } 58534dc93c2SChristoph Hellwig 58634dc93c2SChristoph Hellwig goto retry; 58734dc93c2SChristoph Hellwig } 58834dc93c2SChristoph Hellwig 58934dc93c2SChristoph Hellwig *start_p = cpu_to_be32(count); 590d9186c03SChristoph Hellwig arg->layoutupdate_len = ext_tree_layoutupdate_size(bl, count); 59134dc93c2SChristoph Hellwig 59234dc93c2SChristoph Hellwig if (unlikely(arg->layoutupdate_pages != &arg->layoutupdate_page)) { 59368596bd1SChristoph Hellwig void *p = start_p, *end = p + arg->layoutupdate_len; 594c8975706SKinglong Mee struct page *page = NULL; 59534dc93c2SChristoph Hellwig int i = 0; 59634dc93c2SChristoph Hellwig 597c8975706SKinglong Mee arg->start_p = start_p; 598c8975706SKinglong Mee for ( ; p < end; p += PAGE_SIZE) { 599c8975706SKinglong Mee page = vmalloc_to_page(p); 600c8975706SKinglong Mee arg->layoutupdate_pages[i++] = page; 601c8975706SKinglong Mee get_page(page); 602c8975706SKinglong Mee } 60334dc93c2SChristoph Hellwig } 60434dc93c2SChristoph Hellwig 60534dc93c2SChristoph Hellwig dprintk("%s found %zu ranges\n", __func__, count); 60634dc93c2SChristoph Hellwig return 0; 60734dc93c2SChristoph Hellwig } 60834dc93c2SChristoph Hellwig 60934dc93c2SChristoph Hellwig void 61034dc93c2SChristoph Hellwig ext_tree_mark_committed(struct nfs4_layoutcommit_args *arg, int status) 61134dc93c2SChristoph Hellwig { 61234dc93c2SChristoph Hellwig struct pnfs_block_layout *bl = BLK_LO2EXT(NFS_I(arg->inode)->layout); 6138067253cSChristoph Hellwig struct rb_root *root = &bl->bl_ext_rw; 6148067253cSChristoph Hellwig struct pnfs_block_extent *be; 6158067253cSChristoph Hellwig 6168067253cSChristoph Hellwig dprintk("%s status %d\n", __func__, status); 6178067253cSChristoph Hellwig 61834dc93c2SChristoph Hellwig ext_tree_free_commitdata(arg, arg->layoutupdate_len); 61934dc93c2SChristoph Hellwig 6208067253cSChristoph Hellwig spin_lock(&bl->bl_ext_lock); 6218067253cSChristoph Hellwig for (be = ext_tree_first(root); be; be = ext_tree_next(be)) { 6228067253cSChristoph Hellwig if (be->be_state != PNFS_BLOCK_INVALID_DATA || 6238067253cSChristoph Hellwig be->be_tag != EXTENT_COMMITTING) 6248067253cSChristoph Hellwig continue; 6258067253cSChristoph Hellwig 6268067253cSChristoph Hellwig if (status) { 6278067253cSChristoph Hellwig /* 6288067253cSChristoph Hellwig * Mark as written and try again. 6298067253cSChristoph Hellwig * 6308067253cSChristoph Hellwig * XXX: some real error handling here wouldn't hurt.. 6318067253cSChristoph Hellwig */ 6328067253cSChristoph Hellwig be->be_tag = EXTENT_WRITTEN; 6338067253cSChristoph Hellwig } else { 6348067253cSChristoph Hellwig be->be_state = PNFS_BLOCK_READWRITE_DATA; 6358067253cSChristoph Hellwig be->be_tag = 0; 6368067253cSChristoph Hellwig } 6378067253cSChristoph Hellwig 6388067253cSChristoph Hellwig be = ext_try_to_merge_left(root, be); 6398067253cSChristoph Hellwig be = ext_try_to_merge_right(root, be); 6408067253cSChristoph Hellwig } 6418067253cSChristoph Hellwig spin_unlock(&bl->bl_ext_lock); 6428067253cSChristoph Hellwig } 643