diff options
-rw-r--r-- | fs/ext3/balloc.c | 266 | ||||
-rw-r--r-- | include/linux/ext3_fs.h | 1 |
2 files changed, 267 insertions, 0 deletions
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index b3db22649426..045995c8ce5a 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/ext3_jbd.h> | 20 | #include <linux/ext3_jbd.h> |
21 | #include <linux/quotaops.h> | 21 | #include <linux/quotaops.h> |
22 | #include <linux/buffer_head.h> | 22 | #include <linux/buffer_head.h> |
23 | #include <linux/blkdev.h> | ||
23 | 24 | ||
24 | /* | 25 | /* |
25 | * balloc.c contains the blocks allocation and deallocation routines | 26 | * balloc.c contains the blocks allocation and deallocation routines |
@@ -39,6 +40,21 @@ | |||
39 | 40 | ||
40 | #define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) | 41 | #define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) |
41 | 42 | ||
43 | /* | ||
44 | * Calculate the block group number and offset, given a block number | ||
45 | */ | ||
46 | static void ext3_get_group_no_and_offset(struct super_block *sb, | ||
47 | ext3_fsblk_t blocknr, unsigned long *blockgrpp, ext3_grpblk_t *offsetp) | ||
48 | { | ||
49 | struct ext3_super_block *es = EXT3_SB(sb)->s_es; | ||
50 | |||
51 | blocknr = blocknr - le32_to_cpu(es->s_first_data_block); | ||
52 | if (offsetp) | ||
53 | *offsetp = blocknr % EXT3_BLOCKS_PER_GROUP(sb); | ||
54 | if (blockgrpp) | ||
55 | *blockgrpp = blocknr / EXT3_BLOCKS_PER_GROUP(sb); | ||
56 | } | ||
57 | |||
42 | /** | 58 | /** |
43 | * ext3_get_group_desc() -- load group descriptor from disk | 59 | * ext3_get_group_desc() -- load group descriptor from disk |
44 | * @sb: super block | 60 | * @sb: super block |
@@ -1885,3 +1901,253 @@ unsigned long ext3_bg_num_gdb(struct super_block *sb, int group) | |||
1885 | return ext3_bg_num_gdb_meta(sb,group); | 1901 | return ext3_bg_num_gdb_meta(sb,group); |
1886 | 1902 | ||
1887 | } | 1903 | } |
1904 | |||
1905 | /** | ||
1906 | * ext3_trim_all_free -- function to trim all free space in alloc. group | ||
1907 | * @sb: super block for file system | ||
1908 | * @group: allocation group to trim | ||
1909 | * @start: first group block to examine | ||
1910 | * @max: last group block to examine | ||
1911 | * @gdp: allocation group description structure | ||
1912 | * @minblocks: minimum extent block count | ||
1913 | * | ||
1914 | * ext3_trim_all_free walks through group's block bitmap searching for free | ||
1915 | * blocks. When the free block is found, it tries to allocate this block and | ||
1916 | * consequent free block to get the biggest free extent possible, until it | ||
1917 | * reaches any used block. Then issue a TRIM command on this extent and free | ||
1918 | * the extent in the block bitmap. This is done until whole group is scanned. | ||
1919 | */ | ||
1920 | ext3_grpblk_t ext3_trim_all_free(struct super_block *sb, unsigned int group, | ||
1921 | ext3_grpblk_t start, ext3_grpblk_t max, | ||
1922 | ext3_grpblk_t minblocks) | ||
1923 | { | ||
1924 | handle_t *handle; | ||
1925 | ext3_grpblk_t next, free_blocks, bit, freed, count = 0; | ||
1926 | ext3_fsblk_t discard_block; | ||
1927 | struct ext3_sb_info *sbi; | ||
1928 | struct buffer_head *gdp_bh, *bitmap_bh = NULL; | ||
1929 | struct ext3_group_desc *gdp; | ||
1930 | int err = 0, ret = 0; | ||
1931 | |||
1932 | /* | ||
1933 | * We will update one block bitmap, and one group descriptor | ||
1934 | */ | ||
1935 | handle = ext3_journal_start_sb(sb, 2); | ||
1936 | if (IS_ERR(handle)) | ||
1937 | return PTR_ERR(handle); | ||
1938 | |||
1939 | bitmap_bh = read_block_bitmap(sb, group); | ||
1940 | if (!bitmap_bh) { | ||
1941 | err = -EIO; | ||
1942 | goto err_out; | ||
1943 | } | ||
1944 | |||
1945 | BUFFER_TRACE(bitmap_bh, "getting undo access"); | ||
1946 | err = ext3_journal_get_undo_access(handle, bitmap_bh); | ||
1947 | if (err) | ||
1948 | goto err_out; | ||
1949 | |||
1950 | gdp = ext3_get_group_desc(sb, group, &gdp_bh); | ||
1951 | if (!gdp) { | ||
1952 | err = -EIO; | ||
1953 | goto err_out; | ||
1954 | } | ||
1955 | |||
1956 | BUFFER_TRACE(gdp_bh, "get_write_access"); | ||
1957 | err = ext3_journal_get_write_access(handle, gdp_bh); | ||
1958 | if (err) | ||
1959 | goto err_out; | ||
1960 | |||
1961 | free_blocks = le16_to_cpu(gdp->bg_free_blocks_count); | ||
1962 | sbi = EXT3_SB(sb); | ||
1963 | |||
1964 | /* Walk through the whole group */ | ||
1965 | while (start < max) { | ||
1966 | start = bitmap_search_next_usable_block(start, bitmap_bh, max); | ||
1967 | if (start < 0) | ||
1968 | break; | ||
1969 | next = start; | ||
1970 | |||
1971 | /* | ||
1972 | * Allocate contiguous free extents by setting bits in the | ||
1973 | * block bitmap | ||
1974 | */ | ||
1975 | while (next < max | ||
1976 | && claim_block(sb_bgl_lock(sbi, group), | ||
1977 | next, bitmap_bh)) { | ||
1978 | next++; | ||
1979 | } | ||
1980 | |||
1981 | /* We did not claim any blocks */ | ||
1982 | if (next == start) | ||
1983 | continue; | ||
1984 | |||
1985 | discard_block = (ext3_fsblk_t)start + | ||
1986 | ext3_group_first_block_no(sb, group); | ||
1987 | |||
1988 | /* Update counters */ | ||
1989 | spin_lock(sb_bgl_lock(sbi, group)); | ||
1990 | le16_add_cpu(&gdp->bg_free_blocks_count, start - next); | ||
1991 | spin_unlock(sb_bgl_lock(sbi, group)); | ||
1992 | percpu_counter_sub(&sbi->s_freeblocks_counter, next - start); | ||
1993 | |||
1994 | /* Do not issue a TRIM on extents smaller than minblocks */ | ||
1995 | if ((next - start) < minblocks) | ||
1996 | goto free_extent; | ||
1997 | |||
1998 | /* Send the TRIM command down to the device */ | ||
1999 | err = sb_issue_discard(sb, discard_block, next - start, | ||
2000 | GFP_NOFS, 0); | ||
2001 | count += (next - start); | ||
2002 | free_extent: | ||
2003 | freed = 0; | ||
2004 | |||
2005 | /* | ||
2006 | * Clear bits in the bitmap | ||
2007 | */ | ||
2008 | for (bit = start; bit < next; bit++) { | ||
2009 | BUFFER_TRACE(bitmap_bh, "clear bit"); | ||
2010 | if (!ext3_clear_bit_atomic(sb_bgl_lock(sbi, group), | ||
2011 | bit, bitmap_bh->b_data)) { | ||
2012 | ext3_error(sb, __func__, | ||
2013 | "bit already cleared for block "E3FSBLK, | ||
2014 | (unsigned long)bit); | ||
2015 | BUFFER_TRACE(bitmap_bh, "bit already cleared"); | ||
2016 | } else { | ||
2017 | freed++; | ||
2018 | } | ||
2019 | } | ||
2020 | |||
2021 | /* Update couters */ | ||
2022 | spin_lock(sb_bgl_lock(sbi, group)); | ||
2023 | le16_add_cpu(&gdp->bg_free_blocks_count, freed); | ||
2024 | spin_unlock(sb_bgl_lock(sbi, group)); | ||
2025 | percpu_counter_add(&sbi->s_freeblocks_counter, freed); | ||
2026 | |||
2027 | start = next; | ||
2028 | if (err < 0) { | ||
2029 | if (err != -EOPNOTSUPP) | ||
2030 | ext3_warning(sb, __func__, "Discard command " | ||
2031 | "returned error %d\n", err); | ||
2032 | break; | ||
2033 | } | ||
2034 | |||
2035 | if (fatal_signal_pending(current)) { | ||
2036 | err = -ERESTARTSYS; | ||
2037 | break; | ||
2038 | } | ||
2039 | |||
2040 | cond_resched(); | ||
2041 | |||
2042 | /* No more suitable extents */ | ||
2043 | if ((free_blocks - count) < minblocks) | ||
2044 | break; | ||
2045 | } | ||
2046 | |||
2047 | /* We dirtied the bitmap block */ | ||
2048 | BUFFER_TRACE(bitmap_bh, "dirtied bitmap block"); | ||
2049 | ret = ext3_journal_dirty_metadata(handle, bitmap_bh); | ||
2050 | if (!err) | ||
2051 | err = ret; | ||
2052 | |||
2053 | /* And the group descriptor block */ | ||
2054 | BUFFER_TRACE(gdp_bh, "dirtied group descriptor block"); | ||
2055 | ret = ext3_journal_dirty_metadata(handle, gdp_bh); | ||
2056 | if (!err) | ||
2057 | err = ret; | ||
2058 | |||
2059 | ext3_debug("trimmed %d blocks in the group %d\n", | ||
2060 | count, group); | ||
2061 | |||
2062 | err_out: | ||
2063 | if (err) | ||
2064 | count = err; | ||
2065 | ext3_journal_stop(handle); | ||
2066 | brelse(bitmap_bh); | ||
2067 | |||
2068 | return count; | ||
2069 | } | ||
2070 | |||
2071 | /** | ||
2072 | * ext3_trim_fs() -- trim ioctl handle function | ||
2073 | * @sb: superblock for filesystem | ||
2074 | * @start: First Byte to trim | ||
2075 | * @len: number of Bytes to trim from start | ||
2076 | * @minlen: minimum extent length in Bytes | ||
2077 | * | ||
2078 | * ext3_trim_fs goes through all allocation groups containing Bytes from | ||
2079 | * start to start+len. For each such a group ext3_trim_all_free function | ||
2080 | * is invoked to trim all free space. | ||
2081 | */ | ||
2082 | int ext3_trim_fs(struct super_block *sb, struct fstrim_range *range) | ||
2083 | { | ||
2084 | ext3_grpblk_t last_block, first_block, free_blocks; | ||
2085 | unsigned long first_group, last_group; | ||
2086 | unsigned long group, ngroups; | ||
2087 | struct ext3_group_desc *gdp; | ||
2088 | struct ext3_super_block *es = EXT3_SB(sb)->s_es; | ||
2089 | uint64_t start, len, minlen, trimmed; | ||
2090 | ext3_fsblk_t max_blks = le32_to_cpu(es->s_blocks_count); | ||
2091 | int ret = 0; | ||
2092 | |||
2093 | start = range->start >> sb->s_blocksize_bits; | ||
2094 | len = range->len >> sb->s_blocksize_bits; | ||
2095 | minlen = range->minlen >> sb->s_blocksize_bits; | ||
2096 | trimmed = 0; | ||
2097 | |||
2098 | if (unlikely(minlen > EXT3_BLOCKS_PER_GROUP(sb))) | ||
2099 | return -EINVAL; | ||
2100 | if (start >= max_blks) | ||
2101 | goto out; | ||
2102 | if (start < le32_to_cpu(es->s_first_data_block)) { | ||
2103 | len -= le32_to_cpu(es->s_first_data_block) - start; | ||
2104 | start = le32_to_cpu(es->s_first_data_block); | ||
2105 | } | ||
2106 | if (start + len > max_blks) | ||
2107 | len = max_blks - start; | ||
2108 | |||
2109 | ngroups = EXT3_SB(sb)->s_groups_count; | ||
2110 | smp_rmb(); | ||
2111 | |||
2112 | /* Determine first and last group to examine based on start and len */ | ||
2113 | ext3_get_group_no_and_offset(sb, (ext3_fsblk_t) start, | ||
2114 | &first_group, &first_block); | ||
2115 | ext3_get_group_no_and_offset(sb, (ext3_fsblk_t) (start + len), | ||
2116 | &last_group, &last_block); | ||
2117 | last_group = (last_group > ngroups - 1) ? ngroups - 1 : last_group; | ||
2118 | last_block = EXT3_BLOCKS_PER_GROUP(sb); | ||
2119 | |||
2120 | if (first_group > last_group) | ||
2121 | return -EINVAL; | ||
2122 | |||
2123 | for (group = first_group; group <= last_group; group++) { | ||
2124 | gdp = ext3_get_group_desc(sb, group, NULL); | ||
2125 | if (!gdp) | ||
2126 | break; | ||
2127 | |||
2128 | free_blocks = le16_to_cpu(gdp->bg_free_blocks_count); | ||
2129 | if (free_blocks < minlen) | ||
2130 | continue; | ||
2131 | |||
2132 | if (len >= EXT3_BLOCKS_PER_GROUP(sb)) | ||
2133 | len -= (EXT3_BLOCKS_PER_GROUP(sb) - first_block); | ||
2134 | else | ||
2135 | last_block = first_block + len; | ||
2136 | |||
2137 | ret = ext3_trim_all_free(sb, group, first_block, | ||
2138 | last_block, minlen); | ||
2139 | if (ret < 0) | ||
2140 | break; | ||
2141 | |||
2142 | trimmed += ret; | ||
2143 | first_block = 0; | ||
2144 | } | ||
2145 | |||
2146 | if (ret >= 0) | ||
2147 | ret = 0; | ||
2148 | |||
2149 | out: | ||
2150 | range->len = trimmed * sb->s_blocksize; | ||
2151 | |||
2152 | return ret; | ||
2153 | } | ||
diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 6ce1bca01724..a443965946bb 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h | |||
@@ -856,6 +856,7 @@ extern struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb, | |||
856 | extern int ext3_should_retry_alloc(struct super_block *sb, int *retries); | 856 | extern int ext3_should_retry_alloc(struct super_block *sb, int *retries); |
857 | extern void ext3_init_block_alloc_info(struct inode *); | 857 | extern void ext3_init_block_alloc_info(struct inode *); |
858 | extern void ext3_rsv_window_add(struct super_block *sb, struct ext3_reserve_window_node *rsv); | 858 | extern void ext3_rsv_window_add(struct super_block *sb, struct ext3_reserve_window_node *rsv); |
859 | extern int ext3_trim_fs(struct super_block *sb, struct fstrim_range *range); | ||
859 | 860 | ||
860 | /* dir.c */ | 861 | /* dir.c */ |
861 | extern int ext3_check_dir_entry(const char *, struct inode *, | 862 | extern int ext3_check_dir_entry(const char *, struct inode *, |