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