]>
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 | |
1da177e4 | 23 | /* |
c435ee34 DH |
24 | * Set up an interest-in-callbacks record for a volume on a server and |
25 | * register it with the server. | |
62e63147 | 26 | * - Called with vnode->io_lock held. |
1da177e4 | 27 | */ |
c435ee34 | 28 | int afs_register_server_cb_interest(struct afs_vnode *vnode, |
62e63147 DH |
29 | struct afs_server_list *slist, |
30 | unsigned int index) | |
1da177e4 | 31 | { |
62e63147 DH |
32 | struct afs_server_entry *entry = &slist->servers[index]; |
33 | struct afs_cb_interest *cbi, *vcbi, *new, *old; | |
d2ddc776 | 34 | struct afs_server *server = entry->server; |
c435ee34 DH |
35 | |
36 | again: | |
62e63147 DH |
37 | if (vnode->cb_interest && |
38 | likely(vnode->cb_interest == entry->cb_interest)) | |
39 | return 0; | |
40 | ||
41 | read_lock(&slist->lock); | |
42 | cbi = afs_get_cb_interest(entry->cb_interest); | |
43 | read_unlock(&slist->lock); | |
44 | ||
c435ee34 DH |
45 | vcbi = vnode->cb_interest; |
46 | if (vcbi) { | |
62e63147 DH |
47 | if (vcbi == cbi) { |
48 | afs_put_cb_interest(afs_v2net(vnode), cbi); | |
c435ee34 | 49 | return 0; |
62e63147 | 50 | } |
c435ee34 | 51 | |
62e63147 DH |
52 | /* Use a new interest in the server list for the same server |
53 | * rather than an old one that's still attached to a vnode. | |
54 | */ | |
c435ee34 DH |
55 | if (cbi && vcbi->server == cbi->server) { |
56 | write_seqlock(&vnode->cb_lock); | |
62e63147 DH |
57 | old = vnode->cb_interest; |
58 | vnode->cb_interest = cbi; | |
c435ee34 | 59 | write_sequnlock(&vnode->cb_lock); |
62e63147 | 60 | afs_put_cb_interest(afs_v2net(vnode), old); |
c435ee34 DH |
61 | return 0; |
62 | } | |
1da177e4 | 63 | |
62e63147 | 64 | /* Re-use the one attached to the vnode. */ |
c435ee34 | 65 | if (!cbi && vcbi->server == server) { |
62e63147 DH |
66 | write_lock(&slist->lock); |
67 | if (entry->cb_interest) { | |
68 | write_unlock(&slist->lock); | |
69 | afs_put_cb_interest(afs_v2net(vnode), cbi); | |
c435ee34 DH |
70 | goto again; |
71 | } | |
62e63147 DH |
72 | |
73 | entry->cb_interest = cbi; | |
74 | write_unlock(&slist->lock); | |
c435ee34 DH |
75 | return 0; |
76 | } | |
77 | } | |
1da177e4 | 78 | |
c435ee34 DH |
79 | if (!cbi) { |
80 | new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL); | |
81 | if (!new) | |
82 | return -ENOMEM; | |
83 | ||
84 | refcount_set(&new->usage, 1); | |
85 | new->sb = vnode->vfs_inode.i_sb; | |
86 | new->vid = vnode->volume->vid; | |
87 | new->server = afs_get_server(server); | |
88 | INIT_LIST_HEAD(&new->cb_link); | |
89 | ||
90 | write_lock(&server->cb_break_lock); | |
91 | list_add_tail(&new->cb_link, &server->cb_interests); | |
92 | write_unlock(&server->cb_break_lock); | |
93 | ||
62e63147 DH |
94 | write_lock(&slist->lock); |
95 | if (!entry->cb_interest) { | |
96 | entry->cb_interest = afs_get_cb_interest(new); | |
c435ee34 | 97 | cbi = new; |
62e63147 | 98 | new = NULL; |
c435ee34 | 99 | } else { |
62e63147 | 100 | cbi = afs_get_cb_interest(entry->cb_interest); |
c435ee34 | 101 | } |
62e63147 DH |
102 | write_unlock(&slist->lock); |
103 | afs_put_cb_interest(afs_v2net(vnode), new); | |
08e0e7c8 | 104 | } |
1da177e4 | 105 | |
c435ee34 DH |
106 | ASSERT(cbi); |
107 | ||
108 | /* Change the server the vnode is using. This entails scrubbing any | |
109 | * interest the vnode had in the previous server it was using. | |
110 | */ | |
111 | write_seqlock(&vnode->cb_lock); | |
112 | ||
62e63147 DH |
113 | old = vnode->cb_interest; |
114 | vnode->cb_interest = cbi; | |
c435ee34 DH |
115 | vnode->cb_s_break = cbi->server->cb_s_break; |
116 | clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); | |
117 | ||
118 | write_sequnlock(&vnode->cb_lock); | |
62e63147 | 119 | afs_put_cb_interest(afs_v2net(vnode), old); |
c435ee34 | 120 | return 0; |
08e0e7c8 | 121 | } |
1da177e4 | 122 | |
08e0e7c8 | 123 | /* |
c435ee34 | 124 | * Set a vnode's interest on a server. |
08e0e7c8 | 125 | */ |
c435ee34 | 126 | void afs_set_cb_interest(struct afs_vnode *vnode, struct afs_cb_interest *cbi) |
08e0e7c8 | 127 | { |
c435ee34 | 128 | struct afs_cb_interest *old_cbi = NULL; |
1da177e4 | 129 | |
c435ee34 | 130 | if (vnode->cb_interest == cbi) |
08e0e7c8 | 131 | return; |
1da177e4 | 132 | |
c435ee34 DH |
133 | write_seqlock(&vnode->cb_lock); |
134 | if (vnode->cb_interest != cbi) { | |
135 | afs_get_cb_interest(cbi); | |
136 | old_cbi = vnode->cb_interest; | |
137 | vnode->cb_interest = cbi; | |
1da177e4 | 138 | } |
c435ee34 DH |
139 | write_sequnlock(&vnode->cb_lock); |
140 | afs_put_cb_interest(afs_v2net(vnode), cbi); | |
141 | } | |
1da177e4 | 142 | |
c435ee34 DH |
143 | /* |
144 | * Remove an interest on a server. | |
145 | */ | |
146 | void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) | |
147 | { | |
148 | if (cbi && refcount_dec_and_test(&cbi->usage)) { | |
149 | if (!list_empty(&cbi->cb_link)) { | |
150 | write_lock(&cbi->server->cb_break_lock); | |
151 | list_del_init(&cbi->cb_link); | |
152 | write_unlock(&cbi->server->cb_break_lock); | |
153 | afs_put_server(net, cbi->server); | |
154 | } | |
155 | kfree(cbi); | |
08e0e7c8 | 156 | } |
c435ee34 DH |
157 | } |
158 | ||
159 | /* | |
160 | * allow the fileserver to request callback state (re-)initialisation | |
161 | */ | |
162 | void afs_init_callback_state(struct afs_server *server) | |
163 | { | |
d2ddc776 | 164 | if (!test_and_clear_bit(AFS_SERVER_FL_NEW, &server->flags)) |
c435ee34 | 165 | server->cb_s_break++; |
08e0e7c8 DH |
166 | } |
167 | ||
168 | /* | |
169 | * actually break a callback | |
170 | */ | |
c435ee34 | 171 | void afs_break_callback(struct afs_vnode *vnode) |
08e0e7c8 DH |
172 | { |
173 | _enter(""); | |
174 | ||
c435ee34 DH |
175 | write_seqlock(&vnode->cb_lock); |
176 | ||
177 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { | |
178 | vnode->cb_break++; | |
179 | afs_clear_permits(vnode); | |
08e0e7c8 | 180 | |
08e0e7c8 DH |
181 | spin_lock(&vnode->lock); |
182 | ||
183 | _debug("break callback"); | |
184 | ||
e8d6c554 DH |
185 | if (list_empty(&vnode->granted_locks) && |
186 | !list_empty(&vnode->pending_locks)) | |
187 | afs_lock_may_be_available(vnode); | |
08e0e7c8 DH |
188 | spin_unlock(&vnode->lock); |
189 | } | |
c435ee34 DH |
190 | |
191 | write_sequnlock(&vnode->cb_lock); | |
08e0e7c8 DH |
192 | } |
193 | ||
194 | /* | |
195 | * allow the fileserver to explicitly break one callback | |
196 | * - happens when | |
197 | * - the backing file is changed | |
198 | * - a lock is released | |
199 | */ | |
200 | static void afs_break_one_callback(struct afs_server *server, | |
201 | struct afs_fid *fid) | |
202 | { | |
c435ee34 DH |
203 | struct afs_cb_interest *cbi; |
204 | struct afs_iget_data data; | |
08e0e7c8 | 205 | struct afs_vnode *vnode; |
c435ee34 | 206 | struct inode *inode; |
08e0e7c8 | 207 | |
c435ee34 | 208 | read_lock(&server->cb_break_lock); |
08e0e7c8 | 209 | |
c435ee34 DH |
210 | /* Step through all interested superblocks. There may be more than one |
211 | * because of cell aliasing. | |
212 | */ | |
213 | list_for_each_entry(cbi, &server->cb_interests, cb_link) { | |
214 | if (cbi->vid != fid->vid) | |
215 | continue; | |
216 | ||
217 | data.volume = NULL; | |
218 | data.fid = *fid; | |
219 | inode = ilookup5_nowait(cbi->sb, fid->vnode, afs_iget5_test, &data); | |
220 | if (inode) { | |
221 | vnode = AFS_FS_I(inode); | |
222 | afs_break_callback(vnode); | |
223 | iput(inode); | |
224 | } | |
225 | } | |
08e0e7c8 | 226 | |
c435ee34 | 227 | read_unlock(&server->cb_break_lock); |
ec26815a | 228 | } |
1da177e4 | 229 | |
1da177e4 LT |
230 | /* |
231 | * allow the fileserver to break callback promises | |
232 | */ | |
08e0e7c8 DH |
233 | void afs_break_callbacks(struct afs_server *server, size_t count, |
234 | struct afs_callback callbacks[]) | |
1da177e4 | 235 | { |
08e0e7c8 | 236 | _enter("%p,%zu,", server, count); |
1da177e4 | 237 | |
08e0e7c8 DH |
238 | ASSERT(server != NULL); |
239 | ASSERTCMP(count, <=, AFSCBMAX); | |
1da177e4 | 240 | |
08e0e7c8 | 241 | for (; count > 0; callbacks++, count--) { |
1da177e4 LT |
242 | _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }", |
243 | callbacks->fid.vid, | |
244 | callbacks->fid.vnode, | |
245 | callbacks->fid.unique, | |
246 | callbacks->version, | |
247 | callbacks->expiry, | |
248 | callbacks->type | |
249 | ); | |
08e0e7c8 DH |
250 | afs_break_one_callback(server, &callbacks->fid); |
251 | } | |
252 | ||
253 | _leave(""); | |
254 | return; | |
255 | } | |
1da177e4 | 256 | |
08e0e7c8 | 257 | /* |
c435ee34 | 258 | * Clear the callback interests in a server list. |
08e0e7c8 | 259 | */ |
d2ddc776 | 260 | void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist) |
08e0e7c8 | 261 | { |
c435ee34 | 262 | int i; |
08e0e7c8 | 263 | |
d2ddc776 DH |
264 | for (i = 0; i < slist->nr_servers; i++) { |
265 | afs_put_cb_interest(net, slist->servers[i].cb_interest); | |
266 | slist->servers[i].cb_interest = NULL; | |
08e0e7c8 | 267 | } |
08e0e7c8 | 268 | } |