aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/block_dev.c132
-rw-r--r--fs/super.c48
-rw-r--r--include/linux/fs.h1
3 files changed, 120 insertions, 61 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 22506eb4a58..9cf4b926f8e 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}
272EXPORT_SYMBOL(freeze_bdev); 283EXPORT_SYMBOL(freeze_bdev);
@@ -280,43 +291,44 @@ EXPORT_SYMBOL(freeze_bdev);
280 */ 291 */
281int thaw_bdev(struct block_device *bdev, struct super_block *sb) 292int 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
328out_deactivate:
329 if (sb)
330 deactivate_locked_super(sb);
331out_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 1cb26a3e3df..19eb70b374b 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -465,6 +465,48 @@ rescan:
465} 465}
466 466
467EXPORT_SYMBOL(get_super); 467EXPORT_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 */
477struct 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
469struct super_block * user_get_super(dev_t dev) 511struct 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 72dfbd42397..502d96ef345 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2334,6 +2334,7 @@ extern void get_filesystem(struct file_system_type *fs);
2334extern void put_filesystem(struct file_system_type *fs); 2334extern void put_filesystem(struct file_system_type *fs);
2335extern struct file_system_type *get_fs_type(const char *name); 2335extern struct file_system_type *get_fs_type(const char *name);
2336extern struct super_block *get_super(struct block_device *); 2336extern struct super_block *get_super(struct block_device *);
2337extern struct super_block *get_active_super(struct block_device *bdev);
2337extern struct super_block *user_get_super(dev_t); 2338extern struct super_block *user_get_super(dev_t);
2338extern void drop_super(struct super_block *sb); 2339extern void drop_super(struct super_block *sb);
2339 2340