]>
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 <[email protected]> |
1da177e4 LT |
12 | * David Howells <[email protected]> |
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; | |
51 | new->server = afs_get_server(server); | |
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: | |
d4a96bec DH |
97 | if (vnode->cb_interest && |
98 | likely(vnode->cb_interest == entry->cb_interest)) | |
99 | return 0; | |
100 | ||
101 | read_lock(&slist->lock); | |
102 | cbi = afs_get_cb_interest(entry->cb_interest); | |
103 | read_unlock(&slist->lock); | |
104 | ||
c435ee34 DH |
105 | vcbi = vnode->cb_interest; |
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); | |
d4a96bec DH |
117 | old = vnode->cb_interest; |
118 | vnode->cb_interest = cbi; | |
c435ee34 | 119 | write_sequnlock(&vnode->cb_lock); |
d4a96bec | 120 | afs_put_cb_interest(afs_v2net(vnode), old); |
c435ee34 DH |
121 | return 0; |
122 | } | |
1da177e4 | 123 | |
d4a96bec | 124 | /* Re-use the one attached to the vnode. */ |
c435ee34 | 125 | if (!cbi && vcbi->server == server) { |
d4a96bec DH |
126 | write_lock(&slist->lock); |
127 | if (entry->cb_interest) { | |
128 | write_unlock(&slist->lock); | |
129 | afs_put_cb_interest(afs_v2net(vnode), cbi); | |
c435ee34 DH |
130 | goto again; |
131 | } | |
d4a96bec DH |
132 | |
133 | entry->cb_interest = cbi; | |
134 | write_unlock(&slist->lock); | |
c435ee34 DH |
135 | return 0; |
136 | } | |
137 | } | |
1da177e4 | 138 | |
c435ee34 | 139 | if (!cbi) { |
47ea0f2e | 140 | new = afs_create_interest(server, vnode); |
c435ee34 DH |
141 | if (!new) |
142 | return -ENOMEM; | |
143 | ||
d4a96bec DH |
144 | write_lock(&slist->lock); |
145 | if (!entry->cb_interest) { | |
146 | entry->cb_interest = afs_get_cb_interest(new); | |
c435ee34 | 147 | cbi = new; |
d4a96bec | 148 | new = NULL; |
c435ee34 | 149 | } else { |
d4a96bec | 150 | cbi = afs_get_cb_interest(entry->cb_interest); |
c435ee34 | 151 | } |
d4a96bec DH |
152 | write_unlock(&slist->lock); |
153 | afs_put_cb_interest(afs_v2net(vnode), new); | |
08e0e7c8 | 154 | } |
1da177e4 | 155 | |
c435ee34 DH |
156 | ASSERT(cbi); |
157 | ||
158 | /* Change the server the vnode is using. This entails scrubbing any | |
159 | * interest the vnode had in the previous server it was using. | |
160 | */ | |
161 | write_seqlock(&vnode->cb_lock); | |
162 | ||
d4a96bec DH |
163 | old = vnode->cb_interest; |
164 | vnode->cb_interest = cbi; | |
c435ee34 | 165 | vnode->cb_s_break = cbi->server->cb_s_break; |
68251f0a | 166 | vnode->cb_v_break = vnode->volume->cb_v_break; |
c435ee34 DH |
167 | clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); |
168 | ||
169 | write_sequnlock(&vnode->cb_lock); | |
d4a96bec | 170 | afs_put_cb_interest(afs_v2net(vnode), old); |
c435ee34 | 171 | return 0; |
08e0e7c8 | 172 | } |
1da177e4 | 173 | |
c435ee34 DH |
174 | /* |
175 | * Remove an interest on a server. | |
176 | */ | |
177 | void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) | |
178 | { | |
47ea0f2e DH |
179 | struct afs_vol_interest *vi; |
180 | ||
c435ee34 | 181 | if (cbi && refcount_dec_and_test(&cbi->usage)) { |
47ea0f2e | 182 | if (!hlist_unhashed(&cbi->cb_vlink)) { |
c435ee34 | 183 | write_lock(&cbi->server->cb_break_lock); |
47ea0f2e DH |
184 | |
185 | hlist_del_init(&cbi->cb_vlink); | |
186 | vi = cbi->vol_interest; | |
187 | cbi->vol_interest = NULL; | |
188 | if (--vi->usage == 0) | |
189 | hlist_del(&vi->srv_link); | |
190 | else | |
191 | vi = NULL; | |
192 | ||
c435ee34 | 193 | write_unlock(&cbi->server->cb_break_lock); |
47ea0f2e | 194 | kfree(vi); |
c435ee34 DH |
195 | afs_put_server(net, cbi->server); |
196 | } | |
197 | kfree(cbi); | |
08e0e7c8 | 198 | } |
c435ee34 DH |
199 | } |
200 | ||
201 | /* | |
202 | * allow the fileserver to request callback state (re-)initialisation | |
203 | */ | |
204 | void afs_init_callback_state(struct afs_server *server) | |
205 | { | |
d2ddc776 | 206 | if (!test_and_clear_bit(AFS_SERVER_FL_NEW, &server->flags)) |
c435ee34 | 207 | server->cb_s_break++; |
08e0e7c8 DH |
208 | } |
209 | ||
210 | /* | |
211 | * actually break a callback | |
212 | */ | |
30062bd1 | 213 | void __afs_break_callback(struct afs_vnode *vnode) |
08e0e7c8 DH |
214 | { |
215 | _enter(""); | |
216 | ||
5a813276 | 217 | clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); |
c435ee34 DH |
218 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { |
219 | vnode->cb_break++; | |
220 | afs_clear_permits(vnode); | |
08e0e7c8 | 221 | |
08e0e7c8 DH |
222 | spin_lock(&vnode->lock); |
223 | ||
224 | _debug("break callback"); | |
225 | ||
e8d6c554 DH |
226 | if (list_empty(&vnode->granted_locks) && |
227 | !list_empty(&vnode->pending_locks)) | |
228 | afs_lock_may_be_available(vnode); | |
08e0e7c8 DH |
229 | spin_unlock(&vnode->lock); |
230 | } | |
30062bd1 | 231 | } |
c435ee34 | 232 | |
30062bd1 DH |
233 | void afs_break_callback(struct afs_vnode *vnode) |
234 | { | |
235 | write_seqlock(&vnode->cb_lock); | |
236 | __afs_break_callback(vnode); | |
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 | ||
282 | write_lock(&volume->cb_break_lock); | |
283 | volume->cb_v_break++; | |
284 | write_unlock(&volume->cb_break_lock); | |
285 | } else { | |
286 | data.volume = NULL; | |
287 | data.fid = *fid; | |
288 | inode = ilookup5_nowait(cbi->sb, fid->vnode, | |
289 | afs_iget5_test, &data); | |
290 | if (inode) { | |
291 | vnode = AFS_FS_I(inode); | |
292 | afs_break_callback(vnode); | |
293 | iput(inode); | |
294 | } | |
c435ee34 DH |
295 | } |
296 | } | |
08e0e7c8 | 297 | |
47ea0f2e | 298 | out: |
c435ee34 | 299 | read_unlock(&server->cb_break_lock); |
ec26815a | 300 | } |
1da177e4 | 301 | |
1da177e4 LT |
302 | /* |
303 | * allow the fileserver to break callback promises | |
304 | */ | |
08e0e7c8 | 305 | void afs_break_callbacks(struct afs_server *server, size_t count, |
5cf9dd55 | 306 | struct afs_callback_break *callbacks) |
1da177e4 | 307 | { |
08e0e7c8 | 308 | _enter("%p,%zu,", server, count); |
1da177e4 | 309 | |
08e0e7c8 DH |
310 | ASSERT(server != NULL); |
311 | ASSERTCMP(count, <=, AFSCBMAX); | |
1da177e4 | 312 | |
68251f0a DH |
313 | /* TODO: Sort the callback break list by volume ID */ |
314 | ||
08e0e7c8 | 315 | for (; count > 0; callbacks++, count--) { |
06aeb297 | 316 | _debug("- Fid { vl=%08llx n=%llu u=%u }", |
1da177e4 LT |
317 | callbacks->fid.vid, |
318 | callbacks->fid.vnode, | |
06aeb297 | 319 | callbacks->fid.unique); |
08e0e7c8 DH |
320 | afs_break_one_callback(server, &callbacks->fid); |
321 | } | |
322 | ||
323 | _leave(""); | |
324 | return; | |
325 | } | |
1da177e4 | 326 | |
08e0e7c8 | 327 | /* |
c435ee34 | 328 | * Clear the callback interests in a server list. |
08e0e7c8 | 329 | */ |
d2ddc776 | 330 | void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist) |
08e0e7c8 | 331 | { |
c435ee34 | 332 | int i; |
08e0e7c8 | 333 | |
d2ddc776 DH |
334 | for (i = 0; i < slist->nr_servers; i++) { |
335 | afs_put_cb_interest(net, slist->servers[i].cb_interest); | |
336 | slist->servers[i].cb_interest = NULL; | |
08e0e7c8 | 337 | } |
08e0e7c8 | 338 | } |