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 /fs/ext4 | |
| 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>
Diffstat (limited to 'fs/ext4')
| -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 |
