diff options
Diffstat (limited to 'fs/btrfs/file-item.c')
-rw-r--r-- | fs/btrfs/file-item.c | 226 |
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 | */ | ||
324 | static 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 | */ | ||
381 | int 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 | } | ||
481 | out: | ||
482 | btrfs_free_path(path); | ||
483 | return 0; | ||
484 | } | ||
485 | |||
313 | int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, | 486 | int 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: | |||
518 | fail_unlock: | 703 | fail_unlock: |
519 | goto out; | 704 | goto out; |
520 | } | 705 | } |
521 | |||
522 | int 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 | } | ||