summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2019-05-13 11:14:32 -0400
committerDavid Howells <dhowells@redhat.com>2019-05-16 17:23:21 -0400
commitf642404a0436a50912c218009ccc7856d48d784c (patch)
treeddf39d1741f07866333581e66f1abc0e6709d453
parentc925bd0ac4741badb567f594c41c8cba5e9e9732 (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.c21
-rw-r--r--fs/afs/dir.c15
-rw-r--r--fs/afs/inode.c81
-rw-r--r--fs/afs/internal.h12
-rw-r--r--fs/afs/rotate.c18
-rw-r--r--fs/afs/security.c8
-rw-r--r--fs/afs/super.c4
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
96again: 96again:
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 */
566bool afs_check_validity(struct afs_vnode *vnode) 568bool 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 */
704void afs_evict_inode(struct inode *inode) 718void 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 {
554struct afs_vol_interest { 554struct 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:
487bool afs_select_current_fileserver(struct afs_fs_cursor *fc) 490bool 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);
265out_put: 267out_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}