diff options
Diffstat (limited to 'fs/ext4/resize.c')
| -rw-r--r-- | fs/ext4/resize.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 6076d5e4b513..e8ccb2f8f45b 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c | |||
| @@ -234,6 +234,256 @@ static int extend_or_restart_transaction(handle_t *handle, int thresh) | |||
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | /* | 236 | /* |
| 237 | * set_flexbg_block_bitmap() mark @count blocks starting from @block used. | ||
| 238 | * | ||
| 239 | * Helper function for ext4_setup_new_group_blocks() which set . | ||
| 240 | * | ||
| 241 | * @sb: super block | ||
| 242 | * @handle: journal handle | ||
| 243 | * @flex_gd: flex group data | ||
| 244 | */ | ||
| 245 | static int set_flexbg_block_bitmap(struct super_block *sb, handle_t *handle, | ||
| 246 | struct ext4_new_flex_group_data *flex_gd, | ||
| 247 | ext4_fsblk_t block, ext4_group_t count) | ||
| 248 | { | ||
| 249 | ext4_group_t count2; | ||
| 250 | |||
| 251 | ext4_debug("mark blocks [%llu/%u] used\n", block, count); | ||
| 252 | for (count2 = count; count > 0; count -= count2, block += count2) { | ||
| 253 | ext4_fsblk_t start; | ||
| 254 | struct buffer_head *bh; | ||
| 255 | ext4_group_t group; | ||
| 256 | int err; | ||
| 257 | |||
| 258 | ext4_get_group_no_and_offset(sb, block, &group, NULL); | ||
| 259 | start = ext4_group_first_block_no(sb, group); | ||
| 260 | group -= flex_gd->groups[0].group; | ||
| 261 | |||
| 262 | count2 = sb->s_blocksize * 8 - (block - start); | ||
| 263 | if (count2 > count) | ||
| 264 | count2 = count; | ||
| 265 | |||
| 266 | if (flex_gd->bg_flags[group] & EXT4_BG_BLOCK_UNINIT) { | ||
| 267 | BUG_ON(flex_gd->count > 1); | ||
| 268 | continue; | ||
| 269 | } | ||
| 270 | |||
| 271 | err = extend_or_restart_transaction(handle, 1); | ||
| 272 | if (err) | ||
| 273 | return err; | ||
| 274 | |||
| 275 | bh = sb_getblk(sb, flex_gd->groups[group].block_bitmap); | ||
| 276 | if (!bh) | ||
| 277 | return -EIO; | ||
| 278 | |||
| 279 | err = ext4_journal_get_write_access(handle, bh); | ||
| 280 | if (err) | ||
| 281 | return err; | ||
| 282 | ext4_debug("mark block bitmap %#04llx (+%llu/%u)\n", block, | ||
| 283 | block - start, count2); | ||
| 284 | ext4_set_bits(bh->b_data, block - start, count2); | ||
| 285 | |||
| 286 | err = ext4_handle_dirty_metadata(handle, NULL, bh); | ||
| 287 | if (unlikely(err)) | ||
| 288 | return err; | ||
| 289 | brelse(bh); | ||
| 290 | } | ||
| 291 | |||
| 292 | return 0; | ||
| 293 | } | ||
| 294 | |||
| 295 | /* | ||
| 296 | * Set up the block and inode bitmaps, and the inode table for the new groups. | ||
| 297 | * This doesn't need to be part of the main transaction, since we are only | ||
| 298 | * changing blocks outside the actual filesystem. We still do journaling to | ||
| 299 | * ensure the recovery is correct in case of a failure just after resize. | ||
| 300 | * If any part of this fails, we simply abort the resize. | ||
| 301 | * | ||
| 302 | * setup_new_flex_group_blocks handles a flex group as follow: | ||
| 303 | * 1. copy super block and GDT, and initialize group tables if necessary. | ||
| 304 | * In this step, we only set bits in blocks bitmaps for blocks taken by | ||
| 305 | * super block and GDT. | ||
| 306 | * 2. allocate group tables in block bitmaps, that is, set bits in block | ||
| 307 | * bitmap for blocks taken by group tables. | ||
| 308 | */ | ||
| 309 | static int setup_new_flex_group_blocks(struct super_block *sb, | ||
| 310 | struct ext4_new_flex_group_data *flex_gd) | ||
| 311 | { | ||
| 312 | int group_table_count[] = {1, 1, EXT4_SB(sb)->s_itb_per_group}; | ||
| 313 | ext4_fsblk_t start; | ||
| 314 | ext4_fsblk_t block; | ||
| 315 | struct ext4_sb_info *sbi = EXT4_SB(sb); | ||
| 316 | struct ext4_super_block *es = sbi->s_es; | ||
| 317 | struct ext4_new_group_data *group_data = flex_gd->groups; | ||
| 318 | __u16 *bg_flags = flex_gd->bg_flags; | ||
| 319 | handle_t *handle; | ||
| 320 | ext4_group_t group, count; | ||
| 321 | struct buffer_head *bh = NULL; | ||
| 322 | int reserved_gdb, i, j, err = 0, err2; | ||
| 323 | |||
| 324 | BUG_ON(!flex_gd->count || !group_data || | ||
| 325 | group_data[0].group != sbi->s_groups_count); | ||
| 326 | |||
| 327 | reserved_gdb = le16_to_cpu(es->s_reserved_gdt_blocks); | ||
| 328 | |||
| 329 | /* This transaction may be extended/restarted along the way */ | ||
| 330 | handle = ext4_journal_start_sb(sb, EXT4_MAX_TRANS_DATA); | ||
| 331 | if (IS_ERR(handle)) | ||
| 332 | return PTR_ERR(handle); | ||
| 333 | |||
| 334 | group = group_data[0].group; | ||
| 335 | for (i = 0; i < flex_gd->count; i++, group++) { | ||
| 336 | unsigned long gdblocks; | ||
| 337 | |||
| 338 | gdblocks = ext4_bg_num_gdb(sb, group); | ||
| 339 | start = ext4_group_first_block_no(sb, group); | ||
| 340 | |||
| 341 | /* Copy all of the GDT blocks into the backup in this group */ | ||
| 342 | for (j = 0, block = start + 1; j < gdblocks; j++, block++) { | ||
| 343 | struct buffer_head *gdb; | ||
| 344 | |||
| 345 | ext4_debug("update backup group %#04llx\n", block); | ||
| 346 | err = extend_or_restart_transaction(handle, 1); | ||
| 347 | if (err) | ||
| 348 | goto out; | ||
| 349 | |||
| 350 | gdb = sb_getblk(sb, block); | ||
| 351 | if (!gdb) { | ||
| 352 | err = -EIO; | ||
| 353 | goto out; | ||
| 354 | } | ||
| 355 | |||
| 356 | err = ext4_journal_get_write_access(handle, gdb); | ||
| 357 | if (err) { | ||
| 358 | brelse(gdb); | ||
| 359 | goto out; | ||
| 360 | } | ||
| 361 | memcpy(gdb->b_data, sbi->s_group_desc[j]->b_data, | ||
| 362 | gdb->b_size); | ||
| 363 | set_buffer_uptodate(gdb); | ||
| 364 | |||
| 365 | err = ext4_handle_dirty_metadata(handle, NULL, gdb); | ||
| 366 | if (unlikely(err)) { | ||
| 367 | brelse(gdb); | ||
| 368 | goto out; | ||
| 369 | } | ||
| 370 | brelse(gdb); | ||
| 371 | } | ||
| 372 | |||
| 373 | /* Zero out all of the reserved backup group descriptor | ||
| 374 | * table blocks | ||
| 375 | */ | ||
| 376 | if (ext4_bg_has_super(sb, group)) { | ||
| 377 | err = sb_issue_zeroout(sb, gdblocks + start + 1, | ||
| 378 | reserved_gdb, GFP_NOFS); | ||
| 379 | if (err) | ||
| 380 | goto out; | ||
| 381 | } | ||
| 382 | |||
| 383 | /* Initialize group tables of the grop @group */ | ||
| 384 | if (!(bg_flags[i] & EXT4_BG_INODE_ZEROED)) | ||
| 385 | goto handle_bb; | ||
| 386 | |||
| 387 | /* Zero out all of the inode table blocks */ | ||
| 388 | block = group_data[i].inode_table; | ||
| 389 | ext4_debug("clear inode table blocks %#04llx -> %#04lx\n", | ||
| 390 | block, sbi->s_itb_per_group); | ||
| 391 | err = sb_issue_zeroout(sb, block, sbi->s_itb_per_group, | ||
| 392 | GFP_NOFS); | ||
| 393 | if (err) | ||
| 394 | goto out; | ||
| 395 | |||
| 396 | handle_bb: | ||
| 397 | if (bg_flags[i] & EXT4_BG_BLOCK_UNINIT) | ||
| 398 | goto handle_ib; | ||
| 399 | |||
| 400 | /* Initialize block bitmap of the @group */ | ||
| 401 | block = group_data[i].block_bitmap; | ||
| 402 | err = extend_or_restart_transaction(handle, 1); | ||
| 403 | if (err) | ||
| 404 | goto out; | ||
| 405 | |||
| 406 | bh = bclean(handle, sb, block); | ||
| 407 | if (IS_ERR(bh)) { | ||
| 408 | err = PTR_ERR(bh); | ||
| 409 | goto out; | ||
| 410 | } | ||
| 411 | if (ext4_bg_has_super(sb, group)) { | ||
| 412 | ext4_debug("mark backup superblock %#04llx (+0)\n", | ||
| 413 | start); | ||
| 414 | ext4_set_bits(bh->b_data, 0, gdblocks + reserved_gdb + | ||
| 415 | 1); | ||
| 416 | } | ||
| 417 | ext4_mark_bitmap_end(group_data[i].blocks_count, | ||
| 418 | sb->s_blocksize * 8, bh->b_data); | ||
| 419 | err = ext4_handle_dirty_metadata(handle, NULL, bh); | ||
| 420 | if (err) | ||
| 421 | goto out; | ||
| 422 | brelse(bh); | ||
| 423 | |||
| 424 | handle_ib: | ||
| 425 | if (bg_flags[i] & EXT4_BG_INODE_UNINIT) | ||
| 426 | continue; | ||
| 427 | |||
| 428 | /* Initialize inode bitmap of the @group */ | ||
| 429 | block = group_data[i].inode_bitmap; | ||
| 430 | err = extend_or_restart_transaction(handle, 1); | ||
| 431 | if (err) | ||
| 432 | goto out; | ||
| 433 | /* Mark unused entries in inode bitmap used */ | ||
| 434 | bh = bclean(handle, sb, block); | ||
| 435 | if (IS_ERR(bh)) { | ||
| 436 | err = PTR_ERR(bh); | ||
| 437 | goto out; | ||
| 438 | } | ||
| 439 | |||
| 440 | ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), | ||
| 441 | sb->s_blocksize * 8, bh->b_data); | ||
| 442 | err = ext4_handle_dirty_metadata(handle, NULL, bh); | ||
| 443 | if (err) | ||
| 444 | goto out; | ||
| 445 | brelse(bh); | ||
| 446 | } | ||
| 447 | bh = NULL; | ||
| 448 | |||
| 449 | /* Mark group tables in block bitmap */ | ||
| 450 | for (j = 0; j < GROUP_TABLE_COUNT; j++) { | ||
| 451 | count = group_table_count[j]; | ||
| 452 | start = (&group_data[0].block_bitmap)[j]; | ||
| 453 | block = start; | ||
| 454 | for (i = 1; i < flex_gd->count; i++) { | ||
| 455 | block += group_table_count[j]; | ||
| 456 | if (block == (&group_data[i].block_bitmap)[j]) { | ||
| 457 | count += group_table_count[j]; | ||
| 458 | continue; | ||
| 459 | } | ||
| 460 | err = set_flexbg_block_bitmap(sb, handle, | ||
| 461 | flex_gd, start, count); | ||
| 462 | if (err) | ||
| 463 | goto out; | ||
| 464 | count = group_table_count[j]; | ||
| 465 | start = group_data[i].block_bitmap; | ||
| 466 | block = start; | ||
| 467 | } | ||
| 468 | |||
| 469 | if (count) { | ||
| 470 | err = set_flexbg_block_bitmap(sb, handle, | ||
| 471 | flex_gd, start, count); | ||
| 472 | if (err) | ||
| 473 | goto out; | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | out: | ||
| 478 | brelse(bh); | ||
| 479 | err2 = ext4_journal_stop(handle); | ||
| 480 | if (err2 && !err) | ||
| 481 | err = err2; | ||
| 482 | |||
| 483 | return err; | ||
| 484 | } | ||
| 485 | |||
| 486 | /* | ||
| 237 | * Set up the block and inode bitmaps, and the inode table for the new group. | 487 | * Set up the block and inode bitmaps, and the inode table for the new group. |
| 238 | * This doesn't need to be part of the main transaction, since we are only | 488 | * This doesn't need to be part of the main transaction, since we are only |
| 239 | * changing blocks outside the actual filesystem. We still do journaling to | 489 | * changing blocks outside the actual filesystem. We still do journaling to |
