Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / fs / afs / callback.c
1 /*
2  * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
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  *
11  * Authors: David Woodhouse <dwmw2@infradead.org>
12  *          David Howells <dhowells@redhat.com>
13  *
14  */
15
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/init.h>
19 #include <linux/circ_buf.h>
20 #include <linux/sched.h>
21 #include "internal.h"
22
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
83 /*
84  * Set up an interest-in-callbacks record for a volume on a server and
85  * register it with the server.
86  * - Called with vnode->io_lock held.
87  */
88 int afs_register_server_cb_interest(struct afs_vnode *vnode,
89                                     struct afs_server_list *slist,
90                                     unsigned int index)
91 {
92         struct afs_server_entry *entry = &slist->servers[index];
93         struct afs_cb_interest *cbi, *vcbi, *new, *old;
94         struct afs_server *server = entry->server;
95
96 again:
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
105         vcbi = vnode->cb_interest;
106         if (vcbi) {
107                 if (vcbi == cbi) {
108                         afs_put_cb_interest(afs_v2net(vnode), cbi);
109                         return 0;
110                 }
111
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                  */
115                 if (cbi && vcbi->server == cbi->server) {
116                         write_seqlock(&vnode->cb_lock);
117                         old = vnode->cb_interest;
118                         vnode->cb_interest = cbi;
119                         write_sequnlock(&vnode->cb_lock);
120                         afs_put_cb_interest(afs_v2net(vnode), old);
121                         return 0;
122                 }
123
124                 /* Re-use the one attached to the vnode. */
125                 if (!cbi && vcbi->server == server) {
126                         write_lock(&slist->lock);
127                         if (entry->cb_interest) {
128                                 write_unlock(&slist->lock);
129                                 afs_put_cb_interest(afs_v2net(vnode), cbi);
130                                 goto again;
131                         }
132
133                         entry->cb_interest = cbi;
134                         write_unlock(&slist->lock);
135                         return 0;
136                 }
137         }
138
139         if (!cbi) {
140                 new = afs_create_interest(server, vnode);
141                 if (!new)
142                         return -ENOMEM;
143
144                 write_lock(&slist->lock);
145                 if (!entry->cb_interest) {
146                         entry->cb_interest = afs_get_cb_interest(new);
147                         cbi = new;
148                         new = NULL;
149                 } else {
150                         cbi = afs_get_cb_interest(entry->cb_interest);
151                 }
152                 write_unlock(&slist->lock);
153                 afs_put_cb_interest(afs_v2net(vnode), new);
154         }
155
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
163         old = vnode->cb_interest;
164         vnode->cb_interest = cbi;
165         vnode->cb_s_break = cbi->server->cb_s_break;
166         vnode->cb_v_break = vnode->volume->cb_v_break;
167         clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
168
169         write_sequnlock(&vnode->cb_lock);
170         afs_put_cb_interest(afs_v2net(vnode), old);
171         return 0;
172 }
173
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 {
179         struct afs_vol_interest *vi;
180
181         if (cbi && refcount_dec_and_test(&cbi->usage)) {
182                 if (!hlist_unhashed(&cbi->cb_vlink)) {
183                         write_lock(&cbi->server->cb_break_lock);
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
193                         write_unlock(&cbi->server->cb_break_lock);
194                         kfree(vi);
195                         afs_put_server(net, cbi->server);
196                 }
197                 kfree(cbi);
198         }
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 {
206         if (!test_and_clear_bit(AFS_SERVER_FL_NEW, &server->flags))
207                 server->cb_s_break++;
208 }
209
210 /*
211  * actually break a callback
212  */
213 void afs_break_callback(struct afs_vnode *vnode)
214 {
215         _enter("");
216
217         write_seqlock(&vnode->cb_lock);
218
219         clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
220         if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
221                 vnode->cb_break++;
222                 afs_clear_permits(vnode);
223
224                 spin_lock(&vnode->lock);
225
226                 _debug("break callback");
227
228                 if (list_empty(&vnode->granted_locks) &&
229                     !list_empty(&vnode->pending_locks))
230                         afs_lock_may_be_available(vnode);
231                 spin_unlock(&vnode->lock);
232         }
233
234         write_sequnlock(&vnode->cb_lock);
235 }
236
237 /*
238  * allow the fileserver to explicitly break one callback
239  * - happens when
240  *   - the backing file is changed
241  *   - a lock is released
242  */
243 static void afs_break_one_callback(struct afs_server *server,
244                                    struct afs_fid *fid)
245 {
246         struct afs_vol_interest *vi;
247         struct afs_cb_interest *cbi;
248         struct afs_iget_data data;
249         struct afs_vnode *vnode;
250         struct inode *inode;
251
252         read_lock(&server->cb_break_lock);
253         hlist_for_each_entry(vi, &server->cb_volumes, srv_link) {
254                 if (vi->vid < fid->vid)
255                         continue;
256                 if (vi->vid > fid->vid) {
257                         vi = NULL;
258                         break;
259                 }
260                 //atomic_inc(&vi->usage);
261                 break;
262         }
263
264         /* TODO: Find all matching volumes if we couldn't match the server and
265          * break them anyway.
266          */
267         if (!vi)
268                 goto out;
269
270         /* Step through all interested superblocks.  There may be more than one
271          * because of cell aliasing.
272          */
273         hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
274                 if (fid->vnode == 0 && fid->unique == 0) {
275                         /* The callback break applies to an entire volume. */
276                         struct afs_super_info *as = AFS_FS_S(cbi->sb);
277                         struct afs_volume *volume = as->volume;
278
279                         write_lock(&volume->cb_break_lock);
280                         volume->cb_v_break++;
281                         write_unlock(&volume->cb_break_lock);
282                 } else {
283                         data.volume = NULL;
284                         data.fid = *fid;
285                         inode = ilookup5_nowait(cbi->sb, fid->vnode,
286                                                 afs_iget5_test, &data);
287                         if (inode) {
288                                 vnode = AFS_FS_I(inode);
289                                 afs_break_callback(vnode);
290                                 iput(inode);
291                         }
292                 }
293         }
294
295 out:
296         read_unlock(&server->cb_break_lock);
297 }
298
299 /*
300  * allow the fileserver to break callback promises
301  */
302 void afs_break_callbacks(struct afs_server *server, size_t count,
303                          struct afs_callback_break *callbacks)
304 {
305         _enter("%p,%zu,", server, count);
306
307         ASSERT(server != NULL);
308         ASSERTCMP(count, <=, AFSCBMAX);
309
310         /* TODO: Sort the callback break list by volume ID */
311
312         for (; count > 0; callbacks++, count--) {
313                 _debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
314                        callbacks->fid.vid,
315                        callbacks->fid.vnode,
316                        callbacks->fid.unique,
317                        callbacks->cb.version,
318                        callbacks->cb.expiry,
319                        callbacks->cb.type
320                        );
321                 afs_break_one_callback(server, &callbacks->fid);
322         }
323
324         _leave("");
325         return;
326 }
327
328 /*
329  * Clear the callback interests in a server list.
330  */
331 void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist)
332 {
333         int i;
334
335         for (i = 0; i < slist->nr_servers; i++) {
336                 afs_put_cb_interest(net, slist->servers[i].cb_interest);
337                 slist->servers[i].cb_interest = NULL;
338         }
339 }