diff options
author | Theodore Ts'o <tytso@mit.edu> | 2010-05-16 19:00:00 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2010-05-16 19:00:00 -0400 |
commit | e35fd6609b2fee54484d520deccb8f18bf7d38f3 (patch) | |
tree | 9b786445602819074f599c282b31bead166e8c03 /fs/ext4/inode.c | |
parent | 8e48dcfbd7c0892b4cfd064d682cc4c95a29df32 (diff) |
ext4: Add new abstraction ext4_map_blocks() underneath ext4_get_blocks()
Jack up ext4_get_blocks() and add a new function, ext4_map_blocks()
which uses a much smaller structure, struct ext4_map_blocks which is
20 bytes, as opposed to a struct buffer_head, which nearly 5 times
bigger on an x86_64 machine. By switching things to use
ext4_map_blocks(), we can save stack space by using ext4_map_blocks()
since we can avoid allocating a struct buffer_head on the stack.
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/inode.c')
-rw-r--r-- | fs/ext4/inode.c | 102 |
1 files changed, 58 insertions, 44 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 830336d3911b..ff2f5fd681b5 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -149,7 +149,7 @@ int ext4_truncate_restart_trans(handle_t *handle, struct inode *inode, | |||
149 | int ret; | 149 | int ret; |
150 | 150 | ||
151 | /* | 151 | /* |
152 | * Drop i_data_sem to avoid deadlock with ext4_get_blocks At this | 152 | * Drop i_data_sem to avoid deadlock with ext4_map_blocks. At this |
153 | * moment, get_block can be called only for blocks inside i_size since | 153 | * moment, get_block can be called only for blocks inside i_size since |
154 | * page cache has been already dropped and writes are blocked by | 154 | * page cache has been already dropped and writes are blocked by |
155 | * i_mutex. So we can safely drop the i_data_sem here. | 155 | * i_mutex. So we can safely drop the i_data_sem here. |
@@ -890,9 +890,9 @@ err_out: | |||
890 | } | 890 | } |
891 | 891 | ||
892 | /* | 892 | /* |
893 | * The ext4_ind_get_blocks() function handles non-extents inodes | 893 | * The ext4_ind_map_blocks() function handles non-extents inodes |
894 | * (i.e., using the traditional indirect/double-indirect i_blocks | 894 | * (i.e., using the traditional indirect/double-indirect i_blocks |
895 | * scheme) for ext4_get_blocks(). | 895 | * scheme) for ext4_map_blocks(). |
896 | * | 896 | * |
897 | * Allocation strategy is simple: if we have to allocate something, we will | 897 | * Allocation strategy is simple: if we have to allocate something, we will |
898 | * have to go the whole way to leaf. So let's do it before attaching anything | 898 | * have to go the whole way to leaf. So let's do it before attaching anything |
@@ -917,9 +917,8 @@ err_out: | |||
917 | * down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system | 917 | * down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system |
918 | * blocks. | 918 | * blocks. |
919 | */ | 919 | */ |
920 | static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode, | 920 | static int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, |
921 | ext4_lblk_t iblock, unsigned int maxblocks, | 921 | struct ext4_map_blocks *map, |
922 | struct buffer_head *bh_result, | ||
923 | int flags) | 922 | int flags) |
924 | { | 923 | { |
925 | int err = -EIO; | 924 | int err = -EIO; |
@@ -935,7 +934,7 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode, | |||
935 | 934 | ||
936 | J_ASSERT(!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)); | 935 | J_ASSERT(!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)); |
937 | J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0); | 936 | J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0); |
938 | depth = ext4_block_to_path(inode, iblock, offsets, | 937 | depth = ext4_block_to_path(inode, map->m_lblk, offsets, |
939 | &blocks_to_boundary); | 938 | &blocks_to_boundary); |
940 | 939 | ||
941 | if (depth == 0) | 940 | if (depth == 0) |
@@ -946,10 +945,9 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode, | |||
946 | /* Simplest case - block found, no allocation needed */ | 945 | /* Simplest case - block found, no allocation needed */ |
947 | if (!partial) { | 946 | if (!partial) { |
948 | first_block = le32_to_cpu(chain[depth - 1].key); | 947 | first_block = le32_to_cpu(chain[depth - 1].key); |
949 | clear_buffer_new(bh_result); | ||
950 | count++; | 948 | count++; |
951 | /*map more blocks*/ | 949 | /*map more blocks*/ |
952 | while (count < maxblocks && count <= blocks_to_boundary) { | 950 | while (count < map->m_len && count <= blocks_to_boundary) { |
953 | ext4_fsblk_t blk; | 951 | ext4_fsblk_t blk; |
954 | 952 | ||
955 | blk = le32_to_cpu(*(chain[depth-1].p + count)); | 953 | blk = le32_to_cpu(*(chain[depth-1].p + count)); |
@@ -969,7 +967,7 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode, | |||
969 | /* | 967 | /* |
970 | * Okay, we need to do block allocation. | 968 | * Okay, we need to do block allocation. |
971 | */ | 969 | */ |
972 | goal = ext4_find_goal(inode, iblock, partial); | 970 | goal = ext4_find_goal(inode, map->m_lblk, partial); |
973 | 971 | ||
974 | /* the number of blocks need to allocate for [d,t]indirect blocks */ | 972 | /* the number of blocks need to allocate for [d,t]indirect blocks */ |
975 | indirect_blks = (chain + depth) - partial - 1; | 973 | indirect_blks = (chain + depth) - partial - 1; |
@@ -979,11 +977,11 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode, | |||
979 | * direct blocks to allocate for this branch. | 977 | * direct blocks to allocate for this branch. |
980 | */ | 978 | */ |
981 | count = ext4_blks_to_allocate(partial, indirect_blks, | 979 | count = ext4_blks_to_allocate(partial, indirect_blks, |
982 | maxblocks, blocks_to_boundary); | 980 | map->m_len, blocks_to_boundary); |
983 | /* | 981 | /* |
984 | * Block out ext4_truncate while we alter the tree | 982 | * Block out ext4_truncate while we alter the tree |
985 | */ | 983 | */ |
986 | err = ext4_alloc_branch(handle, inode, iblock, indirect_blks, | 984 | err = ext4_alloc_branch(handle, inode, map->m_lblk, indirect_blks, |
987 | &count, goal, | 985 | &count, goal, |
988 | offsets + (partial - chain), partial); | 986 | offsets + (partial - chain), partial); |
989 | 987 | ||
@@ -995,18 +993,20 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode, | |||
995 | * may need to return -EAGAIN upwards in the worst case. --sct | 993 | * may need to return -EAGAIN upwards in the worst case. --sct |
996 | */ | 994 | */ |
997 | if (!err) | 995 | if (!err) |
998 | err = ext4_splice_branch(handle, inode, iblock, | 996 | err = ext4_splice_branch(handle, inode, map->m_lblk, |
999 | partial, indirect_blks, count); | 997 | partial, indirect_blks, count); |
1000 | if (err) | 998 | if (err) |
1001 | goto cleanup; | 999 | goto cleanup; |
1002 | 1000 | ||
1003 | set_buffer_new(bh_result); | 1001 | map->m_flags |= EXT4_MAP_NEW; |
1004 | 1002 | ||
1005 | ext4_update_inode_fsync_trans(handle, inode, 1); | 1003 | ext4_update_inode_fsync_trans(handle, inode, 1); |
1006 | got_it: | 1004 | got_it: |
1007 | map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); | 1005 | map->m_flags |= EXT4_MAP_MAPPED; |
1006 | map->m_pblk = le32_to_cpu(chain[depth-1].key); | ||
1007 | map->m_len = count; | ||
1008 | if (count > blocks_to_boundary) | 1008 | if (count > blocks_to_boundary) |
1009 | set_buffer_boundary(bh_result); | 1009 | map->m_flags |= EXT4_MAP_BOUNDARY; |
1010 | err = count; | 1010 | err = count; |
1011 | /* Clean up and exit */ | 1011 | /* Clean up and exit */ |
1012 | partial = chain + depth - 1; /* the whole chain */ | 1012 | partial = chain + depth - 1; /* the whole chain */ |
@@ -1016,7 +1016,6 @@ cleanup: | |||
1016 | brelse(partial->bh); | 1016 | brelse(partial->bh); |
1017 | partial--; | 1017 | partial--; |
1018 | } | 1018 | } |
1019 | BUFFER_TRACE(bh_result, "returned"); | ||
1020 | out: | 1019 | out: |
1021 | return err; | 1020 | return err; |
1022 | } | 1021 | } |
@@ -1203,15 +1202,15 @@ static pgoff_t ext4_num_dirty_pages(struct inode *inode, pgoff_t idx, | |||
1203 | } | 1202 | } |
1204 | 1203 | ||
1205 | /* | 1204 | /* |
1206 | * The ext4_get_blocks() function tries to look up the requested blocks, | 1205 | * The ext4_map_blocks() function tries to look up the requested blocks, |
1207 | * and returns if the blocks are already mapped. | 1206 | * and returns if the blocks are already mapped. |
1208 | * | 1207 | * |
1209 | * Otherwise it takes the write lock of the i_data_sem and allocate blocks | 1208 | * Otherwise it takes the write lock of the i_data_sem and allocate blocks |
1210 | * and store the allocated blocks in the result buffer head and mark it | 1209 | * and store the allocated blocks in the result buffer head and mark it |
1211 | * mapped. | 1210 | * mapped. |
1212 | * | 1211 | * |
1213 | * If file type is extents based, it will call ext4_ext_get_blocks(), | 1212 | * If file type is extents based, it will call ext4_ext_map_blocks(), |
1214 | * Otherwise, call with ext4_ind_get_blocks() to handle indirect mapping | 1213 | * Otherwise, call with ext4_ind_map_blocks() to handle indirect mapping |
1215 | * based files | 1214 | * based files |
1216 | * | 1215 | * |
1217 | * On success, it returns the number of blocks being mapped or allocate. | 1216 | * On success, it returns the number of blocks being mapped or allocate. |
@@ -1224,35 +1223,30 @@ static pgoff_t ext4_num_dirty_pages(struct inode *inode, pgoff_t idx, | |||
1224 | * | 1223 | * |
1225 | * It returns the error in case of allocation failure. | 1224 | * It returns the error in case of allocation failure. |
1226 | */ | 1225 | */ |
1227 | int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block, | 1226 | int ext4_map_blocks(handle_t *handle, struct inode *inode, |
1228 | unsigned int max_blocks, struct buffer_head *bh, | 1227 | struct ext4_map_blocks *map, int flags) |
1229 | int flags) | ||
1230 | { | 1228 | { |
1231 | int retval; | 1229 | int retval; |
1232 | 1230 | ||
1233 | clear_buffer_mapped(bh); | 1231 | map->m_flags = 0; |
1234 | clear_buffer_unwritten(bh); | 1232 | ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u," |
1235 | 1233 | "logical block %lu\n", inode->i_ino, flags, map->m_len, | |
1236 | ext_debug("ext4_get_blocks(): inode %lu, flag %d, max_blocks %u," | 1234 | (unsigned long) map->m_lblk); |
1237 | "logical block %lu\n", inode->i_ino, flags, max_blocks, | ||
1238 | (unsigned long)block); | ||
1239 | /* | 1235 | /* |
1240 | * Try to see if we can get the block without requesting a new | 1236 | * Try to see if we can get the block without requesting a new |
1241 | * file system block. | 1237 | * file system block. |
1242 | */ | 1238 | */ |
1243 | down_read((&EXT4_I(inode)->i_data_sem)); | 1239 | down_read((&EXT4_I(inode)->i_data_sem)); |
1244 | if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) { | 1240 | if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) { |
1245 | retval = ext4_ext_get_blocks(handle, inode, block, max_blocks, | 1241 | retval = ext4_ext_map_blocks(handle, inode, map, 0); |
1246 | bh, 0); | ||
1247 | } else { | 1242 | } else { |
1248 | retval = ext4_ind_get_blocks(handle, inode, block, max_blocks, | 1243 | retval = ext4_ind_map_blocks(handle, inode, map, 0); |
1249 | bh, 0); | ||
1250 | } | 1244 | } |
1251 | up_read((&EXT4_I(inode)->i_data_sem)); | 1245 | up_read((&EXT4_I(inode)->i_data_sem)); |
1252 | 1246 | ||
1253 | if (retval > 0 && buffer_mapped(bh)) { | 1247 | if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { |
1254 | int ret = check_block_validity(inode, "file system corruption", | 1248 | int ret = check_block_validity(inode, "file system corruption", |
1255 | block, bh->b_blocknr, retval); | 1249 | map->m_lblk, map->m_pblk, retval); |
1256 | if (ret != 0) | 1250 | if (ret != 0) |
1257 | return ret; | 1251 | return ret; |
1258 | } | 1252 | } |
@@ -1268,7 +1262,7 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block, | |||
1268 | * ext4_ext_get_block() returns th create = 0 | 1262 | * ext4_ext_get_block() returns th create = 0 |
1269 | * with buffer head unmapped. | 1263 | * with buffer head unmapped. |
1270 | */ | 1264 | */ |
1271 | if (retval > 0 && buffer_mapped(bh)) | 1265 | if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) |
1272 | return retval; | 1266 | return retval; |
1273 | 1267 | ||
1274 | /* | 1268 | /* |
@@ -1281,7 +1275,7 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block, | |||
1281 | * of BH_Unwritten and BH_Mapped flags being simultaneously | 1275 | * of BH_Unwritten and BH_Mapped flags being simultaneously |
1282 | * set on the buffer_head. | 1276 | * set on the buffer_head. |
1283 | */ | 1277 | */ |
1284 | clear_buffer_unwritten(bh); | 1278 | map->m_flags &= ~EXT4_MAP_UNWRITTEN; |
1285 | 1279 | ||
1286 | /* | 1280 | /* |
1287 | * New blocks allocate and/or writing to uninitialized extent | 1281 | * New blocks allocate and/or writing to uninitialized extent |
@@ -1304,13 +1298,11 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block, | |||
1304 | * could have changed the inode type in between | 1298 | * could have changed the inode type in between |
1305 | */ | 1299 | */ |
1306 | if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) { | 1300 | if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) { |
1307 | retval = ext4_ext_get_blocks(handle, inode, block, max_blocks, | 1301 | retval = ext4_ext_map_blocks(handle, inode, map, flags); |
1308 | bh, flags); | ||
1309 | } else { | 1302 | } else { |
1310 | retval = ext4_ind_get_blocks(handle, inode, block, | 1303 | retval = ext4_ind_map_blocks(handle, inode, map, flags); |
1311 | max_blocks, bh, flags); | ||
1312 | 1304 | ||
1313 | if (retval > 0 && buffer_new(bh)) { | 1305 | if (retval > 0 && map->m_flags & EXT4_MAP_NEW) { |
1314 | /* | 1306 | /* |
1315 | * We allocated new blocks which will result in | 1307 | * We allocated new blocks which will result in |
1316 | * i_data's format changing. Force the migrate | 1308 | * i_data's format changing. Force the migrate |
@@ -1333,16 +1325,38 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block, | |||
1333 | EXT4_I(inode)->i_delalloc_reserved_flag = 0; | 1325 | EXT4_I(inode)->i_delalloc_reserved_flag = 0; |
1334 | 1326 | ||
1335 | up_write((&EXT4_I(inode)->i_data_sem)); | 1327 | up_write((&EXT4_I(inode)->i_data_sem)); |
1336 | if (retval > 0 && buffer_mapped(bh)) { | 1328 | if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { |
1337 | int ret = check_block_validity(inode, "file system " | 1329 | int ret = check_block_validity(inode, "file system " |
1338 | "corruption after allocation", | 1330 | "corruption after allocation", |
1339 | block, bh->b_blocknr, retval); | 1331 | map->m_lblk, map->m_pblk, |
1332 | retval); | ||
1340 | if (ret != 0) | 1333 | if (ret != 0) |
1341 | return ret; | 1334 | return ret; |
1342 | } | 1335 | } |
1343 | return retval; | 1336 | return retval; |
1344 | } | 1337 | } |
1345 | 1338 | ||
1339 | int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block, | ||
1340 | unsigned int max_blocks, struct buffer_head *bh, | ||
1341 | int flags) | ||
1342 | { | ||
1343 | struct ext4_map_blocks map; | ||
1344 | int ret; | ||
1345 | |||
1346 | map.m_lblk = block; | ||
1347 | map.m_len = max_blocks; | ||
1348 | |||
1349 | ret = ext4_map_blocks(handle, inode, &map, flags); | ||
1350 | if (ret < 0) | ||
1351 | return ret; | ||
1352 | |||
1353 | bh->b_blocknr = map.m_pblk; | ||
1354 | bh->b_size = inode->i_sb->s_blocksize * map.m_len; | ||
1355 | bh->b_bdev = inode->i_sb->s_bdev; | ||
1356 | bh->b_state = (bh->b_state & ~EXT4_MAP_FLAGS) | map.m_flags; | ||
1357 | return ret; | ||
1358 | } | ||
1359 | |||
1346 | /* Maximum number of blocks we map for direct IO at once. */ | 1360 | /* Maximum number of blocks we map for direct IO at once. */ |
1347 | #define DIO_MAX_BLOCKS 4096 | 1361 | #define DIO_MAX_BLOCKS 4096 |
1348 | 1362 | ||