diff options
-rw-r--r-- | fs/block_dev.c | 132 | ||||
-rw-r--r-- | fs/super.c | 48 | ||||
-rw-r--r-- | include/linux/fs.h | 1 |
3 files changed, 120 insertions, 61 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index 22506eb4a58e..9cf4b926f8e4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -230,43 +230,54 @@ struct super_block *freeze_bdev(struct block_device *bdev) | |||
230 | int error = 0; | 230 | int error = 0; |
231 | 231 | ||
232 | mutex_lock(&bdev->bd_fsfreeze_mutex); | 232 | mutex_lock(&bdev->bd_fsfreeze_mutex); |
233 | if (bdev->bd_fsfreeze_count > 0) { | 233 | if (++bdev->bd_fsfreeze_count > 1) { |
234 | bdev->bd_fsfreeze_count++; | 234 | /* |
235 | * We don't even need to grab a reference - the first call | ||
236 | * to freeze_bdev grab an active reference and only the last | ||
237 | * thaw_bdev drops it. | ||
238 | */ | ||
235 | sb = get_super(bdev); | 239 | sb = get_super(bdev); |
240 | drop_super(sb); | ||
236 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 241 | mutex_unlock(&bdev->bd_fsfreeze_mutex); |
237 | return sb; | 242 | return sb; |
238 | } | 243 | } |
239 | bdev->bd_fsfreeze_count++; | 244 | |
240 | 245 | sb = get_active_super(bdev); | |
241 | sb = get_super(bdev); | 246 | if (!sb) |
242 | if (sb && !(sb->s_flags & MS_RDONLY)) { | 247 | goto out; |
243 | sb->s_frozen = SB_FREEZE_WRITE; | 248 | if (sb->s_flags & MS_RDONLY) { |
244 | smp_wmb(); | 249 | deactivate_locked_super(sb); |
245 | 250 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | |
246 | sync_filesystem(sb); | 251 | return sb; |
247 | 252 | } | |
248 | sb->s_frozen = SB_FREEZE_TRANS; | 253 | |
249 | smp_wmb(); | 254 | sb->s_frozen = SB_FREEZE_WRITE; |
250 | 255 | smp_wmb(); | |
251 | sync_blockdev(sb->s_bdev); | 256 | |
252 | 257 | sync_filesystem(sb); | |
253 | if (sb->s_op->freeze_fs) { | 258 | |
254 | error = sb->s_op->freeze_fs(sb); | 259 | sb->s_frozen = SB_FREEZE_TRANS; |
255 | if (error) { | 260 | smp_wmb(); |
256 | printk(KERN_ERR | 261 | |
257 | "VFS:Filesystem freeze failed\n"); | 262 | sync_blockdev(sb->s_bdev); |
258 | sb->s_frozen = SB_UNFROZEN; | 263 | |
259 | drop_super(sb); | 264 | if (sb->s_op->freeze_fs) { |
260 | bdev->bd_fsfreeze_count--; | 265 | error = sb->s_op->freeze_fs(sb); |
261 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 266 | if (error) { |
262 | return ERR_PTR(error); | 267 | printk(KERN_ERR |
263 | } | 268 | "VFS:Filesystem freeze failed\n"); |
269 | sb->s_frozen = SB_UNFROZEN; | ||
270 | deactivate_locked_super(sb); | ||
271 | bdev->bd_fsfreeze_count--; | ||
272 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
273 | return ERR_PTR(error); | ||
264 | } | 274 | } |
265 | } | 275 | } |
276 | up_write(&sb->s_umount); | ||
266 | 277 | ||
278 | out: | ||
267 | sync_blockdev(bdev); | 279 | sync_blockdev(bdev); |
268 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 280 | mutex_unlock(&bdev->bd_fsfreeze_mutex); |
269 | |||
270 | return sb; /* thaw_bdev releases s->s_umount */ | 281 | return sb; /* thaw_bdev releases s->s_umount */ |
271 | } | 282 | } |
272 | EXPORT_SYMBOL(freeze_bdev); | 283 | EXPORT_SYMBOL(freeze_bdev); |
@@ -280,43 +291,44 @@ EXPORT_SYMBOL(freeze_bdev); | |||
280 | */ | 291 | */ |
281 | int thaw_bdev(struct block_device *bdev, struct super_block *sb) | 292 | int thaw_bdev(struct block_device *bdev, struct super_block *sb) |
282 | { | 293 | { |
283 | int error = 0; | 294 | int error = -EINVAL; |
284 | 295 | ||
285 | mutex_lock(&bdev->bd_fsfreeze_mutex); | 296 | mutex_lock(&bdev->bd_fsfreeze_mutex); |
286 | if (!bdev->bd_fsfreeze_count) { | 297 | if (!bdev->bd_fsfreeze_count) |
287 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 298 | goto out_unlock; |
288 | return -EINVAL; | 299 | |
289 | } | 300 | error = 0; |
290 | 301 | if (--bdev->bd_fsfreeze_count > 0) | |
291 | bdev->bd_fsfreeze_count--; | 302 | goto out_unlock; |
292 | if (bdev->bd_fsfreeze_count > 0) { | 303 | |
293 | if (sb) | 304 | if (!sb) |
294 | drop_super(sb); | 305 | goto out_unlock; |
295 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 306 | |
296 | return 0; | 307 | BUG_ON(sb->s_bdev != bdev); |
297 | } | 308 | down_write(&sb->s_umount); |
298 | 309 | if (sb->s_flags & MS_RDONLY) | |
299 | if (sb) { | 310 | goto out_deactivate; |
300 | BUG_ON(sb->s_bdev != bdev); | 311 | |
301 | if (!(sb->s_flags & MS_RDONLY)) { | 312 | if (sb->s_op->unfreeze_fs) { |
302 | if (sb->s_op->unfreeze_fs) { | 313 | error = sb->s_op->unfreeze_fs(sb); |
303 | error = sb->s_op->unfreeze_fs(sb); | 314 | if (error) { |
304 | if (error) { | 315 | printk(KERN_ERR |
305 | printk(KERN_ERR | 316 | "VFS:Filesystem thaw failed\n"); |
306 | "VFS:Filesystem thaw failed\n"); | 317 | sb->s_frozen = SB_FREEZE_TRANS; |
307 | sb->s_frozen = SB_FREEZE_TRANS; | 318 | bdev->bd_fsfreeze_count++; |
308 | bdev->bd_fsfreeze_count++; | 319 | mutex_unlock(&bdev->bd_fsfreeze_mutex); |
309 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 320 | return error; |
310 | return error; | ||
311 | } | ||
312 | } | ||
313 | sb->s_frozen = SB_UNFROZEN; | ||
314 | smp_wmb(); | ||
315 | wake_up(&sb->s_wait_unfrozen); | ||
316 | } | 321 | } |
317 | drop_super(sb); | ||
318 | } | 322 | } |
319 | 323 | ||
324 | sb->s_frozen = SB_UNFROZEN; | ||
325 | smp_wmb(); | ||
326 | wake_up(&sb->s_wait_unfrozen); | ||
327 | |||
328 | out_deactivate: | ||
329 | if (sb) | ||
330 | deactivate_locked_super(sb); | ||
331 | out_unlock: | ||
320 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 332 | mutex_unlock(&bdev->bd_fsfreeze_mutex); |
321 | return 0; | 333 | return 0; |
322 | } | 334 | } |
diff --git a/fs/super.c b/fs/super.c index 1cb26a3e3df0..19eb70b374bc 100644 --- a/fs/super.c +++ b/fs/super.c | |||
@@ -465,6 +465,48 @@ rescan: | |||
465 | } | 465 | } |
466 | 466 | ||
467 | EXPORT_SYMBOL(get_super); | 467 | EXPORT_SYMBOL(get_super); |
468 | |||
469 | /** | ||
470 | * get_active_super - get an active reference to the superblock of a device | ||
471 | * @bdev: device to get the superblock for | ||
472 | * | ||
473 | * Scans the superblock list and finds the superblock of the file system | ||
474 | * mounted on the device given. Returns the superblock with an active | ||
475 | * reference and s_umount held exclusively or %NULL if none was found. | ||
476 | */ | ||
477 | struct super_block *get_active_super(struct block_device *bdev) | ||
478 | { | ||
479 | struct super_block *sb; | ||
480 | |||
481 | if (!bdev) | ||
482 | return NULL; | ||
483 | |||
484 | spin_lock(&sb_lock); | ||
485 | list_for_each_entry(sb, &super_blocks, s_list) { | ||
486 | if (sb->s_bdev != bdev) | ||
487 | continue; | ||
488 | |||
489 | sb->s_count++; | ||
490 | spin_unlock(&sb_lock); | ||
491 | down_write(&sb->s_umount); | ||
492 | if (sb->s_root) { | ||
493 | spin_lock(&sb_lock); | ||
494 | if (sb->s_count > S_BIAS) { | ||
495 | atomic_inc(&sb->s_active); | ||
496 | sb->s_count--; | ||
497 | spin_unlock(&sb_lock); | ||
498 | return sb; | ||
499 | } | ||
500 | spin_unlock(&sb_lock); | ||
501 | } | ||
502 | up_write(&sb->s_umount); | ||
503 | put_super(sb); | ||
504 | yield(); | ||
505 | spin_lock(&sb_lock); | ||
506 | } | ||
507 | spin_unlock(&sb_lock); | ||
508 | return NULL; | ||
509 | } | ||
468 | 510 | ||
469 | struct super_block * user_get_super(dev_t dev) | 511 | struct super_block * user_get_super(dev_t dev) |
470 | { | 512 | { |
@@ -527,11 +569,15 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) | |||
527 | { | 569 | { |
528 | int retval; | 570 | int retval; |
529 | int remount_rw; | 571 | int remount_rw; |
530 | 572 | ||
573 | if (sb->s_frozen != SB_UNFROZEN) | ||
574 | return -EBUSY; | ||
575 | |||
531 | #ifdef CONFIG_BLOCK | 576 | #ifdef CONFIG_BLOCK |
532 | if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev)) | 577 | if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev)) |
533 | return -EACCES; | 578 | return -EACCES; |
534 | #endif | 579 | #endif |
580 | |||
535 | if (flags & MS_RDONLY) | 581 | if (flags & MS_RDONLY) |
536 | acct_auto_close(sb); | 582 | acct_auto_close(sb); |
537 | shrink_dcache_sb(sb); | 583 | shrink_dcache_sb(sb); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 72dfbd423974..502d96ef345d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -2334,6 +2334,7 @@ extern void get_filesystem(struct file_system_type *fs); | |||
2334 | extern void put_filesystem(struct file_system_type *fs); | 2334 | extern void put_filesystem(struct file_system_type *fs); |
2335 | extern struct file_system_type *get_fs_type(const char *name); | 2335 | extern struct file_system_type *get_fs_type(const char *name); |
2336 | extern struct super_block *get_super(struct block_device *); | 2336 | extern struct super_block *get_super(struct block_device *); |
2337 | extern struct super_block *get_active_super(struct block_device *bdev); | ||
2337 | extern struct super_block *user_get_super(dev_t); | 2338 | extern struct super_block *user_get_super(dev_t); |
2338 | extern void drop_super(struct super_block *sb); | 2339 | extern void drop_super(struct super_block *sb); |
2339 | 2340 | ||