diff options
author | David Howells <dhowells@redhat.com> | 2019-05-13 11:14:32 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2019-05-16 17:23:21 -0400 |
commit | f642404a0436a50912c218009ccc7856d48d784c (patch) | |
tree | ddf39d1741f07866333581e66f1abc0e6709d453 | |
parent | c925bd0ac4741badb567f594c41c8cba5e9e9732 (diff) |
afs: Make vnode->cb_interest RCU safe
Use RCU-based freeing for afs_cb_interest struct objects and use RCU on
vnode->cb_interest. Use that change to allow afs_check_validity() to use
read_seqbegin_or_lock() instead of read_seqlock_excl().
This also requires the caller of afs_check_validity() to hold the RCU read
lock across the call.
Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r-- | fs/afs/callback.c | 21 | ||||
-rw-r--r-- | fs/afs/dir.c | 15 | ||||
-rw-r--r-- | fs/afs/inode.c | 81 | ||||
-rw-r--r-- | fs/afs/internal.h | 12 | ||||
-rw-r--r-- | fs/afs/rotate.c | 18 | ||||
-rw-r--r-- | fs/afs/security.c | 8 | ||||
-rw-r--r-- | fs/afs/super.c | 4 |
7 files changed, 100 insertions, 59 deletions
diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 4876079aa643..d441bef72163 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c | |||
@@ -94,15 +94,15 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode, | |||
94 | struct afs_server *server = entry->server; | 94 | struct afs_server *server = entry->server; |
95 | 95 | ||
96 | again: | 96 | again: |
97 | if (vnode->cb_interest && | 97 | vcbi = rcu_dereference_protected(vnode->cb_interest, |
98 | likely(vnode->cb_interest == entry->cb_interest)) | 98 | lockdep_is_held(&vnode->io_lock)); |
99 | if (vcbi && likely(vcbi == entry->cb_interest)) | ||
99 | return 0; | 100 | return 0; |
100 | 101 | ||
101 | read_lock(&slist->lock); | 102 | read_lock(&slist->lock); |
102 | cbi = afs_get_cb_interest(entry->cb_interest); | 103 | cbi = afs_get_cb_interest(entry->cb_interest); |
103 | read_unlock(&slist->lock); | 104 | read_unlock(&slist->lock); |
104 | 105 | ||
105 | vcbi = vnode->cb_interest; | ||
106 | if (vcbi) { | 106 | if (vcbi) { |
107 | if (vcbi == cbi) { | 107 | if (vcbi == cbi) { |
108 | afs_put_cb_interest(afs_v2net(vnode), cbi); | 108 | afs_put_cb_interest(afs_v2net(vnode), cbi); |
@@ -114,8 +114,9 @@ again: | |||
114 | */ | 114 | */ |
115 | if (cbi && vcbi->server == cbi->server) { | 115 | if (cbi && vcbi->server == cbi->server) { |
116 | write_seqlock(&vnode->cb_lock); | 116 | write_seqlock(&vnode->cb_lock); |
117 | old = vnode->cb_interest; | 117 | old = rcu_dereference_protected(vnode->cb_interest, |
118 | vnode->cb_interest = cbi; | 118 | lockdep_is_held(&vnode->cb_lock.lock)); |
119 | rcu_assign_pointer(vnode->cb_interest, cbi); | ||
119 | write_sequnlock(&vnode->cb_lock); | 120 | write_sequnlock(&vnode->cb_lock); |
120 | afs_put_cb_interest(afs_v2net(vnode), old); | 121 | afs_put_cb_interest(afs_v2net(vnode), old); |
121 | return 0; | 122 | return 0; |
@@ -160,8 +161,9 @@ again: | |||
160 | */ | 161 | */ |
161 | write_seqlock(&vnode->cb_lock); | 162 | write_seqlock(&vnode->cb_lock); |
162 | 163 | ||
163 | old = vnode->cb_interest; | 164 | old = rcu_dereference_protected(vnode->cb_interest, |
164 | vnode->cb_interest = cbi; | 165 | lockdep_is_held(&vnode->cb_lock.lock)); |
166 | rcu_assign_pointer(vnode->cb_interest, cbi); | ||
165 | vnode->cb_s_break = cbi->server->cb_s_break; | 167 | vnode->cb_s_break = cbi->server->cb_s_break; |
166 | vnode->cb_v_break = vnode->volume->cb_v_break; | 168 | vnode->cb_v_break = vnode->volume->cb_v_break; |
167 | clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); | 169 | clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); |
@@ -191,10 +193,11 @@ void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) | |||
191 | vi = NULL; | 193 | vi = NULL; |
192 | 194 | ||
193 | write_unlock(&cbi->server->cb_break_lock); | 195 | write_unlock(&cbi->server->cb_break_lock); |
194 | kfree(vi); | 196 | if (vi) |
197 | kfree_rcu(vi, rcu); | ||
195 | afs_put_server(net, cbi->server); | 198 | afs_put_server(net, cbi->server); |
196 | } | 199 | } |
197 | kfree(cbi); | 200 | kfree_rcu(cbi, rcu); |
198 | } | 201 | } |
199 | } | 202 | } |
200 | 203 | ||
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index f7344b045799..338c2260b0a0 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -638,11 +638,12 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, | |||
638 | struct key *key) | 638 | struct key *key) |
639 | { | 639 | { |
640 | struct afs_lookup_cookie *cookie; | 640 | struct afs_lookup_cookie *cookie; |
641 | struct afs_cb_interest *cbi = NULL; | 641 | struct afs_cb_interest *dcbi, *cbi = NULL; |
642 | struct afs_super_info *as = dir->i_sb->s_fs_info; | 642 | struct afs_super_info *as = dir->i_sb->s_fs_info; |
643 | struct afs_status_cb *scb; | 643 | struct afs_status_cb *scb; |
644 | struct afs_iget_data data; | 644 | struct afs_iget_data data; |
645 | struct afs_fs_cursor fc; | 645 | struct afs_fs_cursor fc; |
646 | struct afs_server *server; | ||
646 | struct afs_vnode *dvnode = AFS_FS_I(dir); | 647 | struct afs_vnode *dvnode = AFS_FS_I(dir); |
647 | struct inode *inode = NULL; | 648 | struct inode *inode = NULL; |
648 | int ret, i; | 649 | int ret, i; |
@@ -658,10 +659,14 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, | |||
658 | cookie->nr_fids = 1; /* slot 0 is saved for the fid we actually want */ | 659 | cookie->nr_fids = 1; /* slot 0 is saved for the fid we actually want */ |
659 | 660 | ||
660 | read_seqlock_excl(&dvnode->cb_lock); | 661 | read_seqlock_excl(&dvnode->cb_lock); |
661 | if (dvnode->cb_interest && | 662 | dcbi = rcu_dereference_protected(dvnode->cb_interest, |
662 | dvnode->cb_interest->server && | 663 | lockdep_is_held(&dvnode->cb_lock.lock)); |
663 | test_bit(AFS_SERVER_FL_NO_IBULK, &dvnode->cb_interest->server->flags)) | 664 | if (dcbi) { |
664 | cookie->one_only = true; | 665 | server = dcbi->server; |
666 | if (server && | ||
667 | test_bit(AFS_SERVER_FL_NO_IBULK, &server->flags)) | ||
668 | cookie->one_only = true; | ||
669 | } | ||
665 | read_sequnlock_excl(&dvnode->cb_lock); | 670 | read_sequnlock_excl(&dvnode->cb_lock); |
666 | 671 | ||
667 | for (i = 0; i < 50; i++) | 672 | for (i = 0; i < 50; i++) |
diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 5c95da7d5f3d..ba35b4824408 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c | |||
@@ -139,9 +139,10 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key, | |||
139 | vnode->cb_expires_at = ktime_get_real_seconds(); | 139 | vnode->cb_expires_at = ktime_get_real_seconds(); |
140 | } else { | 140 | } else { |
141 | vnode->cb_expires_at = scb->callback.expires_at; | 141 | vnode->cb_expires_at = scb->callback.expires_at; |
142 | old_cbi = vnode->cb_interest; | 142 | old_cbi = rcu_dereference_protected(vnode->cb_interest, |
143 | lockdep_is_held(&vnode->cb_lock.lock)); | ||
143 | if (cbi != old_cbi) | 144 | if (cbi != old_cbi) |
144 | vnode->cb_interest = afs_get_cb_interest(cbi); | 145 | rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(cbi)); |
145 | else | 146 | else |
146 | old_cbi = NULL; | 147 | old_cbi = NULL; |
147 | set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); | 148 | set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); |
@@ -245,9 +246,10 @@ static void afs_apply_callback(struct afs_fs_cursor *fc, | |||
245 | 246 | ||
246 | if (!afs_cb_is_broken(cb_break, vnode, fc->cbi)) { | 247 | if (!afs_cb_is_broken(cb_break, vnode, fc->cbi)) { |
247 | vnode->cb_expires_at = cb->expires_at; | 248 | vnode->cb_expires_at = cb->expires_at; |
248 | old = vnode->cb_interest; | 249 | old = rcu_dereference_protected(vnode->cb_interest, |
250 | lockdep_is_held(&vnode->cb_lock.lock)); | ||
249 | if (old != fc->cbi) { | 251 | if (old != fc->cbi) { |
250 | vnode->cb_interest = afs_get_cb_interest(fc->cbi); | 252 | rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(fc->cbi)); |
251 | afs_put_cb_interest(afs_v2net(vnode), old); | 253 | afs_put_cb_interest(afs_v2net(vnode), old); |
252 | } | 254 | } |
253 | set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); | 255 | set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); |
@@ -565,36 +567,46 @@ void afs_zap_data(struct afs_vnode *vnode) | |||
565 | */ | 567 | */ |
566 | bool afs_check_validity(struct afs_vnode *vnode) | 568 | bool afs_check_validity(struct afs_vnode *vnode) |
567 | { | 569 | { |
570 | struct afs_cb_interest *cbi; | ||
571 | struct afs_server *server; | ||
572 | struct afs_volume *volume = vnode->volume; | ||
568 | time64_t now = ktime_get_real_seconds(); | 573 | time64_t now = ktime_get_real_seconds(); |
569 | bool valid; | 574 | bool valid; |
575 | unsigned int cb_break, cb_s_break, cb_v_break; | ||
576 | int seq = 0; | ||
570 | 577 | ||
571 | /* Quickly check the callback state. Ideally, we'd use read_seqbegin | 578 | do { |
572 | * here, but we have no way to pass the net namespace to the RCU | 579 | read_seqbegin_or_lock(&vnode->cb_lock, &seq); |
573 | * cleanup for the server record. | 580 | cb_v_break = READ_ONCE(volume->cb_v_break); |
574 | */ | 581 | cb_break = vnode->cb_break; |
575 | read_seqlock_excl(&vnode->cb_lock); | 582 | |
576 | 583 | if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { | |
577 | if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { | 584 | cbi = rcu_dereference(vnode->cb_interest); |
578 | if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break || | 585 | server = rcu_dereference(cbi->server); |
579 | vnode->cb_v_break != vnode->volume->cb_v_break) { | 586 | cb_s_break = READ_ONCE(server->cb_s_break); |
580 | vnode->cb_s_break = vnode->cb_interest->server->cb_s_break; | 587 | |
581 | vnode->cb_v_break = vnode->volume->cb_v_break; | 588 | if (vnode->cb_s_break != cb_s_break || |
582 | valid = false; | 589 | vnode->cb_v_break != cb_v_break) { |
583 | } else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { | 590 | vnode->cb_s_break = cb_s_break; |
584 | valid = false; | 591 | vnode->cb_v_break = cb_v_break; |
585 | } else if (vnode->cb_expires_at - 10 <= now) { | 592 | valid = false; |
586 | valid = false; | 593 | } else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { |
587 | } else { | 594 | valid = false; |
595 | } else if (vnode->cb_expires_at - 10 <= now) { | ||
596 | valid = false; | ||
597 | } else { | ||
598 | valid = true; | ||
599 | } | ||
600 | } else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { | ||
588 | valid = true; | 601 | valid = true; |
602 | } else { | ||
603 | vnode->cb_v_break = cb_v_break; | ||
604 | valid = false; | ||
589 | } | 605 | } |
590 | } else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { | ||
591 | valid = true; | ||
592 | } else { | ||
593 | vnode->cb_v_break = vnode->volume->cb_v_break; | ||
594 | valid = false; | ||
595 | } | ||
596 | 606 | ||
597 | read_sequnlock_excl(&vnode->cb_lock); | 607 | } while (need_seqretry(&vnode->cb_lock, seq)); |
608 | |||
609 | done_seqretry(&vnode->cb_lock, seq); | ||
598 | return valid; | 610 | return valid; |
599 | } | 611 | } |
600 | 612 | ||
@@ -616,7 +628,9 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) | |||
616 | vnode->fid.vid, vnode->fid.vnode, vnode->flags, | 628 | vnode->fid.vid, vnode->fid.vnode, vnode->flags, |
617 | key_serial(key)); | 629 | key_serial(key)); |
618 | 630 | ||
631 | rcu_read_lock(); | ||
619 | valid = afs_check_validity(vnode); | 632 | valid = afs_check_validity(vnode); |
633 | rcu_read_unlock(); | ||
620 | 634 | ||
621 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) | 635 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) |
622 | clear_nlink(&vnode->vfs_inode); | 636 | clear_nlink(&vnode->vfs_inode); |
@@ -703,6 +717,7 @@ int afs_drop_inode(struct inode *inode) | |||
703 | */ | 717 | */ |
704 | void afs_evict_inode(struct inode *inode) | 718 | void afs_evict_inode(struct inode *inode) |
705 | { | 719 | { |
720 | struct afs_cb_interest *cbi; | ||
706 | struct afs_vnode *vnode; | 721 | struct afs_vnode *vnode; |
707 | 722 | ||
708 | vnode = AFS_FS_I(inode); | 723 | vnode = AFS_FS_I(inode); |
@@ -719,10 +734,14 @@ void afs_evict_inode(struct inode *inode) | |||
719 | truncate_inode_pages_final(&inode->i_data); | 734 | truncate_inode_pages_final(&inode->i_data); |
720 | clear_inode(inode); | 735 | clear_inode(inode); |
721 | 736 | ||
722 | if (vnode->cb_interest) { | 737 | write_seqlock(&vnode->cb_lock); |
723 | afs_put_cb_interest(afs_i2net(inode), vnode->cb_interest); | 738 | cbi = rcu_dereference_protected(vnode->cb_interest, |
724 | vnode->cb_interest = NULL; | 739 | lockdep_is_held(&vnode->cb_lock.lock)); |
740 | if (cbi) { | ||
741 | afs_put_cb_interest(afs_i2net(inode), cbi); | ||
742 | rcu_assign_pointer(vnode->cb_interest, NULL); | ||
725 | } | 743 | } |
744 | write_sequnlock(&vnode->cb_lock); | ||
726 | 745 | ||
727 | while (!list_empty(&vnode->wb_keys)) { | 746 | while (!list_empty(&vnode->wb_keys)) { |
728 | struct afs_wb_key *wbk = list_entry(vnode->wb_keys.next, | 747 | struct afs_wb_key *wbk = list_entry(vnode->wb_keys.next, |
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 54688f6ca9e5..3dbb1e840dfd 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -554,7 +554,10 @@ struct afs_server { | |||
554 | struct afs_vol_interest { | 554 | struct afs_vol_interest { |
555 | struct hlist_node srv_link; /* Link in server->cb_volumes */ | 555 | struct hlist_node srv_link; /* Link in server->cb_volumes */ |
556 | struct hlist_head cb_interests; /* List of callback interests on the server */ | 556 | struct hlist_head cb_interests; /* List of callback interests on the server */ |
557 | afs_volid_t vid; /* Volume ID to match */ | 557 | union { |
558 | struct rcu_head rcu; | ||
559 | afs_volid_t vid; /* Volume ID to match */ | ||
560 | }; | ||
558 | unsigned int usage; | 561 | unsigned int usage; |
559 | }; | 562 | }; |
560 | 563 | ||
@@ -566,7 +569,10 @@ struct afs_cb_interest { | |||
566 | struct afs_vol_interest *vol_interest; | 569 | struct afs_vol_interest *vol_interest; |
567 | struct afs_server *server; /* Server on which this interest resides */ | 570 | struct afs_server *server; /* Server on which this interest resides */ |
568 | struct super_block *sb; /* Superblock on which inodes reside */ | 571 | struct super_block *sb; /* Superblock on which inodes reside */ |
569 | afs_volid_t vid; /* Volume ID to match */ | 572 | union { |
573 | struct rcu_head rcu; | ||
574 | afs_volid_t vid; /* Volume ID to match */ | ||
575 | }; | ||
570 | refcount_t usage; | 576 | refcount_t usage; |
571 | }; | 577 | }; |
572 | 578 | ||
@@ -676,7 +682,7 @@ struct afs_vnode { | |||
676 | afs_lock_type_t lock_type : 8; | 682 | afs_lock_type_t lock_type : 8; |
677 | 683 | ||
678 | /* outstanding callback notification on this file */ | 684 | /* outstanding callback notification on this file */ |
679 | struct afs_cb_interest *cb_interest; /* Server on which this resides */ | 685 | struct afs_cb_interest __rcu *cb_interest; /* Server on which this resides */ |
680 | unsigned int cb_s_break; /* Mass break counter on ->server */ | 686 | unsigned int cb_s_break; /* Mass break counter on ->server */ |
681 | unsigned int cb_v_break; /* Mass break counter on ->volume */ | 687 | unsigned int cb_v_break; /* Mass break counter on ->volume */ |
682 | unsigned int cb_break; /* Break counter on vnode */ | 688 | unsigned int cb_break; /* Break counter on vnode */ |
diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index 52f3a9910f0d..b00c739e0e63 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c | |||
@@ -66,7 +66,8 @@ static bool afs_start_fs_iteration(struct afs_fs_cursor *fc, | |||
66 | fc->untried = (1UL << fc->server_list->nr_servers) - 1; | 66 | fc->untried = (1UL << fc->server_list->nr_servers) - 1; |
67 | fc->index = READ_ONCE(fc->server_list->preferred); | 67 | fc->index = READ_ONCE(fc->server_list->preferred); |
68 | 68 | ||
69 | cbi = vnode->cb_interest; | 69 | cbi = rcu_dereference_protected(vnode->cb_interest, |
70 | lockdep_is_held(&vnode->io_lock)); | ||
70 | if (cbi) { | 71 | if (cbi) { |
71 | /* See if the vnode's preferred record is still available */ | 72 | /* See if the vnode's preferred record is still available */ |
72 | for (i = 0; i < fc->server_list->nr_servers; i++) { | 73 | for (i = 0; i < fc->server_list->nr_servers; i++) { |
@@ -87,8 +88,8 @@ static bool afs_start_fs_iteration(struct afs_fs_cursor *fc, | |||
87 | 88 | ||
88 | /* Note that the callback promise is effectively broken */ | 89 | /* Note that the callback promise is effectively broken */ |
89 | write_seqlock(&vnode->cb_lock); | 90 | write_seqlock(&vnode->cb_lock); |
90 | ASSERTCMP(cbi, ==, vnode->cb_interest); | 91 | ASSERTCMP(cbi, ==, rcu_access_pointer(vnode->cb_interest)); |
91 | vnode->cb_interest = NULL; | 92 | rcu_assign_pointer(vnode->cb_interest, NULL); |
92 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) | 93 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) |
93 | vnode->cb_break++; | 94 | vnode->cb_break++; |
94 | write_sequnlock(&vnode->cb_lock); | 95 | write_sequnlock(&vnode->cb_lock); |
@@ -417,7 +418,9 @@ selected_server: | |||
417 | if (error < 0) | 418 | if (error < 0) |
418 | goto failed_set_error; | 419 | goto failed_set_error; |
419 | 420 | ||
420 | fc->cbi = afs_get_cb_interest(vnode->cb_interest); | 421 | fc->cbi = afs_get_cb_interest( |
422 | rcu_dereference_protected(vnode->cb_interest, | ||
423 | lockdep_is_held(&vnode->io_lock))); | ||
421 | 424 | ||
422 | read_lock(&server->fs_lock); | 425 | read_lock(&server->fs_lock); |
423 | alist = rcu_dereference_protected(server->addresses, | 426 | alist = rcu_dereference_protected(server->addresses, |
@@ -487,12 +490,15 @@ failed: | |||
487 | bool afs_select_current_fileserver(struct afs_fs_cursor *fc) | 490 | bool afs_select_current_fileserver(struct afs_fs_cursor *fc) |
488 | { | 491 | { |
489 | struct afs_vnode *vnode = fc->vnode; | 492 | struct afs_vnode *vnode = fc->vnode; |
490 | struct afs_cb_interest *cbi = vnode->cb_interest; | 493 | struct afs_cb_interest *cbi; |
491 | struct afs_addr_list *alist; | 494 | struct afs_addr_list *alist; |
492 | int error = fc->ac.error; | 495 | int error = fc->ac.error; |
493 | 496 | ||
494 | _enter(""); | 497 | _enter(""); |
495 | 498 | ||
499 | cbi = rcu_dereference_protected(vnode->cb_interest, | ||
500 | lockdep_is_held(&vnode->io_lock)); | ||
501 | |||
496 | switch (error) { | 502 | switch (error) { |
497 | case SHRT_MAX: | 503 | case SHRT_MAX: |
498 | if (!cbi) { | 504 | if (!cbi) { |
@@ -501,7 +507,7 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc) | |||
501 | return false; | 507 | return false; |
502 | } | 508 | } |
503 | 509 | ||
504 | fc->cbi = afs_get_cb_interest(vnode->cb_interest); | 510 | fc->cbi = afs_get_cb_interest(cbi); |
505 | 511 | ||
506 | read_lock(&cbi->server->fs_lock); | 512 | read_lock(&cbi->server->fs_lock); |
507 | alist = rcu_dereference_protected(cbi->server->addresses, | 513 | alist = rcu_dereference_protected(cbi->server->addresses, |
diff --git a/fs/afs/security.c b/fs/afs/security.c index 857f09d09ee9..5d8ece98561e 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c | |||
@@ -146,7 +146,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, | |||
146 | } | 146 | } |
147 | 147 | ||
148 | if (afs_cb_is_broken(cb_break, vnode, | 148 | if (afs_cb_is_broken(cb_break, vnode, |
149 | vnode->cb_interest)) { | 149 | rcu_dereference(vnode->cb_interest))) { |
150 | changed = true; | 150 | changed = true; |
151 | break; | 151 | break; |
152 | } | 152 | } |
@@ -176,7 +176,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, | |||
176 | } | 176 | } |
177 | } | 177 | } |
178 | 178 | ||
179 | if (afs_cb_is_broken(cb_break, vnode, vnode->cb_interest)) | 179 | if (afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest))) |
180 | goto someone_else_changed_it; | 180 | goto someone_else_changed_it; |
181 | 181 | ||
182 | /* We need a ref on any permits list we want to copy as we'll have to | 182 | /* We need a ref on any permits list we want to copy as we'll have to |
@@ -253,14 +253,16 @@ found: | |||
253 | 253 | ||
254 | kfree(new); | 254 | kfree(new); |
255 | 255 | ||
256 | rcu_read_lock(); | ||
256 | spin_lock(&vnode->lock); | 257 | spin_lock(&vnode->lock); |
257 | zap = rcu_access_pointer(vnode->permit_cache); | 258 | zap = rcu_access_pointer(vnode->permit_cache); |
258 | if (!afs_cb_is_broken(cb_break, vnode, vnode->cb_interest) && | 259 | if (!afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)) && |
259 | zap == permits) | 260 | zap == permits) |
260 | rcu_assign_pointer(vnode->permit_cache, replacement); | 261 | rcu_assign_pointer(vnode->permit_cache, replacement); |
261 | else | 262 | else |
262 | zap = replacement; | 263 | zap = replacement; |
263 | spin_unlock(&vnode->lock); | 264 | spin_unlock(&vnode->lock); |
265 | rcu_read_unlock(); | ||
264 | afs_put_permits(zap); | 266 | afs_put_permits(zap); |
265 | out_put: | 267 | out_put: |
266 | afs_put_permits(permits); | 268 | afs_put_permits(permits); |
diff --git a/fs/afs/super.c b/fs/afs/super.c index a81c235f8c57..f76473ad7bbb 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c | |||
@@ -677,7 +677,7 @@ static struct inode *afs_alloc_inode(struct super_block *sb) | |||
677 | vnode->volume = NULL; | 677 | vnode->volume = NULL; |
678 | vnode->lock_key = NULL; | 678 | vnode->lock_key = NULL; |
679 | vnode->permit_cache = NULL; | 679 | vnode->permit_cache = NULL; |
680 | vnode->cb_interest = NULL; | 680 | RCU_INIT_POINTER(vnode->cb_interest, NULL); |
681 | #ifdef CONFIG_AFS_FSCACHE | 681 | #ifdef CONFIG_AFS_FSCACHE |
682 | vnode->cache = NULL; | 682 | vnode->cache = NULL; |
683 | #endif | 683 | #endif |
@@ -707,7 +707,7 @@ static void afs_destroy_inode(struct inode *inode) | |||
707 | 707 | ||
708 | _debug("DESTROY INODE %p", inode); | 708 | _debug("DESTROY INODE %p", inode); |
709 | 709 | ||
710 | ASSERTCMP(vnode->cb_interest, ==, NULL); | 710 | ASSERTCMP(rcu_access_pointer(vnode->cb_interest), ==, NULL); |
711 | 711 | ||
712 | atomic_dec(&afs_count_active_inodes); | 712 | atomic_dec(&afs_count_active_inodes); |
713 | } | 713 | } |