1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2014-2016 Christoph Hellwig. 4 */ 5 6 #include <linux/vmalloc.h> 7 8 #include "blocklayout.h" 9 10 #define NFSDBG_FACILITY NFSDBG_PNFS_LD 11 12 static inline struct pnfs_block_extent * 13 ext_node(struct rb_node *node) 14 { 15 return rb_entry(node, struct pnfs_block_extent, be_node); 16 } 17 18 static struct pnfs_block_extent * 19 ext_tree_first(struct rb_root *root) 20 { 21 struct rb_node *node = rb_first(root); 22 return node ? ext_node(node) : NULL; 23 } 24 25 static struct pnfs_block_extent * 26 ext_tree_prev(struct pnfs_block_extent *be) 27 { 28 struct rb_node *node = rb_prev(&be->be_node); 29 return node ? ext_node(node) : NULL; 30 } 31 32 static struct pnfs_block_extent * 33 ext_tree_next(struct pnfs_block_extent *be) 34 { 35 struct rb_node *node = rb_next(&be->be_node); 36 return node ? ext_node(node) : NULL; 37 } 38 39 static inline sector_t 40 ext_f_end(struct pnfs_block_extent *be) 41 { 42 return be->be_f_offset + be->be_length; 43 } 44 45 static struct pnfs_block_extent * 46 __ext_tree_search(struct rb_root *root, sector_t start) 47 { 48 struct rb_node *node = root->rb_node; 49 struct pnfs_block_extent *be = NULL; 50 51 while (node) { 52 be = ext_node(node); 53 if (start < be->be_f_offset) 54 node = node->rb_left; 55 else if (start >= ext_f_end(be)) 56 node = node->rb_right; 57 else 58 return be; 59 } 60 61 if (be) { 62 if (start < be->be_f_offset) 63 return be; 64 65 if (start >= ext_f_end(be)) 66 return ext_tree_next(be); 67 } 68 69 return NULL; 70 } 71 72 static bool 73 ext_can_merge(struct pnfs_block_extent *be1, struct pnfs_block_extent *be2) 74 { 75 if (be1->be_state != be2->be_state) 76 return false; 77 if (be1->be_device != be2->be_device) 78 return false; 79 80 if (be1->be_f_offset + be1->be_length != be2->be_f_offset) 81 return false; 82 83 if (be1->be_state != PNFS_BLOCK_NONE_DATA && 84 (be1->be_v_offset + be1->be_length != be2->be_v_offset)) 85 return false; 86 87 if (be1->be_state == PNFS_BLOCK_INVALID_DATA && 88 be1->be_tag != be2->be_tag) 89 return false; 90 91 return true; 92 } 93 94 static struct pnfs_block_extent * 95 ext_try_to_merge_left(struct rb_root *root, struct pnfs_block_extent *be) 96 { 97 struct pnfs_block_extent *left = ext_tree_prev(be); 98 99 if (left && ext_can_merge(left, be)) { 100 left->be_length += be->be_length; 101 rb_erase(&be->be_node, root); 102 nfs4_put_deviceid_node(be->be_device); 103 kfree(be); 104 return left; 105 } 106 107 return be; 108 } 109 110 static struct pnfs_block_extent * 111 ext_try_to_merge_right(struct rb_root *root, struct pnfs_block_extent *be) 112 { 113 struct pnfs_block_extent *right = ext_tree_next(be); 114 115 if (right && ext_can_merge(be, right)) { 116 be->be_length += right->be_length; 117 rb_erase(&right->be_node, root); 118 nfs4_put_deviceid_node(right->be_device); 119 kfree(right); 120 } 121 122 return be; 123 } 124 125 static void __ext_put_deviceids(struct list_head *head) 126 { 127 struct pnfs_block_extent *be, *tmp; 128 129 list_for_each_entry_safe(be, tmp, head, be_list) { 130 nfs4_put_deviceid_node(be->be_device); 131 kfree(be); 132 } 133 } 134 135 static void 136 __ext_tree_insert(struct rb_root *root, 137 struct pnfs_block_extent *new, bool merge_ok) 138 { 139 struct rb_node **p = &root->rb_node, *parent = NULL; 140 struct pnfs_block_extent *be; 141 142 while (*p) { 143 parent = *p; 144 be = ext_node(parent); 145 146 if (new->be_f_offset < be->be_f_offset) { 147 if (merge_ok && ext_can_merge(new, be)) { 148 be->be_f_offset = new->be_f_offset; 149 if (be->be_state != PNFS_BLOCK_NONE_DATA) 150 be->be_v_offset = new->be_v_offset; 151 be->be_length += new->be_length; 152 be = ext_try_to_merge_left(root, be); 153 goto free_new; 154 } 155 p = &(*p)->rb_left; 156 } else if (new->be_f_offset >= ext_f_end(be)) { 157 if (merge_ok && ext_can_merge(be, new)) { 158 be->be_length += new->be_length; 159 be = ext_try_to_merge_right(root, be); 160 goto free_new; 161 } 162 p = &(*p)->rb_right; 163 } else { 164 BUG(); 165 } 166 } 167 168 rb_link_node(&new->be_node, parent, p); 169 rb_insert_color(&new->be_node, root); 170 return; 171 free_new: 172 nfs4_put_deviceid_node(new->be_device); 173 kfree(new); 174 } 175 176 static int 177 __ext_tree_remove(struct rb_root *root, 178 sector_t start, sector_t end, struct list_head *tmp) 179 { 180 struct pnfs_block_extent *be; 181 sector_t len1 = 0, len2 = 0; 182 sector_t orig_v_offset; 183 sector_t orig_len; 184 185 be = __ext_tree_search(root, start); 186 if (!be) 187 return 0; 188 if (be->be_f_offset >= end) 189 return 0; 190 191 orig_v_offset = be->be_v_offset; 192 orig_len = be->be_length; 193 194 if (start > be->be_f_offset) 195 len1 = start - be->be_f_offset; 196 if (ext_f_end(be) > end) 197 len2 = ext_f_end(be) - end; 198 199 if (len2 > 0) { 200 if (len1 > 0) { 201 struct pnfs_block_extent *new; 202 203 new = kzalloc(sizeof(*new), GFP_ATOMIC); 204 if (!new) 205 return -ENOMEM; 206 207 be->be_length = len1; 208 209 new->be_f_offset = end; 210 if (be->be_state != PNFS_BLOCK_NONE_DATA) { 211 new->be_v_offset = 212 orig_v_offset + orig_len - len2; 213 } 214 new->be_length = len2; 215 new->be_state = be->be_state; 216 new->be_tag = be->be_tag; 217 new->be_device = nfs4_get_deviceid(be->be_device); 218 219 __ext_tree_insert(root, new, true); 220 } else { 221 be->be_f_offset = end; 222 if (be->be_state != PNFS_BLOCK_NONE_DATA) { 223 be->be_v_offset = 224 orig_v_offset + orig_len - len2; 225 } 226 be->be_length = len2; 227 } 228 } else { 229 if (len1 > 0) { 230 be->be_length = len1; 231 be = ext_tree_next(be); 232 } 233 234 while (be && ext_f_end(be) <= end) { 235 struct pnfs_block_extent *next = ext_tree_next(be); 236 237 rb_erase(&be->be_node, root); 238 list_add_tail(&be->be_list, tmp); 239 be = next; 240 } 241 242 if (be && be->be_f_offset < end) { 243 len1 = ext_f_end(be) - end; 244 be->be_f_offset = end; 245 if (be->be_state != PNFS_BLOCK_NONE_DATA) 246 be->be_v_offset += be->be_length - len1; 247 be->be_length = len1; 248 } 249 } 250 251 return 0; 252 } 253 254 int 255 ext_tree_insert(struct pnfs_block_layout *bl, struct pnfs_block_extent *new) 256 { 257 struct pnfs_block_extent *be; 258 struct rb_root *root; 259 int err = 0; 260 261 switch (new->be_state) { 262 case PNFS_BLOCK_READWRITE_DATA: 263 case PNFS_BLOCK_INVALID_DATA: 264 root = &bl->bl_ext_rw; 265 break; 266 case PNFS_BLOCK_READ_DATA: 267 case PNFS_BLOCK_NONE_DATA: 268 root = &bl->bl_ext_ro; 269 break; 270 default: 271 dprintk("invalid extent type\n"); 272 return -EINVAL; 273 } 274 275 spin_lock(&bl->bl_ext_lock); 276 retry: 277 be = __ext_tree_search(root, new->be_f_offset); 278 if (!be || be->be_f_offset >= ext_f_end(new)) { 279 __ext_tree_insert(root, new, true); 280 } else if (new->be_f_offset >= be->be_f_offset) { 281 if (ext_f_end(new) <= ext_f_end(be)) { 282 nfs4_put_deviceid_node(new->be_device); 283 kfree(new); 284 } else { 285 sector_t new_len = ext_f_end(new) - ext_f_end(be); 286 sector_t diff = new->be_length - new_len; 287 288 new->be_f_offset += diff; 289 new->be_v_offset += diff; 290 new->be_length = new_len; 291 goto retry; 292 } 293 } else if (ext_f_end(new) <= ext_f_end(be)) { 294 new->be_length = be->be_f_offset - new->be_f_offset; 295 __ext_tree_insert(root, new, true); 296 } else { 297 struct pnfs_block_extent *split; 298 sector_t new_len = ext_f_end(new) - ext_f_end(be); 299 sector_t diff = new->be_length - new_len; 300 301 split = kmemdup(new, sizeof(*new), GFP_ATOMIC); 302 if (!split) { 303 err = -EINVAL; 304 goto out; 305 } 306 307 split->be_length = be->be_f_offset - split->be_f_offset; 308 split->be_device = nfs4_get_deviceid(new->be_device); 309 __ext_tree_insert(root, split, true); 310 311 new->be_f_offset += diff; 312 new->be_v_offset += diff; 313 new->be_length = new_len; 314 goto retry; 315 } 316 out: 317 spin_unlock(&bl->bl_ext_lock); 318 return err; 319 } 320 321 static bool 322 __ext_tree_lookup(struct rb_root *root, sector_t isect, 323 struct pnfs_block_extent *ret) 324 { 325 struct rb_node *node; 326 struct pnfs_block_extent *be; 327 328 node = root->rb_node; 329 while (node) { 330 be = ext_node(node); 331 if (isect < be->be_f_offset) 332 node = node->rb_left; 333 else if (isect >= ext_f_end(be)) 334 node = node->rb_right; 335 else { 336 *ret = *be; 337 return true; 338 } 339 } 340 341 return false; 342 } 343 344 bool 345 ext_tree_lookup(struct pnfs_block_layout *bl, sector_t isect, 346 struct pnfs_block_extent *ret, bool rw) 347 { 348 bool found = false; 349 350 spin_lock(&bl->bl_ext_lock); 351 if (!rw) 352 found = __ext_tree_lookup(&bl->bl_ext_ro, isect, ret); 353 if (!found) 354 found = __ext_tree_lookup(&bl->bl_ext_rw, isect, ret); 355 spin_unlock(&bl->bl_ext_lock); 356 357 return found; 358 } 359 360 int ext_tree_remove(struct pnfs_block_layout *bl, bool rw, 361 sector_t start, sector_t end) 362 { 363 int err, err2; 364 LIST_HEAD(tmp); 365 366 spin_lock(&bl->bl_ext_lock); 367 err = __ext_tree_remove(&bl->bl_ext_ro, start, end, &tmp); 368 if (rw) { 369 err2 = __ext_tree_remove(&bl->bl_ext_rw, start, end, &tmp); 370 if (!err) 371 err = err2; 372 } 373 spin_unlock(&bl->bl_ext_lock); 374 375 __ext_put_deviceids(&tmp); 376 return err; 377 } 378 379 static int 380 ext_tree_split(struct rb_root *root, struct pnfs_block_extent *be, 381 sector_t split) 382 { 383 struct pnfs_block_extent *new; 384 sector_t orig_len = be->be_length; 385 386 new = kzalloc(sizeof(*new), GFP_ATOMIC); 387 if (!new) 388 return -ENOMEM; 389 390 be->be_length = split - be->be_f_offset; 391 392 new->be_f_offset = split; 393 if (be->be_state != PNFS_BLOCK_NONE_DATA) 394 new->be_v_offset = be->be_v_offset + be->be_length; 395 new->be_length = orig_len - be->be_length; 396 new->be_state = be->be_state; 397 new->be_tag = be->be_tag; 398 new->be_device = nfs4_get_deviceid(be->be_device); 399 400 __ext_tree_insert(root, new, false); 401 return 0; 402 } 403 404 int 405 ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start, 406 sector_t len, u64 lwb) 407 { 408 struct rb_root *root = &bl->bl_ext_rw; 409 sector_t end = start + len; 410 struct pnfs_block_extent *be; 411 int err = 0; 412 LIST_HEAD(tmp); 413 414 spin_lock(&bl->bl_ext_lock); 415 /* 416 * First remove all COW extents or holes from written to range. 417 */ 418 err = __ext_tree_remove(&bl->bl_ext_ro, start, end, &tmp); 419 if (err) 420 goto out; 421 422 /* 423 * Then mark all invalid extents in the range as written to. 424 */ 425 for (be = __ext_tree_search(root, start); be; be = ext_tree_next(be)) { 426 if (be->be_f_offset >= end) 427 break; 428 429 if (be->be_state != PNFS_BLOCK_INVALID_DATA || be->be_tag) 430 continue; 431 432 if (be->be_f_offset < start) { 433 struct pnfs_block_extent *left = ext_tree_prev(be); 434 435 if (left && ext_can_merge(left, be)) { 436 sector_t diff = start - be->be_f_offset; 437 438 left->be_length += diff; 439 440 be->be_f_offset += diff; 441 be->be_v_offset += diff; 442 be->be_length -= diff; 443 } else { 444 err = ext_tree_split(root, be, start); 445 if (err) 446 goto out; 447 } 448 } 449 450 if (ext_f_end(be) > end) { 451 struct pnfs_block_extent *right = ext_tree_next(be); 452 453 if (right && ext_can_merge(be, right)) { 454 sector_t diff = end - be->be_f_offset; 455 456 be->be_length -= diff; 457 458 right->be_f_offset -= diff; 459 right->be_v_offset -= diff; 460 right->be_length += diff; 461 } else { 462 err = ext_tree_split(root, be, end); 463 if (err) 464 goto out; 465 } 466 } 467 468 if (be->be_f_offset >= start && ext_f_end(be) <= end) { 469 be->be_tag = EXTENT_WRITTEN; 470 be = ext_try_to_merge_left(root, be); 471 be = ext_try_to_merge_right(root, be); 472 } 473 } 474 out: 475 if (bl->bl_lwb < lwb) 476 bl->bl_lwb = lwb; 477 spin_unlock(&bl->bl_ext_lock); 478 479 __ext_put_deviceids(&tmp); 480 return err; 481 } 482 483 static size_t ext_tree_layoutupdate_size(struct pnfs_block_layout *bl, size_t count) 484 { 485 if (bl->bl_scsi_layout) 486 return sizeof(__be32) + PNFS_SCSI_RANGE_SIZE * count; 487 else 488 return sizeof(__be32) + PNFS_BLOCK_EXTENT_SIZE * count; 489 } 490 491 static void ext_tree_free_commitdata(struct nfs4_layoutcommit_args *arg, 492 size_t buffer_size) 493 { 494 if (arg->layoutupdate_pages != &arg->layoutupdate_page) { 495 int nr_pages = DIV_ROUND_UP(buffer_size, PAGE_SIZE), i; 496 497 for (i = 0; i < nr_pages; i++) 498 put_page(arg->layoutupdate_pages[i]); 499 vfree(arg->start_p); 500 kfree(arg->layoutupdate_pages); 501 } else { 502 put_page(arg->layoutupdate_page); 503 } 504 } 505 506 static __be32 *encode_block_extent(struct pnfs_block_extent *be, __be32 *p) 507 { 508 p = xdr_encode_opaque_fixed(p, be->be_device->deviceid.data, 509 NFS4_DEVICEID4_SIZE); 510 p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT); 511 p = xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT); 512 p = xdr_encode_hyper(p, 0LL); 513 *p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA); 514 return p; 515 } 516 517 static __be32 *encode_scsi_range(struct pnfs_block_extent *be, __be32 *p) 518 { 519 p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT); 520 return xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT); 521 } 522 523 static int ext_tree_encode_commit(struct pnfs_block_layout *bl, __be32 *p, 524 size_t buffer_size, size_t *count, __u64 *lastbyte) 525 { 526 struct pnfs_block_extent *be; 527 int ret = 0; 528 529 spin_lock(&bl->bl_ext_lock); 530 for (be = ext_tree_first(&bl->bl_ext_rw); be; be = ext_tree_next(be)) { 531 if (be->be_state != PNFS_BLOCK_INVALID_DATA || 532 be->be_tag != EXTENT_WRITTEN) 533 continue; 534 535 (*count)++; 536 if (ext_tree_layoutupdate_size(bl, *count) > buffer_size) { 537 /* keep counting.. */ 538 ret = -ENOSPC; 539 continue; 540 } 541 542 if (bl->bl_scsi_layout) 543 p = encode_scsi_range(be, p); 544 else 545 p = encode_block_extent(be, p); 546 be->be_tag = EXTENT_COMMITTING; 547 } 548 *lastbyte = bl->bl_lwb - 1; 549 bl->bl_lwb = 0; 550 spin_unlock(&bl->bl_ext_lock); 551 552 return ret; 553 } 554 555 int 556 ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg) 557 { 558 struct pnfs_block_layout *bl = BLK_LO2EXT(NFS_I(arg->inode)->layout); 559 size_t count = 0, buffer_size = PAGE_SIZE; 560 __be32 *start_p; 561 int ret; 562 563 dprintk("%s enter\n", __func__); 564 565 arg->layoutupdate_page = alloc_page(GFP_NOFS); 566 if (!arg->layoutupdate_page) 567 return -ENOMEM; 568 start_p = page_address(arg->layoutupdate_page); 569 arg->layoutupdate_pages = &arg->layoutupdate_page; 570 571 retry: 572 ret = ext_tree_encode_commit(bl, start_p + 1, buffer_size, &count, &arg->lastbytewritten); 573 if (unlikely(ret)) { 574 ext_tree_free_commitdata(arg, buffer_size); 575 576 buffer_size = ext_tree_layoutupdate_size(bl, count); 577 count = 0; 578 579 arg->layoutupdate_pages = 580 kcalloc(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 581 sizeof(struct page *), GFP_NOFS); 582 if (!arg->layoutupdate_pages) 583 return -ENOMEM; 584 585 start_p = __vmalloc(buffer_size, GFP_NOFS); 586 if (!start_p) { 587 kfree(arg->layoutupdate_pages); 588 return -ENOMEM; 589 } 590 591 goto retry; 592 } 593 594 *start_p = cpu_to_be32(count); 595 arg->layoutupdate_len = ext_tree_layoutupdate_size(bl, count); 596 597 if (unlikely(arg->layoutupdate_pages != &arg->layoutupdate_page)) { 598 void *p = start_p, *end = p + arg->layoutupdate_len; 599 struct page *page = NULL; 600 int i = 0; 601 602 arg->start_p = start_p; 603 for ( ; p < end; p += PAGE_SIZE) { 604 page = vmalloc_to_page(p); 605 arg->layoutupdate_pages[i++] = page; 606 get_page(page); 607 } 608 } 609 610 dprintk("%s found %zu ranges\n", __func__, count); 611 return 0; 612 } 613 614 void 615 ext_tree_mark_committed(struct nfs4_layoutcommit_args *arg, int status) 616 { 617 struct pnfs_block_layout *bl = BLK_LO2EXT(NFS_I(arg->inode)->layout); 618 struct rb_root *root = &bl->bl_ext_rw; 619 struct pnfs_block_extent *be; 620 621 dprintk("%s status %d\n", __func__, status); 622 623 ext_tree_free_commitdata(arg, arg->layoutupdate_len); 624 625 spin_lock(&bl->bl_ext_lock); 626 for (be = ext_tree_first(root); be; be = ext_tree_next(be)) { 627 if (be->be_state != PNFS_BLOCK_INVALID_DATA || 628 be->be_tag != EXTENT_COMMITTING) 629 continue; 630 631 if (status) { 632 /* 633 * Mark as written and try again. 634 * 635 * XXX: some real error handling here wouldn't hurt.. 636 */ 637 be->be_tag = EXTENT_WRITTEN; 638 } else { 639 be->be_state = PNFS_BLOCK_READWRITE_DATA; 640 be->be_tag = 0; 641 } 642 643 be = ext_try_to_merge_left(root, be); 644 be = ext_try_to_merge_right(root, be); 645 } 646 spin_unlock(&bl->bl_ext_lock); 647 } 648
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.