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