aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/file-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/file-item.c')
-rw-r--r--fs/btrfs/file-item.c226
1 files changed, 192 insertions, 34 deletions
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index a3ad2ce0011..3ebef871ee6 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -310,6 +310,179 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
310 return 0; 310 return 0;
311} 311}
312 312
313/*
314 * helper function for csum removal, this expects the
315 * key to describe the csum pointed to by the path, and it expects
316 * the csum to overlap the range [bytenr, len]
317 *
318 * The csum should not be entirely contained in the range and the
319 * range should not be entirely contained in the csum.
320 *
321 * This calls btrfs_truncate_item with the correct args based on the
322 * overlap, and fixes up the key as required.
323 */
324static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
325 struct btrfs_root *root,
326 struct btrfs_path *path,
327 struct btrfs_key *key,
328 u64 bytenr, u64 len)
329{
330 struct extent_buffer *leaf;
331 u16 csum_size =
332 btrfs_super_csum_size(&root->fs_info->super_copy);
333 u64 csum_end;
334 u64 end_byte = bytenr + len;
335 u32 blocksize_bits = root->fs_info->sb->s_blocksize_bits;
336 int ret;
337
338 leaf = path->nodes[0];
339 csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size;
340 csum_end <<= root->fs_info->sb->s_blocksize_bits;
341 csum_end += key->offset;
342
343 if (key->offset < bytenr && csum_end <= end_byte) {
344 /*
345 * [ bytenr - len ]
346 * [ ]
347 * [csum ]
348 * A simple truncate off the end of the item
349 */
350 u32 new_size = (bytenr - key->offset) >> blocksize_bits;
351 new_size *= csum_size;
352 ret = btrfs_truncate_item(trans, root, path, new_size, 1);
353 BUG_ON(ret);
354 } else if (key->offset >= bytenr && csum_end > end_byte &&
355 end_byte > key->offset) {
356 /*
357 * [ bytenr - len ]
358 * [ ]
359 * [csum ]
360 * we need to truncate from the beginning of the csum
361 */
362 u32 new_size = (csum_end - end_byte) >> blocksize_bits;
363 new_size *= csum_size;
364
365 ret = btrfs_truncate_item(trans, root, path, new_size, 0);
366 BUG_ON(ret);
367
368 key->offset = end_byte;
369 ret = btrfs_set_item_key_safe(trans, root, path, key);
370 BUG_ON(ret);
371 } else {
372 BUG();
373 }
374 return 0;
375}
376
377/*
378 * deletes the csum items from the csum tree for a given
379 * range of bytes.
380 */
381int btrfs_del_csums(struct btrfs_trans_handle *trans,
382 struct btrfs_root *root, u64 bytenr, u64 len)
383{
384 struct btrfs_path *path;
385 struct btrfs_key key;
386 u64 end_byte = bytenr + len;
387 u64 csum_end;
388 struct extent_buffer *leaf;
389 int ret;
390 u16 csum_size =
391 btrfs_super_csum_size(&root->fs_info->super_copy);
392 int blocksize_bits = root->fs_info->sb->s_blocksize_bits;
393
394 root = root->fs_info->csum_root;
395
396 path = btrfs_alloc_path();
397
398 while(1) {
399 key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
400 key.offset = end_byte - 1;
401 key.type = BTRFS_EXTENT_CSUM_KEY;
402
403 ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
404 if (ret > 0) {
405 if (path->slots[0] == 0)
406 goto out;
407 path->slots[0]--;
408 }
409 leaf = path->nodes[0];
410 btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
411
412 if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID ||
413 key.type != BTRFS_EXTENT_CSUM_KEY) {
414 break;
415 }
416
417 if (key.offset >= end_byte)
418 break;
419
420 csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size;
421 csum_end <<= blocksize_bits;
422 csum_end += key.offset;
423
424 /* this csum ends before we start, we're done */
425 if (csum_end <= bytenr)
426 break;
427
428 /* delete the entire item, it is inside our range */
429 if (key.offset >= bytenr && csum_end <= end_byte) {
430 ret = btrfs_del_item(trans, root, path);
431 BUG_ON(ret);
432 } else if (key.offset < bytenr && csum_end > end_byte) {
433 unsigned long offset;
434 unsigned long shift_len;
435 unsigned long item_offset;
436 /*
437 * [ bytenr - len ]
438 * [csum ]
439 *
440 * Our bytes are in the middle of the csum,
441 * we need to split this item and insert a new one.
442 *
443 * But we can't drop the path because the
444 * csum could change, get removed, extended etc.
445 *
446 * The trick here is the max size of a csum item leaves
447 * enough room in the tree block for a single
448 * item header. So, we split the item in place,
449 * adding a new header pointing to the existing
450 * bytes. Then we loop around again and we have
451 * a nicely formed csum item that we can neatly
452 * truncate.
453 */
454 offset = (bytenr - key.offset) >> blocksize_bits;
455 offset *= csum_size;
456
457 shift_len = (len >> blocksize_bits) * csum_size;
458
459 item_offset = btrfs_item_ptr_offset(leaf,
460 path->slots[0]);
461
462 memset_extent_buffer(leaf, 0, item_offset + offset,
463 shift_len);
464 key.offset = bytenr;
465
466 /*
467 * btrfs_split_item returns -EAGAIN when the
468 * item changed size or key
469 */
470 ret = btrfs_split_item(trans, root, path, &key, offset);
471 BUG_ON(ret && ret != -EAGAIN);
472
473 key.offset = end_byte - 1;
474 } else {
475 ret = truncate_one_csum(trans, root, path,
476 &key, bytenr, len);
477 BUG_ON(ret);
478 }
479 btrfs_release_path(root, path);
480 }
481out:
482 btrfs_free_path(path);
483 return 0;
484}
485
313int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, 486int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
314 struct btrfs_root *root, 487 struct btrfs_root *root,
315 struct btrfs_ordered_sum *sums) 488 struct btrfs_ordered_sum *sums)
@@ -396,28 +569,40 @@ again:
396 csum_size, 1); 569 csum_size, 1);
397 if (ret < 0) 570 if (ret < 0)
398 goto fail_unlock; 571 goto fail_unlock;
399 if (ret == 0) { 572
400 BUG(); 573 if (ret > 0) {
401 } 574 if (path->slots[0] == 0)
402 if (path->slots[0] == 0) { 575 goto insert;
403 goto insert; 576 path->slots[0]--;
404 } 577 }
405 path->slots[0]--; 578
406 leaf = path->nodes[0]; 579 leaf = path->nodes[0];
407 btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); 580 btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
408 csum_offset = (bytenr - found_key.offset) >> 581 csum_offset = (bytenr - found_key.offset) >>
409 root->fs_info->sb->s_blocksize_bits; 582 root->fs_info->sb->s_blocksize_bits;
583
410 if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY || 584 if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY ||
411 found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 585 found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID ||
412 csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) { 586 csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) {
413 goto insert; 587 goto insert;
414 } 588 }
589
415 if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) / 590 if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) /
416 csum_size) { 591 csum_size) {
417 u32 diff = (csum_offset + 1) * csum_size; 592 u32 diff = (csum_offset + 1) * csum_size;
593
594 /*
595 * is the item big enough already? we dropped our lock
596 * before and need to recheck
597 */
598 if (diff < btrfs_item_size_nr(leaf, path->slots[0]))
599 goto csum;
600
418 diff = diff - btrfs_item_size_nr(leaf, path->slots[0]); 601 diff = diff - btrfs_item_size_nr(leaf, path->slots[0]);
419 if (diff != csum_size) 602 if (diff != csum_size) {
420 goto insert; 603 goto insert;
604 }
605
421 ret = btrfs_extend_item(trans, root, path, diff); 606 ret = btrfs_extend_item(trans, root, path, diff);
422 BUG_ON(ret); 607 BUG_ON(ret);
423 goto csum; 608 goto csum;
@@ -518,30 +703,3 @@ out:
518fail_unlock: 703fail_unlock:
519 goto out; 704 goto out;
520} 705}
521
522int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
523 struct btrfs_root *root, struct btrfs_path *path,
524 u64 isize)
525{
526 struct btrfs_key key;
527 struct extent_buffer *leaf = path->nodes[0];
528 int slot = path->slots[0];
529 int ret;
530 u32 new_item_size;
531 u64 new_item_span;
532 u64 blocks;
533
534 btrfs_item_key_to_cpu(leaf, &key, slot);
535 if (isize <= key.offset)
536 return 0;
537 new_item_span = isize - key.offset;
538 blocks = (new_item_span + root->sectorsize - 1) >>
539 root->fs_info->sb->s_blocksize_bits;
540 new_item_size = blocks *
541 btrfs_super_csum_size(&root->fs_info->super_copy);
542 if (new_item_size >= btrfs_item_size_nr(leaf, slot))
543 return 0;
544 ret = btrfs_truncate_item(trans, root, path, new_item_size, 1);
545 BUG_ON(ret);
546 return ret;
547}