linux-old/fs/lockd/clntlock.c
<<
>>
Prefs
   1/*
   2 * linux/fs/lockd/clntlock.c
   3 *
   4 * Lock handling for the client side NLM implementation
   5 *
   6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
   7 */
   8
   9#define __KERNEL_SYSCALLS__
  10
  11#include <linux/module.h>
  12#include <linux/types.h>
  13#include <linux/sched.h>
  14#include <linux/nfs_fs.h>
  15#include <linux/unistd.h>
  16#include <linux/sunrpc/clnt.h>
  17#include <linux/sunrpc/svc.h>
  18#include <linux/lockd/lockd.h>
  19#include <linux/smp_lock.h>
  20
  21#define NLMDBG_FACILITY         NLMDBG_CLIENT
  22
  23/*
  24 * Local function prototypes
  25 */
  26static int                      reclaimer(void *ptr);
  27
  28/*
  29 * The following functions handle blocking and granting from the
  30 * client perspective.
  31 */
  32
  33/*
  34 * This is the representation of a blocked client lock.
  35 */
  36struct nlm_wait {
  37        struct nlm_wait *       b_next;         /* linked list */
  38        wait_queue_head_t       b_wait;         /* where to wait on */
  39        struct nlm_host *       b_host;
  40        struct file_lock *      b_lock;         /* local file lock */
  41        unsigned short          b_reclaim;      /* got to reclaim lock */
  42        u32                     b_status;       /* grant callback status */
  43};
  44
  45static struct nlm_wait *        nlm_blocked;
  46
  47/*
  48 * Block on a lock
  49 */
  50int
  51nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
  52{
  53        struct nlm_wait block, **head;
  54        int             err;
  55        u32             pstate;
  56
  57        block.b_host   = host;
  58        block.b_lock   = fl;
  59        init_waitqueue_head(&block.b_wait);
  60        block.b_status = NLM_LCK_BLOCKED;
  61        block.b_next   = nlm_blocked;
  62        nlm_blocked    = &block;
  63
  64        /* Remember pseudo nsm state */
  65        pstate = host->h_state;
  66
  67        /* Go to sleep waiting for GRANT callback. Some servers seem
  68         * to lose callbacks, however, so we're going to poll from
  69         * time to time just to make sure.
  70         *
  71         * For now, the retry frequency is pretty high; normally 
  72         * a 1 minute timeout would do. See the comment before
  73         * nlmclnt_lock for an explanation.
  74         */
  75        sleep_on_timeout(&block.b_wait, 30*HZ);
  76
  77        for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
  78                if (*head == &block) {
  79                        *head = block.b_next;
  80                        break;
  81                }
  82        }
  83
  84        if (!signalled()) {
  85                *statp = block.b_status;
  86                return 0;
  87        }
  88
  89        /* Okay, we were interrupted. Cancel the pending request
  90         * unless the server has rebooted.
  91         */
  92        if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0)
  93                printk(KERN_NOTICE
  94                        "lockd: CANCEL call failed (errno %d)\n", -err);
  95
  96        return -ERESTARTSYS;
  97}
  98
  99/*
 100 * The server lockd has called us back to tell us the lock was granted
 101 */
 102u32
 103nlmclnt_grant(struct nlm_lock *lock)
 104{
 105        struct nlm_wait *block;
 106
 107        /*
 108         * Look up blocked request based on arguments. 
 109         * Warning: must not use cookie to match it!
 110         */
 111        for (block = nlm_blocked; block; block = block->b_next) {
 112                if (nlm_compare_locks(block->b_lock, &lock->fl))
 113                        break;
 114        }
 115
 116        /* Ooops, no blocked request found. */
 117        if (block == NULL)
 118                return nlm_lck_denied;
 119
 120        /* Alright, we found the lock. Set the return status and
 121         * wake up the caller.
 122         */
 123        block->b_status = NLM_LCK_GRANTED;
 124        wake_up(&block->b_wait);
 125
 126        return nlm_granted;
 127}
 128
 129/*
 130 * The following procedures deal with the recovery of locks after a
 131 * server crash.
 132 */
 133
 134/*
 135 * Mark the locks for reclaiming.
 136 * FIXME: In 2.5 we don't want to iterate through any global file_lock_list.
 137 *        Maintain NLM lock reclaiming lists in the nlm_host instead.
 138 */
 139static
 140void nlmclnt_mark_reclaim(struct nlm_host *host)
 141{
 142        struct file_lock *fl;
 143        struct inode *inode;
 144        struct list_head *tmp;
 145
 146        list_for_each(tmp, &file_lock_list) {
 147                fl = list_entry(tmp, struct file_lock, fl_link);
 148
 149                inode = fl->fl_file->f_dentry->d_inode;
 150                if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
 151                        continue;
 152                if (fl->fl_u.nfs_fl.host != host)
 153                        continue;
 154                if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED))
 155                        continue;
 156                fl->fl_u.nfs_fl.flags |= NFS_LCK_RECLAIM;
 157        }
 158}
 159
 160/*
 161 * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number,
 162 * that we mark locks for reclaiming, and that we bump the pseudo NSM state.
 163 */
 164static inline
 165void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate)
 166{
 167        host->h_monitored = 0;
 168        host->h_nsmstate = newstate;
 169        host->h_state++;
 170        host->h_nextrebind = 0;
 171        nlm_rebind_host(host);
 172        nlmclnt_mark_reclaim(host);
 173        dprintk("NLM: reclaiming locks for host %s", host->h_name);
 174}
 175
 176/*
 177 * Reclaim all locks on server host. We do this by spawning a separate
 178 * reclaimer thread.
 179 */
 180void
 181nlmclnt_recovery(struct nlm_host *host, u32 newstate)
 182{
 183        if (host->h_reclaiming++) {
 184                if (host->h_nsmstate == newstate)
 185                        return;
 186                nlmclnt_prepare_reclaim(host, newstate);
 187        } else {
 188                nlmclnt_prepare_reclaim(host, newstate);
 189                nlm_get_host(host);
 190                MOD_INC_USE_COUNT;
 191                if (kernel_thread(reclaimer, host, CLONE_SIGNAL) < 0)
 192                        MOD_DEC_USE_COUNT;
 193        }
 194}
 195
 196static int
 197reclaimer(void *ptr)
 198{
 199        struct nlm_host   *host = (struct nlm_host *) ptr;
 200        struct nlm_wait   *block;
 201        struct list_head *tmp;
 202        struct file_lock *fl;
 203        struct inode *inode;
 204
 205        daemonize();
 206        reparent_to_init();
 207        snprintf(current->comm, sizeof(current->comm),
 208                 "%s-reclaim",
 209                 host->h_name);
 210
 211        /* This one ensures that our parent doesn't terminate while the
 212         * reclaim is in progress */
 213        lock_kernel();
 214        lockd_up();
 215
 216        /* First, reclaim all locks that have been marked. */
 217restart:
 218        list_for_each(tmp, &file_lock_list) {
 219                fl = list_entry(tmp, struct file_lock, fl_link);
 220
 221                inode = fl->fl_file->f_dentry->d_inode;
 222                if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
 223                        continue;
 224                if (fl->fl_u.nfs_fl.host != host)
 225                        continue;
 226                if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM))
 227                        continue;
 228
 229                fl->fl_u.nfs_fl.flags &= ~NFS_LCK_RECLAIM;
 230                nlmclnt_reclaim(host, fl);
 231                if (signalled())
 232                        break;
 233                goto restart;
 234        }
 235
 236        host->h_reclaiming = 0;
 237        wake_up(&host->h_gracewait);
 238
 239        /* Now, wake up all processes that sleep on a blocked lock */
 240        for (block = nlm_blocked; block; block = block->b_next) {
 241                if (block->b_host == host) {
 242                        block->b_status = NLM_LCK_DENIED_GRACE_PERIOD;
 243                        wake_up(&block->b_wait);
 244                }
 245        }
 246
 247        /* Release host handle after use */
 248        nlm_release_host(host);
 249        lockd_down();
 250        unlock_kernel();
 251        MOD_DEC_USE_COUNT;
 252
 253        return 0;
 254}
 255
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.