]>
Commit | Line | Data |
---|---|---|
e8d6c554 DH |
1 | /* AFS file locking support |
2 | * | |
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | |
4 | * Written by David Howells (dhowells@redhat.com) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the License, or (at your option) any later version. | |
10 | */ | |
11 | ||
e8d6c554 DH |
12 | #include "internal.h" |
13 | ||
14 | #define AFS_LOCK_GRANTED 0 | |
15 | #define AFS_LOCK_PENDING 1 | |
4be5975a | 16 | #define AFS_LOCK_YOUR_TRY 2 |
e8d6c554 | 17 | |
f044c884 DH |
18 | struct workqueue_struct *afs_lock_manager; |
19 | ||
4be5975a | 20 | static void afs_next_locker(struct afs_vnode *vnode, int error); |
e8d6c554 DH |
21 | static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl); |
22 | static void afs_fl_release_private(struct file_lock *fl); | |
23 | ||
6aed6285 | 24 | static const struct file_lock_operations afs_lock_ops = { |
e8d6c554 DH |
25 | .fl_copy_lock = afs_fl_copy_lock, |
26 | .fl_release_private = afs_fl_release_private, | |
27 | }; | |
28 | ||
4be5975a DH |
29 | static inline void afs_set_lock_state(struct afs_vnode *vnode, enum afs_lock_state state) |
30 | { | |
31 | _debug("STATE %u -> %u", vnode->lock_state, state); | |
32 | vnode->lock_state = state; | |
33 | } | |
34 | ||
d4696601 DH |
35 | static atomic_t afs_file_lock_debug_id; |
36 | ||
e8d6c554 DH |
37 | /* |
38 | * if the callback is broken on this vnode, then the lock may now be available | |
39 | */ | |
40 | void afs_lock_may_be_available(struct afs_vnode *vnode) | |
41 | { | |
3b6492df | 42 | _enter("{%llx:%llu}", vnode->fid.vid, vnode->fid.vnode); |
e8d6c554 | 43 | |
4be5975a DH |
44 | if (vnode->lock_state != AFS_VNODE_LOCK_WAITING_FOR_CB) |
45 | return; | |
46 | ||
47 | spin_lock(&vnode->lock); | |
48 | if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB) | |
49 | afs_next_locker(vnode, 0); | |
d4696601 | 50 | trace_afs_flock_ev(vnode, NULL, afs_flock_callback_break, 0); |
4be5975a | 51 | spin_unlock(&vnode->lock); |
e8d6c554 DH |
52 | } |
53 | ||
54 | /* | |
55 | * the lock will time out in 5 minutes unless we extend it, so schedule | |
56 | * extension in a bit less than that time | |
57 | */ | |
58 | static void afs_schedule_lock_extension(struct afs_vnode *vnode) | |
59 | { | |
a690f60a DH |
60 | ktime_t expires_at, now, duration; |
61 | u64 duration_j; | |
62 | ||
63 | expires_at = ktime_add_ms(vnode->locked_at, AFS_LOCKWAIT * 1000 / 2); | |
64 | now = ktime_get_real(); | |
65 | duration = ktime_sub(expires_at, now); | |
66 | if (duration <= 0) | |
67 | duration_j = 0; | |
68 | else | |
69 | duration_j = nsecs_to_jiffies(ktime_to_ns(duration)); | |
70 | ||
71 | queue_delayed_work(afs_lock_manager, &vnode->lock_work, duration_j); | |
72 | } | |
73 | ||
74 | /* | |
75 | * In the case of successful completion of a lock operation, record the time | |
76 | * the reply appeared and start the lock extension timer. | |
77 | */ | |
78 | void afs_lock_op_done(struct afs_call *call) | |
79 | { | |
80 | struct afs_vnode *vnode = call->reply[0]; | |
81 | ||
82 | if (call->error == 0) { | |
83 | spin_lock(&vnode->lock); | |
d4696601 | 84 | trace_afs_flock_ev(vnode, NULL, afs_flock_timestamp, 0); |
a690f60a DH |
85 | vnode->locked_at = call->reply_time; |
86 | afs_schedule_lock_extension(vnode); | |
87 | spin_unlock(&vnode->lock); | |
88 | } | |
e8d6c554 DH |
89 | } |
90 | ||
ff8e210a DH |
91 | /* |
92 | * grant one or more locks (readlocks are allowed to jump the queue if the | |
93 | * first lock in the queue is itself a readlock) | |
94 | * - the caller must hold the vnode lock | |
95 | */ | |
4be5975a | 96 | static void afs_grant_locks(struct afs_vnode *vnode) |
ff8e210a DH |
97 | { |
98 | struct file_lock *p, *_p; | |
4be5975a | 99 | bool exclusive = (vnode->lock_type == AFS_LOCK_WRITE); |
ff8e210a | 100 | |
4be5975a DH |
101 | list_for_each_entry_safe(p, _p, &vnode->pending_locks, fl_u.afs.link) { |
102 | if (!exclusive && p->fl_type == F_WRLCK) | |
103 | continue; | |
104 | ||
105 | list_move_tail(&p->fl_u.afs.link, &vnode->granted_locks); | |
106 | p->fl_u.afs.state = AFS_LOCK_GRANTED; | |
d4696601 | 107 | trace_afs_flock_op(vnode, p, afs_flock_op_grant); |
4be5975a DH |
108 | wake_up(&p->fl_wait); |
109 | } | |
110 | } | |
111 | ||
112 | /* | |
113 | * If an error is specified, reject every pending lock that matches the | |
114 | * authentication and type of the lock we failed to get. If there are any | |
115 | * remaining lockers, try to wake up one of them to have a go. | |
116 | */ | |
117 | static void afs_next_locker(struct afs_vnode *vnode, int error) | |
118 | { | |
119 | struct file_lock *p, *_p, *next = NULL; | |
120 | struct key *key = vnode->lock_key; | |
121 | unsigned int fl_type = F_RDLCK; | |
122 | ||
123 | _enter(""); | |
124 | ||
125 | if (vnode->lock_type == AFS_LOCK_WRITE) | |
126 | fl_type = F_WRLCK; | |
127 | ||
128 | list_for_each_entry_safe(p, _p, &vnode->pending_locks, fl_u.afs.link) { | |
129 | if (error && | |
130 | p->fl_type == fl_type && | |
131 | afs_file_key(p->fl_file) == key) { | |
132 | list_del_init(&p->fl_u.afs.link); | |
133 | p->fl_u.afs.state = error; | |
134 | wake_up(&p->fl_wait); | |
ff8e210a | 135 | } |
4be5975a DH |
136 | |
137 | /* Select the next locker to hand off to. */ | |
138 | if (next && | |
139 | (next->fl_type == F_WRLCK || p->fl_type == F_RDLCK)) | |
140 | continue; | |
141 | next = p; | |
ff8e210a | 142 | } |
4be5975a DH |
143 | |
144 | vnode->lock_key = NULL; | |
145 | key_put(key); | |
146 | ||
147 | if (next) { | |
148 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); | |
149 | next->fl_u.afs.state = AFS_LOCK_YOUR_TRY; | |
d4696601 | 150 | trace_afs_flock_op(vnode, next, afs_flock_op_wake); |
4be5975a DH |
151 | wake_up(&next->fl_wait); |
152 | } else { | |
153 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_NONE); | |
d4696601 | 154 | trace_afs_flock_ev(vnode, NULL, afs_flock_no_lockers, 0); |
4be5975a DH |
155 | } |
156 | ||
157 | _leave(""); | |
ff8e210a DH |
158 | } |
159 | ||
d2ddc776 DH |
160 | /* |
161 | * Get a lock on a file | |
162 | */ | |
163 | static int afs_set_lock(struct afs_vnode *vnode, struct key *key, | |
164 | afs_lock_type_t type) | |
165 | { | |
166 | struct afs_fs_cursor fc; | |
167 | int ret; | |
168 | ||
3b6492df | 169 | _enter("%s{%llx:%llu.%u},%x,%u", |
d2ddc776 DH |
170 | vnode->volume->name, |
171 | vnode->fid.vid, | |
172 | vnode->fid.vnode, | |
173 | vnode->fid.unique, | |
174 | key_serial(key), type); | |
175 | ||
176 | ret = -ERESTARTSYS; | |
177 | if (afs_begin_vnode_operation(&fc, vnode, key)) { | |
178 | while (afs_select_fileserver(&fc)) { | |
68251f0a | 179 | fc.cb_break = afs_calc_vnode_cb_break(vnode); |
d2ddc776 DH |
180 | afs_fs_set_lock(&fc, type); |
181 | } | |
182 | ||
183 | afs_check_for_remote_deletion(&fc, fc.vnode); | |
184 | afs_vnode_commit_status(&fc, vnode, fc.cb_break); | |
185 | ret = afs_end_vnode_operation(&fc); | |
186 | } | |
187 | ||
188 | _leave(" = %d", ret); | |
189 | return ret; | |
190 | } | |
191 | ||
192 | /* | |
193 | * Extend a lock on a file | |
194 | */ | |
195 | static int afs_extend_lock(struct afs_vnode *vnode, struct key *key) | |
196 | { | |
197 | struct afs_fs_cursor fc; | |
198 | int ret; | |
199 | ||
3b6492df | 200 | _enter("%s{%llx:%llu.%u},%x", |
d2ddc776 DH |
201 | vnode->volume->name, |
202 | vnode->fid.vid, | |
203 | vnode->fid.vnode, | |
204 | vnode->fid.unique, | |
205 | key_serial(key)); | |
206 | ||
207 | ret = -ERESTARTSYS; | |
208 | if (afs_begin_vnode_operation(&fc, vnode, key)) { | |
209 | while (afs_select_current_fileserver(&fc)) { | |
68251f0a | 210 | fc.cb_break = afs_calc_vnode_cb_break(vnode); |
d2ddc776 DH |
211 | afs_fs_extend_lock(&fc); |
212 | } | |
213 | ||
214 | afs_check_for_remote_deletion(&fc, fc.vnode); | |
215 | afs_vnode_commit_status(&fc, vnode, fc.cb_break); | |
216 | ret = afs_end_vnode_operation(&fc); | |
217 | } | |
218 | ||
219 | _leave(" = %d", ret); | |
220 | return ret; | |
221 | } | |
222 | ||
223 | /* | |
224 | * Release a lock on a file | |
225 | */ | |
226 | static int afs_release_lock(struct afs_vnode *vnode, struct key *key) | |
227 | { | |
228 | struct afs_fs_cursor fc; | |
229 | int ret; | |
230 | ||
3b6492df | 231 | _enter("%s{%llx:%llu.%u},%x", |
d2ddc776 DH |
232 | vnode->volume->name, |
233 | vnode->fid.vid, | |
234 | vnode->fid.vnode, | |
235 | vnode->fid.unique, | |
236 | key_serial(key)); | |
237 | ||
238 | ret = -ERESTARTSYS; | |
239 | if (afs_begin_vnode_operation(&fc, vnode, key)) { | |
240 | while (afs_select_current_fileserver(&fc)) { | |
68251f0a | 241 | fc.cb_break = afs_calc_vnode_cb_break(vnode); |
d2ddc776 DH |
242 | afs_fs_release_lock(&fc); |
243 | } | |
244 | ||
245 | afs_check_for_remote_deletion(&fc, fc.vnode); | |
246 | afs_vnode_commit_status(&fc, vnode, fc.cb_break); | |
247 | ret = afs_end_vnode_operation(&fc); | |
248 | } | |
249 | ||
250 | _leave(" = %d", ret); | |
251 | return ret; | |
252 | } | |
253 | ||
e8d6c554 DH |
254 | /* |
255 | * do work for a lock, including: | |
256 | * - probing for a lock we're waiting on but didn't get immediately | |
257 | * - extending a lock that's close to timing out | |
258 | */ | |
259 | void afs_lock_work(struct work_struct *work) | |
260 | { | |
261 | struct afs_vnode *vnode = | |
262 | container_of(work, struct afs_vnode, lock_work.work); | |
e8d6c554 DH |
263 | struct key *key; |
264 | int ret; | |
265 | ||
3b6492df | 266 | _enter("{%llx:%llu}", vnode->fid.vid, vnode->fid.vnode); |
e8d6c554 DH |
267 | |
268 | spin_lock(&vnode->lock); | |
269 | ||
0fafdc9f DH |
270 | again: |
271 | _debug("wstate %u for %p", vnode->lock_state, vnode); | |
272 | switch (vnode->lock_state) { | |
273 | case AFS_VNODE_LOCK_NEED_UNLOCK: | |
4be5975a | 274 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_UNLOCKING); |
d4696601 | 275 | trace_afs_flock_ev(vnode, NULL, afs_flock_work_unlocking, 0); |
e8d6c554 DH |
276 | spin_unlock(&vnode->lock); |
277 | ||
278 | /* attempt to release the server lock; if it fails, we just | |
0fafdc9f DH |
279 | * wait 5 minutes and it'll expire anyway */ |
280 | ret = afs_release_lock(vnode, vnode->lock_key); | |
e8d6c554 DH |
281 | if (ret < 0) |
282 | printk(KERN_WARNING "AFS:" | |
3b6492df | 283 | " Failed to release lock on {%llx:%llx} error %d\n", |
e8d6c554 DH |
284 | vnode->fid.vid, vnode->fid.vnode, ret); |
285 | ||
286 | spin_lock(&vnode->lock); | |
4be5975a DH |
287 | afs_next_locker(vnode, 0); |
288 | spin_unlock(&vnode->lock); | |
289 | return; | |
e8d6c554 | 290 | |
0fafdc9f DH |
291 | /* If we've already got a lock, then it must be time to extend that |
292 | * lock as AFS locks time out after 5 minutes. | |
293 | */ | |
294 | case AFS_VNODE_LOCK_GRANTED: | |
e8d6c554 DH |
295 | _debug("extend"); |
296 | ||
0fafdc9f DH |
297 | ASSERT(!list_empty(&vnode->granted_locks)); |
298 | ||
299 | key = key_get(vnode->lock_key); | |
4be5975a | 300 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_EXTENDING); |
d4696601 | 301 | trace_afs_flock_ev(vnode, NULL, afs_flock_work_extending, 0); |
e8d6c554 DH |
302 | spin_unlock(&vnode->lock); |
303 | ||
0fafdc9f | 304 | ret = afs_extend_lock(vnode, key); /* RPC */ |
e8d6c554 | 305 | key_put(key); |
0fafdc9f DH |
306 | |
307 | if (ret < 0) | |
3b6492df | 308 | pr_warning("AFS: Failed to extend lock on {%llx:%llx} error %d\n", |
0fafdc9f DH |
309 | vnode->fid.vid, vnode->fid.vnode, ret); |
310 | ||
311 | spin_lock(&vnode->lock); | |
312 | ||
313 | if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING) | |
314 | goto again; | |
4be5975a | 315 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED); |
0fafdc9f | 316 | |
4be5975a | 317 | if (ret != 0) |
e8d6c554 DH |
318 | queue_delayed_work(afs_lock_manager, &vnode->lock_work, |
319 | HZ * 10); | |
0fafdc9f DH |
320 | spin_unlock(&vnode->lock); |
321 | _leave(" [ext]"); | |
e8d6c554 | 322 | return; |
e8d6c554 | 323 | |
4be5975a DH |
324 | /* If we're waiting for a callback to indicate lock release, we can't |
325 | * actually rely on this, so need to recheck at regular intervals. The | |
326 | * problem is that the server might not notify us if the lock just | |
327 | * expires (say because a client died) rather than being explicitly | |
328 | * released. | |
329 | */ | |
0fafdc9f | 330 | case AFS_VNODE_LOCK_WAITING_FOR_CB: |
4be5975a DH |
331 | _debug("retry"); |
332 | afs_next_locker(vnode, 0); | |
e8d6c554 | 333 | spin_unlock(&vnode->lock); |
4be5975a | 334 | return; |
0fafdc9f DH |
335 | |
336 | default: | |
337 | /* Looks like a lock request was withdrawn. */ | |
338 | spin_unlock(&vnode->lock); | |
339 | _leave(" [no]"); | |
e8d6c554 DH |
340 | return; |
341 | } | |
e8d6c554 DH |
342 | } |
343 | ||
344 | /* | |
345 | * pass responsibility for the unlocking of a vnode on the server to the | |
346 | * manager thread, lest a pending signal in the calling thread interrupt | |
347 | * AF_RXRPC | |
348 | * - the caller must hold the vnode lock | |
349 | */ | |
0fafdc9f | 350 | static void afs_defer_unlock(struct afs_vnode *vnode) |
e8d6c554 | 351 | { |
4be5975a | 352 | _enter("%u", vnode->lock_state); |
0fafdc9f | 353 | |
4be5975a DH |
354 | if (list_empty(&vnode->granted_locks) && |
355 | (vnode->lock_state == AFS_VNODE_LOCK_GRANTED || | |
356 | vnode->lock_state == AFS_VNODE_LOCK_EXTENDING)) { | |
0fafdc9f DH |
357 | cancel_delayed_work(&vnode->lock_work); |
358 | ||
4be5975a | 359 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_NEED_UNLOCK); |
d4696601 | 360 | trace_afs_flock_ev(vnode, NULL, afs_flock_defer_unlock, 0); |
4be5975a | 361 | queue_delayed_work(afs_lock_manager, &vnode->lock_work, 0); |
0fafdc9f DH |
362 | } |
363 | } | |
364 | ||
365 | /* | |
366 | * Check that our view of the file metadata is up to date and check to see | |
367 | * whether we think that we have a locking permit. | |
368 | */ | |
369 | static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, | |
370 | afs_lock_type_t type, bool can_sleep) | |
371 | { | |
372 | afs_access_t access; | |
373 | int ret; | |
374 | ||
375 | /* Make sure we've got a callback on this file and that our view of the | |
376 | * data version is up to date. | |
377 | */ | |
378 | ret = afs_validate(vnode, key); | |
379 | if (ret < 0) | |
380 | return ret; | |
381 | ||
382 | /* Check the permission set to see if we're actually going to be | |
383 | * allowed to get a lock on this file. | |
384 | */ | |
385 | ret = afs_check_permit(vnode, key, &access); | |
386 | if (ret < 0) | |
387 | return ret; | |
388 | ||
389 | /* At a rough estimation, you need LOCK, WRITE or INSERT perm to | |
390 | * read-lock a file and WRITE or INSERT perm to write-lock a file. | |
391 | * | |
392 | * We can't rely on the server to do this for us since if we want to | |
393 | * share a read lock that we already have, we won't go the server. | |
394 | */ | |
395 | if (type == AFS_LOCK_READ) { | |
396 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK))) | |
397 | return -EACCES; | |
398 | if (vnode->status.lock_count == -1 && !can_sleep) | |
399 | return -EAGAIN; /* Write locked */ | |
400 | } else { | |
401 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE))) | |
402 | return -EACCES; | |
403 | if (vnode->status.lock_count != 0 && !can_sleep) | |
404 | return -EAGAIN; /* Locked */ | |
405 | } | |
406 | ||
407 | return 0; | |
408 | } | |
409 | ||
e8d6c554 DH |
410 | /* |
411 | * request a lock on a file on the server | |
412 | */ | |
413 | static int afs_do_setlk(struct file *file, struct file_lock *fl) | |
414 | { | |
0fafdc9f | 415 | struct inode *inode = locks_inode(file); |
1c8c601a | 416 | struct afs_vnode *vnode = AFS_FS_I(inode); |
e8d6c554 | 417 | afs_lock_type_t type; |
215804a9 | 418 | struct key *key = afs_file_key(file); |
e8d6c554 DH |
419 | int ret; |
420 | ||
3b6492df | 421 | _enter("{%llx:%llu},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); |
e8d6c554 | 422 | |
e8d6c554 DH |
423 | fl->fl_ops = &afs_lock_ops; |
424 | INIT_LIST_HEAD(&fl->fl_u.afs.link); | |
425 | fl->fl_u.afs.state = AFS_LOCK_PENDING; | |
426 | ||
427 | type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; | |
428 | ||
0fafdc9f | 429 | ret = afs_do_setlk_check(vnode, key, type, fl->fl_flags & FL_SLEEP); |
e8d6c554 | 430 | if (ret < 0) |
0fafdc9f | 431 | return ret; |
e8d6c554 | 432 | |
d4696601 DH |
433 | trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock); |
434 | ||
e8d6c554 | 435 | spin_lock(&vnode->lock); |
4be5975a | 436 | list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); |
e8d6c554 | 437 | |
4be5975a DH |
438 | /* If we've already got a lock on the server then try to move to having |
439 | * the VFS grant the requested lock. Note that this means that other | |
440 | * clients may get starved out. | |
0fafdc9f | 441 | */ |
4be5975a DH |
442 | _debug("try %u", vnode->lock_state); |
443 | if (vnode->lock_state == AFS_VNODE_LOCK_GRANTED) { | |
444 | if (type == AFS_LOCK_READ) { | |
445 | _debug("instant readlock"); | |
446 | list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); | |
447 | fl->fl_u.afs.state = AFS_LOCK_GRANTED; | |
448 | goto vnode_is_locked_u; | |
449 | } | |
e8d6c554 | 450 | |
4be5975a DH |
451 | if (vnode->lock_type == AFS_LOCK_WRITE) { |
452 | _debug("instant writelock"); | |
453 | list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); | |
454 | fl->fl_u.afs.state = AFS_LOCK_GRANTED; | |
455 | goto vnode_is_locked_u; | |
456 | } | |
457 | } | |
e8d6c554 | 458 | |
0fafdc9f DH |
459 | if (vnode->lock_state != AFS_VNODE_LOCK_NONE) |
460 | goto need_to_wait; | |
e8d6c554 | 461 | |
4be5975a | 462 | try_to_lock: |
0fafdc9f DH |
463 | /* We don't have a lock on this vnode and we aren't currently waiting |
464 | * for one either, so ask the server for a lock. | |
465 | * | |
466 | * Note that we need to be careful if we get interrupted by a signal | |
467 | * after dispatching the request as we may still get the lock, even | |
468 | * though we don't wait for the reply (it's not too bad a problem - the | |
4be5975a | 469 | * lock will expire in 5 mins anyway). |
0fafdc9f | 470 | */ |
d4696601 | 471 | trace_afs_flock_ev(vnode, fl, afs_flock_try_to_lock, 0); |
0fafdc9f DH |
472 | vnode->lock_key = key_get(key); |
473 | vnode->lock_type = type; | |
4be5975a | 474 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); |
e8d6c554 DH |
475 | spin_unlock(&vnode->lock); |
476 | ||
0fafdc9f | 477 | ret = afs_set_lock(vnode, key, type); /* RPC */ |
e8d6c554 DH |
478 | |
479 | spin_lock(&vnode->lock); | |
0fafdc9f | 480 | switch (ret) { |
4be5975a DH |
481 | case -EKEYREJECTED: |
482 | case -EKEYEXPIRED: | |
483 | case -EKEYREVOKED: | |
484 | case -EPERM: | |
485 | case -EACCES: | |
486 | fl->fl_u.afs.state = ret; | |
d4696601 | 487 | trace_afs_flock_ev(vnode, fl, afs_flock_fail_perm, ret); |
4be5975a DH |
488 | list_del_init(&fl->fl_u.afs.link); |
489 | afs_next_locker(vnode, ret); | |
490 | goto error_unlock; | |
491 | ||
0fafdc9f | 492 | default: |
4be5975a | 493 | fl->fl_u.afs.state = ret; |
d4696601 | 494 | trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret); |
4be5975a DH |
495 | list_del_init(&fl->fl_u.afs.link); |
496 | afs_next_locker(vnode, 0); | |
497 | goto error_unlock; | |
e8d6c554 | 498 | |
0fafdc9f DH |
499 | case -EWOULDBLOCK: |
500 | /* The server doesn't have a lock-waiting queue, so the client | |
501 | * will have to retry. The server will break the outstanding | |
502 | * callbacks on a file when a lock is released. | |
503 | */ | |
0fafdc9f DH |
504 | ASSERT(list_empty(&vnode->granted_locks)); |
505 | ASSERTCMP(vnode->pending_locks.next, ==, &fl->fl_u.afs.link); | |
4be5975a | 506 | goto lock_is_contended; |
0fafdc9f DH |
507 | |
508 | case 0: | |
4be5975a | 509 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED); |
d4696601 | 510 | trace_afs_flock_ev(vnode, fl, afs_flock_acquired, type); |
4be5975a DH |
511 | afs_grant_locks(vnode); |
512 | goto vnode_is_locked_u; | |
e8d6c554 | 513 | } |
e8d6c554 | 514 | |
4be5975a | 515 | vnode_is_locked_u: |
0fafdc9f | 516 | spin_unlock(&vnode->lock); |
4be5975a DH |
517 | vnode_is_locked: |
518 | /* the lock has been granted by the server... */ | |
519 | ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED); | |
0fafdc9f | 520 | |
4be5975a | 521 | /* ... but the VFS still needs to distribute access on this client. */ |
d4696601 | 522 | trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0); |
4be5975a | 523 | ret = locks_lock_file_wait(file, fl); |
d4696601 | 524 | trace_afs_flock_ev(vnode, fl, afs_flock_vfs_lock, ret); |
e8d6c554 DH |
525 | if (ret < 0) |
526 | goto vfs_rejected_lock; | |
e8d6c554 | 527 | |
0fafdc9f | 528 | /* Again, make sure we've got a callback on this file and, again, make |
e8d6c554 | 529 | * sure that our view of the data version is up to date (we ignore |
0fafdc9f DH |
530 | * errors incurred here and deal with the consequences elsewhere). |
531 | */ | |
d2ddc776 | 532 | afs_validate(vnode, key); |
0fafdc9f DH |
533 | _leave(" = 0"); |
534 | return 0; | |
e8d6c554 | 535 | |
4be5975a DH |
536 | lock_is_contended: |
537 | if (!(fl->fl_flags & FL_SLEEP)) { | |
538 | list_del_init(&fl->fl_u.afs.link); | |
539 | afs_next_locker(vnode, 0); | |
540 | ret = -EAGAIN; | |
541 | goto error_unlock; | |
542 | } | |
543 | ||
544 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_WAITING_FOR_CB); | |
d4696601 | 545 | trace_afs_flock_ev(vnode, fl, afs_flock_would_block, ret); |
4be5975a DH |
546 | queue_delayed_work(afs_lock_manager, &vnode->lock_work, HZ * 5); |
547 | ||
0fafdc9f DH |
548 | need_to_wait: |
549 | /* We're going to have to wait. Either this client doesn't have a lock | |
550 | * on the server yet and we need to wait for a callback to occur, or | |
4be5975a DH |
551 | * the client does have a lock on the server, but it's shared and we |
552 | * need an exclusive lock. | |
0fafdc9f | 553 | */ |
4be5975a | 554 | spin_unlock(&vnode->lock); |
0fafdc9f | 555 | |
d4696601 | 556 | trace_afs_flock_ev(vnode, fl, afs_flock_waiting, 0); |
4be5975a DH |
557 | ret = wait_event_interruptible(fl->fl_wait, |
558 | fl->fl_u.afs.state != AFS_LOCK_PENDING); | |
d4696601 | 559 | trace_afs_flock_ev(vnode, fl, afs_flock_waited, ret); |
0fafdc9f | 560 | |
4be5975a | 561 | if (fl->fl_u.afs.state >= 0 && fl->fl_u.afs.state != AFS_LOCK_GRANTED) { |
0fafdc9f | 562 | spin_lock(&vnode->lock); |
0fafdc9f | 563 | |
4be5975a DH |
564 | switch (fl->fl_u.afs.state) { |
565 | case AFS_LOCK_YOUR_TRY: | |
566 | fl->fl_u.afs.state = AFS_LOCK_PENDING; | |
567 | goto try_to_lock; | |
568 | case AFS_LOCK_PENDING: | |
569 | if (ret > 0) { | |
570 | /* We need to retry the lock. We may not be | |
571 | * notified by the server if it just expired | |
572 | * rather than being released. | |
573 | */ | |
574 | ASSERTCMP(vnode->lock_state, ==, AFS_VNODE_LOCK_WAITING_FOR_CB); | |
575 | afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); | |
576 | fl->fl_u.afs.state = AFS_LOCK_PENDING; | |
577 | goto try_to_lock; | |
578 | } | |
579 | goto error_unlock; | |
580 | case AFS_LOCK_GRANTED: | |
581 | default: | |
582 | break; | |
583 | } | |
0fafdc9f | 584 | |
4be5975a DH |
585 | spin_unlock(&vnode->lock); |
586 | } | |
0fafdc9f | 587 | |
4be5975a DH |
588 | if (fl->fl_u.afs.state == AFS_LOCK_GRANTED) |
589 | goto vnode_is_locked; | |
590 | ret = fl->fl_u.afs.state; | |
591 | goto error; | |
e8d6c554 DH |
592 | |
593 | vfs_rejected_lock: | |
0fafdc9f DH |
594 | /* The VFS rejected the lock we just obtained, so we have to discard |
595 | * what we just got. We defer this to the lock manager work item to | |
596 | * deal with. | |
597 | */ | |
e8d6c554 | 598 | _debug("vfs refused %d", ret); |
0fafdc9f | 599 | spin_lock(&vnode->lock); |
e8d6c554 | 600 | list_del_init(&fl->fl_u.afs.link); |
4be5975a DH |
601 | afs_defer_unlock(vnode); |
602 | ||
603 | error_unlock: | |
604 | spin_unlock(&vnode->lock); | |
605 | error: | |
606 | _leave(" = %d", ret); | |
607 | return ret; | |
e8d6c554 DH |
608 | } |
609 | ||
610 | /* | |
611 | * unlock on a file on the server | |
612 | */ | |
613 | static int afs_do_unlk(struct file *file, struct file_lock *fl) | |
614 | { | |
0fafdc9f | 615 | struct afs_vnode *vnode = AFS_FS_I(locks_inode(file)); |
e8d6c554 DH |
616 | int ret; |
617 | ||
3b6492df | 618 | _enter("{%llx:%llu},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); |
e8d6c554 | 619 | |
d4696601 DH |
620 | trace_afs_flock_op(vnode, fl, afs_flock_op_unlock); |
621 | ||
0fafdc9f DH |
622 | /* Flush all pending writes before doing anything with locks. */ |
623 | vfs_fsync(file, 0); | |
624 | ||
4be5975a | 625 | ret = locks_lock_file_wait(file, fl); |
0fafdc9f DH |
626 | _leave(" = %d [%u]", ret, vnode->lock_state); |
627 | return ret; | |
e8d6c554 DH |
628 | } |
629 | ||
630 | /* | |
631 | * return information about a lock we currently hold, if indeed we hold one | |
632 | */ | |
633 | static int afs_do_getlk(struct file *file, struct file_lock *fl) | |
634 | { | |
0fafdc9f | 635 | struct afs_vnode *vnode = AFS_FS_I(locks_inode(file)); |
215804a9 | 636 | struct key *key = afs_file_key(file); |
e8d6c554 DH |
637 | int ret, lock_count; |
638 | ||
639 | _enter(""); | |
640 | ||
641 | fl->fl_type = F_UNLCK; | |
642 | ||
e8d6c554 | 643 | /* check local lock records first */ |
275afcac AM |
644 | posix_test_lock(file, fl); |
645 | if (fl->fl_type == F_UNLCK) { | |
e8d6c554 | 646 | /* no local locks; consult the server */ |
0c3a5ac2 | 647 | ret = afs_fetch_status(vnode, key, false); |
e8d6c554 DH |
648 | if (ret < 0) |
649 | goto error; | |
0fafdc9f DH |
650 | |
651 | lock_count = READ_ONCE(vnode->status.lock_count); | |
68ce801f DH |
652 | if (lock_count != 0) { |
653 | if (lock_count > 0) | |
654 | fl->fl_type = F_RDLCK; | |
655 | else | |
656 | fl->fl_type = F_WRLCK; | |
657 | fl->fl_start = 0; | |
658 | fl->fl_end = OFFSET_MAX; | |
659 | fl->fl_pid = 0; | |
660 | } | |
e8d6c554 DH |
661 | } |
662 | ||
0fafdc9f | 663 | ret = 0; |
e8d6c554 | 664 | error: |
e8d6c554 DH |
665 | _leave(" = %d [%hd]", ret, fl->fl_type); |
666 | return ret; | |
667 | } | |
668 | ||
669 | /* | |
670 | * manage POSIX locks on a file | |
671 | */ | |
672 | int afs_lock(struct file *file, int cmd, struct file_lock *fl) | |
673 | { | |
0fafdc9f | 674 | struct afs_vnode *vnode = AFS_FS_I(locks_inode(file)); |
d4696601 DH |
675 | enum afs_flock_operation op; |
676 | int ret; | |
e8d6c554 | 677 | |
3b6492df | 678 | _enter("{%llx:%llu},%d,{t=%x,fl=%x,r=%Ld:%Ld}", |
e8d6c554 DH |
679 | vnode->fid.vid, vnode->fid.vnode, cmd, |
680 | fl->fl_type, fl->fl_flags, | |
681 | (long long) fl->fl_start, (long long) fl->fl_end); | |
682 | ||
683 | /* AFS doesn't support mandatory locks */ | |
fc5846e5 | 684 | if (__mandatory_lock(&vnode->vfs_inode) && fl->fl_type != F_UNLCK) |
e8d6c554 DH |
685 | return -ENOLCK; |
686 | ||
687 | if (IS_GETLK(cmd)) | |
688 | return afs_do_getlk(file, fl); | |
d4696601 DH |
689 | |
690 | fl->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id); | |
691 | trace_afs_flock_op(vnode, fl, afs_flock_op_lock); | |
692 | ||
e8d6c554 | 693 | if (fl->fl_type == F_UNLCK) |
d4696601 DH |
694 | ret = afs_do_unlk(file, fl); |
695 | else | |
696 | ret = afs_do_setlk(file, fl); | |
697 | ||
698 | switch (ret) { | |
699 | case 0: op = afs_flock_op_return_ok; break; | |
700 | case -EAGAIN: op = afs_flock_op_return_eagain; break; | |
701 | case -EDEADLK: op = afs_flock_op_return_edeadlk; break; | |
702 | default: op = afs_flock_op_return_error; break; | |
703 | } | |
704 | trace_afs_flock_op(vnode, fl, op); | |
705 | return ret; | |
e8d6c554 DH |
706 | } |
707 | ||
708 | /* | |
709 | * manage FLOCK locks on a file | |
710 | */ | |
711 | int afs_flock(struct file *file, int cmd, struct file_lock *fl) | |
712 | { | |
0fafdc9f | 713 | struct afs_vnode *vnode = AFS_FS_I(locks_inode(file)); |
d4696601 DH |
714 | enum afs_flock_operation op; |
715 | int ret; | |
e8d6c554 | 716 | |
3b6492df | 717 | _enter("{%llx:%llu},%d,{t=%x,fl=%x}", |
e8d6c554 DH |
718 | vnode->fid.vid, vnode->fid.vnode, cmd, |
719 | fl->fl_type, fl->fl_flags); | |
720 | ||
721 | /* | |
722 | * No BSD flocks over NFS allowed. | |
723 | * Note: we could try to fake a POSIX lock request here by | |
724 | * using ((u32) filp | 0x80000000) or some such as the pid. | |
725 | * Not sure whether that would be unique, though, or whether | |
726 | * that would break in other places. | |
727 | */ | |
728 | if (!(fl->fl_flags & FL_FLOCK)) | |
729 | return -ENOLCK; | |
730 | ||
d4696601 DH |
731 | fl->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id); |
732 | trace_afs_flock_op(vnode, fl, afs_flock_op_flock); | |
733 | ||
e8d6c554 | 734 | /* we're simulating flock() locks using posix locks on the server */ |
e8d6c554 | 735 | if (fl->fl_type == F_UNLCK) |
d4696601 DH |
736 | ret = afs_do_unlk(file, fl); |
737 | else | |
738 | ret = afs_do_setlk(file, fl); | |
739 | ||
740 | switch (ret) { | |
741 | case 0: op = afs_flock_op_return_ok; break; | |
742 | case -EAGAIN: op = afs_flock_op_return_eagain; break; | |
743 | case -EDEADLK: op = afs_flock_op_return_edeadlk; break; | |
744 | default: op = afs_flock_op_return_error; break; | |
745 | } | |
746 | trace_afs_flock_op(vnode, fl, op); | |
747 | return ret; | |
e8d6c554 DH |
748 | } |
749 | ||
750 | /* | |
751 | * the POSIX lock management core VFS code copies the lock record and adds the | |
752 | * copy into its own list, so we need to add that copy to the vnode's lock | |
753 | * queue in the same place as the original (which will be deleted shortly | |
754 | * after) | |
755 | */ | |
756 | static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl) | |
757 | { | |
0fafdc9f DH |
758 | struct afs_vnode *vnode = AFS_FS_I(locks_inode(fl->fl_file)); |
759 | ||
e8d6c554 DH |
760 | _enter(""); |
761 | ||
d4696601 DH |
762 | new->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id); |
763 | ||
0fafdc9f | 764 | spin_lock(&vnode->lock); |
d4696601 | 765 | trace_afs_flock_op(vnode, new, afs_flock_op_copy_lock); |
e8d6c554 | 766 | list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link); |
0fafdc9f | 767 | spin_unlock(&vnode->lock); |
e8d6c554 DH |
768 | } |
769 | ||
770 | /* | |
771 | * need to remove this lock from the vnode queue when it's removed from the | |
772 | * VFS's list | |
773 | */ | |
774 | static void afs_fl_release_private(struct file_lock *fl) | |
775 | { | |
0fafdc9f DH |
776 | struct afs_vnode *vnode = AFS_FS_I(locks_inode(fl->fl_file)); |
777 | ||
e8d6c554 DH |
778 | _enter(""); |
779 | ||
0fafdc9f | 780 | spin_lock(&vnode->lock); |
4be5975a | 781 | |
d4696601 | 782 | trace_afs_flock_op(vnode, fl, afs_flock_op_release_lock); |
4be5975a DH |
783 | list_del_init(&fl->fl_u.afs.link); |
784 | if (list_empty(&vnode->granted_locks)) | |
785 | afs_defer_unlock(vnode); | |
786 | ||
0fafdc9f DH |
787 | _debug("state %u for %p", vnode->lock_state, vnode); |
788 | spin_unlock(&vnode->lock); | |
e8d6c554 | 789 | } |