linux-old/fs/lockd/svcsubs.c
<<
>>
Prefs
   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/sched.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)
  30static struct nlm_file *        nlm_files[FILE_NRHASH];
  31static DECLARE_MUTEX(nlm_file_sema);
  32
  33static 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 */
  51u32
  52nlm_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 (!memcmp(&file->f_handle, f, sizeof(*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
 100found:
 101        dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
 102        *result = file;
 103        file->f_count++;
 104        nfserr = 0;
 105
 106out_unlock:
 107        up(&nlm_file_sema);
 108        return nfserr;
 109
 110out_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 */
 124static inline void
 125nlm_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                kdevname(inode->i_dev), 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 */
 150static int
 151nlm_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
 157again:
 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) < 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 */
 194static inline int
 195nlm_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 */
 212static int
 213nlm_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 */
 253void
 254nlm_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 */
 274void
 275nlmsvc_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 */
 285void
 286nlmsvc_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 a client when the nfsd entry is removed.
 298 */
 299void
 300nlmsvc_invalidate_client(struct svc_client *clnt)
 301{
 302        struct nlm_host *host;
 303
 304        if ((host = nlm_lookup_host(clnt, NULL, 0, 0)) != NULL) {
 305                dprintk("lockd: invalidating client for %s\n", host->h_name);
 306                nlmsvc_free_host_resources(host);
 307                host->h_expires = 0;
 308                host->h_killed = 1;
 309                nlm_release_host(host);
 310        }
 311}
 312
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.