aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/file-item.c
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2008-12-10 09:10:46 -0500
committerChris Mason <chris.mason@oracle.com>2008-12-10 09:10:46 -0500
commit459931eca5f4b8c9ad259d07cc1ca49afed54804 (patch)
tree86088c14cff53f93281dc25022b61fb1d86c2458 /fs/btrfs/file-item.c
parent580afd76e451deb6772d0507de580fb1df14da6c (diff)
Btrfs: Delete csum items when freeing extents
This finishes off the new checksumming code by removing csum items for extents that are no longer in use. The trick is doing it without racing because a single csum item may hold csums for more than one extent. Extra checks are added to btrfs_csum_file_blocks to make sure that we are using the correct csum item after dropping locks. A new btrfs_split_item is added to split a single csum item so it can be split without dropping the leaf lock. This is used to remove csum bytes from the middle of an item. Signed-off-by: Chris Mason <chris.mason@oracle.com>
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 a3ad2ce00116..3ebef871ee6c 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}