Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | } |