aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2009-08-03 17:28:35 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2009-09-24 07:47:41 -0400
commit4504230a71566785a05d3e6b53fa1ee071b864eb (patch)
treee73070fcad99b800f0324992c392d44c6dddc97c
parent4fadd7bb20a1e7c774ed88dc703d8fbcd00ff917 (diff)
freeze_bdev: grab active reference to frozen superblocks
Currently we held s_umount while a filesystem is frozen, despite that we might return to userspace and unlock it from a different process. Instead grab an active reference to keep the file system busy and add an explicit check for frozen filesystems in remount and reject the remount instead of blocking on s_umount. Add a new get_active_super helper to super.c for use by freeze_bdev that grabs an active reference to a superblock from a given block device. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-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 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}
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 1cb26a3e3df0..19eb70b374bc 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 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);
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