Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
08e0e7c8 | 2 | * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved. |
1da177e4 LT |
3 | * |
4 | * This software may be freely redistributed under the terms of the | |
5 | * GNU General Public License. | |
6 | * | |
7 | * You should have received a copy of the GNU General Public License | |
8 | * along with this program; if not, write to the Free Software | |
9 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
10 | * | |
44d1b980 | 11 | * Authors: David Woodhouse <dwmw2@infradead.org> |
1da177e4 LT |
12 | * David Howells <dhowells@redhat.com> |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/init.h> | |
08e0e7c8 | 19 | #include <linux/circ_buf.h> |
e8edc6e0 | 20 | #include <linux/sched.h> |
1da177e4 | 21 | #include "internal.h" |
08e0e7c8 | 22 | |
c435ee34 | 23 | /* |
3c4c4075 DH |
24 | * Allow the fileserver to request callback state (re-)initialisation. |
25 | * Unfortunately, UUIDs are not guaranteed unique. | |
c435ee34 DH |
26 | */ |
27 | void afs_init_callback_state(struct afs_server *server) | |
28 | { | |
3c4c4075 DH |
29 | rcu_read_lock(); |
30 | do { | |
31 | server->cb_s_break++; | |
32 | server = rcu_dereference(server->uuid_next); | |
33 | } while (0); | |
34 | rcu_read_unlock(); | |
08e0e7c8 DH |
35 | } |
36 | ||
37 | /* | |
38 | * actually break a callback | |
39 | */ | |
051d2525 | 40 | void __afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason) |
08e0e7c8 DH |
41 | { |
42 | _enter(""); | |
43 | ||
5a813276 | 44 | clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); |
c435ee34 DH |
45 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { |
46 | vnode->cb_break++; | |
47 | afs_clear_permits(vnode); | |
08e0e7c8 | 48 | |
c7226e40 | 49 | if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB) |
e8d6c554 | 50 | afs_lock_may_be_available(vnode); |
051d2525 DH |
51 | |
52 | trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, true); | |
53 | } else { | |
54 | trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, false); | |
08e0e7c8 | 55 | } |
30062bd1 | 56 | } |
c435ee34 | 57 | |
051d2525 | 58 | void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason) |
30062bd1 DH |
59 | { |
60 | write_seqlock(&vnode->cb_lock); | |
051d2525 | 61 | __afs_break_callback(vnode, reason); |
c435ee34 | 62 | write_sequnlock(&vnode->cb_lock); |
08e0e7c8 DH |
63 | } |
64 | ||
8230fd82 | 65 | /* |
20325960 | 66 | * Look up a volume by volume ID under RCU conditions. |
8230fd82 | 67 | */ |
20325960 DH |
68 | static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, |
69 | afs_volid_t vid) | |
8230fd82 | 70 | { |
20325960 | 71 | struct afs_volume *volume = NULL; |
8230fd82 DH |
72 | struct rb_node *p; |
73 | int seq = 0; | |
74 | ||
75 | do { | |
76 | /* Unfortunately, rbtree walking doesn't give reliable results | |
77 | * under just the RCU read lock, so we have to check for | |
78 | * changes. | |
79 | */ | |
20325960 | 80 | read_seqbegin_or_lock(&cell->volume_lock, &seq); |
8230fd82 | 81 | |
20325960 | 82 | p = rcu_dereference_raw(cell->volumes.rb_node); |
8230fd82 | 83 | while (p) { |
20325960 | 84 | volume = rb_entry(p, struct afs_volume, cell_node); |
8230fd82 | 85 | |
20325960 | 86 | if (volume->vid < vid) |
8230fd82 | 87 | p = rcu_dereference_raw(p->rb_left); |
20325960 | 88 | else if (volume->vid > vid) |
8230fd82 DH |
89 | p = rcu_dereference_raw(p->rb_right); |
90 | else | |
91 | break; | |
20325960 | 92 | volume = NULL; |
8230fd82 DH |
93 | } |
94 | ||
20325960 | 95 | } while (need_seqretry(&cell->volume_lock, seq)); |
8230fd82 | 96 | |
20325960 DH |
97 | done_seqretry(&cell->volume_lock, seq); |
98 | return volume; | |
8230fd82 DH |
99 | } |
100 | ||
08e0e7c8 DH |
101 | /* |
102 | * allow the fileserver to explicitly break one callback | |
103 | * - happens when | |
104 | * - the backing file is changed | |
105 | * - a lock is released | |
106 | */ | |
20325960 DH |
107 | static void afs_break_one_callback(struct afs_volume *volume, |
108 | struct afs_fid *fid) | |
08e0e7c8 | 109 | { |
20325960 | 110 | struct super_block *sb; |
08e0e7c8 | 111 | struct afs_vnode *vnode; |
c435ee34 | 112 | struct inode *inode; |
08e0e7c8 | 113 | |
20325960 DH |
114 | if (fid->vnode == 0 && fid->unique == 0) { |
115 | /* The callback break applies to an entire volume. */ | |
116 | write_lock(&volume->cb_v_break_lock); | |
117 | volume->cb_v_break++; | |
118 | trace_afs_cb_break(fid, volume->cb_v_break, | |
119 | afs_cb_break_for_volume_callback, false); | |
120 | write_unlock(&volume->cb_v_break_lock); | |
121 | return; | |
122 | } | |
68251f0a | 123 | |
20325960 DH |
124 | /* See if we can find a matching inode - even an I_NEW inode needs to |
125 | * be marked as it can have its callback broken before we finish | |
126 | * setting up the local inode. | |
127 | */ | |
128 | sb = rcu_dereference(volume->sb); | |
129 | if (!sb) | |
130 | return; | |
131 | ||
132 | inode = find_inode_rcu(sb, fid->vnode, afs_ilookup5_test_by_fid, fid); | |
133 | if (inode) { | |
134 | vnode = AFS_FS_I(inode); | |
135 | afs_break_callback(vnode, afs_cb_break_for_callback); | |
136 | } else { | |
137 | trace_afs_cb_miss(fid, afs_cb_break_for_callback); | |
c435ee34 | 138 | } |
8230fd82 | 139 | } |
08e0e7c8 | 140 | |
8230fd82 DH |
141 | static void afs_break_some_callbacks(struct afs_server *server, |
142 | struct afs_callback_break *cbb, | |
143 | size_t *_count) | |
144 | { | |
145 | struct afs_callback_break *residue = cbb; | |
20325960 | 146 | struct afs_volume *volume; |
8230fd82 DH |
147 | afs_volid_t vid = cbb->fid.vid; |
148 | size_t i; | |
149 | ||
20325960 | 150 | volume = afs_lookup_volume_rcu(server->cell, vid); |
8230fd82 DH |
151 | |
152 | /* TODO: Find all matching volumes if we couldn't match the server and | |
153 | * break them anyway. | |
154 | */ | |
155 | ||
156 | for (i = *_count; i > 0; cbb++, i--) { | |
157 | if (cbb->fid.vid == vid) { | |
158 | _debug("- Fid { vl=%08llx n=%llu u=%u }", | |
159 | cbb->fid.vid, | |
160 | cbb->fid.vnode, | |
161 | cbb->fid.unique); | |
162 | --*_count; | |
20325960 DH |
163 | if (volume) |
164 | afs_break_one_callback(volume, &cbb->fid); | |
8230fd82 DH |
165 | } else { |
166 | *residue++ = *cbb; | |
167 | } | |
168 | } | |
ec26815a | 169 | } |
1da177e4 | 170 | |
1da177e4 LT |
171 | /* |
172 | * allow the fileserver to break callback promises | |
173 | */ | |
08e0e7c8 | 174 | void afs_break_callbacks(struct afs_server *server, size_t count, |
5cf9dd55 | 175 | struct afs_callback_break *callbacks) |
1da177e4 | 176 | { |
08e0e7c8 | 177 | _enter("%p,%zu,", server, count); |
1da177e4 | 178 | |
08e0e7c8 | 179 | ASSERT(server != NULL); |
1da177e4 | 180 | |
8230fd82 | 181 | rcu_read_lock(); |
68251f0a | 182 | |
8230fd82 DH |
183 | while (count > 0) |
184 | afs_break_some_callbacks(server, callbacks, &count); | |
08e0e7c8 | 185 | |
8230fd82 | 186 | rcu_read_unlock(); |
08e0e7c8 DH |
187 | return; |
188 | } |