diff options
author | Tao Ma <tao.ma@oracle.com> | 2009-08-17 23:24:49 -0400 |
---|---|---|
committer | Joel Becker <joel.becker@oracle.com> | 2009-09-22 23:09:33 -0400 |
commit | 1823cb0b9fe5e6d48017ee3f92428f69c0235d87 (patch) | |
tree | 461e5d6cc255c400acac69dd968fae935a9c073f /fs | |
parent | e73a819db9c2d6c4065b7cab7374709b6939e8f1 (diff) |
ocfs2: Add support of decrementing refcount for delete.
Given a physical cpos and length, decrement the refcount
in the tree. If the refcount for any portion of the extent goes
to zero, that portion is queued for freeing.
Signed-off-by: Tao Ma <tao.ma@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ocfs2/alloc.c | 6 | ||||
-rw-r--r-- | fs/ocfs2/alloc.h | 3 | ||||
-rw-r--r-- | fs/ocfs2/refcounttree.c | 256 | ||||
-rw-r--r-- | fs/ocfs2/refcounttree.h | 5 |
4 files changed, 265 insertions, 5 deletions
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 2c8ce32adf01..9dd68cd7b0ad 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c | |||
@@ -6522,9 +6522,9 @@ ocfs2_find_per_slot_free_list(int type, | |||
6522 | return fl; | 6522 | return fl; |
6523 | } | 6523 | } |
6524 | 6524 | ||
6525 | static int ocfs2_cache_block_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, | 6525 | int ocfs2_cache_block_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, |
6526 | int type, int slot, u64 blkno, | 6526 | int type, int slot, u64 blkno, |
6527 | unsigned int bit) | 6527 | unsigned int bit) |
6528 | { | 6528 | { |
6529 | int ret; | 6529 | int ret; |
6530 | struct ocfs2_per_slot_free_list *fl; | 6530 | struct ocfs2_per_slot_free_list *fl; |
diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h index 3f4348923b73..0610ba148ea0 100644 --- a/fs/ocfs2/alloc.h +++ b/fs/ocfs2/alloc.h | |||
@@ -202,6 +202,9 @@ static inline void ocfs2_init_dealloc_ctxt(struct ocfs2_cached_dealloc_ctxt *c) | |||
202 | } | 202 | } |
203 | int ocfs2_cache_cluster_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, | 203 | int ocfs2_cache_cluster_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, |
204 | u64 blkno, unsigned int bit); | 204 | u64 blkno, unsigned int bit); |
205 | int ocfs2_cache_block_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, | ||
206 | int type, int slot, u64 blkno, | ||
207 | unsigned int bit); | ||
205 | static inline int ocfs2_dealloc_has_cluster(struct ocfs2_cached_dealloc_ctxt *c) | 208 | static inline int ocfs2_dealloc_has_cluster(struct ocfs2_cached_dealloc_ctxt *c) |
206 | { | 209 | { |
207 | return c->c_global_allocator != NULL; | 210 | return c->c_global_allocator != NULL; |
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index ee0422ce72c4..2c7974cccaf8 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c | |||
@@ -1071,6 +1071,10 @@ static void ocfs2_refcount_rec_merge(struct ocfs2_refcount_block *rb, | |||
1071 | ocfs2_rotate_refcount_rec_left(rb, index); | 1071 | ocfs2_rotate_refcount_rec_left(rb, index); |
1072 | } | 1072 | } |
1073 | 1073 | ||
1074 | /* | ||
1075 | * Change the refcount indexed by "index" in ref_bh. | ||
1076 | * If refcount reaches 0, remove it. | ||
1077 | */ | ||
1074 | static int ocfs2_change_refcount_rec(handle_t *handle, | 1078 | static int ocfs2_change_refcount_rec(handle_t *handle, |
1075 | struct ocfs2_caching_info *ci, | 1079 | struct ocfs2_caching_info *ci, |
1076 | struct buffer_head *ref_leaf_bh, | 1080 | struct buffer_head *ref_leaf_bh, |
@@ -1079,7 +1083,8 @@ static int ocfs2_change_refcount_rec(handle_t *handle, | |||
1079 | int ret; | 1083 | int ret; |
1080 | struct ocfs2_refcount_block *rb = | 1084 | struct ocfs2_refcount_block *rb = |
1081 | (struct ocfs2_refcount_block *)ref_leaf_bh->b_data; | 1085 | (struct ocfs2_refcount_block *)ref_leaf_bh->b_data; |
1082 | struct ocfs2_refcount_rec *rec = &rb->rf_records.rl_recs[index]; | 1086 | struct ocfs2_refcount_list *rl = &rb->rf_records; |
1087 | struct ocfs2_refcount_rec *rec = &rl->rl_recs[index]; | ||
1083 | 1088 | ||
1084 | ret = ocfs2_journal_access_rb(handle, ci, ref_leaf_bh, | 1089 | ret = ocfs2_journal_access_rb(handle, ci, ref_leaf_bh, |
1085 | OCFS2_JOURNAL_ACCESS_WRITE); | 1090 | OCFS2_JOURNAL_ACCESS_WRITE); |
@@ -1092,7 +1097,18 @@ static int ocfs2_change_refcount_rec(handle_t *handle, | |||
1092 | le32_to_cpu(rec->r_refcount), change); | 1097 | le32_to_cpu(rec->r_refcount), change); |
1093 | le32_add_cpu(&rec->r_refcount, change); | 1098 | le32_add_cpu(&rec->r_refcount, change); |
1094 | 1099 | ||
1095 | ocfs2_refcount_rec_merge(rb, index); | 1100 | if (!rec->r_refcount) { |
1101 | if (index != le16_to_cpu(rl->rl_used) - 1) { | ||
1102 | memmove(rec, rec + 1, | ||
1103 | (le16_to_cpu(rl->rl_used) - index - 1) * | ||
1104 | sizeof(struct ocfs2_refcount_rec)); | ||
1105 | memset(&rl->rl_recs[le16_to_cpu(rl->rl_used) - 1], | ||
1106 | 0, sizeof(struct ocfs2_refcount_rec)); | ||
1107 | } | ||
1108 | |||
1109 | le16_add_cpu(&rl->rl_used, -1); | ||
1110 | } else | ||
1111 | ocfs2_refcount_rec_merge(rb, index); | ||
1096 | 1112 | ||
1097 | ret = ocfs2_journal_dirty(handle, ref_leaf_bh); | 1113 | ret = ocfs2_journal_dirty(handle, ref_leaf_bh); |
1098 | if (ret) | 1114 | if (ret) |
@@ -1901,3 +1917,239 @@ out: | |||
1901 | brelse(ref_leaf_bh); | 1917 | brelse(ref_leaf_bh); |
1902 | return ret; | 1918 | return ret; |
1903 | } | 1919 | } |
1920 | |||
1921 | static int ocfs2_remove_refcount_extent(handle_t *handle, | ||
1922 | struct ocfs2_caching_info *ci, | ||
1923 | struct buffer_head *ref_root_bh, | ||
1924 | struct buffer_head *ref_leaf_bh, | ||
1925 | struct ocfs2_alloc_context *meta_ac, | ||
1926 | struct ocfs2_cached_dealloc_ctxt *dealloc) | ||
1927 | { | ||
1928 | int ret; | ||
1929 | struct super_block *sb = ocfs2_metadata_cache_get_super(ci); | ||
1930 | struct ocfs2_refcount_block *rb = | ||
1931 | (struct ocfs2_refcount_block *)ref_leaf_bh->b_data; | ||
1932 | struct ocfs2_extent_tree et; | ||
1933 | |||
1934 | BUG_ON(rb->rf_records.rl_used); | ||
1935 | |||
1936 | ocfs2_init_refcount_extent_tree(&et, ci, ref_root_bh); | ||
1937 | ret = ocfs2_remove_extent(handle, &et, le32_to_cpu(rb->rf_cpos), | ||
1938 | 1, meta_ac, dealloc); | ||
1939 | if (ret) { | ||
1940 | mlog_errno(ret); | ||
1941 | goto out; | ||
1942 | } | ||
1943 | |||
1944 | ocfs2_remove_from_cache(ci, ref_leaf_bh); | ||
1945 | |||
1946 | /* | ||
1947 | * add the freed block to the dealloc so that it will be freed | ||
1948 | * when we run dealloc. | ||
1949 | */ | ||
1950 | ret = ocfs2_cache_block_dealloc(dealloc, EXTENT_ALLOC_SYSTEM_INODE, | ||
1951 | le16_to_cpu(rb->rf_suballoc_slot), | ||
1952 | le64_to_cpu(rb->rf_blkno), | ||
1953 | le16_to_cpu(rb->rf_suballoc_bit)); | ||
1954 | if (ret) { | ||
1955 | mlog_errno(ret); | ||
1956 | goto out; | ||
1957 | } | ||
1958 | |||
1959 | ret = ocfs2_journal_access_rb(handle, ci, ref_root_bh, | ||
1960 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
1961 | if (ret) { | ||
1962 | mlog_errno(ret); | ||
1963 | goto out; | ||
1964 | } | ||
1965 | |||
1966 | rb = (struct ocfs2_refcount_block *)ref_root_bh->b_data; | ||
1967 | |||
1968 | le32_add_cpu(&rb->rf_clusters, -1); | ||
1969 | |||
1970 | /* | ||
1971 | * check whether we need to restore the root refcount block if | ||
1972 | * there is no leaf extent block at atll. | ||
1973 | */ | ||
1974 | if (!rb->rf_list.l_next_free_rec) { | ||
1975 | BUG_ON(rb->rf_clusters); | ||
1976 | |||
1977 | mlog(0, "reset refcount tree root %llu to be a record block.\n", | ||
1978 | (unsigned long long)ref_root_bh->b_blocknr); | ||
1979 | |||
1980 | rb->rf_flags = 0; | ||
1981 | rb->rf_parent = 0; | ||
1982 | rb->rf_cpos = 0; | ||
1983 | memset(&rb->rf_records, 0, sb->s_blocksize - | ||
1984 | offsetof(struct ocfs2_refcount_block, rf_records)); | ||
1985 | rb->rf_records.rl_count = | ||
1986 | cpu_to_le16(ocfs2_refcount_recs_per_rb(sb)); | ||
1987 | } | ||
1988 | |||
1989 | ocfs2_journal_dirty(handle, ref_root_bh); | ||
1990 | |||
1991 | out: | ||
1992 | return ret; | ||
1993 | } | ||
1994 | |||
1995 | static int ocfs2_decrease_refcount_rec(handle_t *handle, | ||
1996 | struct ocfs2_caching_info *ci, | ||
1997 | struct buffer_head *ref_root_bh, | ||
1998 | struct buffer_head *ref_leaf_bh, | ||
1999 | int index, u64 cpos, unsigned int len, | ||
2000 | struct ocfs2_alloc_context *meta_ac, | ||
2001 | struct ocfs2_cached_dealloc_ctxt *dealloc) | ||
2002 | { | ||
2003 | int ret; | ||
2004 | struct ocfs2_refcount_block *rb = | ||
2005 | (struct ocfs2_refcount_block *)ref_leaf_bh->b_data; | ||
2006 | struct ocfs2_refcount_rec *rec = &rb->rf_records.rl_recs[index]; | ||
2007 | |||
2008 | BUG_ON(cpos < le64_to_cpu(rec->r_cpos)); | ||
2009 | BUG_ON(cpos + len > | ||
2010 | le64_to_cpu(rec->r_cpos) + le32_to_cpu(rec->r_clusters)); | ||
2011 | |||
2012 | if (cpos == le64_to_cpu(rec->r_cpos) && | ||
2013 | len == le32_to_cpu(rec->r_clusters)) | ||
2014 | ret = ocfs2_change_refcount_rec(handle, ci, | ||
2015 | ref_leaf_bh, index, -1); | ||
2016 | else { | ||
2017 | struct ocfs2_refcount_rec split = *rec; | ||
2018 | split.r_cpos = cpu_to_le64(cpos); | ||
2019 | split.r_clusters = cpu_to_le32(len); | ||
2020 | |||
2021 | le32_add_cpu(&split.r_refcount, -1); | ||
2022 | |||
2023 | mlog(0, "split refcount rec, start %llu, " | ||
2024 | "len %u, count %u, original start %llu, len %u\n", | ||
2025 | (unsigned long long)le64_to_cpu(split.r_cpos), | ||
2026 | len, le32_to_cpu(split.r_refcount), | ||
2027 | (unsigned long long)le64_to_cpu(rec->r_cpos), | ||
2028 | le32_to_cpu(rec->r_clusters)); | ||
2029 | ret = ocfs2_split_refcount_rec(handle, ci, | ||
2030 | ref_root_bh, ref_leaf_bh, | ||
2031 | &split, index, | ||
2032 | meta_ac, dealloc); | ||
2033 | } | ||
2034 | |||
2035 | if (ret) { | ||
2036 | mlog_errno(ret); | ||
2037 | goto out; | ||
2038 | } | ||
2039 | |||
2040 | /* Remove the leaf refcount block if it contains no refcount record. */ | ||
2041 | if (!rb->rf_records.rl_used && ref_leaf_bh != ref_root_bh) { | ||
2042 | ret = ocfs2_remove_refcount_extent(handle, ci, ref_root_bh, | ||
2043 | ref_leaf_bh, meta_ac, | ||
2044 | dealloc); | ||
2045 | if (ret) | ||
2046 | mlog_errno(ret); | ||
2047 | } | ||
2048 | |||
2049 | out: | ||
2050 | return ret; | ||
2051 | } | ||
2052 | |||
2053 | static int __ocfs2_decrease_refcount(handle_t *handle, | ||
2054 | struct ocfs2_caching_info *ci, | ||
2055 | struct buffer_head *ref_root_bh, | ||
2056 | u64 cpos, u32 len, | ||
2057 | struct ocfs2_alloc_context *meta_ac, | ||
2058 | struct ocfs2_cached_dealloc_ctxt *dealloc) | ||
2059 | { | ||
2060 | int ret = 0, index = 0; | ||
2061 | struct ocfs2_refcount_rec rec; | ||
2062 | unsigned int r_count = 0, r_len; | ||
2063 | struct super_block *sb = ocfs2_metadata_cache_get_super(ci); | ||
2064 | struct buffer_head *ref_leaf_bh = NULL; | ||
2065 | |||
2066 | mlog(0, "Tree owner %llu, decrease refcount start %llu, len %u\n", | ||
2067 | (unsigned long long)ocfs2_metadata_cache_owner(ci), | ||
2068 | (unsigned long long)cpos, len); | ||
2069 | |||
2070 | while (len) { | ||
2071 | ret = ocfs2_get_refcount_rec(ci, ref_root_bh, | ||
2072 | cpos, len, &rec, &index, | ||
2073 | &ref_leaf_bh); | ||
2074 | if (ret) { | ||
2075 | mlog_errno(ret); | ||
2076 | goto out; | ||
2077 | } | ||
2078 | |||
2079 | r_count = le32_to_cpu(rec.r_refcount); | ||
2080 | BUG_ON(r_count == 0); | ||
2081 | |||
2082 | r_len = min((u64)(cpos + len), le64_to_cpu(rec.r_cpos) + | ||
2083 | le32_to_cpu(rec.r_clusters)) - cpos; | ||
2084 | |||
2085 | ret = ocfs2_decrease_refcount_rec(handle, ci, ref_root_bh, | ||
2086 | ref_leaf_bh, index, | ||
2087 | cpos, r_len, | ||
2088 | meta_ac, dealloc); | ||
2089 | if (ret) { | ||
2090 | mlog_errno(ret); | ||
2091 | goto out; | ||
2092 | } | ||
2093 | |||
2094 | if (le32_to_cpu(rec.r_refcount) == 1) { | ||
2095 | ret = ocfs2_cache_cluster_dealloc(dealloc, | ||
2096 | ocfs2_clusters_to_blocks(sb, cpos), | ||
2097 | r_len); | ||
2098 | if (ret) { | ||
2099 | mlog_errno(ret); | ||
2100 | goto out; | ||
2101 | } | ||
2102 | } | ||
2103 | |||
2104 | cpos += r_len; | ||
2105 | len -= r_len; | ||
2106 | brelse(ref_leaf_bh); | ||
2107 | ref_leaf_bh = NULL; | ||
2108 | } | ||
2109 | |||
2110 | out: | ||
2111 | brelse(ref_leaf_bh); | ||
2112 | return ret; | ||
2113 | } | ||
2114 | |||
2115 | /* Caller must hold refcount tree lock. */ | ||
2116 | int ocfs2_decrease_refcount(struct inode *inode, | ||
2117 | handle_t *handle, u32 cpos, u32 len, | ||
2118 | struct ocfs2_alloc_context *meta_ac, | ||
2119 | struct ocfs2_cached_dealloc_ctxt *dealloc) | ||
2120 | { | ||
2121 | int ret; | ||
2122 | u64 ref_blkno; | ||
2123 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
2124 | struct buffer_head *ref_root_bh = NULL; | ||
2125 | struct ocfs2_refcount_tree *tree; | ||
2126 | |||
2127 | BUG_ON(!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL)); | ||
2128 | |||
2129 | ret = ocfs2_get_refcount_block(inode, &ref_blkno); | ||
2130 | if (ret) { | ||
2131 | mlog_errno(ret); | ||
2132 | goto out; | ||
2133 | } | ||
2134 | |||
2135 | ret = ocfs2_get_refcount_tree(OCFS2_SB(inode->i_sb), ref_blkno, &tree); | ||
2136 | if (ret) { | ||
2137 | mlog_errno(ret); | ||
2138 | goto out; | ||
2139 | } | ||
2140 | |||
2141 | ret = ocfs2_read_refcount_block(&tree->rf_ci, tree->rf_blkno, | ||
2142 | &ref_root_bh); | ||
2143 | if (ret) { | ||
2144 | mlog_errno(ret); | ||
2145 | goto out; | ||
2146 | } | ||
2147 | |||
2148 | ret = __ocfs2_decrease_refcount(handle, &tree->rf_ci, ref_root_bh, | ||
2149 | cpos, len, meta_ac, dealloc); | ||
2150 | if (ret) | ||
2151 | mlog_errno(ret); | ||
2152 | out: | ||
2153 | brelse(ref_root_bh); | ||
2154 | return ret; | ||
2155 | } | ||
diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h index 2ea7fc52c23c..ad4b483ec5c7 100644 --- a/fs/ocfs2/refcounttree.h +++ b/fs/ocfs2/refcounttree.h | |||
@@ -40,4 +40,9 @@ int ocfs2_lock_refcount_tree(struct ocfs2_super *osb, u64 ref_blkno, int rw, | |||
40 | void ocfs2_unlock_refcount_tree(struct ocfs2_super *osb, | 40 | void ocfs2_unlock_refcount_tree(struct ocfs2_super *osb, |
41 | struct ocfs2_refcount_tree *tree, | 41 | struct ocfs2_refcount_tree *tree, |
42 | int rw); | 42 | int rw); |
43 | |||
44 | int ocfs2_decrease_refcount(struct inode *inode, | ||
45 | handle_t *handle, u32 cpos, u32 len, | ||
46 | struct ocfs2_alloc_context *meta_ac, | ||
47 | struct ocfs2_cached_dealloc_ctxt *dealloc); | ||
43 | #endif /* OCFS2_REFCOUNTTREE_H */ | 48 | #endif /* OCFS2_REFCOUNTTREE_H */ |