Put generic_show_options read access to s_options under rcu_read_lock, split save_mount_options() into "we are setting it the first time" (uses in foo_fill_super()) and "we are relacing and freeing the old one", synchronize_rcu() before kfree() in the latter.
Unified · Split
| @@ -75,8 +75,7 @@ static int capifs_remount(struct super_block *s, int *flags, char *data) · 74 unmodified lines | ||
| 75 | 75 | } |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | − kfree(s->s_options); | |
| 79 | − s->s_options = new_opt; | |
| 78 | + replace_mount_options(s, new_opt); | |
| 80 | 79 | |
| 81 | 80 | config.setuid = setuid; |
| 82 | 81 | config.setgid = setgid; |
| @@ -507,8 +507,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) · 506 unmodified lines | ||
| 507 | 507 | kfree(new_opts); |
| 508 | 508 | return -EINVAL; |
| 509 | 509 | } |
| 510 | − kfree(sb->s_options); | |
| 511 | − sb->s_options = new_opts; | |
| 510 | + replace_mount_options(sb, new_opts); | |
| 512 | 511 | |
| 513 | 512 | sbi->s_flags = mount_flags; |
| 514 | 513 | sbi->s_mode = mode; |
| @@ -408,17 +408,17 @@ static int afs_get_sb(struct file_system_type *fs_type, · 407 unmodified lines | ||
| 408 | 408 | deactivate_locked_super(sb); |
| 409 | 409 | goto error; |
| 410 | 410 | } |
| 411 | − sb->s_options = new_opts; | |
| 411 | + save_mount_options(sb, new_opts); | |
| 412 | 412 | sb->s_flags |= MS_ACTIVE; |
| 413 | 413 | } else { |
| 414 | 414 | _debug("reuse"); |
| 415 | − kfree(new_opts); | |
| 416 | 415 | ASSERTCMP(sb->s_flags, &, MS_ACTIVE); |
| 417 | 416 | } |
| 418 | 417 | |
| 419 | 418 | simple_set_mnt(mnt, sb); |
| 420 | 419 | afs_put_volume(params.volume); |
| 421 | 420 | afs_put_cell(params.cell); |
| 421 | + kfree(new_opts); | |
| 422 | 422 | _leave(" = 0 [%p]", sb); |
| 423 | 423 | return 0; |
| 424 | 424 | |
| @@ -423,8 +423,7 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data) · 422 unmodified lines | ||
| 423 | 423 | |
| 424 | 424 | if (!(*flags & MS_RDONLY)) mark_dirty(s); |
| 425 | 425 | |
| 426 | − kfree(s->s_options); | |
| 427 | − s->s_options = new_opts; | |
| 426 | + replace_mount_options(s, new_opts); | |
| 428 | 427 | |
| 429 | 428 | return 0; |
| 430 | 429 | |
| @@ -695,12 +695,16 @@ static inline void mangle(struct seq_file *m, const char *s) · 694 unmodified lines | ||
| 695 | 695 | */ |
| 696 | 696 | int generic_show_options(struct seq_file *m, struct vfsmount *mnt) |
| 697 | 697 | { |
| 698 | − const char *options = mnt->mnt_sb->s_options; | |
| 698 | + const char *options; | |
| 699 | + | |
| 700 | + rcu_read_lock(); | |
| 701 | + options = rcu_dereference(mnt->mnt_sb->s_options); | |
| 699 | 702 | |
| 700 | 703 | if (options != NULL && options[0]) { |
| 701 | 704 | seq_putc(m, ','); |
| 702 | 705 | mangle(m, options); |
| 703 | 706 | } |
| 707 | + rcu_read_unlock(); | |
| 704 | 708 | |
| 705 | 709 | return 0; |
| 706 | 710 | } |
| @@ -721,11 +725,22 @@ EXPORT_SYMBOL(generic_show_options); · 14 unmodified lines | ||
| 721 | 725 | */ |
| 722 | 726 | void save_mount_options(struct super_block *sb, char *options) |
| 723 | 727 | { |
| 724 | − kfree(sb->s_options); | |
| 725 | − sb->s_options = kstrdup(options, GFP_KERNEL); | |
| 728 | + BUG_ON(sb->s_options); | |
| 729 | + rcu_assign_pointer(sb->s_options, kstrdup(options, GFP_KERNEL)); | |
| 726 | 730 | } |
| 727 | 731 | EXPORT_SYMBOL(save_mount_options); |
| 728 | 732 | |
| 733 | +void replace_mount_options(struct super_block *sb, char *options) | |
| 734 | +{ | |
| 735 | + char *old = sb->s_options; | |
| 736 | + rcu_assign_pointer(sb->s_options, options); | |
| 737 | + if (old) { | |
| 738 | + synchronize_rcu(); | |
| 739 | + kfree(old); | |
| 740 | + } | |
| 741 | +} | |
| 742 | +EXPORT_SYMBOL(replace_mount_options); | |
| 743 | + | |
| 729 | 744 | #ifdef CONFIG_PROC_FS |
| 730 | 745 | /* iterator */ |
| 731 | 746 | static void *m_start(struct seq_file *m, loff_t *pos) |
| @@ -1316,8 +1316,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg) · 1315 unmodified lines | ||
| 1316 | 1316 | } |
| 1317 | 1317 | |
| 1318 | 1318 | out_ok: |
| 1319 | − kfree(s->s_options); | |
| 1320 | − s->s_options = new_opts; | |
| 1319 | + replace_mount_options(s, new_opts); | |
| 1321 | 1320 | return 0; |
| 1322 | 1321 | |
| 1323 | 1322 | out_err: |
| @@ -2368,6 +2368,7 @@ extern void file_update_time(struct file *file); · 2367 unmodified lines | ||
| 2368 | 2368 | |
| 2369 | 2369 | extern int generic_show_options(struct seq_file *m, struct vfsmount *mnt); |
| 2370 | 2370 | extern void save_mount_options(struct super_block *sb, char *options); |
| 2371 | +extern void replace_mount_options(struct super_block *sb, char *options); | |
| 2371 | 2372 | |
| 2372 | 2373 | static inline ino_t parent_ino(struct dentry *dentry) |
| 2373 | 2374 | { |