]>
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 | |
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 | |
c435ee34 DH |
99 | /* |
100 | * Remove an interest on a server. | |
101 | */ | |
102 | void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) | |
103 | { | |
104 | if (cbi && refcount_dec_and_test(&cbi->usage)) { | |
105 | if (!list_empty(&cbi->cb_link)) { | |
106 | write_lock(&cbi->server->cb_break_lock); | |
107 | list_del_init(&cbi->cb_link); | |
108 | write_unlock(&cbi->server->cb_break_lock); | |
109 | afs_put_server(net, cbi->server); | |
110 | } | |
111 | kfree(cbi); | |
08e0e7c8 | 112 | } |
c435ee34 DH |
113 | } |
114 | ||
115 | /* | |
116 | * allow the fileserver to request callback state (re-)initialisation | |
117 | */ | |
118 | void afs_init_callback_state(struct afs_server *server) | |
119 | { | |
d2ddc776 | 120 | if (!test_and_clear_bit(AFS_SERVER_FL_NEW, &server->flags)) |
c435ee34 | 121 | server->cb_s_break++; |
08e0e7c8 DH |
122 | } |
123 | ||
124 | /* | |
125 | * actually break a callback | |
126 | */ | |
c435ee34 | 127 | void afs_break_callback(struct afs_vnode *vnode) |
08e0e7c8 DH |
128 | { |
129 | _enter(""); | |
130 | ||
c435ee34 DH |
131 | write_seqlock(&vnode->cb_lock); |
132 | ||
5a813276 | 133 | clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); |
c435ee34 DH |
134 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { |
135 | vnode->cb_break++; | |
136 | afs_clear_permits(vnode); | |
08e0e7c8 | 137 | |
08e0e7c8 DH |
138 | spin_lock(&vnode->lock); |
139 | ||
140 | _debug("break callback"); | |
141 | ||
e8d6c554 DH |
142 | if (list_empty(&vnode->granted_locks) && |
143 | !list_empty(&vnode->pending_locks)) | |
144 | afs_lock_may_be_available(vnode); | |
08e0e7c8 DH |
145 | spin_unlock(&vnode->lock); |
146 | } | |
c435ee34 DH |
147 | |
148 | write_sequnlock(&vnode->cb_lock); | |
08e0e7c8 DH |
149 | } |
150 | ||
151 | /* | |
152 | * allow the fileserver to explicitly break one callback | |
153 | * - happens when | |
154 | * - the backing file is changed | |
155 | * - a lock is released | |
156 | */ | |
157 | static void afs_break_one_callback(struct afs_server *server, | |
158 | struct afs_fid *fid) | |
159 | { | |
c435ee34 DH |
160 | struct afs_cb_interest *cbi; |
161 | struct afs_iget_data data; | |
08e0e7c8 | 162 | struct afs_vnode *vnode; |
c435ee34 | 163 | struct inode *inode; |
08e0e7c8 | 164 | |
c435ee34 | 165 | read_lock(&server->cb_break_lock); |
08e0e7c8 | 166 | |
c435ee34 DH |
167 | /* Step through all interested superblocks. There may be more than one |
168 | * because of cell aliasing. | |
169 | */ | |
170 | list_for_each_entry(cbi, &server->cb_interests, cb_link) { | |
171 | if (cbi->vid != fid->vid) | |
172 | continue; | |
173 | ||
174 | data.volume = NULL; | |
175 | data.fid = *fid; | |
176 | inode = ilookup5_nowait(cbi->sb, fid->vnode, afs_iget5_test, &data); | |
177 | if (inode) { | |
178 | vnode = AFS_FS_I(inode); | |
179 | afs_break_callback(vnode); | |
180 | iput(inode); | |
181 | } | |
182 | } | |
08e0e7c8 | 183 | |
c435ee34 | 184 | read_unlock(&server->cb_break_lock); |
ec26815a | 185 | } |
1da177e4 | 186 | |
1da177e4 LT |
187 | /* |
188 | * allow the fileserver to break callback promises | |
189 | */ | |
08e0e7c8 | 190 | void afs_break_callbacks(struct afs_server *server, size_t count, |
5cf9dd55 | 191 | struct afs_callback_break *callbacks) |
1da177e4 | 192 | { |
08e0e7c8 | 193 | _enter("%p,%zu,", server, count); |
1da177e4 | 194 | |
08e0e7c8 DH |
195 | ASSERT(server != NULL); |
196 | ASSERTCMP(count, <=, AFSCBMAX); | |
1da177e4 | 197 | |
08e0e7c8 | 198 | for (; count > 0; callbacks++, count--) { |
1da177e4 LT |
199 | _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }", |
200 | callbacks->fid.vid, | |
201 | callbacks->fid.vnode, | |
202 | callbacks->fid.unique, | |
5cf9dd55 DH |
203 | callbacks->cb.version, |
204 | callbacks->cb.expiry, | |
205 | callbacks->cb.type | |
1da177e4 | 206 | ); |
08e0e7c8 DH |
207 | afs_break_one_callback(server, &callbacks->fid); |
208 | } | |
209 | ||
210 | _leave(""); | |
211 | return; | |
212 | } | |
1da177e4 | 213 | |
08e0e7c8 | 214 | /* |
c435ee34 | 215 | * Clear the callback interests in a server list. |
08e0e7c8 | 216 | */ |
d2ddc776 | 217 | void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist) |
08e0e7c8 | 218 | { |
c435ee34 | 219 | int i; |
08e0e7c8 | 220 | |
d2ddc776 DH |
221 | for (i = 0; i < slist->nr_servers; i++) { |
222 | afs_put_cb_interest(net, slist->servers[i].cb_interest); | |
223 | slist->servers[i].cb_interest = NULL; | |
08e0e7c8 | 224 | } |
08e0e7c8 | 225 | } |