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 | |
47ea0f2e DH |
23 | /* |
24 | * Create volume and callback interests on a server. | |
25 | */ | |
26 | static struct afs_cb_interest *afs_create_interest(struct afs_server *server, | |
27 | struct afs_vnode *vnode) | |
28 | { | |
29 | struct afs_vol_interest *new_vi, *vi; | |
30 | struct afs_cb_interest *new; | |
31 | struct hlist_node **pp; | |
32 | ||
33 | new_vi = kzalloc(sizeof(struct afs_vol_interest), GFP_KERNEL); | |
34 | if (!new_vi) | |
35 | return NULL; | |
36 | ||
37 | new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL); | |
38 | if (!new) { | |
39 | kfree(new_vi); | |
40 | return NULL; | |
41 | } | |
42 | ||
43 | new_vi->usage = 1; | |
44 | new_vi->vid = vnode->volume->vid; | |
45 | INIT_HLIST_NODE(&new_vi->srv_link); | |
46 | INIT_HLIST_HEAD(&new_vi->cb_interests); | |
47 | ||
48 | refcount_set(&new->usage, 1); | |
49 | new->sb = vnode->vfs_inode.i_sb; | |
50 | new->vid = vnode->volume->vid; | |
45218193 | 51 | new->server = afs_get_server(server, afs_server_trace_get_new_cbi); |
47ea0f2e DH |
52 | INIT_HLIST_NODE(&new->cb_vlink); |
53 | ||
54 | write_lock(&server->cb_break_lock); | |
55 | ||
56 | for (pp = &server->cb_volumes.first; *pp; pp = &(*pp)->next) { | |
57 | vi = hlist_entry(*pp, struct afs_vol_interest, srv_link); | |
58 | if (vi->vid < new_vi->vid) | |
59 | continue; | |
60 | if (vi->vid > new_vi->vid) | |
61 | break; | |
62 | vi->usage++; | |
63 | goto found_vi; | |
64 | } | |
65 | ||
66 | new_vi->srv_link.pprev = pp; | |
67 | new_vi->srv_link.next = *pp; | |
68 | if (*pp) | |
69 | (*pp)->pprev = &new_vi->srv_link.next; | |
70 | *pp = &new_vi->srv_link; | |
71 | vi = new_vi; | |
72 | new_vi = NULL; | |
73 | found_vi: | |
74 | ||
75 | new->vol_interest = vi; | |
76 | hlist_add_head(&new->cb_vlink, &vi->cb_interests); | |
77 | ||
78 | write_unlock(&server->cb_break_lock); | |
79 | kfree(new_vi); | |
80 | return new; | |
81 | } | |
82 | ||
1da177e4 | 83 | /* |
c435ee34 DH |
84 | * Set up an interest-in-callbacks record for a volume on a server and |
85 | * register it with the server. | |
d4a96bec | 86 | * - Called with vnode->io_lock held. |
1da177e4 | 87 | */ |
c435ee34 | 88 | int afs_register_server_cb_interest(struct afs_vnode *vnode, |
d4a96bec DH |
89 | struct afs_server_list *slist, |
90 | unsigned int index) | |
1da177e4 | 91 | { |
d4a96bec DH |
92 | struct afs_server_entry *entry = &slist->servers[index]; |
93 | struct afs_cb_interest *cbi, *vcbi, *new, *old; | |
d2ddc776 | 94 | struct afs_server *server = entry->server; |
c435ee34 DH |
95 | |
96 | again: | |
f642404a DH |
97 | vcbi = rcu_dereference_protected(vnode->cb_interest, |
98 | lockdep_is_held(&vnode->io_lock)); | |
99 | if (vcbi && likely(vcbi == entry->cb_interest)) | |
d4a96bec DH |
100 | return 0; |
101 | ||
102 | read_lock(&slist->lock); | |
103 | cbi = afs_get_cb_interest(entry->cb_interest); | |
104 | read_unlock(&slist->lock); | |
105 | ||
c435ee34 | 106 | if (vcbi) { |
d4a96bec DH |
107 | if (vcbi == cbi) { |
108 | afs_put_cb_interest(afs_v2net(vnode), cbi); | |
c435ee34 | 109 | return 0; |
d4a96bec | 110 | } |
c435ee34 | 111 | |
d4a96bec DH |
112 | /* Use a new interest in the server list for the same server |
113 | * rather than an old one that's still attached to a vnode. | |
114 | */ | |
c435ee34 DH |
115 | if (cbi && vcbi->server == cbi->server) { |
116 | write_seqlock(&vnode->cb_lock); | |
f642404a DH |
117 | old = rcu_dereference_protected(vnode->cb_interest, |
118 | lockdep_is_held(&vnode->cb_lock.lock)); | |
119 | rcu_assign_pointer(vnode->cb_interest, cbi); | |
c435ee34 | 120 | write_sequnlock(&vnode->cb_lock); |
d4a96bec | 121 | afs_put_cb_interest(afs_v2net(vnode), old); |
c435ee34 DH |
122 | return 0; |
123 | } | |
1da177e4 | 124 | |
d4a96bec | 125 | /* Re-use the one attached to the vnode. */ |
c435ee34 | 126 | if (!cbi && vcbi->server == server) { |
d4a96bec DH |
127 | write_lock(&slist->lock); |
128 | if (entry->cb_interest) { | |
129 | write_unlock(&slist->lock); | |
130 | afs_put_cb_interest(afs_v2net(vnode), cbi); | |
c435ee34 DH |
131 | goto again; |
132 | } | |
d4a96bec DH |
133 | |
134 | entry->cb_interest = cbi; | |
135 | write_unlock(&slist->lock); | |
c435ee34 DH |
136 | return 0; |
137 | } | |
138 | } | |
1da177e4 | 139 | |
c435ee34 | 140 | if (!cbi) { |
47ea0f2e | 141 | new = afs_create_interest(server, vnode); |
c435ee34 DH |
142 | if (!new) |
143 | return -ENOMEM; | |
144 | ||
d4a96bec DH |
145 | write_lock(&slist->lock); |
146 | if (!entry->cb_interest) { | |
147 | entry->cb_interest = afs_get_cb_interest(new); | |
c435ee34 | 148 | cbi = new; |
d4a96bec | 149 | new = NULL; |
c435ee34 | 150 | } else { |
d4a96bec | 151 | cbi = afs_get_cb_interest(entry->cb_interest); |
c435ee34 | 152 | } |
d4a96bec DH |
153 | write_unlock(&slist->lock); |
154 | afs_put_cb_interest(afs_v2net(vnode), new); | |
08e0e7c8 | 155 | } |
1da177e4 | 156 | |
c435ee34 DH |
157 | ASSERT(cbi); |
158 | ||
159 | /* Change the server the vnode is using. This entails scrubbing any | |
160 | * interest the vnode had in the previous server it was using. | |
161 | */ | |
162 | write_seqlock(&vnode->cb_lock); | |
163 | ||
f642404a DH |
164 | old = rcu_dereference_protected(vnode->cb_interest, |
165 | lockdep_is_held(&vnode->cb_lock.lock)); | |
166 | rcu_assign_pointer(vnode->cb_interest, cbi); | |
c435ee34 | 167 | vnode->cb_s_break = cbi->server->cb_s_break; |
68251f0a | 168 | vnode->cb_v_break = vnode->volume->cb_v_break; |
c435ee34 DH |
169 | clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); |
170 | ||
171 | write_sequnlock(&vnode->cb_lock); | |
d4a96bec | 172 | afs_put_cb_interest(afs_v2net(vnode), old); |
c435ee34 | 173 | return 0; |
08e0e7c8 | 174 | } |
1da177e4 | 175 | |
c435ee34 DH |
176 | /* |
177 | * Remove an interest on a server. | |
178 | */ | |
179 | void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) | |
180 | { | |
47ea0f2e DH |
181 | struct afs_vol_interest *vi; |
182 | ||
c435ee34 | 183 | if (cbi && refcount_dec_and_test(&cbi->usage)) { |
47ea0f2e | 184 | if (!hlist_unhashed(&cbi->cb_vlink)) { |
c435ee34 | 185 | write_lock(&cbi->server->cb_break_lock); |
47ea0f2e DH |
186 | |
187 | hlist_del_init(&cbi->cb_vlink); | |
188 | vi = cbi->vol_interest; | |
189 | cbi->vol_interest = NULL; | |
190 | if (--vi->usage == 0) | |
191 | hlist_del(&vi->srv_link); | |
192 | else | |
193 | vi = NULL; | |
194 | ||
c435ee34 | 195 | write_unlock(&cbi->server->cb_break_lock); |
f642404a DH |
196 | if (vi) |
197 | kfree_rcu(vi, rcu); | |
45218193 | 198 | afs_put_server(net, cbi->server, afs_server_trace_put_cbi); |
c435ee34 | 199 | } |
f642404a | 200 | kfree_rcu(cbi, rcu); |
08e0e7c8 | 201 | } |
c435ee34 DH |
202 | } |
203 | ||
204 | /* | |
205 | * allow the fileserver to request callback state (re-)initialisation | |
206 | */ | |
207 | void afs_init_callback_state(struct afs_server *server) | |
208 | { | |
eeba1e9c | 209 | server->cb_s_break++; |
08e0e7c8 DH |
210 | } |
211 | ||
212 | /* | |
213 | * actually break a callback | |
214 | */ | |
051d2525 | 215 | void __afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason) |
08e0e7c8 DH |
216 | { |
217 | _enter(""); | |
218 | ||
5a813276 | 219 | clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); |
c435ee34 DH |
220 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { |
221 | vnode->cb_break++; | |
222 | afs_clear_permits(vnode); | |
08e0e7c8 | 223 | |
c7226e40 | 224 | if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB) |
e8d6c554 | 225 | afs_lock_may_be_available(vnode); |
051d2525 DH |
226 | |
227 | trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, true); | |
228 | } else { | |
229 | trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, false); | |
08e0e7c8 | 230 | } |
30062bd1 | 231 | } |
c435ee34 | 232 | |
051d2525 | 233 | void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason) |
30062bd1 DH |
234 | { |
235 | write_seqlock(&vnode->cb_lock); | |
051d2525 | 236 | __afs_break_callback(vnode, reason); |
c435ee34 | 237 | write_sequnlock(&vnode->cb_lock); |
08e0e7c8 DH |
238 | } |
239 | ||
240 | /* | |
241 | * allow the fileserver to explicitly break one callback | |
242 | * - happens when | |
243 | * - the backing file is changed | |
244 | * - a lock is released | |
245 | */ | |
246 | static void afs_break_one_callback(struct afs_server *server, | |
247 | struct afs_fid *fid) | |
248 | { | |
47ea0f2e | 249 | struct afs_vol_interest *vi; |
c435ee34 DH |
250 | struct afs_cb_interest *cbi; |
251 | struct afs_iget_data data; | |
08e0e7c8 | 252 | struct afs_vnode *vnode; |
c435ee34 | 253 | struct inode *inode; |
08e0e7c8 | 254 | |
c435ee34 | 255 | read_lock(&server->cb_break_lock); |
47ea0f2e DH |
256 | hlist_for_each_entry(vi, &server->cb_volumes, srv_link) { |
257 | if (vi->vid < fid->vid) | |
258 | continue; | |
259 | if (vi->vid > fid->vid) { | |
260 | vi = NULL; | |
261 | break; | |
262 | } | |
263 | //atomic_inc(&vi->usage); | |
264 | break; | |
265 | } | |
266 | ||
267 | /* TODO: Find all matching volumes if we couldn't match the server and | |
268 | * break them anyway. | |
269 | */ | |
270 | if (!vi) | |
271 | goto out; | |
08e0e7c8 | 272 | |
c435ee34 DH |
273 | /* Step through all interested superblocks. There may be more than one |
274 | * because of cell aliasing. | |
275 | */ | |
47ea0f2e | 276 | hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) { |
68251f0a DH |
277 | if (fid->vnode == 0 && fid->unique == 0) { |
278 | /* The callback break applies to an entire volume. */ | |
279 | struct afs_super_info *as = AFS_FS_S(cbi->sb); | |
280 | struct afs_volume *volume = as->volume; | |
281 | ||
90fa9b64 | 282 | write_lock(&volume->cb_v_break_lock); |
68251f0a | 283 | volume->cb_v_break++; |
051d2525 DH |
284 | trace_afs_cb_break(fid, volume->cb_v_break, |
285 | afs_cb_break_for_volume_callback, false); | |
90fa9b64 | 286 | write_unlock(&volume->cb_v_break_lock); |
68251f0a DH |
287 | } else { |
288 | data.volume = NULL; | |
289 | data.fid = *fid; | |
290 | inode = ilookup5_nowait(cbi->sb, fid->vnode, | |
291 | afs_iget5_test, &data); | |
292 | if (inode) { | |
293 | vnode = AFS_FS_I(inode); | |
051d2525 | 294 | afs_break_callback(vnode, afs_cb_break_for_callback); |
68251f0a | 295 | iput(inode); |
051d2525 DH |
296 | } else { |
297 | trace_afs_cb_miss(fid, afs_cb_break_for_callback); | |
68251f0a | 298 | } |
c435ee34 DH |
299 | } |
300 | } | |
08e0e7c8 | 301 | |
47ea0f2e | 302 | out: |
c435ee34 | 303 | read_unlock(&server->cb_break_lock); |
ec26815a | 304 | } |
1da177e4 | 305 | |
1da177e4 LT |
306 | /* |
307 | * allow the fileserver to break callback promises | |
308 | */ | |
08e0e7c8 | 309 | void afs_break_callbacks(struct afs_server *server, size_t count, |
5cf9dd55 | 310 | struct afs_callback_break *callbacks) |
1da177e4 | 311 | { |
08e0e7c8 | 312 | _enter("%p,%zu,", server, count); |
1da177e4 | 313 | |
08e0e7c8 DH |
314 | ASSERT(server != NULL); |
315 | ASSERTCMP(count, <=, AFSCBMAX); | |
1da177e4 | 316 | |
68251f0a DH |
317 | /* TODO: Sort the callback break list by volume ID */ |
318 | ||
08e0e7c8 | 319 | for (; count > 0; callbacks++, count--) { |
06aeb297 | 320 | _debug("- Fid { vl=%08llx n=%llu u=%u }", |
1da177e4 LT |
321 | callbacks->fid.vid, |
322 | callbacks->fid.vnode, | |
06aeb297 | 323 | callbacks->fid.unique); |
08e0e7c8 DH |
324 | afs_break_one_callback(server, &callbacks->fid); |
325 | } | |
326 | ||
327 | _leave(""); | |
328 | return; | |
329 | } | |
1da177e4 | 330 | |
08e0e7c8 | 331 | /* |
c435ee34 | 332 | * Clear the callback interests in a server list. |
08e0e7c8 | 333 | */ |
d2ddc776 | 334 | void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist) |
08e0e7c8 | 335 | { |
c435ee34 | 336 | int i; |
08e0e7c8 | 337 | |
d2ddc776 DH |
338 | for (i = 0; i < slist->nr_servers; i++) { |
339 | afs_put_cb_interest(net, slist->servers[i].cb_interest); | |
340 | slist->servers[i].cb_interest = NULL; | |
08e0e7c8 | 341 | } |
08e0e7c8 | 342 | } |