diff options
author | Yongqiang Yang <xiaoqiangnk@gmail.com> | 2011-05-03 12:23:07 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2011-05-03 12:23:07 -0400 |
commit | 47ea3bb59cf47298b1cbb4d7bef056b3afea0879 (patch) | |
tree | 20d3f182b3fe743e7ca221dc2ae2cf7ab93ba6b8 | |
parent | 197217a5af79c23609da03eda2a52ee8603eec52 (diff) |
ext4: add ext4_split_extent_at() and ext4_split_extent()
Add two functions: ext4_split_extent_at(), which splits an extent into
two extents at given logical block, and ext4_split_extent() which
splits an extent into three extents.
Signed-off-by: Yongqiang Yang <xiaoqiangnk@gmail.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Tested-by: Allison Henderson <achender@linux.vnet.ibm.com>
-rw-r--r-- | fs/ext4/extents.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 23a98b6f00a2..10317eb1d2a9 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -2554,6 +2554,193 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) | |||
2554 | return ret; | 2554 | return ret; |
2555 | } | 2555 | } |
2556 | 2556 | ||
2557 | /* | ||
2558 | * used by extent splitting. | ||
2559 | */ | ||
2560 | #define EXT4_EXT_MAY_ZEROOUT 0x1 /* safe to zeroout if split fails \ | ||
2561 | due to ENOSPC */ | ||
2562 | #define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */ | ||
2563 | #define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */ | ||
2564 | |||
2565 | /* | ||
2566 | * ext4_split_extent_at() splits an extent at given block. | ||
2567 | * | ||
2568 | * @handle: the journal handle | ||
2569 | * @inode: the file inode | ||
2570 | * @path: the path to the extent | ||
2571 | * @split: the logical block where the extent is splitted. | ||
2572 | * @split_flags: indicates if the extent could be zeroout if split fails, and | ||
2573 | * the states(init or uninit) of new extents. | ||
2574 | * @flags: flags used to insert new extent to extent tree. | ||
2575 | * | ||
2576 | * | ||
2577 | * Splits extent [a, b] into two extents [a, @split) and [@split, b], states | ||
2578 | * of which are deterimined by split_flag. | ||
2579 | * | ||
2580 | * There are two cases: | ||
2581 | * a> the extent are splitted into two extent. | ||
2582 | * b> split is not needed, and just mark the extent. | ||
2583 | * | ||
2584 | * return 0 on success. | ||
2585 | */ | ||
2586 | static int ext4_split_extent_at(handle_t *handle, | ||
2587 | struct inode *inode, | ||
2588 | struct ext4_ext_path *path, | ||
2589 | ext4_lblk_t split, | ||
2590 | int split_flag, | ||
2591 | int flags) | ||
2592 | { | ||
2593 | ext4_fsblk_t newblock; | ||
2594 | ext4_lblk_t ee_block; | ||
2595 | struct ext4_extent *ex, newex, orig_ex; | ||
2596 | struct ext4_extent *ex2 = NULL; | ||
2597 | unsigned int ee_len, depth; | ||
2598 | int err = 0; | ||
2599 | |||
2600 | ext_debug("ext4_split_extents_at: inode %lu, logical" | ||
2601 | "block %llu\n", inode->i_ino, (unsigned long long)split); | ||
2602 | |||
2603 | ext4_ext_show_leaf(inode, path); | ||
2604 | |||
2605 | depth = ext_depth(inode); | ||
2606 | ex = path[depth].p_ext; | ||
2607 | ee_block = le32_to_cpu(ex->ee_block); | ||
2608 | ee_len = ext4_ext_get_actual_len(ex); | ||
2609 | newblock = split - ee_block + ext4_ext_pblock(ex); | ||
2610 | |||
2611 | BUG_ON(split < ee_block || split >= (ee_block + ee_len)); | ||
2612 | |||
2613 | err = ext4_ext_get_access(handle, inode, path + depth); | ||
2614 | if (err) | ||
2615 | goto out; | ||
2616 | |||
2617 | if (split == ee_block) { | ||
2618 | /* | ||
2619 | * case b: block @split is the block that the extent begins with | ||
2620 | * then we just change the state of the extent, and splitting | ||
2621 | * is not needed. | ||
2622 | */ | ||
2623 | if (split_flag & EXT4_EXT_MARK_UNINIT2) | ||
2624 | ext4_ext_mark_uninitialized(ex); | ||
2625 | else | ||
2626 | ext4_ext_mark_initialized(ex); | ||
2627 | |||
2628 | if (!(flags & EXT4_GET_BLOCKS_PRE_IO)) | ||
2629 | ext4_ext_try_to_merge(inode, path, ex); | ||
2630 | |||
2631 | err = ext4_ext_dirty(handle, inode, path + depth); | ||
2632 | goto out; | ||
2633 | } | ||
2634 | |||
2635 | /* case a */ | ||
2636 | memcpy(&orig_ex, ex, sizeof(orig_ex)); | ||
2637 | ex->ee_len = cpu_to_le16(split - ee_block); | ||
2638 | if (split_flag & EXT4_EXT_MARK_UNINIT1) | ||
2639 | ext4_ext_mark_uninitialized(ex); | ||
2640 | |||
2641 | /* | ||
2642 | * path may lead to new leaf, not to original leaf any more | ||
2643 | * after ext4_ext_insert_extent() returns, | ||
2644 | */ | ||
2645 | err = ext4_ext_dirty(handle, inode, path + depth); | ||
2646 | if (err) | ||
2647 | goto fix_extent_len; | ||
2648 | |||
2649 | ex2 = &newex; | ||
2650 | ex2->ee_block = cpu_to_le32(split); | ||
2651 | ex2->ee_len = cpu_to_le16(ee_len - (split - ee_block)); | ||
2652 | ext4_ext_store_pblock(ex2, newblock); | ||
2653 | if (split_flag & EXT4_EXT_MARK_UNINIT2) | ||
2654 | ext4_ext_mark_uninitialized(ex2); | ||
2655 | |||
2656 | err = ext4_ext_insert_extent(handle, inode, path, &newex, flags); | ||
2657 | if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) { | ||
2658 | err = ext4_ext_zeroout(inode, &orig_ex); | ||
2659 | if (err) | ||
2660 | goto fix_extent_len; | ||
2661 | /* update the extent length and mark as initialized */ | ||
2662 | ex->ee_len = cpu_to_le32(ee_len); | ||
2663 | ext4_ext_try_to_merge(inode, path, ex); | ||
2664 | err = ext4_ext_dirty(handle, inode, path + depth); | ||
2665 | goto out; | ||
2666 | } else if (err) | ||
2667 | goto fix_extent_len; | ||
2668 | |||
2669 | out: | ||
2670 | ext4_ext_show_leaf(inode, path); | ||
2671 | return err; | ||
2672 | |||
2673 | fix_extent_len: | ||
2674 | ex->ee_len = orig_ex.ee_len; | ||
2675 | ext4_ext_dirty(handle, inode, path + depth); | ||
2676 | return err; | ||
2677 | } | ||
2678 | |||
2679 | /* | ||
2680 | * ext4_split_extents() splits an extent and mark extent which is covered | ||
2681 | * by @map as split_flags indicates | ||
2682 | * | ||
2683 | * It may result in splitting the extent into multiple extents (upto three) | ||
2684 | * There are three possibilities: | ||
2685 | * a> There is no split required | ||
2686 | * b> Splits in two extents: Split is happening at either end of the extent | ||
2687 | * c> Splits in three extents: Somone is splitting in middle of the extent | ||
2688 | * | ||
2689 | */ | ||
2690 | static int ext4_split_extent(handle_t *handle, | ||
2691 | struct inode *inode, | ||
2692 | struct ext4_ext_path *path, | ||
2693 | struct ext4_map_blocks *map, | ||
2694 | int split_flag, | ||
2695 | int flags) | ||
2696 | { | ||
2697 | ext4_lblk_t ee_block; | ||
2698 | struct ext4_extent *ex; | ||
2699 | unsigned int ee_len, depth; | ||
2700 | int err = 0; | ||
2701 | int uninitialized; | ||
2702 | int split_flag1, flags1; | ||
2703 | |||
2704 | depth = ext_depth(inode); | ||
2705 | ex = path[depth].p_ext; | ||
2706 | ee_block = le32_to_cpu(ex->ee_block); | ||
2707 | ee_len = ext4_ext_get_actual_len(ex); | ||
2708 | uninitialized = ext4_ext_is_uninitialized(ex); | ||
2709 | |||
2710 | if (map->m_lblk + map->m_len < ee_block + ee_len) { | ||
2711 | split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT ? | ||
2712 | EXT4_EXT_MAY_ZEROOUT : 0; | ||
2713 | flags1 = flags | EXT4_GET_BLOCKS_PRE_IO; | ||
2714 | if (uninitialized) | ||
2715 | split_flag1 |= EXT4_EXT_MARK_UNINIT1 | | ||
2716 | EXT4_EXT_MARK_UNINIT2; | ||
2717 | err = ext4_split_extent_at(handle, inode, path, | ||
2718 | map->m_lblk + map->m_len, split_flag1, flags1); | ||
2719 | } | ||
2720 | |||
2721 | ext4_ext_drop_refs(path); | ||
2722 | path = ext4_ext_find_extent(inode, map->m_lblk, path); | ||
2723 | if (IS_ERR(path)) | ||
2724 | return PTR_ERR(path); | ||
2725 | |||
2726 | if (map->m_lblk >= ee_block) { | ||
2727 | split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT ? | ||
2728 | EXT4_EXT_MAY_ZEROOUT : 0; | ||
2729 | if (uninitialized) | ||
2730 | split_flag1 |= EXT4_EXT_MARK_UNINIT1; | ||
2731 | if (split_flag & EXT4_EXT_MARK_UNINIT2) | ||
2732 | split_flag1 |= EXT4_EXT_MARK_UNINIT2; | ||
2733 | err = ext4_split_extent_at(handle, inode, path, | ||
2734 | map->m_lblk, split_flag1, flags); | ||
2735 | if (err) | ||
2736 | goto out; | ||
2737 | } | ||
2738 | |||
2739 | ext4_ext_show_leaf(inode, path); | ||
2740 | out: | ||
2741 | return err ? err : map->m_len; | ||
2742 | } | ||
2743 | |||
2557 | #define EXT4_EXT_ZERO_LEN 7 | 2744 | #define EXT4_EXT_ZERO_LEN 7 |
2558 | /* | 2745 | /* |
2559 | * This function is called by ext4_ext_map_blocks() if someone tries to write | 2746 | * This function is called by ext4_ext_map_blocks() if someone tries to write |