diff options
Diffstat (limited to 'fs/lockd/svcsubs.c')
-rw-r--r-- | fs/lockd/svcsubs.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c new file mode 100644 index 000000000000..de7536358c7c --- /dev/null +++ b/fs/lockd/svcsubs.c | |||
@@ -0,0 +1,309 @@ | |||
1 | /* | ||
2 | * linux/fs/lockd/svcsubs.c | ||
3 | * | ||
4 | * Various support routines for the NLM server. | ||
5 | * | ||
6 | * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> | ||
7 | */ | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/string.h> | ||
12 | #include <linux/time.h> | ||
13 | #include <linux/in.h> | ||
14 | #include <linux/sunrpc/svc.h> | ||
15 | #include <linux/sunrpc/clnt.h> | ||
16 | #include <linux/nfsd/nfsfh.h> | ||
17 | #include <linux/nfsd/export.h> | ||
18 | #include <linux/lockd/lockd.h> | ||
19 | #include <linux/lockd/share.h> | ||
20 | #include <linux/lockd/sm_inter.h> | ||
21 | |||
22 | #define NLMDBG_FACILITY NLMDBG_SVCSUBS | ||
23 | |||
24 | |||
25 | /* | ||
26 | * Global file hash table | ||
27 | */ | ||
28 | #define FILE_HASH_BITS 5 | ||
29 | #define FILE_NRHASH (1<<FILE_HASH_BITS) | ||
30 | static struct nlm_file * nlm_files[FILE_NRHASH]; | ||
31 | static DECLARE_MUTEX(nlm_file_sema); | ||
32 | |||
33 | static inline unsigned int file_hash(struct nfs_fh *f) | ||
34 | { | ||
35 | unsigned int tmp=0; | ||
36 | int i; | ||
37 | for (i=0; i<NFS2_FHSIZE;i++) | ||
38 | tmp += f->data[i]; | ||
39 | return tmp & (FILE_NRHASH - 1); | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Lookup file info. If it doesn't exist, create a file info struct | ||
44 | * and open a (VFS) file for the given inode. | ||
45 | * | ||
46 | * FIXME: | ||
47 | * Note that we open the file O_RDONLY even when creating write locks. | ||
48 | * This is not quite right, but for now, we assume the client performs | ||
49 | * the proper R/W checking. | ||
50 | */ | ||
51 | u32 | ||
52 | nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, | ||
53 | struct nfs_fh *f) | ||
54 | { | ||
55 | struct nlm_file *file; | ||
56 | unsigned int hash; | ||
57 | u32 nfserr; | ||
58 | u32 *fhp = (u32*)f->data; | ||
59 | |||
60 | dprintk("lockd: nlm_file_lookup(%08x %08x %08x %08x %08x %08x)\n", | ||
61 | fhp[0], fhp[1], fhp[2], fhp[3], fhp[4], fhp[5]); | ||
62 | |||
63 | |||
64 | hash = file_hash(f); | ||
65 | |||
66 | /* Lock file table */ | ||
67 | down(&nlm_file_sema); | ||
68 | |||
69 | for (file = nlm_files[hash]; file; file = file->f_next) | ||
70 | if (!nfs_compare_fh(&file->f_handle, f)) | ||
71 | goto found; | ||
72 | |||
73 | dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n", | ||
74 | fhp[0], fhp[1], fhp[2], fhp[3], fhp[4], fhp[5]); | ||
75 | |||
76 | nfserr = nlm_lck_denied_nolocks; | ||
77 | file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL); | ||
78 | if (!file) | ||
79 | goto out_unlock; | ||
80 | |||
81 | memset(file, 0, sizeof(*file)); | ||
82 | memcpy(&file->f_handle, f, sizeof(struct nfs_fh)); | ||
83 | file->f_hash = hash; | ||
84 | init_MUTEX(&file->f_sema); | ||
85 | |||
86 | /* Open the file. Note that this must not sleep for too long, else | ||
87 | * we would lock up lockd:-) So no NFS re-exports, folks. | ||
88 | * | ||
89 | * We have to make sure we have the right credential to open | ||
90 | * the file. | ||
91 | */ | ||
92 | if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) { | ||
93 | dprintk("lockd: open failed (nfserr %d)\n", ntohl(nfserr)); | ||
94 | goto out_free; | ||
95 | } | ||
96 | |||
97 | file->f_next = nlm_files[hash]; | ||
98 | nlm_files[hash] = file; | ||
99 | |||
100 | found: | ||
101 | dprintk("lockd: found file %p (count %d)\n", file, file->f_count); | ||
102 | *result = file; | ||
103 | file->f_count++; | ||
104 | nfserr = 0; | ||
105 | |||
106 | out_unlock: | ||
107 | up(&nlm_file_sema); | ||
108 | return nfserr; | ||
109 | |||
110 | out_free: | ||
111 | kfree(file); | ||
112 | #ifdef CONFIG_LOCKD_V4 | ||
113 | if (nfserr == 1) | ||
114 | nfserr = nlm4_stale_fh; | ||
115 | else | ||
116 | #endif | ||
117 | nfserr = nlm_lck_denied; | ||
118 | goto out_unlock; | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * Delete a file after having released all locks, blocks and shares | ||
123 | */ | ||
124 | static inline void | ||
125 | nlm_delete_file(struct nlm_file *file) | ||
126 | { | ||
127 | struct inode *inode = file->f_file->f_dentry->d_inode; | ||
128 | struct nlm_file **fp, *f; | ||
129 | |||
130 | dprintk("lockd: closing file %s/%ld\n", | ||
131 | inode->i_sb->s_id, inode->i_ino); | ||
132 | fp = nlm_files + file->f_hash; | ||
133 | while ((f = *fp) != NULL) { | ||
134 | if (f == file) { | ||
135 | *fp = file->f_next; | ||
136 | nlmsvc_ops->fclose(file->f_file); | ||
137 | kfree(file); | ||
138 | return; | ||
139 | } | ||
140 | fp = &f->f_next; | ||
141 | } | ||
142 | |||
143 | printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Loop over all locks on the given file and perform the specified | ||
148 | * action. | ||
149 | */ | ||
150 | static int | ||
151 | nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action) | ||
152 | { | ||
153 | struct inode *inode = nlmsvc_file_inode(file); | ||
154 | struct file_lock *fl; | ||
155 | struct nlm_host *lockhost; | ||
156 | |||
157 | again: | ||
158 | file->f_locks = 0; | ||
159 | for (fl = inode->i_flock; fl; fl = fl->fl_next) { | ||
160 | if (!(fl->fl_flags & FL_LOCKD)) | ||
161 | continue; | ||
162 | |||
163 | /* update current lock count */ | ||
164 | file->f_locks++; | ||
165 | lockhost = (struct nlm_host *) fl->fl_owner; | ||
166 | if (action == NLM_ACT_MARK) | ||
167 | lockhost->h_inuse = 1; | ||
168 | else if (action == NLM_ACT_CHECK) | ||
169 | return 1; | ||
170 | else if (action == NLM_ACT_UNLOCK) { | ||
171 | struct file_lock lock = *fl; | ||
172 | |||
173 | if (host && lockhost != host) | ||
174 | continue; | ||
175 | |||
176 | lock.fl_type = F_UNLCK; | ||
177 | lock.fl_start = 0; | ||
178 | lock.fl_end = OFFSET_MAX; | ||
179 | if (posix_lock_file(file->f_file, &lock) < 0) { | ||
180 | printk("lockd: unlock failure in %s:%d\n", | ||
181 | __FILE__, __LINE__); | ||
182 | return 1; | ||
183 | } | ||
184 | goto again; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Operate on a single file | ||
193 | */ | ||
194 | static inline int | ||
195 | nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) | ||
196 | { | ||
197 | if (action == NLM_ACT_CHECK) { | ||
198 | /* Fast path for mark and sweep garbage collection */ | ||
199 | if (file->f_count || file->f_blocks || file->f_shares) | ||
200 | return 1; | ||
201 | } else { | ||
202 | if (nlmsvc_traverse_blocks(host, file, action) | ||
203 | || nlmsvc_traverse_shares(host, file, action)) | ||
204 | return 1; | ||
205 | } | ||
206 | return nlm_traverse_locks(host, file, action); | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * Loop over all files in the file table. | ||
211 | */ | ||
212 | static int | ||
213 | nlm_traverse_files(struct nlm_host *host, int action) | ||
214 | { | ||
215 | struct nlm_file *file, **fp; | ||
216 | int i; | ||
217 | |||
218 | down(&nlm_file_sema); | ||
219 | for (i = 0; i < FILE_NRHASH; i++) { | ||
220 | fp = nlm_files + i; | ||
221 | while ((file = *fp) != NULL) { | ||
222 | /* Traverse locks, blocks and shares of this file | ||
223 | * and update file->f_locks count */ | ||
224 | if (nlm_inspect_file(host, file, action)) { | ||
225 | up(&nlm_file_sema); | ||
226 | return 1; | ||
227 | } | ||
228 | |||
229 | /* No more references to this file. Let go of it. */ | ||
230 | if (!file->f_blocks && !file->f_locks | ||
231 | && !file->f_shares && !file->f_count) { | ||
232 | *fp = file->f_next; | ||
233 | nlmsvc_ops->fclose(file->f_file); | ||
234 | kfree(file); | ||
235 | } else { | ||
236 | fp = &file->f_next; | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | up(&nlm_file_sema); | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | /* | ||
245 | * Release file. If there are no more remote locks on this file, | ||
246 | * close it and free the handle. | ||
247 | * | ||
248 | * Note that we can't do proper reference counting without major | ||
249 | * contortions because the code in fs/locks.c creates, deletes and | ||
250 | * splits locks without notification. Our only way is to walk the | ||
251 | * entire lock list each time we remove a lock. | ||
252 | */ | ||
253 | void | ||
254 | nlm_release_file(struct nlm_file *file) | ||
255 | { | ||
256 | dprintk("lockd: nlm_release_file(%p, ct = %d)\n", | ||
257 | file, file->f_count); | ||
258 | |||
259 | /* Lock file table */ | ||
260 | down(&nlm_file_sema); | ||
261 | |||
262 | /* If there are no more locks etc, delete the file */ | ||
263 | if(--file->f_count == 0) { | ||
264 | if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK)) | ||
265 | nlm_delete_file(file); | ||
266 | } | ||
267 | |||
268 | up(&nlm_file_sema); | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * Mark all hosts that still hold resources | ||
273 | */ | ||
274 | void | ||
275 | nlmsvc_mark_resources(void) | ||
276 | { | ||
277 | dprintk("lockd: nlmsvc_mark_resources\n"); | ||
278 | |||
279 | nlm_traverse_files(NULL, NLM_ACT_MARK); | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * Release all resources held by the given client | ||
284 | */ | ||
285 | void | ||
286 | nlmsvc_free_host_resources(struct nlm_host *host) | ||
287 | { | ||
288 | dprintk("lockd: nlmsvc_free_host_resources\n"); | ||
289 | |||
290 | if (nlm_traverse_files(host, NLM_ACT_UNLOCK)) | ||
291 | printk(KERN_WARNING | ||
292 | "lockd: couldn't remove all locks held by %s", | ||
293 | host->h_name); | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * delete all hosts structs for clients | ||
298 | */ | ||
299 | void | ||
300 | nlmsvc_invalidate_all(void) | ||
301 | { | ||
302 | struct nlm_host *host; | ||
303 | while ((host = nlm_find_client()) != NULL) { | ||
304 | nlmsvc_free_host_resources(host); | ||
305 | host->h_expires = 0; | ||
306 | host->h_killed = 1; | ||
307 | nlm_release_host(host); | ||
308 | } | ||
309 | } | ||