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 | ||
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; | |
f8314dc6 | 103 | file = kzalloc(sizeof(*file), GFP_KERNEL); |
1da177e4 LT |
104 | if (!file) |
105 | goto out_unlock; | |
106 | ||
1da177e4 LT |
107 | memcpy(&file->f_handle, f, sizeof(struct nfs_fh)); |
108 | file->f_hash = hash; | |
109 | init_MUTEX(&file->f_sema); | |
110 | ||
111 | /* Open the file. Note that this must not sleep for too long, else | |
112 | * we would lock up lockd:-) So no NFS re-exports, folks. | |
113 | * | |
114 | * We have to make sure we have the right credential to open | |
115 | * the file. | |
116 | */ | |
117 | if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) { | |
118 | dprintk("lockd: open failed (nfserr %d)\n", ntohl(nfserr)); | |
119 | goto out_free; | |
120 | } | |
121 | ||
122 | file->f_next = nlm_files[hash]; | |
123 | nlm_files[hash] = file; | |
124 | ||
125 | found: | |
126 | dprintk("lockd: found file %p (count %d)\n", file, file->f_count); | |
127 | *result = file; | |
128 | file->f_count++; | |
129 | nfserr = 0; | |
130 | ||
131 | out_unlock: | |
353ab6e9 | 132 | mutex_unlock(&nlm_file_mutex); |
1da177e4 LT |
133 | return nfserr; |
134 | ||
135 | out_free: | |
136 | kfree(file); | |
137 | #ifdef CONFIG_LOCKD_V4 | |
138 | if (nfserr == 1) | |
139 | nfserr = nlm4_stale_fh; | |
140 | else | |
141 | #endif | |
142 | nfserr = nlm_lck_denied; | |
143 | goto out_unlock; | |
144 | } | |
145 | ||
146 | /* | |
147 | * Delete a file after having released all locks, blocks and shares | |
148 | */ | |
149 | static inline void | |
150 | nlm_delete_file(struct nlm_file *file) | |
151 | { | |
1da177e4 LT |
152 | struct nlm_file **fp, *f; |
153 | ||
0bbacc40 CL |
154 | nlm_debug_print_file("closing file", file); |
155 | ||
1da177e4 LT |
156 | fp = nlm_files + file->f_hash; |
157 | while ((f = *fp) != NULL) { | |
158 | if (f == file) { | |
159 | *fp = file->f_next; | |
160 | nlmsvc_ops->fclose(file->f_file); | |
161 | kfree(file); | |
162 | return; | |
163 | } | |
164 | fp = &f->f_next; | |
165 | } | |
166 | ||
167 | printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); | |
168 | } | |
169 | ||
170 | /* | |
171 | * Loop over all locks on the given file and perform the specified | |
172 | * action. | |
173 | */ | |
174 | static int | |
175 | nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action) | |
176 | { | |
177 | struct inode *inode = nlmsvc_file_inode(file); | |
178 | struct file_lock *fl; | |
179 | struct nlm_host *lockhost; | |
180 | ||
181 | again: | |
182 | file->f_locks = 0; | |
183 | for (fl = inode->i_flock; fl; fl = fl->fl_next) { | |
7117bf3d | 184 | if (fl->fl_lmops != &nlmsvc_lock_operations) |
1da177e4 LT |
185 | continue; |
186 | ||
187 | /* update current lock count */ | |
188 | file->f_locks++; | |
189 | lockhost = (struct nlm_host *) fl->fl_owner; | |
190 | if (action == NLM_ACT_MARK) | |
191 | lockhost->h_inuse = 1; | |
192 | else if (action == NLM_ACT_CHECK) | |
193 | return 1; | |
194 | else if (action == NLM_ACT_UNLOCK) { | |
195 | struct file_lock lock = *fl; | |
196 | ||
197 | if (host && lockhost != host) | |
198 | continue; | |
199 | ||
200 | lock.fl_type = F_UNLCK; | |
201 | lock.fl_start = 0; | |
202 | lock.fl_end = OFFSET_MAX; | |
203 | if (posix_lock_file(file->f_file, &lock) < 0) { | |
204 | printk("lockd: unlock failure in %s:%d\n", | |
205 | __FILE__, __LINE__); | |
206 | return 1; | |
207 | } | |
208 | goto again; | |
209 | } | |
210 | } | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | /* | |
216 | * Operate on a single file | |
217 | */ | |
218 | static inline int | |
219 | nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) | |
220 | { | |
221 | if (action == NLM_ACT_CHECK) { | |
222 | /* Fast path for mark and sweep garbage collection */ | |
223 | if (file->f_count || file->f_blocks || file->f_shares) | |
224 | return 1; | |
225 | } else { | |
f3ee439f | 226 | nlmsvc_traverse_blocks(host, file, action); |
5f12191b | 227 | nlmsvc_traverse_shares(host, file, action); |
1da177e4 LT |
228 | } |
229 | return nlm_traverse_locks(host, file, action); | |
230 | } | |
231 | ||
232 | /* | |
233 | * Loop over all files in the file table. | |
234 | */ | |
235 | static int | |
236 | nlm_traverse_files(struct nlm_host *host, int action) | |
237 | { | |
238 | struct nlm_file *file, **fp; | |
01df9c5e | 239 | int i, ret = 0; |
1da177e4 | 240 | |
353ab6e9 | 241 | mutex_lock(&nlm_file_mutex); |
1da177e4 LT |
242 | for (i = 0; i < FILE_NRHASH; i++) { |
243 | fp = nlm_files + i; | |
244 | while ((file = *fp) != NULL) { | |
01df9c5e TM |
245 | file->f_count++; |
246 | mutex_unlock(&nlm_file_mutex); | |
247 | ||
1da177e4 LT |
248 | /* Traverse locks, blocks and shares of this file |
249 | * and update file->f_locks count */ | |
01df9c5e TM |
250 | if (nlm_inspect_file(host, file, action)) |
251 | ret = 1; | |
1da177e4 | 252 | |
01df9c5e TM |
253 | mutex_lock(&nlm_file_mutex); |
254 | file->f_count--; | |
1da177e4 LT |
255 | /* No more references to this file. Let go of it. */ |
256 | if (!file->f_blocks && !file->f_locks | |
257 | && !file->f_shares && !file->f_count) { | |
258 | *fp = file->f_next; | |
259 | nlmsvc_ops->fclose(file->f_file); | |
260 | kfree(file); | |
261 | } else { | |
262 | fp = &file->f_next; | |
263 | } | |
264 | } | |
265 | } | |
353ab6e9 | 266 | mutex_unlock(&nlm_file_mutex); |
01df9c5e | 267 | return ret; |
1da177e4 LT |
268 | } |
269 | ||
270 | /* | |
271 | * Release file. If there are no more remote locks on this file, | |
272 | * close it and free the handle. | |
273 | * | |
274 | * Note that we can't do proper reference counting without major | |
275 | * contortions because the code in fs/locks.c creates, deletes and | |
276 | * splits locks without notification. Our only way is to walk the | |
277 | * entire lock list each time we remove a lock. | |
278 | */ | |
279 | void | |
280 | nlm_release_file(struct nlm_file *file) | |
281 | { | |
282 | dprintk("lockd: nlm_release_file(%p, ct = %d)\n", | |
283 | file, file->f_count); | |
284 | ||
285 | /* Lock file table */ | |
353ab6e9 | 286 | mutex_lock(&nlm_file_mutex); |
1da177e4 LT |
287 | |
288 | /* If there are no more locks etc, delete the file */ | |
289 | if(--file->f_count == 0) { | |
290 | if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK)) | |
291 | nlm_delete_file(file); | |
292 | } | |
293 | ||
353ab6e9 | 294 | mutex_unlock(&nlm_file_mutex); |
1da177e4 LT |
295 | } |
296 | ||
297 | /* | |
298 | * Mark all hosts that still hold resources | |
299 | */ | |
300 | void | |
301 | nlmsvc_mark_resources(void) | |
302 | { | |
303 | dprintk("lockd: nlmsvc_mark_resources\n"); | |
304 | ||
305 | nlm_traverse_files(NULL, NLM_ACT_MARK); | |
306 | } | |
307 | ||
308 | /* | |
309 | * Release all resources held by the given client | |
310 | */ | |
311 | void | |
312 | nlmsvc_free_host_resources(struct nlm_host *host) | |
313 | { | |
314 | dprintk("lockd: nlmsvc_free_host_resources\n"); | |
315 | ||
316 | if (nlm_traverse_files(host, NLM_ACT_UNLOCK)) | |
317 | printk(KERN_WARNING | |
318 | "lockd: couldn't remove all locks held by %s", | |
319 | host->h_name); | |
320 | } | |
321 | ||
322 | /* | |
323 | * delete all hosts structs for clients | |
324 | */ | |
325 | void | |
326 | nlmsvc_invalidate_all(void) | |
327 | { | |
328 | struct nlm_host *host; | |
329 | while ((host = nlm_find_client()) != NULL) { | |
330 | nlmsvc_free_host_resources(host); | |
331 | host->h_expires = 0; | |
332 | host->h_killed = 1; | |
333 | nlm_release_host(host); | |
334 | } | |
335 | } |