diff options
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/ext4.h | 2 | ||||
-rw-r--r-- | fs/ext4/mballoc.c | 185 | ||||
-rw-r--r-- | fs/ext4/super.c | 1 |
3 files changed, 188 insertions, 0 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index ca9fda64dd4f..98dde2b7dd6f 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -1694,6 +1694,8 @@ extern int ext4_mb_add_groupinfo(struct super_block *sb, | |||
1694 | extern int ext4_mb_get_buddy_cache_lock(struct super_block *, ext4_group_t); | 1694 | extern int ext4_mb_get_buddy_cache_lock(struct super_block *, ext4_group_t); |
1695 | extern void ext4_mb_put_buddy_cache_lock(struct super_block *, | 1695 | extern void ext4_mb_put_buddy_cache_lock(struct super_block *, |
1696 | ext4_group_t, int); | 1696 | ext4_group_t, int); |
1697 | extern int ext4_trim_fs(struct super_block *, struct fstrim_range *); | ||
1698 | |||
1697 | /* inode.c */ | 1699 | /* inode.c */ |
1698 | struct buffer_head *ext4_getblk(handle_t *, struct inode *, | 1700 | struct buffer_head *ext4_getblk(handle_t *, struct inode *, |
1699 | ext4_lblk_t, int, int *); | 1701 | ext4_lblk_t, int, int *); |
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 11c2eec386ef..e3bcc06b4906 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c | |||
@@ -4685,3 +4685,188 @@ error_return: | |||
4685 | ext4_std_error(sb, err); | 4685 | ext4_std_error(sb, err); |
4686 | return; | 4686 | return; |
4687 | } | 4687 | } |
4688 | |||
4689 | /** | ||
4690 | * ext4_trim_extent -- function to TRIM one single free extent in the group | ||
4691 | * @sb: super block for the file system | ||
4692 | * @start: starting block of the free extent in the alloc. group | ||
4693 | * @count: number of blocks to TRIM | ||
4694 | * @group: alloc. group we are working with | ||
4695 | * @e4b: ext4 buddy for the group | ||
4696 | * | ||
4697 | * Trim "count" blocks starting at "start" in the "group". To assure that no | ||
4698 | * one will allocate those blocks, mark it as used in buddy bitmap. This must | ||
4699 | * be called with under the group lock. | ||
4700 | */ | ||
4701 | static int ext4_trim_extent(struct super_block *sb, int start, int count, | ||
4702 | ext4_group_t group, struct ext4_buddy *e4b) | ||
4703 | { | ||
4704 | struct ext4_free_extent ex; | ||
4705 | int ret = 0; | ||
4706 | |||
4707 | assert_spin_locked(ext4_group_lock_ptr(sb, group)); | ||
4708 | |||
4709 | ex.fe_start = start; | ||
4710 | ex.fe_group = group; | ||
4711 | ex.fe_len = count; | ||
4712 | |||
4713 | /* | ||
4714 | * Mark blocks used, so no one can reuse them while | ||
4715 | * being trimmed. | ||
4716 | */ | ||
4717 | mb_mark_used(e4b, &ex); | ||
4718 | ext4_unlock_group(sb, group); | ||
4719 | |||
4720 | ret = ext4_issue_discard(sb, group, start, count); | ||
4721 | if (ret) | ||
4722 | ext4_std_error(sb, ret); | ||
4723 | |||
4724 | ext4_lock_group(sb, group); | ||
4725 | mb_free_blocks(NULL, e4b, start, ex.fe_len); | ||
4726 | return ret; | ||
4727 | } | ||
4728 | |||
4729 | /** | ||
4730 | * ext4_trim_all_free -- function to trim all free space in alloc. group | ||
4731 | * @sb: super block for file system | ||
4732 | * @e4b: ext4 buddy | ||
4733 | * @start: first group block to examine | ||
4734 | * @max: last group block to examine | ||
4735 | * @minblocks: minimum extent block count | ||
4736 | * | ||
4737 | * ext4_trim_all_free walks through group's buddy bitmap searching for free | ||
4738 | * extents. When the free block is found, ext4_trim_extent is called to TRIM | ||
4739 | * the extent. | ||
4740 | * | ||
4741 | * | ||
4742 | * ext4_trim_all_free walks through group's block bitmap searching for free | ||
4743 | * extents. When the free extent is found, mark it as used in group buddy | ||
4744 | * bitmap. Then issue a TRIM command on this extent and free the extent in | ||
4745 | * the group buddy bitmap. This is done until whole group is scanned. | ||
4746 | */ | ||
4747 | ext4_grpblk_t ext4_trim_all_free(struct super_block *sb, struct ext4_buddy *e4b, | ||
4748 | ext4_grpblk_t start, ext4_grpblk_t max, ext4_grpblk_t minblocks) | ||
4749 | { | ||
4750 | void *bitmap; | ||
4751 | ext4_grpblk_t next, count = 0; | ||
4752 | ext4_group_t group; | ||
4753 | int ret = 0; | ||
4754 | |||
4755 | BUG_ON(e4b == NULL); | ||
4756 | |||
4757 | bitmap = e4b->bd_bitmap; | ||
4758 | group = e4b->bd_group; | ||
4759 | start = (e4b->bd_info->bb_first_free > start) ? | ||
4760 | e4b->bd_info->bb_first_free : start; | ||
4761 | ext4_lock_group(sb, group); | ||
4762 | |||
4763 | while (start < max) { | ||
4764 | start = mb_find_next_zero_bit(bitmap, max, start); | ||
4765 | if (start >= max) | ||
4766 | break; | ||
4767 | next = mb_find_next_bit(bitmap, max, start); | ||
4768 | |||
4769 | if ((next - start) >= minblocks) { | ||
4770 | ret = ext4_trim_extent(sb, start, | ||
4771 | next - start, group, e4b); | ||
4772 | if (ret < 0) | ||
4773 | break; | ||
4774 | count += next - start; | ||
4775 | } | ||
4776 | start = next + 1; | ||
4777 | |||
4778 | if (fatal_signal_pending(current)) { | ||
4779 | count = -ERESTARTSYS; | ||
4780 | break; | ||
4781 | } | ||
4782 | |||
4783 | if (need_resched()) { | ||
4784 | ext4_unlock_group(sb, group); | ||
4785 | cond_resched(); | ||
4786 | ext4_lock_group(sb, group); | ||
4787 | } | ||
4788 | |||
4789 | if ((e4b->bd_info->bb_free - count) < minblocks) | ||
4790 | break; | ||
4791 | } | ||
4792 | ext4_unlock_group(sb, group); | ||
4793 | |||
4794 | ext4_debug("trimmed %d blocks in the group %d\n", | ||
4795 | count, group); | ||
4796 | |||
4797 | if (ret < 0) | ||
4798 | count = ret; | ||
4799 | |||
4800 | return count; | ||
4801 | } | ||
4802 | |||
4803 | /** | ||
4804 | * ext4_trim_fs() -- trim ioctl handle function | ||
4805 | * @sb: superblock for filesystem | ||
4806 | * @range: fstrim_range structure | ||
4807 | * | ||
4808 | * start: First Byte to trim | ||
4809 | * len: number of Bytes to trim from start | ||
4810 | * minlen: minimum extent length in Bytes | ||
4811 | * ext4_trim_fs goes through all allocation groups containing Bytes from | ||
4812 | * start to start+len. For each such a group ext4_trim_all_free function | ||
4813 | * is invoked to trim all free space. | ||
4814 | */ | ||
4815 | int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) | ||
4816 | { | ||
4817 | struct ext4_buddy e4b; | ||
4818 | ext4_group_t first_group, last_group; | ||
4819 | ext4_group_t group, ngroups = ext4_get_groups_count(sb); | ||
4820 | ext4_grpblk_t cnt = 0, first_block, last_block; | ||
4821 | uint64_t start, len, minlen, trimmed; | ||
4822 | int ret = 0; | ||
4823 | |||
4824 | start = range->start >> sb->s_blocksize_bits; | ||
4825 | len = range->len >> sb->s_blocksize_bits; | ||
4826 | minlen = range->minlen >> sb->s_blocksize_bits; | ||
4827 | trimmed = 0; | ||
4828 | |||
4829 | if (unlikely(minlen > EXT4_BLOCKS_PER_GROUP(sb))) | ||
4830 | return -EINVAL; | ||
4831 | |||
4832 | /* Determine first and last group to examine based on start and len */ | ||
4833 | ext4_get_group_no_and_offset(sb, (ext4_fsblk_t) start, | ||
4834 | &first_group, &first_block); | ||
4835 | ext4_get_group_no_and_offset(sb, (ext4_fsblk_t) (start + len), | ||
4836 | &last_group, &last_block); | ||
4837 | last_group = (last_group > ngroups - 1) ? ngroups - 1 : last_group; | ||
4838 | last_block = EXT4_BLOCKS_PER_GROUP(sb); | ||
4839 | |||
4840 | if (first_group > last_group) | ||
4841 | return -EINVAL; | ||
4842 | |||
4843 | for (group = first_group; group <= last_group; group++) { | ||
4844 | ret = ext4_mb_load_buddy(sb, group, &e4b); | ||
4845 | if (ret) { | ||
4846 | ext4_error(sb, "Error in loading buddy " | ||
4847 | "information for %u", group); | ||
4848 | break; | ||
4849 | } | ||
4850 | |||
4851 | if (len >= EXT4_BLOCKS_PER_GROUP(sb)) | ||
4852 | len -= (EXT4_BLOCKS_PER_GROUP(sb) - first_block); | ||
4853 | else | ||
4854 | last_block = len; | ||
4855 | |||
4856 | if (e4b.bd_info->bb_free >= minlen) { | ||
4857 | cnt = ext4_trim_all_free(sb, &e4b, first_block, | ||
4858 | last_block, minlen); | ||
4859 | if (cnt < 0) { | ||
4860 | ret = cnt; | ||
4861 | ext4_mb_unload_buddy(&e4b); | ||
4862 | break; | ||
4863 | } | ||
4864 | } | ||
4865 | ext4_mb_unload_buddy(&e4b); | ||
4866 | trimmed += cnt; | ||
4867 | first_block = 0; | ||
4868 | } | ||
4869 | range->len = trimmed * sb->s_blocksize; | ||
4870 | |||
4871 | return ret; | ||
4872 | } | ||
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index e13b3c3534d7..01e60aa6c478 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
@@ -1189,6 +1189,7 @@ static const struct super_operations ext4_sops = { | |||
1189 | .quota_write = ext4_quota_write, | 1189 | .quota_write = ext4_quota_write, |
1190 | #endif | 1190 | #endif |
1191 | .bdev_try_to_free_page = bdev_try_to_free_page, | 1191 | .bdev_try_to_free_page = bdev_try_to_free_page, |
1192 | .trim_fs = ext4_trim_fs | ||
1192 | }; | 1193 | }; |
1193 | 1194 | ||
1194 | static const struct super_operations ext4_nojournal_sops = { | 1195 | static const struct super_operations ext4_nojournal_sops = { |