diff options
author | David Howells <dhowells@redhat.com> | 2007-07-16 02:40:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:43 -0400 |
commit | e8d6c554126b830217c5e9f549e0e21f865a0a8a (patch) | |
tree | c43219c6ef4e6a9b4f0ac46d6bd8b675dc249a8b /fs/afs/flock.c | |
parent | b0fed3140f57c435d2783b698c5090f325c22bad (diff) |
AFS: implement file locking
Implement file locking for AFS.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/afs/flock.c')
-rw-r--r-- | fs/afs/flock.c | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/fs/afs/flock.c b/fs/afs/flock.c new file mode 100644 index 000000000000..8f07f8d1bfa9 --- /dev/null +++ b/fs/afs/flock.c | |||
@@ -0,0 +1,558 @@ | |||
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 | |||
12 | #include <linux/smp_lock.h> | ||
13 | #include "internal.h" | ||
14 | |||
15 | #define AFS_LOCK_GRANTED 0 | ||
16 | #define AFS_LOCK_PENDING 1 | ||
17 | |||
18 | static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl); | ||
19 | static void afs_fl_release_private(struct file_lock *fl); | ||
20 | |||
21 | static struct workqueue_struct *afs_lock_manager; | ||
22 | |||
23 | static struct file_lock_operations afs_lock_ops = { | ||
24 | .fl_copy_lock = afs_fl_copy_lock, | ||
25 | .fl_release_private = afs_fl_release_private, | ||
26 | }; | ||
27 | |||
28 | /* | ||
29 | * initialise the lock manager thread if it isn't already running | ||
30 | */ | ||
31 | static int afs_init_lock_manager(void) | ||
32 | { | ||
33 | if (!afs_lock_manager) { | ||
34 | afs_lock_manager = create_singlethread_workqueue("kafs_lockd"); | ||
35 | if (!afs_lock_manager) | ||
36 | return -ENOMEM; | ||
37 | } | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | /* | ||
42 | * destroy the lock manager thread if it's running | ||
43 | */ | ||
44 | void __exit afs_kill_lock_manager(void) | ||
45 | { | ||
46 | if (afs_lock_manager) | ||
47 | destroy_workqueue(afs_lock_manager); | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * if the callback is broken on this vnode, then the lock may now be available | ||
52 | */ | ||
53 | void afs_lock_may_be_available(struct afs_vnode *vnode) | ||
54 | { | ||
55 | _enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode); | ||
56 | |||
57 | queue_delayed_work(afs_lock_manager, &vnode->lock_work, 0); | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * the lock will time out in 5 minutes unless we extend it, so schedule | ||
62 | * extension in a bit less than that time | ||
63 | */ | ||
64 | static void afs_schedule_lock_extension(struct afs_vnode *vnode) | ||
65 | { | ||
66 | queue_delayed_work(afs_lock_manager, &vnode->lock_work, | ||
67 | AFS_LOCKWAIT * HZ / 2); | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * do work for a lock, including: | ||
72 | * - probing for a lock we're waiting on but didn't get immediately | ||
73 | * - extending a lock that's close to timing out | ||
74 | */ | ||
75 | void afs_lock_work(struct work_struct *work) | ||
76 | { | ||
77 | struct afs_vnode *vnode = | ||
78 | container_of(work, struct afs_vnode, lock_work.work); | ||
79 | struct file_lock *fl; | ||
80 | afs_lock_type_t type; | ||
81 | struct key *key; | ||
82 | int ret; | ||
83 | |||
84 | _enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode); | ||
85 | |||
86 | spin_lock(&vnode->lock); | ||
87 | |||
88 | if (test_bit(AFS_VNODE_UNLOCKING, &vnode->flags)) { | ||
89 | _debug("unlock"); | ||
90 | spin_unlock(&vnode->lock); | ||
91 | |||
92 | /* attempt to release the server lock; if it fails, we just | ||
93 | * wait 5 minutes and it'll time out anyway */ | ||
94 | ret = afs_vnode_release_lock(vnode, vnode->unlock_key); | ||
95 | if (ret < 0) | ||
96 | printk(KERN_WARNING "AFS:" | ||
97 | " Failed to release lock on {%x:%x} error %d\n", | ||
98 | vnode->fid.vid, vnode->fid.vnode, ret); | ||
99 | |||
100 | spin_lock(&vnode->lock); | ||
101 | key_put(vnode->unlock_key); | ||
102 | vnode->unlock_key = NULL; | ||
103 | clear_bit(AFS_VNODE_UNLOCKING, &vnode->flags); | ||
104 | } | ||
105 | |||
106 | /* if we've got a lock, then it must be time to extend that lock as AFS | ||
107 | * locks time out after 5 minutes */ | ||
108 | if (!list_empty(&vnode->granted_locks)) { | ||
109 | _debug("extend"); | ||
110 | |||
111 | if (test_and_set_bit(AFS_VNODE_LOCKING, &vnode->flags)) | ||
112 | BUG(); | ||
113 | fl = list_entry(vnode->granted_locks.next, | ||
114 | struct file_lock, fl_u.afs.link); | ||
115 | key = key_get(fl->fl_file->private_data); | ||
116 | spin_unlock(&vnode->lock); | ||
117 | |||
118 | ret = afs_vnode_extend_lock(vnode, key); | ||
119 | clear_bit(AFS_VNODE_LOCKING, &vnode->flags); | ||
120 | key_put(key); | ||
121 | switch (ret) { | ||
122 | case 0: | ||
123 | afs_schedule_lock_extension(vnode); | ||
124 | break; | ||
125 | default: | ||
126 | /* ummm... we failed to extend the lock - retry | ||
127 | * extension shortly */ | ||
128 | printk(KERN_WARNING "AFS:" | ||
129 | " Failed to extend lock on {%x:%x} error %d\n", | ||
130 | vnode->fid.vid, vnode->fid.vnode, ret); | ||
131 | queue_delayed_work(afs_lock_manager, &vnode->lock_work, | ||
132 | HZ * 10); | ||
133 | break; | ||
134 | } | ||
135 | _leave(" [extend]"); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | /* if we don't have a granted lock, then we must've been called back by | ||
140 | * the server, and so if might be possible to get a lock we're | ||
141 | * currently waiting for */ | ||
142 | if (!list_empty(&vnode->pending_locks)) { | ||
143 | _debug("get"); | ||
144 | |||
145 | if (test_and_set_bit(AFS_VNODE_LOCKING, &vnode->flags)) | ||
146 | BUG(); | ||
147 | fl = list_entry(vnode->pending_locks.next, | ||
148 | struct file_lock, fl_u.afs.link); | ||
149 | key = key_get(fl->fl_file->private_data); | ||
150 | type = (fl->fl_type == F_RDLCK) ? | ||
151 | AFS_LOCK_READ : AFS_LOCK_WRITE; | ||
152 | spin_unlock(&vnode->lock); | ||
153 | |||
154 | ret = afs_vnode_set_lock(vnode, key, type); | ||
155 | clear_bit(AFS_VNODE_LOCKING, &vnode->flags); | ||
156 | switch (ret) { | ||
157 | case -EWOULDBLOCK: | ||
158 | _debug("blocked"); | ||
159 | break; | ||
160 | case 0: | ||
161 | _debug("acquired"); | ||
162 | if (type == AFS_LOCK_READ) | ||
163 | set_bit(AFS_VNODE_READLOCKED, &vnode->flags); | ||
164 | else | ||
165 | set_bit(AFS_VNODE_WRITELOCKED, &vnode->flags); | ||
166 | ret = AFS_LOCK_GRANTED; | ||
167 | default: | ||
168 | spin_lock(&vnode->lock); | ||
169 | /* the pending lock may have been withdrawn due to a | ||
170 | * signal */ | ||
171 | if (list_entry(vnode->pending_locks.next, | ||
172 | struct file_lock, fl_u.afs.link) == fl) { | ||
173 | fl->fl_u.afs.state = ret; | ||
174 | if (ret == AFS_LOCK_GRANTED) | ||
175 | list_move_tail(&fl->fl_u.afs.link, | ||
176 | &vnode->granted_locks); | ||
177 | else | ||
178 | list_del_init(&fl->fl_u.afs.link); | ||
179 | wake_up(&fl->fl_wait); | ||
180 | spin_unlock(&vnode->lock); | ||
181 | } else { | ||
182 | _debug("withdrawn"); | ||
183 | clear_bit(AFS_VNODE_READLOCKED, &vnode->flags); | ||
184 | clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags); | ||
185 | spin_unlock(&vnode->lock); | ||
186 | afs_vnode_release_lock(vnode, key); | ||
187 | if (!list_empty(&vnode->pending_locks)) | ||
188 | afs_lock_may_be_available(vnode); | ||
189 | } | ||
190 | break; | ||
191 | } | ||
192 | key_put(key); | ||
193 | _leave(" [pend]"); | ||
194 | return; | ||
195 | } | ||
196 | |||
197 | /* looks like the lock request was withdrawn on a signal */ | ||
198 | spin_unlock(&vnode->lock); | ||
199 | _leave(" [no locks]"); | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * pass responsibility for the unlocking of a vnode on the server to the | ||
204 | * manager thread, lest a pending signal in the calling thread interrupt | ||
205 | * AF_RXRPC | ||
206 | * - the caller must hold the vnode lock | ||
207 | */ | ||
208 | static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key) | ||
209 | { | ||
210 | cancel_delayed_work(&vnode->lock_work); | ||
211 | if (!test_and_clear_bit(AFS_VNODE_READLOCKED, &vnode->flags) && | ||
212 | !test_and_clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags)) | ||
213 | BUG(); | ||
214 | if (test_and_set_bit(AFS_VNODE_UNLOCKING, &vnode->flags)) | ||
215 | BUG(); | ||
216 | vnode->unlock_key = key_get(key); | ||
217 | afs_lock_may_be_available(vnode); | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * request a lock on a file on the server | ||
222 | */ | ||
223 | static int afs_do_setlk(struct file *file, struct file_lock *fl) | ||
224 | { | ||
225 | struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); | ||
226 | afs_lock_type_t type; | ||
227 | struct key *key = file->private_data; | ||
228 | int ret; | ||
229 | |||
230 | _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); | ||
231 | |||
232 | /* only whole-file locks are supported */ | ||
233 | if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX) | ||
234 | return -EINVAL; | ||
235 | |||
236 | ret = afs_init_lock_manager(); | ||
237 | if (ret < 0) | ||
238 | return ret; | ||
239 | |||
240 | fl->fl_ops = &afs_lock_ops; | ||
241 | INIT_LIST_HEAD(&fl->fl_u.afs.link); | ||
242 | fl->fl_u.afs.state = AFS_LOCK_PENDING; | ||
243 | |||
244 | type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; | ||
245 | |||
246 | lock_kernel(); | ||
247 | |||
248 | /* make sure we've got a callback on this file and that our view of the | ||
249 | * data version is up to date */ | ||
250 | ret = afs_vnode_fetch_status(vnode, NULL, key); | ||
251 | if (ret < 0) | ||
252 | goto error; | ||
253 | |||
254 | if (vnode->status.lock_count != 0 && !(fl->fl_flags & FL_SLEEP)) { | ||
255 | ret = -EAGAIN; | ||
256 | goto error; | ||
257 | } | ||
258 | |||
259 | spin_lock(&vnode->lock); | ||
260 | |||
261 | if (list_empty(&vnode->pending_locks)) { | ||
262 | /* if there's no-one else with a lock on this vnode, then we | ||
263 | * need to ask the server for a lock */ | ||
264 | if (list_empty(&vnode->granted_locks)) { | ||
265 | _debug("not locked"); | ||
266 | ASSERTCMP(vnode->flags & | ||
267 | ((1 << AFS_VNODE_LOCKING) | | ||
268 | (1 << AFS_VNODE_READLOCKED) | | ||
269 | (1 << AFS_VNODE_WRITELOCKED)), ==, 0); | ||
270 | list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); | ||
271 | set_bit(AFS_VNODE_LOCKING, &vnode->flags); | ||
272 | spin_unlock(&vnode->lock); | ||
273 | |||
274 | ret = afs_vnode_set_lock(vnode, key, type); | ||
275 | clear_bit(AFS_VNODE_LOCKING, &vnode->flags); | ||
276 | switch (ret) { | ||
277 | case 0: | ||
278 | goto acquired_server_lock; | ||
279 | case -EWOULDBLOCK: | ||
280 | spin_lock(&vnode->lock); | ||
281 | ASSERT(list_empty(&vnode->granted_locks)); | ||
282 | ASSERTCMP(vnode->pending_locks.next, ==, | ||
283 | &fl->fl_u.afs.link); | ||
284 | goto wait; | ||
285 | default: | ||
286 | spin_lock(&vnode->lock); | ||
287 | list_del_init(&fl->fl_u.afs.link); | ||
288 | spin_unlock(&vnode->lock); | ||
289 | goto error; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /* if we've already got a readlock on the server and no waiting | ||
294 | * writelocks, then we might be able to instantly grant another | ||
295 | * readlock */ | ||
296 | if (type == AFS_LOCK_READ && | ||
297 | vnode->flags & (1 << AFS_VNODE_READLOCKED)) { | ||
298 | _debug("instant readlock"); | ||
299 | ASSERTCMP(vnode->flags & | ||
300 | ((1 << AFS_VNODE_LOCKING) | | ||
301 | (1 << AFS_VNODE_WRITELOCKED)), ==, 0); | ||
302 | ASSERT(!list_empty(&vnode->granted_locks)); | ||
303 | goto sharing_existing_lock; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | /* otherwise, we need to wait for a local lock to become available */ | ||
308 | _debug("wait local"); | ||
309 | list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); | ||
310 | wait: | ||
311 | if (!(fl->fl_flags & FL_SLEEP)) { | ||
312 | _debug("noblock"); | ||
313 | ret = -EAGAIN; | ||
314 | goto abort_attempt; | ||
315 | } | ||
316 | spin_unlock(&vnode->lock); | ||
317 | |||
318 | /* now we need to sleep and wait for the lock manager thread to get the | ||
319 | * lock from the server */ | ||
320 | _debug("sleep"); | ||
321 | ret = wait_event_interruptible(fl->fl_wait, | ||
322 | fl->fl_u.afs.state <= AFS_LOCK_GRANTED); | ||
323 | if (fl->fl_u.afs.state <= AFS_LOCK_GRANTED) { | ||
324 | ret = fl->fl_u.afs.state; | ||
325 | if (ret < 0) | ||
326 | goto error; | ||
327 | spin_lock(&vnode->lock); | ||
328 | goto given_lock; | ||
329 | } | ||
330 | |||
331 | /* we were interrupted, but someone may still be in the throes of | ||
332 | * giving us the lock */ | ||
333 | _debug("intr"); | ||
334 | ASSERTCMP(ret, ==, -ERESTARTSYS); | ||
335 | |||
336 | spin_lock(&vnode->lock); | ||
337 | if (fl->fl_u.afs.state <= AFS_LOCK_GRANTED) { | ||
338 | ret = fl->fl_u.afs.state; | ||
339 | if (ret < 0) { | ||
340 | spin_unlock(&vnode->lock); | ||
341 | goto error; | ||
342 | } | ||
343 | goto given_lock; | ||
344 | } | ||
345 | |||
346 | abort_attempt: | ||
347 | /* we aren't going to get the lock, either because we're unwilling to | ||
348 | * wait, or because some signal happened */ | ||
349 | _debug("abort"); | ||
350 | if (list_empty(&vnode->granted_locks) && | ||
351 | vnode->pending_locks.next == &fl->fl_u.afs.link) { | ||
352 | if (vnode->pending_locks.prev != &fl->fl_u.afs.link) { | ||
353 | /* kick the next pending lock into having a go */ | ||
354 | list_del_init(&fl->fl_u.afs.link); | ||
355 | afs_lock_may_be_available(vnode); | ||
356 | } | ||
357 | } else { | ||
358 | list_del_init(&fl->fl_u.afs.link); | ||
359 | } | ||
360 | spin_unlock(&vnode->lock); | ||
361 | goto error; | ||
362 | |||
363 | acquired_server_lock: | ||
364 | /* we've acquired a server lock, but it needs to be renewed after 5 | ||
365 | * mins */ | ||
366 | spin_lock(&vnode->lock); | ||
367 | afs_schedule_lock_extension(vnode); | ||
368 | if (type == AFS_LOCK_READ) | ||
369 | set_bit(AFS_VNODE_READLOCKED, &vnode->flags); | ||
370 | else | ||
371 | set_bit(AFS_VNODE_WRITELOCKED, &vnode->flags); | ||
372 | sharing_existing_lock: | ||
373 | /* the lock has been granted as far as we're concerned... */ | ||
374 | fl->fl_u.afs.state = AFS_LOCK_GRANTED; | ||
375 | list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); | ||
376 | given_lock: | ||
377 | /* ... but we do still need to get the VFS's blessing */ | ||
378 | ASSERT(!(vnode->flags & (1 << AFS_VNODE_LOCKING))); | ||
379 | ASSERT((vnode->flags & ((1 << AFS_VNODE_READLOCKED) | | ||
380 | (1 << AFS_VNODE_WRITELOCKED))) != 0); | ||
381 | ret = posix_lock_file(file, fl, NULL); | ||
382 | if (ret < 0) | ||
383 | goto vfs_rejected_lock; | ||
384 | spin_unlock(&vnode->lock); | ||
385 | |||
386 | /* again, make sure we've got a callback on this file and, again, make | ||
387 | * sure that our view of the data version is up to date (we ignore | ||
388 | * errors incurred here and deal with the consequences elsewhere) */ | ||
389 | afs_vnode_fetch_status(vnode, NULL, key); | ||
390 | |||
391 | error: | ||
392 | unlock_kernel(); | ||
393 | _leave(" = %d", ret); | ||
394 | return ret; | ||
395 | |||
396 | vfs_rejected_lock: | ||
397 | /* the VFS rejected the lock we just obtained, so we have to discard | ||
398 | * what we just got */ | ||
399 | _debug("vfs refused %d", ret); | ||
400 | list_del_init(&fl->fl_u.afs.link); | ||
401 | if (list_empty(&vnode->granted_locks)) | ||
402 | afs_defer_unlock(vnode, key); | ||
403 | spin_unlock(&vnode->lock); | ||
404 | goto abort_attempt; | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | * unlock on a file on the server | ||
409 | */ | ||
410 | static int afs_do_unlk(struct file *file, struct file_lock *fl) | ||
411 | { | ||
412 | struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); | ||
413 | struct key *key = file->private_data; | ||
414 | int ret; | ||
415 | |||
416 | _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); | ||
417 | |||
418 | /* only whole-file unlocks are supported */ | ||
419 | if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX) | ||
420 | return -EINVAL; | ||
421 | |||
422 | fl->fl_ops = &afs_lock_ops; | ||
423 | INIT_LIST_HEAD(&fl->fl_u.afs.link); | ||
424 | fl->fl_u.afs.state = AFS_LOCK_PENDING; | ||
425 | |||
426 | spin_lock(&vnode->lock); | ||
427 | ret = posix_lock_file(file, fl, NULL); | ||
428 | if (ret < 0) { | ||
429 | spin_unlock(&vnode->lock); | ||
430 | _leave(" = %d [vfs]", ret); | ||
431 | return ret; | ||
432 | } | ||
433 | |||
434 | /* discard the server lock only if all granted locks are gone */ | ||
435 | if (list_empty(&vnode->granted_locks)) | ||
436 | afs_defer_unlock(vnode, key); | ||
437 | spin_unlock(&vnode->lock); | ||
438 | _leave(" = 0"); | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | /* | ||
443 | * return information about a lock we currently hold, if indeed we hold one | ||
444 | */ | ||
445 | static int afs_do_getlk(struct file *file, struct file_lock *fl) | ||
446 | { | ||
447 | struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); | ||
448 | struct key *key = file->private_data; | ||
449 | int ret, lock_count; | ||
450 | |||
451 | _enter(""); | ||
452 | |||
453 | fl->fl_type = F_UNLCK; | ||
454 | |||
455 | mutex_lock(&vnode->vfs_inode.i_mutex); | ||
456 | |||
457 | /* check local lock records first */ | ||
458 | ret = 0; | ||
459 | if (posix_test_lock(file, fl) == 0) { | ||
460 | /* no local locks; consult the server */ | ||
461 | ret = afs_vnode_fetch_status(vnode, NULL, key); | ||
462 | if (ret < 0) | ||
463 | goto error; | ||
464 | lock_count = vnode->status.lock_count; | ||
465 | if (lock_count) { | ||
466 | if (lock_count > 0) | ||
467 | fl->fl_type = F_RDLCK; | ||
468 | else | ||
469 | fl->fl_type = F_WRLCK; | ||
470 | fl->fl_start = 0; | ||
471 | fl->fl_end = OFFSET_MAX; | ||
472 | } | ||
473 | } | ||
474 | |||
475 | error: | ||
476 | mutex_unlock(&vnode->vfs_inode.i_mutex); | ||
477 | _leave(" = %d [%hd]", ret, fl->fl_type); | ||
478 | return ret; | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * manage POSIX locks on a file | ||
483 | */ | ||
484 | int afs_lock(struct file *file, int cmd, struct file_lock *fl) | ||
485 | { | ||
486 | struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode); | ||
487 | |||
488 | _enter("{%x:%u},%d,{t=%x,fl=%x,r=%Ld:%Ld}", | ||
489 | vnode->fid.vid, vnode->fid.vnode, cmd, | ||
490 | fl->fl_type, fl->fl_flags, | ||
491 | (long long) fl->fl_start, (long long) fl->fl_end); | ||
492 | |||
493 | /* AFS doesn't support mandatory locks */ | ||
494 | if ((vnode->vfs_inode.i_mode & (S_ISGID | S_IXGRP)) == S_ISGID && | ||
495 | fl->fl_type != F_UNLCK) | ||
496 | return -ENOLCK; | ||
497 | |||
498 | if (IS_GETLK(cmd)) | ||
499 | return afs_do_getlk(file, fl); | ||
500 | if (fl->fl_type == F_UNLCK) | ||
501 | return afs_do_unlk(file, fl); | ||
502 | return afs_do_setlk(file, fl); | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * manage FLOCK locks on a file | ||
507 | */ | ||
508 | int afs_flock(struct file *file, int cmd, struct file_lock *fl) | ||
509 | { | ||
510 | struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode); | ||
511 | |||
512 | _enter("{%x:%u},%d,{t=%x,fl=%x}", | ||
513 | vnode->fid.vid, vnode->fid.vnode, cmd, | ||
514 | fl->fl_type, fl->fl_flags); | ||
515 | |||
516 | /* | ||
517 | * No BSD flocks over NFS allowed. | ||
518 | * Note: we could try to fake a POSIX lock request here by | ||
519 | * using ((u32) filp | 0x80000000) or some such as the pid. | ||
520 | * Not sure whether that would be unique, though, or whether | ||
521 | * that would break in other places. | ||
522 | */ | ||
523 | if (!(fl->fl_flags & FL_FLOCK)) | ||
524 | return -ENOLCK; | ||
525 | |||
526 | /* we're simulating flock() locks using posix locks on the server */ | ||
527 | fl->fl_owner = (fl_owner_t) file; | ||
528 | fl->fl_start = 0; | ||
529 | fl->fl_end = OFFSET_MAX; | ||
530 | |||
531 | if (fl->fl_type == F_UNLCK) | ||
532 | return afs_do_unlk(file, fl); | ||
533 | return afs_do_setlk(file, fl); | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | * the POSIX lock management core VFS code copies the lock record and adds the | ||
538 | * copy into its own list, so we need to add that copy to the vnode's lock | ||
539 | * queue in the same place as the original (which will be deleted shortly | ||
540 | * after) | ||
541 | */ | ||
542 | static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl) | ||
543 | { | ||
544 | _enter(""); | ||
545 | |||
546 | list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link); | ||
547 | } | ||
548 | |||
549 | /* | ||
550 | * need to remove this lock from the vnode queue when it's removed from the | ||
551 | * VFS's list | ||
552 | */ | ||
553 | static void afs_fl_release_private(struct file_lock *fl) | ||
554 | { | ||
555 | _enter(""); | ||
556 | |||
557 | list_del_init(&fl->fl_u.afs.link); | ||
558 | } | ||