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 |