Import 2.3.26pre2
[davej-history.git] / fs / lockd / svcsubs.c
blob1b1c4106984c4afff820625a0f2fec46bd7519ae
1 /*
2 * linux/fs/lockd/svcsubs.c
4 * Various support routines for the NLM server.
6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
7 */
9 #include <linux/types.h>
10 #include <linux/string.h>
11 #include <linux/sched.h>
12 #include <linux/in.h>
13 #include <linux/sunrpc/svc.h>
14 #include <linux/sunrpc/clnt.h>
15 #include <linux/nfsd/nfsfh.h>
16 #include <linux/nfsd/export.h>
17 #include <linux/lockd/lockd.h>
18 #include <linux/lockd/share.h>
19 #include <linux/lockd/sm_inter.h>
21 #define NLMDBG_FACILITY NLMDBG_SVCSUBS
25 * Global file hash table
27 #define FILE_NRHASH 32
28 #define FILE_HASH_BITS 5
29 static struct nlm_file * nlm_files[FILE_NRHASH];
30 static DECLARE_MUTEX(nlm_file_sema);
32 static unsigned int file_hash(dev_t dev, ino_t ino)
34 unsigned long tmp = (unsigned long) ino | (unsigned long) dev;
35 tmp = tmp + (tmp >> FILE_HASH_BITS) + (tmp >> FILE_HASH_BITS*2);
36 return tmp & (FILE_NRHASH - 1);
40 * Lookup file info. If it doesn't exist, create a file info struct
41 * and open a (VFS) file for the given inode.
43 * FIXME:
44 * Note that we open the file O_RDONLY even when creating write locks.
45 * This is not quite right, but for now, we assume the client performs
46 * the proper R/W checking.
48 u32
49 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
50 struct nfs_fh *f)
52 struct knfs_fh *fh = (struct knfs_fh *) f;
53 struct nlm_file *file;
54 unsigned int hash;
55 u32 nfserr;
57 dprintk("lockd: nlm_file_lookup(%s/%u)\n",
58 kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino);
60 hash = file_hash(u32_to_kdev_t(fh->fh_dev), u32_to_ino_t(fh->fh_ino));
62 /* Lock file table */
63 down(&nlm_file_sema);
65 for (file = nlm_files[hash]; file; file = file->f_next) {
66 if (file->f_handle.fh_dcookie == fh->fh_dcookie &&
67 !memcmp(&file->f_handle, fh, sizeof(*fh)))
68 goto found;
71 dprintk("lockd: creating file for %s/%u\n",
72 kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino);
73 nfserr = nlm_lck_denied_nolocks;
74 file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL);
75 if (!file)
76 goto out_unlock;
78 memset(file, 0, sizeof(*file));
79 file->f_handle = *fh;
80 init_MUTEX(&file->f_sema);
82 /* Open the file. Note that this must not sleep for too long, else
83 * we would lock up lockd:-) So no NFS re-exports, folks.
85 * We have to make sure we have the right credential to open
86 * the file.
88 if ((nfserr = nlmsvc_ops->fopen(rqstp, fh, &file->f_file)) != 0) {
89 dprintk("lockd: open failed (nfserr %ld)\n", ntohl(nfserr));
90 goto out_free;
93 file->f_next = nlm_files[hash];
94 nlm_files[hash] = file;
96 found:
97 dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
98 *result = file;
99 file->f_count++;
100 nfserr = 0;
102 out_unlock:
103 up(&nlm_file_sema);
104 return nfserr;
106 out_free:
107 kfree(file);
108 nfserr = nlm_lck_denied;
109 goto out_unlock;
113 * Delete a file after having released all locks, blocks and shares
115 static inline void
116 nlm_delete_file(struct nlm_file *file)
118 struct inode *inode = file->f_file.f_dentry->d_inode;
119 struct nlm_file **fp, *f;
121 dprintk("lockd: closing file %s/%ld\n",
122 kdevname(inode->i_dev), inode->i_ino);
123 fp = nlm_files + file_hash(inode->i_dev, inode->i_ino);
124 while ((f = *fp) != NULL) {
125 if (f == file) {
126 *fp = file->f_next;
127 nlmsvc_ops->fclose(&file->f_file);
128 kfree(file);
129 return;
131 fp = &f->f_next;
134 printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
138 * Loop over all locks on the given file and perform the specified
139 * action.
141 static int
142 nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action)
144 struct inode *inode = nlmsvc_file_inode(file);
145 struct file_lock *fl;
146 struct nlm_host *lockhost;
148 again:
149 file->f_locks = 0;
150 for (fl = inode->i_flock; fl; fl = fl->fl_next) {
151 if (!(fl->fl_flags & FL_LOCKD))
152 continue;
154 /* update current lock count */
155 file->f_locks++;
156 lockhost = (struct nlm_host *) fl->fl_owner;
157 if (action == NLM_ACT_MARK)
158 lockhost->h_inuse = 1;
159 else if (action == NLM_ACT_CHECK)
160 return 1;
161 else if (action == NLM_ACT_UNLOCK) {
162 struct file_lock lock = *fl;
164 if (host && lockhost != host)
165 continue;
167 lock.fl_type = F_UNLCK;
168 lock.fl_start = 0;
169 lock.fl_end = NLM_OFFSET_MAX;
170 if (posix_lock_file(&file->f_file, &lock, 0) < 0) {
171 printk("lockd: unlock failure in %s:%d\n",
172 __FILE__, __LINE__);
173 return 1;
175 goto again;
179 return 0;
183 * Operate on a single file
185 static inline int
186 nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action)
188 if (action == NLM_ACT_CHECK) {
189 /* Fast path for mark and sweep garbage collection */
190 if (file->f_count || file->f_blocks || file->f_shares)
191 return 1;
192 } else {
193 if (nlmsvc_traverse_blocks(host, file, action)
194 || nlmsvc_traverse_shares(host, file, action))
195 return 1;
197 return nlm_traverse_locks(host, file, action);
201 * Loop over all files in the file table.
203 static int
204 nlm_traverse_files(struct nlm_host *host, int action)
206 struct nlm_file *file, **fp;
207 int i;
209 down(&nlm_file_sema);
210 for (i = 0; i < FILE_NRHASH; i++) {
211 fp = nlm_files + i;
212 while ((file = *fp) != NULL) {
213 /* Traverse locks, blocks and shares of this file
214 * and update file->f_locks count */
215 if (nlm_inspect_file(host, file, action)) {
216 up(&nlm_file_sema);
217 return 1;
220 /* No more references to this file. Let go of it. */
221 if (!file->f_blocks && !file->f_locks
222 && !file->f_shares && !file->f_count) {
223 *fp = file->f_next;
224 nlmsvc_ops->fclose(&file->f_file);
225 kfree(file);
226 } else {
227 fp = &file->f_next;
231 up(&nlm_file_sema);
232 return 0;
236 * Release file. If there are no more remote locks on this file,
237 * close it and free the handle.
239 * Note that we can't do proper reference counting without major
240 * contortions because the code in fs/locks.c creates, deletes and
241 * splits locks without notification. Our only way is to walk the
242 * entire lock list each time we remove a lock.
244 void
245 nlm_release_file(struct nlm_file *file)
247 dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
248 file, file->f_count);
250 /* Lock file table */
251 down(&nlm_file_sema);
253 /* If there are no more locks etc, delete the file */
254 if(--file->f_count == 0) {
255 if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK))
256 nlm_delete_file(file);
259 up(&nlm_file_sema);
263 * Mark all hosts that still hold resources
265 void
266 nlmsvc_mark_resources(void)
268 dprintk("lockd: nlmsvc_mark_resources\n");
270 nlm_traverse_files(NULL, NLM_ACT_MARK);
274 * Release all resources held by the given client
276 void
277 nlmsvc_free_host_resources(struct nlm_host *host)
279 dprintk("lockd: nlmsvc_free_host_resources\n");
281 if (nlm_traverse_files(host, NLM_ACT_UNLOCK))
282 printk(KERN_WARNING
283 "lockd: couldn't remove all locks held by %s",
284 host->h_name);
288 * Delete a client when the nfsd entry is removed.
290 void
291 nlmsvc_invalidate_client(struct svc_client *clnt)
293 struct nlm_host *host;
295 if ((host = nlm_lookup_host(clnt, NULL, 0, 0)) != NULL) {
296 dprintk("lockd: invalidating client for %s\n", host->h_name);
297 nlmsvc_free_host_resources(host);
298 host->h_expires = 0;
299 nlm_release_host(host);