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 | |
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>
-rw-r--r-- | fs/afs/Makefile | 1 | ||||
-rw-r--r-- | fs/afs/afs.h | 8 | ||||
-rw-r--r-- | fs/afs/afs_fs.h | 3 | ||||
-rw-r--r-- | fs/afs/callback.c | 3 | ||||
-rw-r--r-- | fs/afs/dir.c | 1 | ||||
-rw-r--r-- | fs/afs/file.c | 2 | ||||
-rw-r--r-- | fs/afs/flock.c | 558 | ||||
-rw-r--r-- | fs/afs/fsclient.c | 155 | ||||
-rw-r--r-- | fs/afs/internal.h | 30 | ||||
-rw-r--r-- | fs/afs/main.c | 1 | ||||
-rw-r--r-- | fs/afs/misc.c | 1 | ||||
-rw-r--r-- | fs/afs/super.c | 3 | ||||
-rw-r--r-- | fs/afs/vnode.c | 130 | ||||
-rw-r--r-- | include/linux/fs.h | 4 |
14 files changed, 885 insertions, 15 deletions
diff --git a/fs/afs/Makefile b/fs/afs/Makefile index 73ce561f3ea0..a66671082cfb 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile | |||
@@ -8,6 +8,7 @@ kafs-objs := \ | |||
8 | cmservice.o \ | 8 | cmservice.o \ |
9 | dir.o \ | 9 | dir.o \ |
10 | file.o \ | 10 | file.o \ |
11 | flock.o \ | ||
11 | fsclient.o \ | 12 | fsclient.o \ |
12 | inode.o \ | 13 | inode.o \ |
13 | main.o \ | 14 | main.o \ |
diff --git a/fs/afs/afs.h b/fs/afs/afs.h index 245257948140..c548aa346f0d 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h | |||
@@ -37,6 +37,13 @@ typedef enum { | |||
37 | AFS_FTYPE_SYMLINK = 3, | 37 | AFS_FTYPE_SYMLINK = 3, |
38 | } afs_file_type_t; | 38 | } afs_file_type_t; |
39 | 39 | ||
40 | typedef enum { | ||
41 | AFS_LOCK_READ = 0, /* read lock request */ | ||
42 | AFS_LOCK_WRITE = 1, /* write lock request */ | ||
43 | } afs_lock_type_t; | ||
44 | |||
45 | #define AFS_LOCKWAIT (5 * 60) /* time until a lock times out (seconds) */ | ||
46 | |||
40 | /* | 47 | /* |
41 | * AFS file identifier | 48 | * AFS file identifier |
42 | */ | 49 | */ |
@@ -120,6 +127,7 @@ struct afs_file_status { | |||
120 | struct afs_fid parent; /* parent dir ID for non-dirs only */ | 127 | struct afs_fid parent; /* parent dir ID for non-dirs only */ |
121 | time_t mtime_client; /* last time client changed data */ | 128 | time_t mtime_client; /* last time client changed data */ |
122 | time_t mtime_server; /* last time server changed data */ | 129 | time_t mtime_server; /* last time server changed data */ |
130 | s32 lock_count; /* file lock count (0=UNLK -1=WRLCK +ve=#RDLCK */ | ||
123 | }; | 131 | }; |
124 | 132 | ||
125 | /* | 133 | /* |
diff --git a/fs/afs/afs_fs.h b/fs/afs/afs_fs.h index a18c374ebe08..eb647323d8f0 100644 --- a/fs/afs/afs_fs.h +++ b/fs/afs/afs_fs.h | |||
@@ -31,6 +31,9 @@ enum AFS_FS_Operations { | |||
31 | FSGETVOLUMEINFO = 148, /* AFS Get information about a volume */ | 31 | FSGETVOLUMEINFO = 148, /* AFS Get information about a volume */ |
32 | FSGETVOLUMESTATUS = 149, /* AFS Get volume status information */ | 32 | FSGETVOLUMESTATUS = 149, /* AFS Get volume status information */ |
33 | FSGETROOTVOLUME = 151, /* AFS Get root volume name */ | 33 | FSGETROOTVOLUME = 151, /* AFS Get root volume name */ |
34 | FSSETLOCK = 156, /* AFS Request a file lock */ | ||
35 | FSEXTENDLOCK = 157, /* AFS Extend a file lock */ | ||
36 | FSRELEASELOCK = 158, /* AFS Release a file lock */ | ||
34 | FSLOOKUP = 161, /* AFS lookup file in directory */ | 37 | FSLOOKUP = 161, /* AFS lookup file in directory */ |
35 | FSFETCHDATA64 = 65537, /* AFS Fetch file data */ | 38 | FSFETCHDATA64 = 65537, /* AFS Fetch file data */ |
36 | FSSTOREDATA64 = 65538, /* AFS Store file data */ | 39 | FSSTOREDATA64 = 65538, /* AFS Store file data */ |
diff --git a/fs/afs/callback.c b/fs/afs/callback.c index bacf518c6fa8..b8243945818d 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c | |||
@@ -125,6 +125,9 @@ static void afs_break_callback(struct afs_server *server, | |||
125 | spin_unlock(&server->cb_lock); | 125 | spin_unlock(&server->cb_lock); |
126 | 126 | ||
127 | queue_work(afs_callback_update_worker, &vnode->cb_broken_work); | 127 | queue_work(afs_callback_update_worker, &vnode->cb_broken_work); |
128 | if (list_empty(&vnode->granted_locks) && | ||
129 | !list_empty(&vnode->pending_locks)) | ||
130 | afs_lock_may_be_available(vnode); | ||
128 | spin_unlock(&vnode->lock); | 131 | spin_unlock(&vnode->lock); |
129 | } | 132 | } |
130 | } | 133 | } |
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 546c59522eb1..33fe39ad4e03 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -44,6 +44,7 @@ const struct file_operations afs_dir_file_operations = { | |||
44 | .open = afs_dir_open, | 44 | .open = afs_dir_open, |
45 | .release = afs_release, | 45 | .release = afs_release, |
46 | .readdir = afs_readdir, | 46 | .readdir = afs_readdir, |
47 | .lock = afs_lock, | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | const struct inode_operations afs_dir_inode_operations = { | 50 | const struct inode_operations afs_dir_inode_operations = { |
diff --git a/fs/afs/file.c b/fs/afs/file.c index aede7eb66dd4..525f7c56e068 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c | |||
@@ -34,6 +34,8 @@ const struct file_operations afs_file_operations = { | |||
34 | .mmap = generic_file_readonly_mmap, | 34 | .mmap = generic_file_readonly_mmap, |
35 | .splice_read = generic_file_splice_read, | 35 | .splice_read = generic_file_splice_read, |
36 | .fsync = afs_fsync, | 36 | .fsync = afs_fsync, |
37 | .lock = afs_lock, | ||
38 | .flock = afs_flock, | ||
37 | }; | 39 | }; |
38 | 40 | ||
39 | const struct inode_operations afs_file_inode_operations = { | 41 | const struct inode_operations afs_file_inode_operations = { |
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 | } | ||
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 5dff1308b6f0..023b95b0d9d7 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c | |||
@@ -67,7 +67,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, | |||
67 | EXTRACT(status->group); | 67 | EXTRACT(status->group); |
68 | bp++; /* sync counter */ | 68 | bp++; /* sync counter */ |
69 | data_version |= (u64) ntohl(*bp++) << 32; | 69 | data_version |= (u64) ntohl(*bp++) << 32; |
70 | bp++; /* lock count */ | 70 | EXTRACT(status->lock_count); |
71 | size |= (u64) ntohl(*bp++) << 32; | 71 | size |= (u64) ntohl(*bp++) << 32; |
72 | bp++; /* spare 4 */ | 72 | bp++; /* spare 4 */ |
73 | *_bp = bp; | 73 | *_bp = bp; |
@@ -1748,3 +1748,156 @@ int afs_fs_get_volume_status(struct afs_server *server, | |||
1748 | 1748 | ||
1749 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | 1749 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); |
1750 | } | 1750 | } |
1751 | |||
1752 | /* | ||
1753 | * deliver reply data to an FS.SetLock, FS.ExtendLock or FS.ReleaseLock | ||
1754 | */ | ||
1755 | static int afs_deliver_fs_xxxx_lock(struct afs_call *call, | ||
1756 | struct sk_buff *skb, bool last) | ||
1757 | { | ||
1758 | const __be32 *bp; | ||
1759 | |||
1760 | _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); | ||
1761 | |||
1762 | afs_transfer_reply(call, skb); | ||
1763 | if (!last) | ||
1764 | return 0; | ||
1765 | |||
1766 | if (call->reply_size != call->reply_max) | ||
1767 | return -EBADMSG; | ||
1768 | |||
1769 | /* unmarshall the reply once we've received all of it */ | ||
1770 | bp = call->buffer; | ||
1771 | /* xdr_decode_AFSVolSync(&bp, call->replyX); */ | ||
1772 | |||
1773 | _leave(" = 0 [done]"); | ||
1774 | return 0; | ||
1775 | } | ||
1776 | |||
1777 | /* | ||
1778 | * FS.SetLock operation type | ||
1779 | */ | ||
1780 | static const struct afs_call_type afs_RXFSSetLock = { | ||
1781 | .name = "FS.SetLock", | ||
1782 | .deliver = afs_deliver_fs_xxxx_lock, | ||
1783 | .abort_to_error = afs_abort_to_error, | ||
1784 | .destructor = afs_flat_call_destructor, | ||
1785 | }; | ||
1786 | |||
1787 | /* | ||
1788 | * FS.ExtendLock operation type | ||
1789 | */ | ||
1790 | static const struct afs_call_type afs_RXFSExtendLock = { | ||
1791 | .name = "FS.ExtendLock", | ||
1792 | .deliver = afs_deliver_fs_xxxx_lock, | ||
1793 | .abort_to_error = afs_abort_to_error, | ||
1794 | .destructor = afs_flat_call_destructor, | ||
1795 | }; | ||
1796 | |||
1797 | /* | ||
1798 | * FS.ReleaseLock operation type | ||
1799 | */ | ||
1800 | static const struct afs_call_type afs_RXFSReleaseLock = { | ||
1801 | .name = "FS.ReleaseLock", | ||
1802 | .deliver = afs_deliver_fs_xxxx_lock, | ||
1803 | .abort_to_error = afs_abort_to_error, | ||
1804 | .destructor = afs_flat_call_destructor, | ||
1805 | }; | ||
1806 | |||
1807 | /* | ||
1808 | * get a lock on a file | ||
1809 | */ | ||
1810 | int afs_fs_set_lock(struct afs_server *server, | ||
1811 | struct key *key, | ||
1812 | struct afs_vnode *vnode, | ||
1813 | afs_lock_type_t type, | ||
1814 | const struct afs_wait_mode *wait_mode) | ||
1815 | { | ||
1816 | struct afs_call *call; | ||
1817 | __be32 *bp; | ||
1818 | |||
1819 | _enter(""); | ||
1820 | |||
1821 | call = afs_alloc_flat_call(&afs_RXFSSetLock, 5 * 4, 6 * 4); | ||
1822 | if (!call) | ||
1823 | return -ENOMEM; | ||
1824 | |||
1825 | call->key = key; | ||
1826 | call->reply = vnode; | ||
1827 | call->service_id = FS_SERVICE; | ||
1828 | call->port = htons(AFS_FS_PORT); | ||
1829 | |||
1830 | /* marshall the parameters */ | ||
1831 | bp = call->request; | ||
1832 | *bp++ = htonl(FSSETLOCK); | ||
1833 | *bp++ = htonl(vnode->fid.vid); | ||
1834 | *bp++ = htonl(vnode->fid.vnode); | ||
1835 | *bp++ = htonl(vnode->fid.unique); | ||
1836 | *bp++ = htonl(type); | ||
1837 | |||
1838 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | ||
1839 | } | ||
1840 | |||
1841 | /* | ||
1842 | * extend a lock on a file | ||
1843 | */ | ||
1844 | int afs_fs_extend_lock(struct afs_server *server, | ||
1845 | struct key *key, | ||
1846 | struct afs_vnode *vnode, | ||
1847 | const struct afs_wait_mode *wait_mode) | ||
1848 | { | ||
1849 | struct afs_call *call; | ||
1850 | __be32 *bp; | ||
1851 | |||
1852 | _enter(""); | ||
1853 | |||
1854 | call = afs_alloc_flat_call(&afs_RXFSExtendLock, 4 * 4, 6 * 4); | ||
1855 | if (!call) | ||
1856 | return -ENOMEM; | ||
1857 | |||
1858 | call->key = key; | ||
1859 | call->reply = vnode; | ||
1860 | call->service_id = FS_SERVICE; | ||
1861 | call->port = htons(AFS_FS_PORT); | ||
1862 | |||
1863 | /* marshall the parameters */ | ||
1864 | bp = call->request; | ||
1865 | *bp++ = htonl(FSEXTENDLOCK); | ||
1866 | *bp++ = htonl(vnode->fid.vid); | ||
1867 | *bp++ = htonl(vnode->fid.vnode); | ||
1868 | *bp++ = htonl(vnode->fid.unique); | ||
1869 | |||
1870 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | ||
1871 | } | ||
1872 | |||
1873 | /* | ||
1874 | * release a lock on a file | ||
1875 | */ | ||
1876 | int afs_fs_release_lock(struct afs_server *server, | ||
1877 | struct key *key, | ||
1878 | struct afs_vnode *vnode, | ||
1879 | const struct afs_wait_mode *wait_mode) | ||
1880 | { | ||
1881 | struct afs_call *call; | ||
1882 | __be32 *bp; | ||
1883 | |||
1884 | _enter(""); | ||
1885 | |||
1886 | call = afs_alloc_flat_call(&afs_RXFSReleaseLock, 4 * 4, 6 * 4); | ||
1887 | if (!call) | ||
1888 | return -ENOMEM; | ||
1889 | |||
1890 | call->key = key; | ||
1891 | call->reply = vnode; | ||
1892 | call->service_id = FS_SERVICE; | ||
1893 | call->port = htons(AFS_FS_PORT); | ||
1894 | |||
1895 | /* marshall the parameters */ | ||
1896 | bp = call->request; | ||
1897 | *bp++ = htonl(FSRELEASELOCK); | ||
1898 | *bp++ = htonl(vnode->fid.vid); | ||
1899 | *bp++ = htonl(vnode->fid.vnode); | ||
1900 | *bp++ = htonl(vnode->fid.unique); | ||
1901 | |||
1902 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | ||
1903 | } | ||
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 2c55dd94a1de..6306438f331f 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -351,10 +351,18 @@ struct afs_vnode { | |||
351 | #define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */ | 351 | #define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */ |
352 | #define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */ | 352 | #define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */ |
353 | #define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ | 353 | #define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ |
354 | #define AFS_VNODE_LOCKING 6 /* set if waiting for lock on vnode */ | ||
355 | #define AFS_VNODE_READLOCKED 7 /* set if vnode is read-locked on the server */ | ||
356 | #define AFS_VNODE_WRITELOCKED 8 /* set if vnode is write-locked on the server */ | ||
357 | #define AFS_VNODE_UNLOCKING 9 /* set if vnode is being unlocked on the server */ | ||
354 | 358 | ||
355 | long acl_order; /* ACL check count (callback break count) */ | 359 | long acl_order; /* ACL check count (callback break count) */ |
356 | 360 | ||
357 | struct list_head writebacks; /* alterations in pagecache that need writing */ | 361 | struct list_head writebacks; /* alterations in pagecache that need writing */ |
362 | struct list_head pending_locks; /* locks waiting to be granted */ | ||
363 | struct list_head granted_locks; /* locks granted on this file */ | ||
364 | struct delayed_work lock_work; /* work to be done in locking */ | ||
365 | struct key *unlock_key; /* key to be used in unlocking */ | ||
358 | 366 | ||
359 | /* outstanding callback notification on this file */ | 367 | /* outstanding callback notification on this file */ |
360 | struct rb_node server_rb; /* link in server->fs_vnodes */ | 368 | struct rb_node server_rb; /* link in server->fs_vnodes */ |
@@ -474,6 +482,15 @@ extern int afs_open(struct inode *, struct file *); | |||
474 | extern int afs_release(struct inode *, struct file *); | 482 | extern int afs_release(struct inode *, struct file *); |
475 | 483 | ||
476 | /* | 484 | /* |
485 | * flock.c | ||
486 | */ | ||
487 | extern void __exit afs_kill_lock_manager(void); | ||
488 | extern void afs_lock_work(struct work_struct *); | ||
489 | extern void afs_lock_may_be_available(struct afs_vnode *); | ||
490 | extern int afs_lock(struct file *, int, struct file_lock *); | ||
491 | extern int afs_flock(struct file *, int, struct file_lock *); | ||
492 | |||
493 | /* | ||
477 | * fsclient.c | 494 | * fsclient.c |
478 | */ | 495 | */ |
479 | extern int afs_fs_fetch_file_status(struct afs_server *, struct key *, | 496 | extern int afs_fs_fetch_file_status(struct afs_server *, struct key *, |
@@ -513,6 +530,15 @@ extern int afs_fs_get_volume_status(struct afs_server *, struct key *, | |||
513 | struct afs_vnode *, | 530 | struct afs_vnode *, |
514 | struct afs_volume_status *, | 531 | struct afs_volume_status *, |
515 | const struct afs_wait_mode *); | 532 | const struct afs_wait_mode *); |
533 | extern int afs_fs_set_lock(struct afs_server *, struct key *, | ||
534 | struct afs_vnode *, afs_lock_type_t, | ||
535 | const struct afs_wait_mode *); | ||
536 | extern int afs_fs_extend_lock(struct afs_server *, struct key *, | ||
537 | struct afs_vnode *, | ||
538 | const struct afs_wait_mode *); | ||
539 | extern int afs_fs_release_lock(struct afs_server *, struct key *, | ||
540 | struct afs_vnode *, | ||
541 | const struct afs_wait_mode *); | ||
516 | 542 | ||
517 | /* | 543 | /* |
518 | * inode.c | 544 | * inode.c |
@@ -681,6 +707,10 @@ extern int afs_vnode_store_data(struct afs_writeback *, pgoff_t, pgoff_t, | |||
681 | extern int afs_vnode_setattr(struct afs_vnode *, struct key *, struct iattr *); | 707 | extern int afs_vnode_setattr(struct afs_vnode *, struct key *, struct iattr *); |
682 | extern int afs_vnode_get_volume_status(struct afs_vnode *, struct key *, | 708 | extern int afs_vnode_get_volume_status(struct afs_vnode *, struct key *, |
683 | struct afs_volume_status *); | 709 | struct afs_volume_status *); |
710 | extern int afs_vnode_set_lock(struct afs_vnode *, struct key *, | ||
711 | afs_lock_type_t); | ||
712 | extern int afs_vnode_extend_lock(struct afs_vnode *, struct key *); | ||
713 | extern int afs_vnode_release_lock(struct afs_vnode *, struct key *); | ||
684 | 714 | ||
685 | /* | 715 | /* |
686 | * volume.c | 716 | * volume.c |
diff --git a/fs/afs/main.c b/fs/afs/main.c index cd21195bbb24..0f60f6b35769 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c | |||
@@ -168,6 +168,7 @@ static void __exit afs_exit(void) | |||
168 | printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n"); | 168 | printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n"); |
169 | 169 | ||
170 | afs_fs_exit(); | 170 | afs_fs_exit(); |
171 | afs_kill_lock_manager(); | ||
171 | afs_close_socket(); | 172 | afs_close_socket(); |
172 | afs_purge_servers(); | 173 | afs_purge_servers(); |
173 | afs_callback_update_kill(); | 174 | afs_callback_update_kill(); |
diff --git a/fs/afs/misc.c b/fs/afs/misc.c index d1a889c40742..2d33a5f7d218 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c | |||
@@ -35,6 +35,7 @@ int afs_abort_to_error(u32 abort_code) | |||
35 | case VOVERQUOTA: return -EDQUOT; | 35 | case VOVERQUOTA: return -EDQUOT; |
36 | case VBUSY: return -EBUSY; | 36 | case VBUSY: return -EBUSY; |
37 | case VMOVED: return -ENXIO; | 37 | case VMOVED: return -ENXIO; |
38 | case 0x2f6df0a: return -EWOULDBLOCK; | ||
38 | case 0x2f6df0c: return -EACCES; | 39 | case 0x2f6df0c: return -EACCES; |
39 | case 0x2f6df0f: return -EBUSY; | 40 | case 0x2f6df0f: return -EBUSY; |
40 | case 0x2f6df10: return -EEXIST; | 41 | case 0x2f6df10: return -EEXIST; |
diff --git a/fs/afs/super.c b/fs/afs/super.c index 2e8496ba1205..993cdf1cce3a 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c | |||
@@ -460,6 +460,9 @@ static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep, | |||
460 | spin_lock_init(&vnode->writeback_lock); | 460 | spin_lock_init(&vnode->writeback_lock); |
461 | spin_lock_init(&vnode->lock); | 461 | spin_lock_init(&vnode->lock); |
462 | INIT_LIST_HEAD(&vnode->writebacks); | 462 | INIT_LIST_HEAD(&vnode->writebacks); |
463 | INIT_LIST_HEAD(&vnode->pending_locks); | ||
464 | INIT_LIST_HEAD(&vnode->granted_locks); | ||
465 | INIT_DELAYED_WORK(&vnode->lock_work, afs_lock_work); | ||
463 | INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work); | 466 | INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work); |
464 | } | 467 | } |
465 | 468 | ||
diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c index 232c55dc245d..ac811e445b2d 100644 --- a/fs/afs/vnode.c +++ b/fs/afs/vnode.c | |||
@@ -887,11 +887,6 @@ int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key, | |||
887 | vnode->fid.unique, | 887 | vnode->fid.unique, |
888 | key_serial(key)); | 888 | key_serial(key)); |
889 | 889 | ||
890 | /* this op will fetch the status */ | ||
891 | spin_lock(&vnode->lock); | ||
892 | vnode->update_cnt++; | ||
893 | spin_unlock(&vnode->lock); | ||
894 | |||
895 | do { | 890 | do { |
896 | /* pick a server to query */ | 891 | /* pick a server to query */ |
897 | server = afs_volume_pick_fileserver(vnode); | 892 | server = afs_volume_pick_fileserver(vnode); |
@@ -905,20 +900,127 @@ int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key, | |||
905 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | 900 | } while (!afs_volume_release_fileserver(vnode, server, ret)); |
906 | 901 | ||
907 | /* adjust the flags */ | 902 | /* adjust the flags */ |
908 | if (ret == 0) { | 903 | if (ret == 0) |
909 | afs_vnode_finalise_status_update(vnode, server); | 904 | afs_put_server(server); |
905 | |||
906 | _leave(" = %d", ret); | ||
907 | return ret; | ||
908 | |||
909 | no_server: | ||
910 | return PTR_ERR(server); | ||
911 | } | ||
912 | |||
913 | /* | ||
914 | * get a lock on a file | ||
915 | */ | ||
916 | int afs_vnode_set_lock(struct afs_vnode *vnode, struct key *key, | ||
917 | afs_lock_type_t type) | ||
918 | { | ||
919 | struct afs_server *server; | ||
920 | int ret; | ||
921 | |||
922 | _enter("%s{%x:%u.%u},%x,%u", | ||
923 | vnode->volume->vlocation->vldb.name, | ||
924 | vnode->fid.vid, | ||
925 | vnode->fid.vnode, | ||
926 | vnode->fid.unique, | ||
927 | key_serial(key), type); | ||
928 | |||
929 | do { | ||
930 | /* pick a server to query */ | ||
931 | server = afs_volume_pick_fileserver(vnode); | ||
932 | if (IS_ERR(server)) | ||
933 | goto no_server; | ||
934 | |||
935 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | ||
936 | |||
937 | ret = afs_fs_set_lock(server, key, vnode, type, &afs_sync_call); | ||
938 | |||
939 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | ||
940 | |||
941 | /* adjust the flags */ | ||
942 | if (ret == 0) | ||
943 | afs_put_server(server); | ||
944 | |||
945 | _leave(" = %d", ret); | ||
946 | return ret; | ||
947 | |||
948 | no_server: | ||
949 | return PTR_ERR(server); | ||
950 | } | ||
951 | |||
952 | /* | ||
953 | * extend a lock on a file | ||
954 | */ | ||
955 | int afs_vnode_extend_lock(struct afs_vnode *vnode, struct key *key) | ||
956 | { | ||
957 | struct afs_server *server; | ||
958 | int ret; | ||
959 | |||
960 | _enter("%s{%x:%u.%u},%x", | ||
961 | vnode->volume->vlocation->vldb.name, | ||
962 | vnode->fid.vid, | ||
963 | vnode->fid.vnode, | ||
964 | vnode->fid.unique, | ||
965 | key_serial(key)); | ||
966 | |||
967 | do { | ||
968 | /* pick a server to query */ | ||
969 | server = afs_volume_pick_fileserver(vnode); | ||
970 | if (IS_ERR(server)) | ||
971 | goto no_server; | ||
972 | |||
973 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | ||
974 | |||
975 | ret = afs_fs_extend_lock(server, key, vnode, &afs_sync_call); | ||
976 | |||
977 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | ||
978 | |||
979 | /* adjust the flags */ | ||
980 | if (ret == 0) | ||
981 | afs_put_server(server); | ||
982 | |||
983 | _leave(" = %d", ret); | ||
984 | return ret; | ||
985 | |||
986 | no_server: | ||
987 | return PTR_ERR(server); | ||
988 | } | ||
989 | |||
990 | /* | ||
991 | * release a lock on a file | ||
992 | */ | ||
993 | int afs_vnode_release_lock(struct afs_vnode *vnode, struct key *key) | ||
994 | { | ||
995 | struct afs_server *server; | ||
996 | int ret; | ||
997 | |||
998 | _enter("%s{%x:%u.%u},%x", | ||
999 | vnode->volume->vlocation->vldb.name, | ||
1000 | vnode->fid.vid, | ||
1001 | vnode->fid.vnode, | ||
1002 | vnode->fid.unique, | ||
1003 | key_serial(key)); | ||
1004 | |||
1005 | do { | ||
1006 | /* pick a server to query */ | ||
1007 | server = afs_volume_pick_fileserver(vnode); | ||
1008 | if (IS_ERR(server)) | ||
1009 | goto no_server; | ||
1010 | |||
1011 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | ||
1012 | |||
1013 | ret = afs_fs_release_lock(server, key, vnode, &afs_sync_call); | ||
1014 | |||
1015 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | ||
1016 | |||
1017 | /* adjust the flags */ | ||
1018 | if (ret == 0) | ||
910 | afs_put_server(server); | 1019 | afs_put_server(server); |
911 | } else { | ||
912 | afs_vnode_status_update_failed(vnode, ret); | ||
913 | } | ||
914 | 1020 | ||
915 | _leave(" = %d", ret); | 1021 | _leave(" = %d", ret); |
916 | return ret; | 1022 | return ret; |
917 | 1023 | ||
918 | no_server: | 1024 | no_server: |
919 | spin_lock(&vnode->lock); | ||
920 | vnode->update_cnt--; | ||
921 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
922 | spin_unlock(&vnode->lock); | ||
923 | return PTR_ERR(server); | 1025 | return PTR_ERR(server); |
924 | } | 1026 | } |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 51c938a71dec..aa4530c1ff7a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -820,6 +820,10 @@ struct file_lock { | |||
820 | union { | 820 | union { |
821 | struct nfs_lock_info nfs_fl; | 821 | struct nfs_lock_info nfs_fl; |
822 | struct nfs4_lock_info nfs4_fl; | 822 | struct nfs4_lock_info nfs4_fl; |
823 | struct { | ||
824 | struct list_head link; /* link in AFS vnode's pending_locks list */ | ||
825 | int state; /* state of grant or error if -ve */ | ||
826 | } afs; | ||
823 | } fl_u; | 827 | } fl_u; |
824 | }; | 828 | }; |
825 | 829 | ||